• 【Java深入研究】6、CGLib动态代理机制详解


    一、首先说一下JDK中的动态代理

    JDK中的动态代理是通过反射类Proxy以及InvocationHandler回调接口实现的

    但是,JDK中所要进行动态代理的类必须要实现一个接口,也就是说只能对该类所实现接口中定义的方法进行代理,这在实际编程中具有一定的局限性,而且使用反射的效率也并不是很高。

    二、使用CGLib实现

    使用CGLib实现动态代理,完全不受代理类必须实现接口的限制,而且CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。

    下面,将通过一个实例介绍使用CGLib实现动态代理。

    1、被代理类

    首先,定义一个类,该类没有实现任何接口,包含两个方法。

    Java代码  收藏代码
    1. public class ConcreteClassNoInterface {  
    2.     public String getConcreteMethodA(String str){  
    3.         System.out.println("ConcreteMethod A ... "+str);  
    4.         return str;  
    5.     }  
    6.     public int getConcreteMethodB(int n){  
    7.         System.out.println("ConcreteMethod B ... "+n);  
    8.         return n+10;  
    9.     }  
    10. }  

    2、拦截器

    定义一个拦截器。在调用目标方法时,CGLib会回调MethodInterceptor接口方法拦截,来实现你自己的代理逻辑,类似于JDK中的InvocationHandler接口。

    Java代码  收藏代码
    1. public class ConcreteClassInterceptor implements MethodInterceptor{  
    2.     public Object intercept(Object obj, Method method, Object[] arg, MethodProxy proxy) throws Throwable {  
    3.         System.out.println("Before:"+method);    
    4.         Object object=proxy.invokeSuper(obj, arg);  
    5.         System.out.println("After:"+method);   
    6.         return object;  
    7.     }  
    8. }  

    参数:Object为由CGLib动态生成的代理类实例,Method为上文中实体类所调用的被代理的方法引用,Object[]为参数值列表,MethodProxy为生成的代理类对方法的代理引用。

    返回:从代理实例的方法调用返回的值。

    其中,proxy.invokeSuper(obj,arg):

    调用代理类实例上的proxy方法的父类方法(即实体类ConcreteClassNoInterface中对应的方法)

    在这个示例中,只在调用被代理类方法前后各打印了一句话,当然实际编程中可以是其它复杂逻辑。

    3、生成动态代理类

    Java代码  收藏代码
    1. Enhancer enhancer=new Enhancer();  
    2. enhancer.setSuperclass(ConcreteClassNoInterface.class);  
    3. enhancer.setCallback(new ConcreteClassInterceptor());  
    4. ConcreteClassNoInterface ccni=(ConcreteClassNoInterface)enhancer.create();  

    这里Enhancer类是CGLib中的一个字节码增强器,它可以方便的对你想要处理的类进行扩展,以后会经常看到它。

    首先将被代理类ConcreteClassNoInterface设置成父类,然后设置拦截器ConcreteClassInterceptor,最后执行enhancer.create()动态生成一个代理类,并从Object强制转型成父类型ConcreteClassNoInterface。

    最后,在代理类上调用方法:

    Java代码  收藏代码
    1. ccni.getConcreteMethodA("shensy");  
    2. ccni.getConcreteMethodB(0);  

    查看控制台输出:

    控制台代码  收藏代码
    1. Before :public java.lang.String generic.cglib.proxy.ConcreteClassNoInterface.getConcreteMethodA(java.lang.String)  
    2. ConcreteMethod A ... shensy  
    3. After :public java.lang.String generic.cglib.proxy.ConcreteClassNoInterface.getConcreteMethodA(java.lang.String)  
    4. Before :public int generic.cglib.proxy.ConcreteClassNoInterface.getConcreteMethodB(int)  
    5. ConcreteMethod B ... 0  
    6. After :public int generic.cglib.proxy.ConcreteClassNoInterface.getConcreteMethodB(int)  

    可以看到,拦截器在调用被代理类方法前后都执行了print操作。

    转自:http://shensy.iteye.com/blog/1873155

  • 相关阅读:
    .Net连接字符串设置连接池大小显著提高数据库速度
    转载:MongoDB之旅(超赞,适合初学者)
    MongoDB安装成为Windows服务及日常使用遇到问题总结
    开启Windows文件共享必须开启的两个服务
    Cocos2d-JS中瓦片地图API
    EF-CodeFirst 继承关系TPH、TPT、TPC
    MVC5-4 ViewResult
    MVC5-3 Result分析
    MVC5-2 MVC的管道流与路由
    MVC5-1 ASP.NET的管道流
  • 原文地址:https://www.cnblogs.com/wangzhongqiu/p/6672174.html
Copyright © 2020-2023  润新知