用户工具

站点工具


kdasmdoc

坑爹的asm官方文档

因为工作需要,想对jedis的代码做字节码增强,然后吭哧吭哧填入如下代码:

public class Instrumentor {
 
    @SuppressWarnings("rawtypes")
    public static byte[] insr(Class targetClass) throws IOException {
        // 读取class
        ClassReader cr = new ClassReader(targetClass.getName());
        ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
        AddTimerAdapter cp = new AddTimerAdapter(cw);
        cr.accept(cp, 0);
        return cw.toByteArray();
    }
}

由于仅仅是想做下测试能否跑通,就参照asm的官方文档搞了个classloader:

class MyClassLoader extends ClassLoader {
          public Class defineClass(String name, byte[] b) {
            return defineClass(name, b, 0, b.length);
          }
}

然后测试代码:

Instrumentor inst = new Instrumentor();
 ClassLoader loader = new MyClassloader(Thread.currentThread().getContextClassLoader());
        // Class<?> clzz = loader.loadClass("Test1");
        Class<?> clzz = loader.defineClass("redis.clients.jedis.Jedis",inst.insr(Jedis.class));
        Object redisObj = clzz.getConstructor(String.class, int.class).newInstance("localhost",
                6379);
        Method get = clzz.getMethod("get", new Class[] { String.class });
        get.invoke(redisObj, new Object[] { "test" });

结果他妈的报错:

Caused by: java.lang.IllegalAccessError: tried to access method redis.clients.jedis.Connection.sendCommand(Lredis/clients/jedis/Protocol$Command;[Ljava/lang/String;)Lredis/clients/jedis/Connection; from class redis.clients.jedis.Jedis

at redis.clients.jedis.Jedis.get(Jedis.java:98)
... 24 more

我日,debug进去发现是个protected方法:

protected Connection sendCommand(final Command cmd, final String... args) {

莫非是权限问题?查看了ASM整个官方文档以及API说明,根本没地方设置。咋整呢?

构建了测试代码:

public class Test1 {
    protected Test2 t2 = new Test2();
 
    public void get(String str){
        t2.get2(str);
        System.out.println("haha"+str);
    }
 
}
 
public class Test2 {
 
    protected void get1(String str){
        System.out.println("haha22"+str);
    }
 
    public void get2(String str){
        System.out.println("haha22"+str);
    }
 
}

用那个该死的myclassloader来装载,发现果然test1.get2是好的,test1.get1报那个该死的:java.lang.IllegalAccessError

然后就怀疑classloader根本就不应该直接调用defineClass,还是老老实实的重载:findClass

public class MyClassloader extends ClassLoader {
 
    public MyClassloader(ClassLoader parent) {
        super(parent);
    }
 
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        if ("redis.clients.jedis.Jedis".equals(name)) {
            ClassReader cr;
            try {
                cr = new ClassReader(name);
                ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
                AddTimerAdapter cp = new AddTimerAdapter(cw);
                cr.accept(cp, 0);
                return defineClass(name, cw.toByteArray(), 0, cw.toByteArray().length);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
 
        }
        return super.findClass(name);
    }
 
}

结果他娘的好了,卧槽,坑爹啊,网上一搜,居然大家都用的那个官方的defineClass,难道没有在复杂场景测试过吗?

kdasmdoc.txt · 最后更改: 2018/10/14 15:31 (外部编辑)