问题记录:想要替换别人的代码,可是没办法或不能修改别人的代码

这是我第一篇博客,谢谢指教!!!!!!!!!!!java

 

如下的代码用到asm5.0.4api

maven:框架

<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>5.0.4</version>
</dependency>

或者手动下载:http://central.maven.org/maven2/org/ow2/asm/asm/5.0.4/asm-5.0.4.jarmaven

 

一开始,我用asm和自定义classloader的方法,成功动态加载了修改过的class(asm怎么用我就不说了,之后有空我可能会写些相关内容)。ide

先定义一个咱们用来修改的类this

public class TestAop {
    public void halloAop() {
        System.out.println("Hello Aop");
    }
}

而后是我实现的代码code

public class AopClassLoader extends ClassLoader implements Opcodes {
    public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, InstantiationException {
        new AopClassLoader("util.TestAop").loadClass("util.TestAop");
        new TestAop().halloAop();
    }
    public static void beforeInvoke() {
        System.out.println("before");
    }
    public static void afterInvoke() {
        System.out.println("after");
    }
    
    public AopClassLoader(ClassLoader parent, String... classes) {
        super(parent);
        this.parent = parent;
        this.classes = classes;
    }
    public AopClassLoader(String... classes) {
        this(Thread.currentThread().getContextClassLoader(), classes);
    }
    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        boolean tag = true;
        for (String clazz: classes) {
            if (clazz.equals(name)) {
                tag = false;
            }
        }
        if (tag) {
            return super.loadClass(name);
        }
        try {
            ClassWriter cw = new ClassWriter(0);
            ClassReader reader = new ClassReader(name);
            reader.accept(new AopClassAdapter(ASM5, cw), 0);
            
            byte[] code = cw.toByteArray();
            
            Method method = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
            method.setAccessible(true);
            
            return (Class<?>)method.invoke(parent, name, code, 0, code.length);
        } catch (Throwable e) {
            e.printStackTrace();
            throw new ClassNotFoundException();
        }
    }
    private class AopClassAdapter extends ClassVisitor implements Opcodes {
        AopClassAdapter(int api, ClassVisitor cv) {
            super(api, cv);
        }
        @Override
        public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
            MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
            if ("<init>".equals(name)) {
                return mv;
            }
            return new AopMethod(this.api, mv);
        }
        private class AopMethod extends MethodVisitor implements Opcodes {
            AopMethod(int api, MethodVisitor mv) {
                super(api, mv);
            }
            @Override
            public void visitCode() {
                super.visitCode();
                this.visitMethodInsn(INVOKESTATIC, AopClassLoader.class.getName().replace(".", "/"), "beforeInvoke", "()V", false);
            }
            @Override
            public void visitInsn(int opcode) {
                if (opcode == RETURN) {
                    mv.visitMethodInsn(INVOKESTATIC, AopClassLoader.class.getName().replace(".", "/"), "afterInvoke", "()V", false);
                }
                super.visitInsn(opcode);
            }
        }
    }
    private ClassLoader parent;
    private String[] classes;
}

网上也有很多相似的代码,上面这份代码虽然不是个人最终答卷,可是这份代码也算是实现的比较好的代码了,为何这么说呢,由于是用自定义的classloader去load这个咱们要修改的类,原本是没办法直接调用helloAop,只能用反射的方式调用的(被不一样的classloader加载的类,是不可以互相转化的,也就是说,没办法强制转化成TestAop,固然就不可以当作TestAop的对象去执行方法了。ps:代码写到TestAop.class的时候,就被系统classloader给加载了,而咱们的类是被咱们自定义的classloader加载的)。可是,上面的代码强制转化成功了!由于,我新建(能够当作是新建了一个class)的这个class,继承了原来的TestAop(注意这一句return super.loadClass(name),也就是TestAop是被父类的也就是系统classloader加载的,若是去看源代码,其实父类调用了parent去加载这个TestAop,也就是说TestAop被加载一次,可是同时两个classloader加载进去了,这个跟被两个classloader分别加载是有区别的),因此,咱们能够把它强制转化成TestAop的对象。xml

 

