8、代理模式

1、代理模式介绍

代理模式就是调用者直接调用代理类,通过代理类再去调用业务类,最终完成逻辑处理的过程,

代理模式一个主要的功能就是实现延迟加载,加快系统启动速度。

系统初始化时只需要初始化代理类即可,等到真正要处理业务逻辑,代理类才需要初始化业务类,并且调用业务类的对应方法来实现功能,将结果范围给调用方。

代理模式有两种:静态代理和动态代理

  • 1、静态代理:

    定义一个interface,代理类和业务逻辑实现类都实现这个接口,业务逻辑类是真正实现抽象方法的具体逻辑的,而代理类只是调用业务类的对应方法,并且将结果返回给调用方。缺点:代理类需要实现接口的每一个方法,繁琐

  • 2、动态代理:

    动态代理就是为了解决上述代理类繁琐的操作的,有几种实现方式。

      1:)原生的jdk;
      2:)cglib;
      3:)javassist;
      4:)ASM。
      用的比较多的是前面三种,通过代理类,反射调用业务逻辑类的方法可以实现对方法的调用。
    

2、静态代理

/**
 * 静态代理中,代理类和实现类都需要实现相同的接口
 * 拥有相同的操作入口,同时可以使用接口作为引用进行操作
 *
 * [@Author](https://my.oschina.net/arthor) liufu
 * @CreateTime 2018/3/23  16:28
 */
public class StaticProxy {
    public static void main(String[] args) {
        //只是创建代理类,此时不需要创建实现类(构建需要很久)
        Person proxy = new ChineseProxy();
        proxy.say();
    }


    interface Person {
        void say();
    }

    static class Chinese implements Person {
        public Chinese() {
            //模拟大实例类Chinese的构建耗时
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        [@Override](https://my.oschina.net/u/1162528)
        public void say() {
            System.out.println("im chinese!");
        }
    }

    static class ChineseProxy implements Person {
        private Chinese chinese = null;

        [@Override](https://my.oschina.net/u/1162528)
        public void say() {
            // 只有用到的时候再去创建,延迟加载,
            // 如果Chinese对象非常大,提高系统启动速度
            if (chinese == null) {
                chinese = new Chinese();
            }

            //最后还是调用真正实现类的方法去实现
            chinese.say();
        }
    }
}

3、动态代理

1、java自身的动态代理
  • 1、定义接口

      public interface JavaProxyInterface {
          String getString();
      }
    
  • 2、实现这个接口的实现类

      public class JavaProxyImpl implements JavaProxyInterface{
          [@Override](https://my.oschina.net/u/1162528)
          public String getString() {
              return "this is result";
          }
      }
    
  • 3、代理类

      package com.surfilter.mass;
    
      import java.lang.reflect.InvocationHandler;
      import java.lang.reflect.Method;
      import java.lang.reflect.Proxy;
      import java.util.HashMap;
    
      public class JavaProxy {
    
          public static void main(String[] args) {
              //这里必须传入实现类,因为要newInstance()
              JavaProxyImpl proxy = (JavaProxyImpl) createLocalProxy(JavaProxyImpl.class);
              System.out.println(proxy.getString());
    
              //实现不在本地,而是远端,所以这里传入接口,只要知道需要调用那个方法即可
              JavaProxyInterface proxy1 = (JavaProxyInterface) createRPCProxy(JavaProxyInterface.class);
              System.out.println(proxy1.getString());
          }
    
          /**
           * 创建本地代理对象
           * 注意,这里传入的必须是实现类,实现类在本工程,不是RPC
           * 这样才能直接method.invoke(targetClass.newInstance(), args);
           *
           * 由于要newInstance(),上面又要获取接口,所以java动态代理的类必须实现接口
           * 而CGlib和javasiset就不需要,直接传入业务类就行了,此类不需要实现接口
           *
           * @param targetClass
           * @return
           */
          public static Object createLocalProxy(Class targetClass) {
              Object o = Proxy.newProxyInstance(
                      targetClass.getClassLoader(),  //第一个参数,指定类加载器
                      targetClass.getInterfaces(),   //第二个参数,这个类的接口是什么
                      new InvocationHandler() {      //第三个参数,编写如何处理这个方法逻辑
                          @Override
                          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                              return method.invoke(targetClass.newInstance(), args);
                          }
                      }
              );
              return o;
          }
    
          /**
           * 创建RPC代理对象
           * 注意,由于是RPC,接口实现不是在本地,而是在远端,所以这里面传入的是接口
           * 主要是为了获取方法名字,参数,以及接口名字,从而组装json发送到远端
           * 这样远端rpc服务器就能知道:1、调用那个类,2、那个方法,3、参数是什么
           *
           * @param targetClass
           * @return
           */
          public static Object createRPCProxy(Class targetClass) {  //本身是接口
              Object o = Proxy.newProxyInstance(
                      targetClass.getClassLoader(),  //第一个参数,指定类加载器
                      new Class[]{targetClass},     //第二个参数,这个类的接口是什么
                      new InvocationHandler() {     //第三个参数,编写如何处理这个方法逻辑
                          @Override
                          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                              HashMap<String, Object> requestMap = new HashMap<>();
                              requestMap.put("interfaceName", targetClass.getName());
                              requestMap.put("methodName", method.getName());
                              requestMap.put("args", args);
    
                              // 通过netty发送这些参数到远端rpc服务器,这样就可以执行得到结果了
                              String result = "rpc result";
                              return result;
                          }
                      }
              );
              return o;
          }
      }
    
2、CGLIB实现动态代理
  • 1、maven依赖

