设计模式(三)

代理模式

扩展目标对象的功能
  1. 静态代理

    每一个代理类都必须实现一遍委托类的接口,如果接口增加方法,则代理类也跟着修改,违背“开闭原则”

  2. 动态代理

    1. jdk动态代理

      public interface Subject { //委托类的接口
          void sysHello();
      }
      public class RealSubject implements Subject{ //真正的委托类
          @Override
          public void sysHello() {
              System.out.println("hello world");
          }
      }
      public class MyProxy implements InvocationHandler {// 用户代理类
          public Object object;
          public MyProxy(Object object) {
              this.object = object;
          }
          @Override
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
              pre();
              Object invoke = method.invoke(object, args);
              post();
              return invoke;
          }
          private void pre(){
              System.out.println("方法执行之前************************");
          }
          private void post(){
              System.out.println("方法执行之后************************");
          }
      }
      public class Client {
          public static void main(String[] args) {
              Subject subject = new RealSubject();
              MyProxy myProxy = new MyProxy(subject);
              ClassLoader loader = subject.getClass().getClassLoader();
              Class<?>[] interfaces = subject.getClass().getInterfaces();
              Subject s = (Subject)Proxy.newProxyInstance(loader, interfaces, myProxy);
              s.sysHello();
          }
      }
      

      类加载器、接口数组可以理解为一个方法树,后面 s.sysHello();来告诉jvm方法树上的哪个方法。

      classLoader 将字节码文件加载进虚拟机并生成相应的class

      总结:

      1. 动态代理程序运行时,通过反射机制动态生成的
      2. 一般代理接口下所有的类
    2. cglib动态代理

      1. 首先导入 CGLIB 相关 jar 包

        	<dependency>
                    <groupId>cglib</groupId>
                    <artifactId>cglib</artifactId>
                    <version>3.2.4</version>
              </dependency>
        
      2. 创建真实角色

        public class RealSubject {
            public void request() {
                System.out.println("真实角色");
            }
        }
        
      3. 创建一个自定义方法拦截器,这个自定义方法拦截器实现了拦截器类

        public class MyMethodInterceptor implements MethodInterceptor {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                rep();
                Object o1 = methodProxy.invokeSuper(o, objects);
                post();
                return o1 ;
            }
        
            public void rep(){
                System.out.println("方法之前。。。。。。。。。。。。。");
            }
            public void post(){
                System.out.println("方法之后。。。。。。。。。。。。。");
            }
        }
        

        Object obj: obj 是 CGLIB 动态生成代理类实例

        Method method: Method 为实体类所调用的被代理的方法引用

        Objectp[] args: 这个就是方法的参数列表

        MethodProxy methodProxy : 这个就是生成的代理类对方法的引用。

      4. 测试类

            public static void main(String[] args) {
                Enhancer enhancer = new Enhancer();
                enhancer.setSuperclass(RealSubject.class);
                enhancer.setCallback(new MyMethodInterceptor());
                RealSubject proxy = (RealSubject) enhancer.create();
                proxy.request();
            }
        

      原理: CGLIB是⼀个强⼤的⾼性能的代码⽣成包,底层是通过使⽤⼀个⼩⽽快的字节码处理框架ASM,它可以在 运⾏期扩展Java类与实现Java接

      Enhancer是CGLIB的字节码增强器,可以很⽅便的对类进⾏拓展

      创建代理对象的⼏个步骤:

      1、⽣成代理类的⼆进制字节码⽂件

      2、加载⼆进制字节码,⽣成Class对象( 例如使⽤Class.forName()⽅法 )

      3、通过反射机制获得实例构造,并创建代理类对象

      总结

      jdk动态代理:利⽤拦截器(拦截器必须实现InvocationHanlder)加上反射机制⽣成⼀个实现代理接⼝的 匿名类,在调⽤具体⽅法前调⽤InvokeHandler来处理。只能对实现了接⼝的类⽣成代理只能对实现了 接⼝的类⽣成代理

      cglib:利⽤ASM开源包,对代理对象类的class⽂件加载进来,通过修改其字节码⽣成⼦类来处理。主要是对指定的类⽣成⼀个⼦类,覆盖其中的⽅法,并覆盖其中⽅法实现增强,但是因为采⽤的是继承, 对于final类或⽅法,是⽆法继承的。

      选择

      a. 如果⽬标对象实现了接⼝,默认情况下会采⽤JDK的动态代理实现AOP。

      b. 如果⽬标对象实现了接⼝,可以强制使⽤CGLIB实现AOP。

      c. 如果⽬标对象没有实现了接⼝,必须采⽤CGLIB库,Spring会⾃动在JDK动态代理和CGLIB之间转 换。

posted @ 2021-02-10 00:09  一个平凡的程序员  阅读(46)  评论(0)    收藏  举报