Java学习之:JDK动态代理与CGLIB动态代理

代理的概念:简单的理解就是通过为某一个对象创建一个代理对象,我们不直接引用原本的对象,而是由创建的代理对象来控制对原对象的引用。

动态代理:是指在程序运行时由Java反射机制动态生成,无需手动编写代码。动态代理不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java反射机制可以生成任意类型的动态代理类。

代理原理:代理对象内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。

以下通过一个简单的例子来看基于JDK动态代理与CGLIB分别如何实现代理。

代理接口UserService:

1 package com.liang.test;

2 

3 public interface UserService {

4     public void say(String arg);

5 }

 

UserService实现类UserServiceImpl:

1 package com.liang.test;

2 

3 public class UserServiceImpl implements UserService {

4     @Override

5     public void say(String arg) {

6         System.out.println("hello, I am " + arg);

7     }

8 }

 

JDK通过java.lang.reflect包下的Proxy类和一个InvocationHandler接口来生成动态代理类和动态代理对象。

首先创建一个实现InvocationHandler接口的调用控制器对象MyInvocationHandler:(当执行动态代理对象里的目标方法时,实际上会替换成调用MyInvocationHandler的invoke方法)

 1 package com.liang.test;

 2 

 3 import java.lang.reflect.InvocationHandler;

 4 import java.lang.reflect.Method;

 5 

 6 public class MyInvocationHandler implements InvocationHandler { //实现InvocationHandler

 7 

 8     private Object object; //被代理对象

 9     

10     public MyInvocationHandler(Object object){ //接收被代理对象

11         this.object = object;

12     }

13     @Override

14     public Object invoke(Object proxy, Method method, Object[] args)

15             throws Throwable { //say()方法被替换执行成当前方法,可在其中添加额外功能增强目标方法

16         System.out.println("before method");

17         Object obj = method.invoke(object, args); //通过反射调用UserService的say()方法

18         System.out.println("after method");

19         return obj;

20     }

21 

22 }

通过Proxy结合MyInvocationHandler为UserService创建动态代理类:

 1 package com.liang.test;

 2 

 3 import java.lang.reflect.InvocationHandler;

 4 import java.lang.reflect.Proxy;

 5 

 6 public class TestProxy {

 7 

 8     public static void main(String[] args) {

 9         UserService userService = new UserServiceImpl();

10         InvocationHandler handler = new MyInvocationHandler(userService);

11 

12         UserService proxy = (UserService)Proxy.newProxyInstance(userService.getClass()

13                 .getClassLoader(), userService.getClass().getInterfaces(),

14                 handler);

15         proxy.say("孙悟空"); //调用代理实例

16     }

17 

18 }

运行结果:

before method

hello, I am 孙悟空

after method

可以看到执行代理实例的say方法时执行的是MyInvocationHandler的invoke()方法。

 

而CGLIB则是采用非常底层的字节码技术,为被代理对象创建一个子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,并顺势植入增强代码。CGLIB实现动态代理的示例如下:

 1 package com.liang.test;

 2 

 3 import java.lang.reflect.Method;

 4 

 5 import net.sf.cglib.proxy.Enhancer;

 6 import net.sf.cglib.proxy.MethodInterceptor;

 7 import net.sf.cglib.proxy.MethodProxy;

 8 

 9 public class CglibProxy implements MethodInterceptor {

10     

11     private Enhancer enhancer = new Enhancer();

12 

13     public Object getProxy(Class clazz) {

14         enhancer.setSuperclass(clazz); // 设置需要创建子类的类

15         enhancer.setCallback(this);

16         return enhancer.create();// 创建子类实例

17     }

18 

19     @Override

20     public Object intercept(Object object, Method method, Object[] args,

21             MethodProxy proxy) throws Throwable { //拦截父类中所有的方法调用

22         System.out.println("cglib: before method");

23         Object obj = proxy.invokeSuper(object, args); //通过代理类调用父类中的方法

24         System.out.println("cglib: after method");

25         return obj;

26     }

27 

28 }

 

测试代码:

 1 package com.liang.test;

 2 

 3 public class TestProxy {

 4 

 5     public static void main(String[] args) {

 6         CglibProxy proxy = new CglibProxy();

 7         UserServiceImpl userServiceImpl = (UserServiceImpl)proxy.getProxy(UserServiceImpl.class);

 8         userServiceImpl.say("孙悟空");

 9     }

10 

11 }

 

运行结果:

cglib: before method

hello, I am 孙悟空

cglib: after method

 

 

小结:JDK动态代理与CGLIB动态代理最常见的应用是SpringAOP的底层实现,基于JDK的动态代理在创建代理对象时所花费的时间比CGLIB短,但是JDK只能为接口创建代理实例,这一点可以从newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)中看出:第二个入参就是代理示例实现的接口列表。而CGLIB创建的代理对象性能比JDK创建的代理对象高,但是花费的时间相对长一些,并且由于CGLIB采用动态创建子类的方式生成代理对象,所以对于由final修饰的方法不能通过CGLIB进行代理。基于此,我们对于singgleton的代理对象或者具有实例池的代理比较适合使用CGLIB动态代理技术,对于每次都要new出新实例的一般采用JDK的动态代理技术。

 本次学习资料参照了:

  1.《精通Spring2.x-企业应用开发详解》——陈雄华著;

  2.http://rejoy.iteye.com/blog/1627405——JDK动态代理实现原理

        

 

你可能感兴趣的:(JDK动态代理)