[编织消息框架][JAVA核心技术]动态代理应用5-javassist

基础部份:

修改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 }

 

native: 177 native: 183 native: 128 native: 171 native: 127 native: 141 native: 184 native: 126 native: 127 native: 127 native: 127 native: 126 native: 127 native: 126 native: 127
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

posted @ 2017-04-06 17:00  solq321  阅读(325)  评论(0编辑  收藏  举报