Javassist实现动态代理

阅读更多

Javassist实现动态代理

 

动态代理模式简述:之所以会出现代理这种模式就是因为我们常有这么一种需求:在被代理类的方法调用前后执行一些其它的逻辑,这些逻辑不适合由被代理类来实现,那这些逻辑谁来实现?当然是代理类。那代理类是谁?从哪里来?代理类是我们利用字节码生成工具动态创建的,然后利用反射实例化而得到代理对象。

 

tips:这篇文章讲的不是动态代理模式的思想而是怎么实现(简单实现)一个和java.lang.reflect.Proxy类相似的类。

 

按照JDK动态代理的套路来,先定义一个InvocationHandler来给客户端添加代理的逻辑(这个类和JDK中的一模一样):

 

 

Java代码 
  1. package cc.lixiaohui.demo.javassist.proxy;  
  2.   
  3. import java.lang.reflect.Method;  
  4.   
  5. /** 
  6.  * @author lixiaohui 
  7.  * @date 2016年9月27日 下午9:53:59 
  8.  *  
  9.  */  
  10. public interface InvocationHandler {  
  11.       
  12.     /** 
  13.      * 业务逻辑填充 
  14.      *  
  15.      * @param proxy 生成的代理对象 
  16.      * @param method 调用的方法 
  17.      * @param args 调用该方法的参数 
  18.      * @return 调用该方法的返回值 
  19.      * @throws Throwable throws if any exception 
  20.      */  
  21.     Object invoke(Object proxy, Method method, Object[] args) throws Throwable;  
  22.       
  23. }  

 

 

接下来是Proxy类,代理类由该类动态生成并且都继承自该类,和JDK的一样,这也是为什么JDK的动态代理只能代理接口而不能代理父类,这里我就按照JDK的套路来,当然理解了这个后要实现代理父类也没什么难度了。Proxy类是怎么生成类的呢?首先代理的是接口,所以先遍历所有接口,再遍历接口的所有方法,为代理类生成与这些方法同签名同返回值的方法,也就是相当于实现(implement)这些接口的方法,至于生成过程具体是怎么样的我们不用管这也是用javassist的好处(字节码是怎么生成的是javassist的事,我们只需要用它的API即可)。

 

 