可是,这样还不能在运行时修改别人的类啊,由于不能用new的方法获取我自定义class的对象,而后我就开始想办法替换系统classloader(其实有些框架,在调用classloader时是使用Thread.currentThread().getContextClassLoader()来获取classloader,这个时候就能够提早经过Thread.currentThread().setContextClassLoader(cl)去设置classloader,这样某些框架获取到的classloader就是咱们自定义的classloader,当别人经过这个classloader读取到的类,就是变成咱们写的类。可是,大多数框架,并不会手动去load一个类,就算去load了,也是new出本身要的对象,问题是刚才说了,如今new出来对象并非咱们自定义class的实例)。可是,我找了不少办法,并无一个通用的方法去替换系统classloader。对象

 

而后我又想了一个方法,能不能,直接用系统classloader去加载咱们自定义的类。具体方法就是,仍是用asm去自定义类并生成byte[],可是这个时候咱们用系统classloader去defineClass(方法是私有的,须要用反射去执行),封装了以后,具体代码在下面。继承

public class AopClassLoader extends ClassLoader implements Opcodes {
	public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, InstantiationException {
		new AopClassLoader("test1.TestAop").loadClass("test1.TestAop");
		new TestAop().halloAop();
	}
    public static void beforeInvoke() {
        System.out.println("before");
    };
    public static void afterInvoke() {
        System.out.println("after");
    };
    public AopClassLoader(ClassLoader parent, String... classes) {
        super(parent);
        this.parent = parent;
        this.classes = classes;
    }
    public AopClassLoader(String... classes) {
        this(Thread.currentThread().getContextClassLoader(), classes);
    }
    public Class<?> loadClass(String name) throws ClassNotFoundException {
    	boolean tag = true;
    	for (String clazz: classes) {
    		if (clazz.equals(name))
    			tag = false;
    	}
    	if (tag)
    		return super.loadClass(name);
        try {
            ClassWriter cw = new ClassWriter(0);
            ClassReader reader = new ClassReader(name);
            reader.accept(new AopClassAdapter(ASM5, cw), 0);
            
            byte[] code = cw.toByteArray();
            
            Method method = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
            method.setAccessible(true);
            
            return (Class<?>)method.invoke(parent, name, code, 0, code.length);
        } catch (Throwable e) {
        	e.printStackTrace();
            throw new ClassNotFoundException();
        }
    }
    private class AopClassAdapter extends ClassVisitor implements Opcodes {
        AopClassAdapter(int api, ClassVisitor cv) {
            super(api, cv);
        }
        public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
            MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
            if ("<init>".equals(name))
            	return mv;
            return new AopMethod(this.api, mv);
        }
        private class AopMethod extends MethodVisitor implements Opcodes {
            AopMethod(int api, MethodVisitor mv) {
                super(api, mv);
            }
            public void visitCode() {
                super.visitCode();
                this.visitMethodInsn(INVOKESTATIC, AopClassLoader.class.getName().replace(".", "/"), "beforeInvoke", "()V", false);
            }
            public void visitInsn(int opcode) {
            	if (opcode == RETURN)
            		mv.visitMethodInsn(INVOKESTATIC, AopClassLoader.class.getName().replace(".", "/"), "afterInvoke", "()V", false);
            	super.visitInsn(opcode);
            }
        }
    }
    private ClassLoader parent;
    private String[] classes;
}

解决,成功new出来咱们自定义的类的对象!!!!!!!!

不少细节,慢慢琢磨,不懂再问,但但愿本身先琢磨。

ps:注意,上面我没有用TestAop.class.getName()而是用了"test1.TestAOp",刚刚也说过,若是代码写到了TestAop.class,其实TestAop就已经被系统classloader加载了,这个时候,咱们还调用系统classloader去defineclass类TestAop,会报错。

 

转载请注明出处!

相关文章
相关标签/搜索