欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Javassist实现动态代理

程序员文章站 2022-05-23 16:27:04
...

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

 

 

使用测试

 使用方式和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实现动态代理
            
    
    博客分类: 设计思想Java 动态代理代理javassist设计模式 
 
 

 

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

 

Java代码 
  1. /*** Eclipse Class Decompiler plugin, copyright (c) 2016 Chen Chao (cnfree2000@hotmail.com) ***/  
  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. }