Java代码 
  1. package cc.lixiaohui.demo.javassist.proxy;  
  2.   
  3. import java.util.Objects;  
  4. import java.util.concurrent.atomic.AtomicInteger;  
  5.   
  6. import javassist.CannotCompileException;  
  7. import javassist.ClassPool;  
  8. import javassist.CtClass;  
  9. import javassist.CtConstructor;  
  10. import javassist.CtField;  
  11. import javassist.CtMethod;  
  12. import javassist.Modifier;  
  13. import javassist.NotFoundException;  
  14.   
  15. import org.slf4j.Logger;  
  16. import org.slf4j.LoggerFactory;  
  17.   
  18. import cc.lixiaohui.demo.javassist.proxy.util.CompoundKeyWeakHashMap;  
  19.   
  20. /** 
  21.  * 负责代理类类的生成 
  22.  *  
  23.  * 
       
    •  * 生成的代理类: 
    •  * 
    • public final 修饰(和JDK动态代理不同的是:JDK生成的代理类也是final的, 但不一定是public的, 当所代理的接口中有至少一个以上的接口不是public时生成的代理就不是public的)
    •  
    •  *  
    •  * 
    • 类名以$Proxy为前缀, 后缀为数字, 如cc.lixiaohui.$Proxy0, cc.lixiaohui.$Proxy1...
    •  
    •  *  
    •  * 
    • 生成的代理类继承自{@link Proxy}
    •  
    •  *  
    •  * 
    • 生成的代理类所在package只有一种情况下才是确定的: 当所有接口中有且只有一个接口是non-public时.其他情况所在package不确定
    •  
    •  * 
     
  24.  *   
  25.  * @author lixiaohui 
  26.  * @date 2016年9月27日 下午9:51:24 
  27.  *  
  28.  */  
  29. public class Proxy {  
  30.       
  31.     private static final Logger logger = LoggerFactory.getLogger(Proxy.class);  
  32.       
  33.     /** 
  34.      * 生成的代理类名前缀 
  35.      */  
  36.     private static final String PROXY_CLASSNAME_PREFIX = "$Proxy";  
  37.       
  38.     /** 
  39.      * 类后缀数字生成器 
  40.      */  
  41.     private static final AtomicInteger SUFFIX_GENERATOR = new AtomicInteger();   
  42.       
  43.     private static final boolean SHOULD_BE_FINAL = true;  
  44.     private static final boolean SHOULD_BE_ABSTRACT = false;  
  45.     private static final boolean SHOULD_BE_PUBLIC = true;  
  46.       
  47.     protected InvocationHandler invocationHandler;  
  48.       
  49.     /** 
  50.      * 弱引用已生成的Class的缓存, ClassLoader和被代理Class都相同时生成的代理Class才是相同的(这个类自己实现的,简单扩展一下java.util.Map就可以实现) 
  51.      * <类加载器, 被代理Class, 生成的代理Class> 
  52.      */  
  53.     private static CompoundKeyWeakHashMap, Class> proxyClassCache = new CompoundKeyWeakHashMap, Class>();  
  54.       
  55.     protected Proxy(InvocationHandler invocationHandler) {  
  56.         this.invocationHandler = invocationHandler;  
  57.     }  
  58.       
  59.     public static Object newProxyInstance(ClassLoader classLoader, Class targetClass, InvocationHandler invocationHandler)   
  60.             throws Exception {  
  61.         // check not null  
  62.         classLoader = Objects.requireNonNull(classLoader, "classLoader cannot be null");  
  63.         targetClass = Objects.requireNonNull(targetClass, "targetClass cannot be null");  
  64.         invocationHandler = Objects.requireNonNull(invocationHandler, "invocationHandler cannot be null");  
  65.           
  66.         Class proxyClass = proxyClassCache.get(classLoader, targetClass);  
  67.         // 有缓存  
  68.         if (proxyClass != null) {  
  69.             logger.debug("get proxy from cache");  
  70.             return proxyClass.getConstructor(InvocationHandler.class).newInstance(invocationHandler);  
  71.         }  
  72.           
  73.         // singleton instance of classpool   
  74.         ClassPool pool = ClassPool.getDefault();  
  75.         //生成代理类的全限定名  
  76.         String qualifiedName = generateQualifiedName(targetClass);  
  77.         // 创建代理类  
  78.         CtClass proxy = pool.makeClass(qualifiedName);  
  79.         // 设被代理类继承自Proxy  
  80.         setSuperClass(pool, proxy);  
  81.         // 获取被代理类的所有接口  
  82.         CtClass[] interfaces = pool.get(targetClass.getName()).getInterfaces();  
  83.           
  84.         int methodIndex = 0;  
  85.         // 遍历这些接口  
  86.         for (CtClass parent : interfaces) {  
  87.             proxy.addInterface(parent);  
  88.               
  89.             // 获取该接口的所有方法  
  90.             CtMethod[] methods = parent.getDeclaredMethods();  
  91.             for (int j = 0; j < methods.length; ++j) {  
  92.                 CtMethod method = methods[j];  
  93.                 String fieldSrc = String.format("private static java.lang.reflect.Method method%d = Class.forName(\"%s\").getDeclaredMethods()[%d];"  
  94.                         , methodIndex, parent.getName(), j);  
  95.                 logger.debug("field src for method {}: {}", method.getName(), fieldSrc);  
  96.                 // 生成字段  
  97.                 CtField ctField = CtField.make(fieldSrc, proxy);  
  98.                 // 添加字段  
  99.                 proxy.addField(ctField);  
  100.                 // 生成对应的Method  
  101.                 generateMethod(pool, proxy, method, methodIndex);  
  102.                   
  103.                 ++methodIndex;  
  104.             }  
  105.         }  
  106.         // 设置代理类的类修饰符  
  107.         setModifiers(proxy, SHOULD_BE_PUBLIC, SHOULD_BE_FINAL, SHOULD_BE_ABSTRACT);  
  108.         // 生成构造方法  
  109.         generateConstructor(pool, proxy);  
  110.         // 持久化class到硬盘, for use of debug  
  111.         proxy.writeFile(".");  
  112.         // to java.lang.Class  
  113.         proxyClass = proxy.toClass(classLoader, null);  
  114.         // 缓存  
  115.         proxyClassCache.put(classLoader, targetClass, proxyClass);  
  116.         return proxyClass.getConstructor(InvocationHandler.class).newInstance(invocationHandler);  
  117.     }  
  118.   
  119.     /** 
  120.      * 生成代理类的全限定名  
  121.      */  
  122.     private static String generateQualifiedName(Class targetClass) throws Exception {  
  123.         CtClass theInterface = null;  
  124.         for (CtClass parent : ClassPool.getDefault().get(targetClass.getName()).getInterfaces()) {  
  125.             if (theInterface == null) {  
  126.                 theInterface = parent;  
  127.             }  
  128.             if (!Modifier.isPublic(parent.getModifiers())) {  
  129.                 theInterface = parent;  
  130.                 break;  
  131.             }  
  132.         }  
  133.         String name = theInterface.getPackageName() + "." + PROXY_CLASSNAME_PREFIX + SUFFIX_GENERATOR.getAndIncrement();  
  134.         return name;  
  135.     }  
  136.   
  137.   
  138.     /** 
  139.      * 设置类的修饰符 
  140.      */  
  141.     private static void setModifiers(CtClass proxy, boolean shouldBePublic, boolean shouldBeFinal, boolean shouldBeAbstract) {  
  142.         int modifier = 0;  
  143.         modifier = shouldBePublic ? modifier | Modifier.PUBLIC : modifier;  
  144.         modifier = shouldBeFinal ? modifier | Modifier.FINAL : modifier;  
  145.         modifier = shouldBeAbstract ? modifier | Modifier.ABSTRACT : modifier;  
  146.         logger.error(Modifier.toString(modifier));  
  147.         proxy.setModifiers(modifier);  
  148.     }  
  149.   
  150.     /** 
  151.      * 生成构造函数 
  152.      */  
  153.     private static void generateConstructor(ClassPool pool, CtClass proxy) throws NotFoundException, CannotCompileException {  
  154.         CtConstructor ctConstructor = new CtConstructor(new CtClass[]{pool.get(InvocationHandler.class.getName())}, proxy);  
  155.         String methodBodySrc = String.format("super(%s);""$1");  
  156.         logger.debug("constructor body for constructor {}: {}", ctConstructor.getName(), methodBodySrc);  
  157.         ctConstructor.setBody(methodBodySrc);  
  158.         proxy.addConstructor(ctConstructor);  
  159.     }  
  160.   
  161.   
  162.     /** 
  163.      * 生成代理方法 
  164.      */  
  165.     private static void generateMethod(ClassPool pool, CtClass proxy, CtMethod method, int methodIndex) throws NotFoundException, CannotCompileException {  
  166.         CtMethod ctMethod = new CtMethod(method.getReturnType(), method.getName(), method.getParameterTypes(), proxy);  
  167.         String methodBodySrc = String.format("return super.invocationHandler.invoke(this, method%d, $args);", methodIndex);  
  168.         logger.debug("method body for method {}: {}", method.getName(), methodBodySrc);  
  169.         ctMethod.setBody(methodBodySrc);  
  170.         proxy.addMethod(ctMethod);  
  171.     }  
  172.   
  173.     /** 
  174.      * 把proxy类的父类设置为Proxy 
  175.      *  
  176.      */  
  177.     private static void setSuperClass(ClassPool pool, CtClass proxy) throws CannotCompileException, NotFoundException {  
  178.         proxy.setSuperclass(pool.get(Proxy.class.getName()));  
  179.     }  
  180.   
  181. }  

 

 