      <dependency>
         <groupId>cglib</groupId>
         <artifactId>cglib</artifactId>
         <version>3.2.4</version>
      </dependency>
    
  • 2、接口

      public interface CGLibProxyInterface {
          String getString();
      }
    
  • 3、业务类(可以实现接口,也可以不实现接口,不要求)

      public class CGLibProxyImpl {
          public String getString() {
              return "this is result";
          }
      }
    
  • 4、代理类

      import net.sf.cglib.proxy.Enhancer;
      import net.sf.cglib.proxy.MethodInterceptor;
      import net.sf.cglib.proxy.MethodProxy;
    
      import java.lang.reflect.Method;
    
      /**
       * CGLibProxy 代理本地业务类,则需要传入业务实现类,但是这个类不要求实现接口(却别与JavaProxy)
       * 代理接口进行RPC远程过程调用,则需要传入接口
       *
       * 不管传入的是接口还是实现类,都可以使用enhancer.setSuperclass(targetClass);
       * 因为他内部会做是否是接口的判断
       * @Author liufu
       * @CreateTime 2018/3/28  9:00
       */
      public class CGLibProxy {
          public static void main(String[] args) {
              //创建本地的代理,需要传入真实的实现类,但是这个类不要求实现接口,这是和JavaProxy的区别
              CGLibProxyImpl proxyLocal = (CGLibProxyImpl) createLocalProxy(CGLibProxyImpl.class);
    
              //创建rpc代理,需要传入接口,因为你也没有实现类,实现类在远端服务器
              CGLibProxyInterface proxyRpc = (CGLibProxyInterface) createRemoteProxy(CGLibProxyInterface.class);
    
              System.out.println(proxyLocal.getString());
              System.out.println(proxyRpc.getString());
          }
    
          /**
           * 创建本地代理,需要传入实现类
           *
           * @param targetClass
           * @return
           */
          public static Object createLocalProxy(Class targetClass) {
              Enhancer enhancer = new Enhancer();
              enhancer.setSuperclass(targetClass);
              enhancer.setCallback(new MethodInterceptor() {
                  @Override
                  public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                      System.out.println("before:" + method.getName());
                      //看清楚这里是要用methodProxy,而不是method
                      Object invoke = methodProxy.invokeSuper(o, objects);
                      System.out.println("after:" + method.getName());
                      return invoke;
                  }
              });
              return enhancer.create();
          }
    
          /**
           * 创建rpc代理,只需要传入接口即可,因为只需要他的方法名,参数
           *
           * @param targetClass
           * @return
           */
          public static Object createRemoteProxy(Class targetClass) {
              Enhancer enhancer = new Enhancer();
              /**
               *传入的是接口,也可以使用setSuperclass
               * 因为他内部会对class进行判断
               *
               public void setSuperclass(Class superclass) {
                   if(superclass != null && superclass.isInterface()) {
                      this.setInterfaces(new Class[]{superclass});
                   } else if(superclass != null && superclass.equals(Object.class)) {
                      this.superclass = null;
                   } else {
                      this.superclass = superclass;
                   }
               }
               */
              enhancer.setSuperclass(targetClass);
              //enhancer.setInterfaces(new Class[]{targetClass});
    
              enhancer.setCallback(new MethodInterceptor() {
                  @Override
                  public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                      System.out.println("before:" + method.getName());
                      //传入的是接口,会报NoSuchMethodError异常
                      //Object result = methodProxy.invokeSuper(o, objects);
                      System.out.println("after:" + method.getName());
                      return "this is rpc result";
                  }
              });
              return enhancer.create();
          }
    
