关于代理模式的介绍,以及动态代理(jdk、cglib)的介绍
代理模式
简要介绍
代理,就是代理人
当A想找B做一件事,但是可能B在十万八千里之外,联系不上,但是C又能联系上B,而且A也能找到C,所以A找C办这件事,但真正做事的人是B,C只是个代理人
一句话:代理人可以控制客户端对其他对象的访问
代理种类:
- 远程代理(Remote Proxy):控制对远程对象(不同地址空间)的访问,它负责将请求及其参数进行编码,并向不同地址空间中的对象发送已经编码的请求。
 - 虚拟代理(Virtual Proxy):根据需要创建开销很大的对象,它可以缓存实体的附加信息,以便延迟对它的访问,例如在网站加载一个很大图片时,不能马上完成,可以用虚拟代理缓存图片的大小信息,然后生成一张临时图片代替原始图片。
 - 保护代理(Protection Proxy):按权限控制对象的访问,它负责检查调用者是否具有实现一个请求所必须的访问权限。
 - 智能代理(Smart Reference):取代了简单的指针,它在访问对象时执行一些附加操作:记录对象的引用次数;当第一次引用一个对象时,将它装入内存;在访问一个实际对象前,检查是否已经锁定了它,以确保其它对象不能改变它。
 
哪里用到这个模式了?
- 使用过的一些中间件例如;
RPC框架,在拿到jar包对接口的描述后,中间件会在服务启动的时候生成对应的代理类,当调用接口的时候,实际是通过代理类发出的socket信息进行通过。 - 另外像我们常用的
MyBatis,基本是定义接口但是不需要写实现类,就可以对xml或者自定义注解里的sql语句进行 
类图

在上图中,Client就是A,Proxy就是C,RealSubject就是B,也就是真正做事的人
例子
定义一个接口叫HelloService,然后定义一个sayHello方法,我们通过代理来调用这个方法,来实现在目标对象执行sayHello方法的前后记录日志
HelloService
1 2 3 4 5 6 7
   | public interface HelloService {
      
 
      void say(); }
  | 
实现类(具体做事的人):HelloServiceImpl
1 2 3 4 5 6
   | public class HelloServiceImpl implements HelloService{     @Override     public void say() {         System.out.println("hello!");     } }
  | 
代理:HelloServiceProxy
可以看到我们就是在这里直接调用真正做事的人执行方法,并且前后还可以加额外的东西
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
   | public class HelloServiceProxy implements HelloService{
      private final HelloService target;
      public HelloServiceProxy(HelloService target) {         this.target = target;     }
      @Override     public void say() {         System.out.println("记录日志");         target.say();         System.out.println("清理数据");     } }
  | 
客户端:Main
1 2 3 4 5 6 7 8 9
   | public class Main {     public static void main(String[] args) {                  HelloService target = new HelloServiceImpl();                  HelloServiceProxy helloServiceProxy = new HelloServiceProxy(target);         helloServiceProxy.say();     } }
  | 
执行结果:
缺点
经过上面的介绍,我们可以发现,一个代理类只能为一个接口服务,平时开发是有N多个接口的,肯定会产生很多的代理类的,所以我们就会想,有没有可能一个代理类,可以完成所有代理的功能,所以就有了动态代理
动态代理
JDK
JDK为我们提供了动态代理的支持,我们的代理类需要实现java.lang.reflect.InvocationHandler接口并且调用java.lang.reflect.Proxy类的newProxyInstance方法创建一个代理实例,然后调用具体的方法,其实就是通过反射生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理
在上述代码基础上加入MyInvocationHandler:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
   | public class MyInvocationHandler implements InvocationHandler {
      private final Object target;
      public MyInvocationHandler(Object target) {         this.target = target;     }
      
 
 
 
 
 
      @Override     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {                  System.out.println(target.getClass().getName() + "." + method.getName());                  System.out.println("记录日志");         Object result = method.invoke(target, args);         System.out.println("清理数据");         return result;     }
 
      public Object getProxy() {                           return Proxy.newProxyInstance(target.getClass().getClassLoader(),                 target.getClass().getInterfaces(), this);     } }
  | 
关于Proxy.newProxyInstance方法参数的说明:
- 该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
 - 第一个参数指定产生代理对象的类加载器(就是要为谁加代理,就传谁的class),需要将其指定为和目标对象同一个类加载器
 - 第二个参数要实现和目标对象一样的接口(可以看出jdk的动态代理,其实就是把我们手动创建和代理对象相同接口的代理类自动化了),所以只需要拿到目标对象的实现接口
 - 第三个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法
 
下面使用一下这个动态代理,看看好使不:
1 2 3 4 5 6 7 8 9
   | public class Main {     public static void main(String[] args) {                  HelloService target = new HelloServiceImpl();         MyInvocationHandler invocationHandler = new MyInvocationHandler(target);         HelloService proxy = (HelloService) invocationHandler.getProxy();         proxy.say();     } }
  | 
结果:
1 2 3 4
   | com.hc.basics.dynamicproxy.jdk.HelloServiceImpl.say 记录日志 hello! 清理数据
   | 
cglib
jdk动态代理的局限性:只能对实现了接口的类进行代理,对于没有实现接口的类无法代理
cglib对于没有实现接口的类也可以进行代理
这个是咋动态生成代理的呢?他利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
cglib需要引入依赖:
1 2 3 4 5 6
   |  <dependency>     <groupId>cglib</groupId>     <artifactId>cglib</artifactId>     <version>3.2.5</version> </dependency>
 
  | 
在上述基础,再加一个CglibProxyFactory类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
   | public class CglibProxyFactory implements MethodInterceptor {
      
 
      private final Enhancer enhancer = new Enhancer();
      private final Object target;
      public CglibProxyFactory(Object target) {         this.target = target;     }
      public Object getProxy() {                  enhancer.setSuperclass(target.getClass());                  enhancer.setCallback(this);                  return enhancer.create();     }
      @Override     public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {                  System.out.println(target.getClass().getName() + "." + method.getName());                  System.out.println("记录日志");         Object result = method.invoke(target, args);         System.out.println("清理数据");         return result;     } }
  | 
测试:
1 2 3 4 5 6 7 8 9 10 11 12 13
   | public class Main {     public static void main(String[] args) {         HelloService helloService = new HelloServiceImpl();         CglibProxyFactory helloServiceProxyFactory = new CglibProxyFactory(helloService);         HelloService helloServiceProxy = (HelloService) helloServiceProxyFactory.getProxy();         helloServiceProxy.say();                  Student student = new Student();         CglibProxyFactory studentProxyFactory = new CglibProxyFactory(student);         Student studentProxy = (Student) studentProxyFactory.getProxy();         studentProxy.study();     } }
  | 
1 2 3 4 5 6 7 8
   | com.hc.basics.dynamicproxy.jdk.HelloServiceImpl.say 记录日志 hello! 清理数据 com.hc.basics.dynamicproxy.cglib.Student.study 记录日志 student study! 清理数据
   |