Java代理
来源:互联网 发布:淘宝售后职责 编辑:程序博客网 时间:2024/06/11 01:00
突然想了解下java动态修改类执行行为的知识。
1. 首先看了下AspectJ,它可以通过静态植入在不改变原java代码点情况下修改代码的执行逻辑。启中cflow功能更强大更灵活。cflow一般指的是一个方法里的所有代码的片段切换。
一、Java Instrumentation。 详细讲解可参考http://blog.csdn.net/productshop/article/details/50623626
premain方式(>=JDK1.5)。应用启动时需要通过-javaagent:xxx.jar指定代理类,且jar包含META-INF/MANIFEST.MF
Manifest-Version: 1.0
Premain-Class:com.pre.PreMain
Can-Redefine-Classes: true
在使用的时候遇到几个问题
问题1:使用redefineClasses方式时,Caused by: java.lang.UnsupportedOperationException: redefineClasses is not supported in this environment
通过在MANIFEST.MF增加配置Can-Redefine-Classes: true解决
问题2:使用addTransformer 方式时,报java.lang.NoClassDefFoundError: com/TransClass (wrong name: com/TransClass2)
这个主要是由于要替换的class为TransClass2,跟被替换的TransClass不匹配导致,后修一致改为TransClass解决
agentmain方式(>=JDK1.6),应用正常启动,代理类通过attach方式追加到应用系统
Manifest-Version: 1.0
Can-Retransform-Classes: true
Agent-Class: com.agent.AgentMain
问题1:Caused by: java.lang.UnsupportedOperationException: adding retransformable transformers is not supported in this environment
通过在MANIFEST.MF增加配置Can-Retransform-Classes: true解决
二、java代理
Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
自动生成实现interfaces的java代理类,然后通过loader加载该代理类。实现没个接口的方法,调用h的
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable方法。主要的业务逻辑在InvocationHandler的invoke方法中实现。自我感觉proxy这个参数意义不大,至少还没有发现应用场景
代理是基于接口的,代理生成的对象也是接口。开发时首先定义需要代理的接口,然后在InvocationHandler定义代理的业务逻辑。业务逻辑可以实现代理接口,也可以与代理接口无关
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");是的动态代理的class文件保存在磁盘
三、ASM
下载地址http://forge.ow2.org/project/showfiles.php?group_id=23&release_id=5902
public class Account {public void foo(){operation();}public void operation(){System.out.println("operation...");}public static void main(String[] args) throws Exception{Checker.check();}}public class AddCheckClassAdapter extends ClassAdapter {public AddCheckClassAdapter(ClassVisitor arg0) {super(arg0);} public MethodVisitor visitMethod(final int access, final String name, final String desc,final String signature, final String[] exceptions){MethodVisitor mv = super.visitMethod(access, name, desc, signature,exceptions);MethodVisitor wrappedMv = mv; if (mv != null) { // 对于 "operation" 方法 if (name.equals("operation")) { // 使用自定义 MethodVisitor,实际改写方法内容 wrappedMv = new AddCheckMethodAdapter(mv); } } return wrappedMv; }}public class AddCheckMethodAdapter extends MethodAdapter {public AddCheckMethodAdapter(MethodVisitor mv) {super(mv);// TODO Auto-generated constructor stub}public void visitCode() {visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Checker.class), "check", "()V"); } }public class Generator {public static void main(String[] args) throws Exception { ClassReader cr = new ClassReader(Account.class.getName()); ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); ClassAdapter classAdapter = new AddCheckClassAdapter(cw); cr.accept(classAdapter, ClassReader.SKIP_DEBUG); byte[] data = cw.toByteArray(); File file = new File("com/asm/Account.class"); FileOutputStream fout = new FileOutputStream(file); fout.write(data); fout.close(); }}public class Main {public static void main(String[] args) throws Exception{Account account = new Account(); account.foo();}}
首先执行Generator,再执行Main会发现代码已经被修改,使用反编译工具也会看到Account的operation已经被插入。
问题1:Exception in thread "main" java.lang.NoClassDefFoundError: com.Checker
at com.asm.Account.operation(Unknown Source)
at com.asm.Account.foo(Unknown Source)
at Main.main(Main.java:6)
主要是没有使用/格式的类名字,而是使用了“.”,使用Type.getInternalName(Checker.class)转换后解决
asm还可以实现不生成class文件进行动态代理
4:cglib
cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
package com.cglib;public interface Test {public void test();}package com.cglib;public class TestImpl implements Test {public void test(){System.out.println("test");}}package com.cglib;public class TestMain {public static void main(String[] args) { TestProxy proxy = new TestProxy();Test test = (Test)proxy.getInstance(new TestImpl());test.test();}}package com.cglib;import java.lang.reflect.Method;import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; public class TestProxy implements MethodInterceptor{private Object target; public Object getInstance(Object target) { this.target = target; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(this.target.getClass()); // 回调方法 enhancer.setCallback(this); // 创建代理对象 return enhancer.create(); } public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("cglib 开始"); proxy.invokeSuper(obj, args); System.out.println("cglib 结束");return null;}}
5:spring cglib(同cglib)
package com.spring;public interface Test {public String test();}package com.spring;public class TestImpl implements Test {public String test(){System.out.println("test");return "test result";}}package com.spring;import java.lang.reflect.Method;import org.springframework.cglib.proxy.Enhancer;import org.springframework.cglib.proxy.MethodInterceptor;import org.springframework.cglib.proxy.MethodProxy;public class TestProxy implements MethodInterceptor{private Object target; public Object getInstance(Object target) { this.target = target; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(this.target.getClass()); // 回调方法 enhancer.setCallback(this); // 创建代理对象 return enhancer.create(); } public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("cglib 开始"); Object result = proxy.invokeSuper(obj, args); System.out.println("cglib 结束");return result;}}package com.spring;public class TestMain {public static void main(String[] args) { TestProxy proxy = new TestProxy();Test test = (Test)proxy.getInstance(new TestImpl());System.out.println(test.test());}}
6:javassist
比asm更加高级语言化的修改class。
package com.javassist;public interface TestJavassistInt {public void test();}package com.javassist;public class TestJavassist1 implements TestJavassistInt{public void test(){System.out.println("javassist test");}public static void main(String[] args)throws Exception{TestJavassist app = new TestJavassist();app.test();}}package com.javassist;public class TestJavassist implements TestJavassistInt{public void test(){System.out.println("javassist test");}public static void main(String[] args)throws Exception{TestJavassist app = new TestJavassist();app.test();}}package com.javassist;import javassist.ClassPool;import javassist.CtClass;import com.asm.ByteClassLoader;public class Generator {//public static void main(String[] args) throws Exception { // ClassPool pool = ClassPool.getDefault(); // //创建Programmer类 // CtClass cc= pool.makeClass("Programmer"); // //定义code方法 // CtMethod method = CtNewMethod.make("public static void main(String[] args)throws Exception{}", cc); // //插入方法代码 // method.insertBefore("System.out.println(\"I'm a Programmer,Just Coding.....\");"); // cc.addMethod(method); // //保存生成的字节码 // cc.writeFile("/Users/wangql/source/spring_core/ssh/ssh");// }public static void main(String[] args) throws Exception { ClassPool pool = ClassPool.getDefault(); //创建Programmer类 CtClass cc = pool.get(TestJavassist.class.getName()); cc.getMethod("test", "()V").insertBefore("System.out.println(\"Insert Javassist Test\");"); // cc.writeFile("/Users/wangql/source/spring_core/ssh/ssh/"); // ByteClassLoader loader = new ByteClassLoader();// Object obj = loader.defineClassFromClassFile("com.javassist.TestJavassist1",cc.toBytecode()).newInstance();// ((TestJavassist1)obj).test(); ByteClassLoader loader = new ByteClassLoader(); Class clazz = loader.defineClassFromClassFile("com.javassist.TestJavassist",cc.toBytecode()); Object o= clazz.newInstance(); try { //调用Programmer的code方法 clazz.getMethod("test", null).invoke(o, null); } catch (Exception e) { e.printStackTrace(); } }}package com.javassist;public class Main {public static void main(String[] args)throws Exception{TestJavassist app = new TestJavassist();app.test();}}
本来想通过ctclass的toBytecode方法在程序中直接转换class,但是失败,总提示classcast错误,想了想应该是由于两个类的classloader不属于同一个造成的,但是转化为interface的化是可以的,因为interface是用的同一个classloader。可以通过反射来执行
- Java 代理,动态代理
- java代理静态代理
- Java代理
- java代理
- java代理
- java 代理
- java代理
- JAVA 代理
- Java----代理
- java代理
- java代理
- java代理
- java代理
- java 代理
- java 代理
- java代理
- java代理
- Java----代理
- SCI、UART与RS232区别
- 大数据分页功能 的一种实现方式
- TP5怎么隐藏index.php
- Linux学习篇第三章之~squid
- Quartz定时任务简单demo及与Spring整合
- Java代理
- What's New on Java 7 Phaser
- Android 使用Ant+Emma+Junit收集android代码覆盖率(来自联想的一位同事2012.12.24)
- Could not publish to the server.Please assign JRE to the server
- js中 var a = function(){} 与function b(){}两种函数声明的区别
- java-service-wrapper 社区版本(windows X64)
- React-Native到0.44版本后Navigator 不能用的问题
- 搭建JEESZ分布式架构1--CentOs下安装jdk7(环境准备)
- 哈希表