          /**
           * 这个类是具体的业务处理类,上面在setCallback中使用的是匿名内部类
           */
          class ProxyCallBack implements MethodInterceptor {
              @Override
              public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                  System.out.println("before:" + method.getName());
                  //看清楚这里是要用methodProxy,而不是method
                  Object invoke = methodProxy.invokeSuper(o, objects);
                  System.out.println("after:" + method.getName());
                  return invoke;
              }
          }
      }
    
3、JavaSsist代理
  • 1、maven依赖

      <dependency>
          <groupId>org.javassist</groupId>
          <artifactId>javassist</artifactId>
          <version>3.21.0-GA</version>
      </dependency>
    
  • 2、接口

      public interface JavaSsistProxyInterface {
          String getString();
      }
    
  • 3、业务类

      public class JavaSsistProxyImpl {
          public String getString(){
              return "JavaSsistProxyImpl result";
          }
      }
    
  • 4、JavaSsist代理类

      import javassist.util.proxy.MethodHandler;
      import javassist.util.proxy.ProxyFactory;
      import javassist.util.proxy.ProxyObject;
      import java.lang.reflect.Method;
    
      /**
       * JavaSsist代理测试
       * @Author liufu
       * @CreateTime 2018/3/28  12:29
       */
      public class JavassistProxy {
          public static void main(String[] args) throws InstantiationException, IllegalAccessException {
              //java业务类代理
              JavaSsistProxyImpl proxy  = (JavaSsistProxyImpl) createLocalProxy(JavaSsistProxyImpl.class);
              System.out.println(proxy.getString());
    
              //java接口代理
              JavaSsistProxyInterface proxy1 = (JavaSsistProxyInterface) createRpcProxy(JavaSsistProxyInterface.class);
              System.out.println(proxy1.getString());
          }
    
          /**
           * 创建本地的代理,可以使用具体业务类
           * 而且这个类不要求实现接口,这是和JavaProxy的区别,和CGlibProxy很像
           * 只是CGlibProxy的setSuperclass方法比较通用,可以传递interface也可以传递impl
           * @param targetClass
           * @return
           * @throws IllegalAccessException
           * @throws InstantiationException
           */
          public static Object createLocalProxy(Class targetClass) throws IllegalAccessException, InstantiationException {
              ProxyFactory proxyFactory = new ProxyFactory();
              proxyFactory.setSuperclass(targetClass);
              ProxyObject instance = (ProxyObject)proxyFactory.createClass().newInstance();
    
              instance.setHandler(new MethodHandler() {
                  @Override
                  // method是真实的方法,method1是代理方法,都不为null
                  // 一定要用method1.invoke,而不是method.invoke
                  public Object invoke(Object o, Method method, Method method1, Object[] objects) throws Throwable {
                      System.out.println(String.format("aop start, run method :%s", method.getName()));
                      String result = "this is result" + method1.invoke(o, objects);
                      System.out.println(String.format("aop end, finish run method :%s", method.getName()));
                      return result;
                  }
              });
    
              return instance;
          }
    
          /**
           * 创建rpc代理,传入接口类,使用netty等客户端进行远程过程调用
           * 注意:这个时候method1 == null
           * @param targetClass
           * @return
           * @throws IllegalAccessException
           * @throws InstantiationException
           */
          public static Object createRpcProxy(Class targetClass) throws IllegalAccessException, InstantiationException {
              ProxyFactory proxyFactory = new ProxyFactory();
              proxyFactory.setInterfaces(new Class[]{targetClass});
              ProxyObject instance = (ProxyObject)proxyFactory.createClass().newInstance();
    
              instance.setHandler(new MethodHandler() {
                  @Override
                  // method是真实的方法,此时一样不为null
                  // method1是代理方法,此时为null
                  public Object invoke(Object o, Method method, Method method1, Object[] objects) throws Throwable {
                      System.out.println(String.format("aop start, run method :%s", method.getName()));
                      String result = "this is result" + "===>RPC 返回了结果";
                      System.out.println(String.format("aop end, finish run method :%s", method.getName()));
                      return result;
                  }
              });
    
              return instance;
          }
      }
    

转载于:https://my.oschina.net/liufukin/blog/2222540

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值