JAVA代理模式的理解和应用

代理模式:

代理模式通俗一点的解释就是在操做一个对象和对象中的方法时,不是直接操做这个对象,仍是经过一个代理对象来操做这个实际的目标对象。应用场景通常是须要在执行某个已经写好的方法先后再添加一段逻辑,好比执行方法前打印日志,或者在执行方法以前和以后打时间戳来计算方法的执行时间,诸如此类的。固然这些操做能够在写方法的时候就去写好,可是这样的话效率很是低,重复代码复制了一遍又一遍,若是要统一改点什么数量多起来的话基本上是个不可能完成的任务,而代理模式就是专门解决这种问题的。java


静态代理:

静态代理其实代理类Proxy中定义了一个方法,这个方法来调用被代理类Target中的方法,这样咱们就能够在执行这个方法的先后增长逻辑了,代理类和被代理类是组合关系。这里实现一个接口是为了有更好的扩展性,代理类Proxy中声明接受这个接口类型,那么被代理类只要实现了这个接口就可使用代理类Proxy进行代理操做了,这里是JAVA的多态特性。spring

  • 被代理的目标的实现接口编程

    public interface TargetImpl {
    
      void doSomething();
    
    }
  • 被代理的目标类maven

    public class Target implements TargetImpl {
    
      public void doSomething(){
          System.out.println("target do something!!!");
      }
    }
  • 代理类ide

    public class Proxy implements TargetImpl {
    
      private TargetImpl baseObject;
    
      public Proxy(TargetImpl baseObject) {
          this.baseObject = baseObject;
      }
    
      public void doSomething(){
          System.out.println("before method");
          baseObject.doSomething();
          System.out.println("after method");
      }
    }
  • 测试类:测试

    public class TestMain {
    
      public static void main(String[] args){
          staticProxy();
      }
    
      public static void staticProxy(){
          Target target = new Target();
          Proxy proxy = new Proxy(target);
          proxy.doSomething();
      }
    }

动态代理:

上面静态代理类已经帮咱们解决了不少冗余代码,可是存在的问题仍是不少,好比一个代理类只能对一种类型目标类有效,换一种类型要新增一个代理类,并且若是有不少地方使用目标类就得在每一个地方调用代理类,很麻烦,而动态代理则能够解决这种问题。this

  • 代理类,须要实现InvocationHandler接口,这个接口是JAVA自带的,实现invoke()方法,被代理的目标类会在invoke()方法中被调用,只须要在这个方法中添加逻辑便可。而proxy()方法则是调用了Proxy.newProxyInstance()方法,这个是JAVA原生类Proxy中的方法,接收目标类的类型参数和目标类的对象参数。代理

    // 这个接口是JDK自带的,全部的代理类都要实现这个接口
    // 这样才能调用Proxy.newProxyInstance()这个生成代理类的静态方法
    public class MyProxy implements InvocationHandler {
      private Object proxy;
    
      public MyProxy(Object proxy) {
          this.proxy = proxy;
      }
    
      // 代理类实现接口中的一个方法,接收参数分别是被代理的类,要执行的方法,执行方法的参数,返回则是执行方法返回的参数
      // 代理对象的全部方法调用都会转到这个方法中
      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
          System.out.println("before invoke");
          Object rTarget = method.invoke(this.proxy, args);
          System.out.println("after invoke");
          return rTarget;
      }
    
      // JDK自带的生成代理类的静态方法,第一个参数是类加载器 第二个参数是被代理类的接口 第三个参数是被代理的对象
      // 这个方法内部的大体原理就是动态的加载这个类,而后放到内存中,因此不是编译时期生成的,是运行的时候生成的
      public static Object proxy(Class interfaceClazz, Object proxy) {
          return Proxy.newProxyInstance(interfaceClazz.getClassLoader(), new Class[]{interfaceClazz},
                  new MyProxy(proxy));
      }
    }
  • 被代理的目标类的实现接口日志

    public interface TargetImpl {
    
      void doSomething1();
    
      void doSomething2();
    
      String doSomething3();
    }
  • 被代理的目标类code

    public class Target implements TargetImpl {
    
      private String text;
    
      public Target(String text) {
          this.text = text;
      }
    
      public void doSomething1(){
          System.out.println("doSomething1-" + text);
      }
    
      public void doSomething2(){
          System.out.println("doSomething2-" + text);
      }
    
      public String doSomething3(){
          System.out.println("doSomething3-" + text);
          String result = "doSomething3-" + text;
         return result;
      }
    }
  • 测试类,调用proxy()方法,把目标类实现接口的字节码和目标类的对象传入,得到返回的一个代理类对象,而后就能够调用对应的方法,这个时候会发现方法执行前会执行前面在invoke()方法中添加的逻辑。

    public class TestMain {
    
      public static void main(String[] args){
          jdkProxy();
      }
    
      public static void jdkProxy(){
          TargetImpl target = (TargetImpl) MyProxy.proxy(TargetImpl.class, new Target("target"));
          target.doSomething1();
          target.doSomething2();
          System.out.println(target.doSomething3());
      }
    
    }
  • 大体原理

    • 动态代理之因此叫动态代理就是由于代理类不是在编译时生成的,而是代码运行后动态生成的。
    • 在调用了Proxy.newProxyInstance()方法以后,由于把目标类实现接口的字节码和目标类的对象出入进行了,因此这个方法的源码作的大体操做就是根据这个字节码和对象来获取目标类中的方法等各类信息而后动态的生成一个代理类,而代理类中全部的方法调用又会中转到invoke()方法中,invoke()方法又再去调用目标类中的方法,因此只须要在invoke()方法中添加须要添加的逻辑便可。
    • 注意,若是使用JAVA自带的动态代理,目标类是必定要实现一个接口才能够的。

