博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
问题记录:想要替换别人的代码,但是没办法或不能修改别人的代码
阅读量:5884 次
发布时间:2019-06-19

本文共 7761 字,大约阅读时间需要 25 分钟。

hot3.png

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

 

以下的代码用到asm5.0.4

maven:

org.ow2.asm
asm
5.0.4

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

 

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

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

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

然后是我实现的代码

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 ("
".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的对象。

 

但是,这样还不能在运行时修改别人的类啊,因为不能用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 ("
".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,会报错。

 

转载请注明出处!

转载于:https://my.oschina.net/u/3056071/blog/790403

你可能感兴趣的文章
==和equals区别
查看>>
2010技术应用计划
查看>>
XML 节点类型
查看>>
驯服 Tiger: 并发集合 超越 Map、Collection、List 和 Set
查看>>
Winform开发框架之权限管理系统改进的经验总结(3)-系统登录黑白名单的实现...
查看>>
LeetCode – LRU Cache (Java)
查看>>
JavaScript高级程序设计--对象,数组(栈方法,队列方法,重排序方法,迭代方法)...
查看>>
【转】 学习ios(必看经典)牛人40天精通iOS开发的学习方法【2015.12.2
查看>>
在 ASP.NET MVC 中使用异步控制器
查看>>
SQL语句的执行过程
查看>>
详解Linux中Load average负载
查看>>
HTTP 协议 Cache-Control 头——性能啊~~~
查看>>
PHP遍历文件夹及子文件夹所有文件
查看>>
WinForm程序中两份mdf文件问题的解决
查看>>
程序计数器、反汇编工具
查看>>
Android N: jack server failed
查看>>
如何将lotus 通讯簿导入到outlook 2003中
查看>>
WinForm 应用程序中开启新的进程及控制
查看>>
js replace,正则截取字符串内容
查看>>
Thinkphp5笔记三:创建基类
查看>>