基础部份:
修改class我们用到javassist,在pom.xml添加
<properties> <javassist.version>3.18.2-GA</javassist.version> </properties> <dependency> <groupId>org.javassist</groupId> <artifactId>javassist</artifactId> <version>${javassist.version}</version> </dependency>
1.ClassPool 负责加载CtClass,其中可添加ClassPath,优先级从父ClassPath加载
2.CtClass 处理class信息 从ClassPool make
3.CtField 处理field信息
4.CtMethod 处理method信息
5.CtConstructor 处理constructor信息
ClassPool 源码浅读:
AccessController.doPrivileged 方法能够使一段受信任代码获得更大的权限,甚至比调用它的应用程序还要多,可做到临时访问更多的资源,
由于技术内容跟本书无太大关系,我们只关心当抛出java.security.AccessControlException异常时,就考虑添加上去即可
实现部份:
1 public class TestJavassist { 2 public interface TestObject { 3 public void a(int a, String b); 4 5 public void setAge(int value); 6 7 public int getAge(); 8 } 9 10 public static void main(String[] args) throws Exception { 11 String proxyClassName = TestObject.class.getCanonicalName() + "$$$$"; 12 ClassPool classPool = ClassPool.getDefault(); 13 CtClass ctClass = classPool.makeClass(proxyClassName); 14 15 // 设置接口 16 CtClass[] interfaces = new CtClass[1]; 17 interfaces[0] = classPool.get(TestObject.class.getName()); 18 ctClass.setInterfaces(interfaces); 19 20 // 添加字段 21 CtField ctField = new CtField(classPool.get(int.class.getName()), "age", ctClass); 22 // 设置field属性 23 ctField.setModifiers(Modifier.PRIVATE); 24 ctClass.addField(ctField); 25 26 // 添加 age getter'setter方法 27 String setbody = "{this.age = $1;}"; 28 String getbody = "{return this.age;}"; 29 for (Method method : TestObject.class.getDeclaredMethods()) { 30 String body = null; 31 switch (method.getName()) { 32 case "setAge": 33 body = setbody; 34 break; 35 case "getAge": 36 body = getbody; 37 break; 38 case "a": 39 body = "{System.out.println($1);System.out.println($2);}"; 40 break; 41 default: 42 continue; 43 } 44 45 Class<?> returnType = method.getReturnType(); 46 // 转换参数CtClass 47 CtClass[] parameterCtClass = new CtClass[method.getParameterTypes().length]; 48 for (int i = 0; i < method.getParameterTypes().length; i++) { 49 parameterCtClass[i] = classPool.get(method.getParameterTypes()[i].getName()); 50 } 51 CtMethod ctMethod = new CtMethod(classPool.get(returnType.getName()), method.getName(), parameterCtClass, ctClass); 52 ctMethod.setModifiers(method.getModifiers()); 53 ctMethod.setBody(body); 54 ctClass.addMethod(ctMethod); 55 56 } 57 // 添加构造 58 ctClass.addConstructor(CtNewConstructor.defaultConstructor(ctClass)); 59 60 Class<?> clz = ctClass.toClass(); 61 TestObject obj = (TestObject) clz.newInstance(); 62 obj.setAge(30); 63 System.out.println("age : " + obj.getAge()); 64 obj.a(111, "bbb"); 65 66 // ctClass.writeFile("f:/test.class"); 67 } 68 }
源码讲解
1.先通过classPool.makeClass 创建新的类,名称是原来类+自定义标记
2.所有的java类型必须转换成CtClass
3.要给创建的自定义类CtClass 添加接口类或继承父类,保留原类型
4.创建方法时特别注意处理java类型
5.可添加构造方法
下面给出常用的参数对照表,注意:在不同域使用不同有的少点,不过都大同小异
接着在之前的TestProxy 测试添加 testJavassist 看下执行效率,速度与native相差不大,原因是生成的指令很少,感兴趣的话通过 ctClass.writeFile()保存class 然后再用javap查看生成指令数
1 public static void testJavassist() throws Exception { 2 String proxyClassName = UserService.class.getCanonicalName() + "$$$$"; 3 4 ClassPool classPool = ClassPool.getDefault(); 5 CtClass ctClass = classPool.makeClass(proxyClassName); 6 7 // 设置接口 8 CtClass[] interfaces = new CtClass[1]; 9 interfaces[0] = classPool.get(UserService.class.getName()); 10 ctClass.setInterfaces(interfaces); 11 12 // 添加 方法 13 for (Method method : UserService.class.getDeclaredMethods()) { 14 String body = null; 15 switch (method.getName()) { 16 case "getName": 17 body = "{return $1 + "";}"; 18 break; 19 case "getAge": 20 body = "{return ($w)$1;}"; 21 break; 22 default: 23 continue; 24 } 25 26 Class<?> returnType = method.getReturnType(); 27 // 转换参数CtClass 28 CtClass[] parameterCtClass = new CtClass[method.getParameterTypes().length]; 29 for (int i = 0; i < method.getParameterTypes().length; i++) { 30 parameterCtClass[i] = classPool.get(method.getParameterTypes()[i].getName()); 31 } 32 CtMethod ctMethod = new CtMethod(classPool.get(returnType.getName()), method.getName(), parameterCtClass, ctClass); 33 ctMethod.setModifiers(method.getModifiers()); 34 ctMethod.setBody(body); 35 ctClass.addMethod(ctMethod); 36 37 } 38 // 添加构造 39 ctClass.addConstructor(CtNewConstructor.defaultConstructor(ctClass)); 40 Class<?> clz = ctClass.toClass(); 41 UserService proxy = (UserService) clz.newInstance(); 42 run("javassist", proxy); 43 }
jdk: 214 jdk: 169 jdk: 169 jdk: 169 jdk: 169 jdk: 169 jdk: 170 jdk: 169 jdk: 170 jdk: 169 jdk: 169 jdk: 171 jdk: 170 jdk: 169 jdk: 170
javassist: 146 javassist: 139 javassist: 140 javassist: 140 javassist: 140 javassist: 139 javassist: 140 javassist: 140 javassist: 138 javassist: 140 javassist: 140 javassist: 141 javassist: 139 javassist: 139 javassist: 139
资料来源 :http://jboss-javassist.github.io/javassist/tutorial/tutorial2.html#runtime
http://wsmajunfeng.iteye.com/blog/1912983