使用测试

 使用方式和JDK的没啥区别

先定义两个被代理的接口:

 

Java代码 
  1. package cc.lixiaohui.demo.javassist.proxy.example;  
  2.   
  3. public interface Talkable {   
  4.     Object talk(String words) throws Exception;   
  5. }  

 

Java代码 
  1. package cc.lixiaohui.demo.javassist.proxy.example;  
  2.   
  3. interface Smileable {  
  4.     Object smile() throws Exception;  
  5. }  

 

Person实现上面两接口:

 

Java代码 
  1. package cc.lixiaohui.demo.javassist.proxy.example;  
  2.   
  3. public class Person implements Smileable, Talkable {  
  4.   
  5.     private String name;  
  6.       
  7.     public Person(String name) {  
  8.         this.name = name;  
  9.     }  
  10.       
  11.     public Object talk(String words) throws Exception {  
  12.         System.out.println(name + " says: " + words);  
  13.         return words;  
  14.     }  
  15.       
  16.     public Object smile() throws Exception {  
  17.         System.out.println(name + " start smiling");  
  18.         System.out.println(name + " stop smiling");  
  19.         return null;  
  20.     }  
  21.   
  22. }  

 

 

代理逻辑的实现:

 

Java代码 
  1. package cc.lixiaohui.demo.javassist.proxy.example;  
  2.   
  3. import java.lang.reflect.Method;  
  4.   
  5. import cc.lixiaohui.demo.javassist.proxy.InvocationHandler;  
  6. import cc.lixiaohui.demo.javassist.proxy.Proxy;  
  7.   
  8. public class JavassistProxyFactory implements InvocationHandler{  
  9.   
  10.     //被代理类的对象  
  11.     private Object target;  
  12.       
  13.     public JavassistProxyFactory(Object target) {  
  14.         this.target = target;  
  15.     }  
  16.       
  17.     /*  
  18.      * @see cc.lixiaohui.demo.javassist.proxy.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[]) 
  19.      */  
  20.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
  21.         System.out.println("------- intercept before --------");  
  22.                 // 调用原来的方法  
  23.         Object result = method.invoke(target, args);  
  24.         System.out.println("--------intercept after ---------");  
  25.         return result;  
  26.     }  
  27.     // 获取代理类的对象  
  28.     public Object getProxy() throws Exception {  
  29.         return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), target.getClass(), this);  
  30.     }  
  31.       
  32. }  

 