cglib代理:

cglib是一个开源的库,能够在运行时动态的修改和生成字节码,原理其实和JAVA原生的动态代理差很少,可是不一样的地方是它是基于被代理的目标类生成一个子类,而后在在子类中重载父类的方法,因此它能够代理没有接口实现的目标类,这点是和JAVA原生的动态代理最大的不一样之处。

  • 引入maven

    <dependency>
       <groupId>cglib</groupId>
       <artifactId>cglib</artifactId>
       <version>3.2.12</version>
    </dependency>
  • 代理类,实现MethodInterceptor接口,在intercept方法中添加额外的逻辑并调用methodProxy.invokeSuper()方法来执行目标类中的方法得到代理类的对象。

    public class Proxy implements MethodInterceptor {
    
      @Override
      public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
          System.out.println("before invoke");
          Object object = methodProxy.invokeSuper(o,objects);
          System.out.println("after invoke");
          return object;
      }
    }
  • 被代理的目标类的实现接口

    public interface TargetImpl {
    
      void doSomething1();
    
      void doSomething2();
    
      String doSomething3();
    }
  • 被代理的目标类

    public class Target implements TargetImpl {
    
      private String text;
    
      public Target(String text) {
          this.text = text;
      }
    
      public void doSomething1(){
          System.out.println("doSomething1-" + text);
      }
    
      public void doSomething2(){
          System.out.println("doSomething2-" + text);
      }
    
      public String doSomething3(){
          System.out.println("doSomething3-" + text);
          String result = "doSomething3-" + text;
         return result;
      }
  • 测试类

    public class TestMain {
    
      public static void main(String[] args){
          jdkProxy();
      }
    
      public static void jdkProxy(){
          TargetImpl target = (TargetImpl) MyProxy.proxy(TargetImpl.class, new Target("target"));
          target.doSomething1();
          target.doSomething2();
          System.out.println(target.doSomething3());
      }
    }
  • 大体原理

    • 能够看到除了代理类和JAVA原生的动态代理略有不一样其余的地方基本是相同的,也是在运行时动态的生成代理类。
    • 注意,由于前面说了cglib生成的代理类实际上是目标类的一个子类,因此被final声明的类是没办法使用cglib的,会抛出java.lang.IllegalArgumentException: Cannot subclass final class cglib.HelloConcret异常,而被final声明的方法也是没办法被重载的,因此会被忽略。

总结:

  • 能够看到在这三种代理方式中都有使用到JAVA中多态的特性。
  • 静态代理就是单纯简单的使用了多态和组合的特性。
  • JAVA动态代理和cglib则是再这个基础上使用了动态编译的方式使得扩展性更强,只不过二者的动态生成的方式不一样,因此注意事项也有所不一样。

扩展:

  • spring中就大量使用了两种动态代理,AOP切面就是使用了动态代理,之因此叫切面就是好比说原来A方法、B方法是依次调用,而如今配置Spring AOP就能够动态的在A、B方法的先后添加逻辑,这样就能够在原本依次调用的A、B方法之间插入新的逻辑,因此叫面向切面编程。
相关文章
相关标签/搜索