我们来谈谈什么是动态代理。假设这样一个场景:

你们是一家软件公司,你是一位软件工程师。现在又客户带着一个需求来找你们公司做项目。显然不是找你直接谈,而是去找商务去谈,此时客户就会认为商务就代表你们公司。然后当客户与商务交流的差不多了,商务就会来找你接洽。显然商务与工程师之间是代理与被代理的关系。

Spring中通常用JDK和CGLIB。Mybatis中还是用了javassist

  • JDK与CGLIB
    jdk动态代理中,我们必须使用接口。而cglib则不需要。
    因为在JDK动态代理中,需要这个接口作为参数传入。Proxy.newProxyInstance(···, obj.getClass().getInterfaces(),···);

下面我们讨论梁中最常用的代理:

JDK动态代理:

他必须通过接口才能生成代理对象,首先我们先生成一个接口

  1. package com.rick.proxy;
  2. public interface Hello {
  3. public void sayHello();
  4. }

然后提供接口的实现类

  1. package com.rick.proxy;
  2. public class HelloImpl implements Hello{
  3. @Override
  4. public void sayHello() {
  5. System.out.println("say Hello......");
  6. }
  7. }

此时我们就可以开始动态代理了,分为两个步骤:

  1. 首先建立起代理对象和真是对象的关系
  2. 然后实现代理逻辑package com.rick.proxy;
  1. import java.lang.reflect.InvocationHandler;
  2. import java.lang.reflect.Method;
  3. import java.lang.reflect.Proxy;
  4. public class JdkProxyExample implements InvocationHandler{
  5. //真对象
  6. private Object target = null;
  7. //创建代理和真实对象的代理关系,并返回对象
  8. public Object bind(Object target){
  9. this.target = target;
  10. return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),this);
  11. /*
  12. * 第一个参数是累的加载器,我们采用了target本身的类加载器
  13. * 第二个参数是吧生成的动态代理对象挂在哪些接口下面
  14. * 第三个表示定义实现方法逻辑的代理类,this表示当前对象他必须
  15. * 实现InvocationHandler的invoke方法,他就是代理逻辑方法的实现方法
  16. */
  17. }
  18. //实现代理逻辑
  19. @Override
  20. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  21. /*
  22. * proxy为代理对象,就是bind方法生成的对象
  23. * method为当前调度的方法
  24. * args为调度方法的参数
  25. */
  26. System.out.println("进入代理逻辑方法");
  27. System.out.println("在调度真实对象之前。。");
  28. Object obj = method.invoke(target, args);//相当于调用sayHello方法
  29. System.out.println("在调度真实对象之后。。");
  30. return obj;
  31. }
  32. }

类比前面的例子,proxy相当于商务对象,target相当于软件工程师对象,bind方法就是建立商务和软件工程师代理关系的方法。而invoke就是商务逻辑,他控制软件工程师的访问

测试

  1. package com.rick.proxy;
  2. public class Test {
  3. public static void main(String[] args) {
  4. JdkProxy();
  5. }
  6. private static void JdkProxy() {
  7. JdkProxyExample jdkProxyExample = new JdkProxyExample();
  8. Hello proxy = (Hello) jdkProxyExample.bind(new HelloImpl());
  9. proxy.sayHello();
  10. }
  11. }

CGLIB动态代理:

jkd动态代理必须提供接口才能使用,在一些不能提供接口的场景中,只能采取第三方技术。比如CGLIB动态代理。它的优势在于不需要提供接口,只要一个非抽象类就能实现动态代理。

例如:

  1. package com.rick.proxy;
  2. public class UserImpl {
  3. public void sayHello(String name) {
  4. System.out.println("Hello:"+name);
  5. }
  6. }

他不存在实现任何借口,所以没有办法使用JDK动态代理,之力就要采用CGLIB动态代理

  1. package com.rick.proxy;
  2. import java.lang.reflect.Method;
  3. import net.sf.cglib.proxy.Enhancer;
  4. import net.sf.cglib.proxy.MethodInterceptor;
  5. import net.sf.cglib.proxy.MethodProxy;
  6. public class CglibProxyExample implements MethodInterceptor{
  7. public Object getProxy(Class cls) {
  8. // CGLIB enhancer 增强类对象
  9. Enhancer enhancer = new Enhancer();
  10. // 设置增强类型
  11. enhancer.setSuperclass(cls);
  12. // 定义代理逻辑对象为当前对象,要求当前对象实现MethodInterceptor方法
  13. enhancer.setCallback(this);
  14. //生成并返回代理对象
  15. return enhancer.create();
  16. }
  17. @Override
  18. public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
  19. System.out.println("调用真是对象前");
  20. // CGLIB反射调用真实对象方法
  21. Object result = methodProxy.invokeSuper(proxy, args);
  22. System.out.println("调用真实对象后");
  23. return result;
  24. }
  25. }

创建测试:

  1. package com.rick.proxy;
  2. public class Test {
  3. public static void main(String[] args) {
  4. // JdkProxy();
  5. CGLIBProxy();
  6. }
  7. // private static void JdkProxy() {
  8. // JdkProxyExample jdkProxyExample = new JdkProxyExample();
  9. // Hello proxy = (Hello) jdkProxyExample.bind(new HelloImpl());
  10. // proxy.sayHello();
  11. // }
  12. public static void CGLIBProxy() {
  13. CglibProxyExample cglibProxyExample = new CglibProxyExample();
  14. UserImpl proxy = (UserImpl) cglibProxyExample.getProxy(UserImpl.class);
  15. proxy.sayHello("zhangsan");
  16. }
  17. }