测试主程序:

Java代码 
  1. package cc.lixiaohui.demo.javassist.proxy.example;  
  2.   
  3. import java.lang.reflect.Modifier;  
  4.   
  5. import org.junit.Test;  
  6.   
  7. public class Tester {  
  8.       
  9.     @Test  
  10.     public void testJavassist() throws Exception {  
  11.         Person person = new Person("小明");  
  12.         Object proxy = new JavassistProxyFactory(person).getProxy();  
  13.                 // System.gc(); // 主动触发gc  
  14.         Object proxy1 = new JavassistProxyFactory(person).getProxy();  
  15.         ((Talkable) proxy).talk("hello world");  
  16.         ((Smileable) proxy).smile();  
  17.           
  18.         System.out.println("package: " + proxy.getClass().getPackage().getName());  
  19.         System.out.println("classname: " + proxy.getClass().getName());  
  20.         System.out.println("modifiers: " + Modifier.toString(proxy.getClass().getModifiers()));  
  21.                 System.out.println(proxy.getClass() == proxy1.getClass()); // 测试缓存是否起作用  
  22.     }  
  23.   
  24. }  

 

结果输出:

Javassist实现动态代理_第1张图片
 
 

 

在 Proxy类中生成代理Class时把这个Class持久化了到硬盘中,通过反编译工具查看生成的代理类的源码:

 

Java代码 
  1. /*** Eclipse Class Decompiler plugin, copyright (c) 2016 Chen Chao ([email protected]) ***/  
  2. package cc.lixiaohui.demo.javassist.proxy.example;  
  3.   
  4. import cc.lixiaohui.demo.javassist.proxy.InvocationHandler;  
  5. import cc.lixiaohui.demo.javassist.proxy.Proxy;  
  6. import java.lang.reflect.Method;  
  7.   
  8. public final class $Proxy0 extends Proxy implements Smileable, Talkable {  
  9.     private static Method method0 = java.lang.Class.forName("cc.lixiaohui.demo.javassist.proxy.example.Smileable").getDeclaredMethods()[0];  
  10.     private static Method method1 = java.lang.Class.forName("cc.lixiaohui.demo.javassist.proxy.example.Talkable").getDeclaredMethods()[0];  
  11.   
  12.     public Object smile() {  
  13.         return this.invocationHandler.invoke(this, method0, new Object[0]);  
  14.     }  
  15.   
  16.     public Object talk(String paramString) {  
  17.         return this.invocationHandler.invoke(this, method1, new Object[] { paramString });  
  18.     }  
  19.   
  20.     public $Proxy0(InvocationHandler paramInvocationHandler) {  
  21.         super(paramInvocationHandler);  
  22.     }  
  23. }  

你可能感兴趣的:(动态代理,代理,javassist,设计模式)