设计公司商业模式案例 (代理模式100种原理)

代理模式(Proxy Design Pattern ) 原始定义是:让你能够提供对象的替代品或 其占位符。代理控制着对于原对象的访问,并允许将请求提交给对象前后进行 一些处理。

代理模式也叫做委托模式,它是一项基本设计技巧。许多其他的模式,如状态模式、策 略模式、访问者模式本质上是在更特殊的场合采用了委托模式,而且在日常的应用中,代理 模式可以提供非常好的访问控制。在一些著名开源软件中也经常见到它的身影,如Struts2的 Form元素映射就采用了代理模式(准确地说是动态代理模式)。

我们先看一下类图中的三个 角色的定义:

● Subject抽象主题角色 抽象主题类可以是抽象类也可以是接口,是一个最普通的业务类型定义,无特殊要求。

● RealSubject具体主题角色 也叫做被委托角色、被代理角色。它才是冤大头,是业务逻辑的具体执行者。

● Proxy代理主题角色 也叫做委托类、代理类。它负责对真实角色的应用,把所有抽象主题类定义的方法限制 委托给真实主题角色实现,并且在真实主题角色处理完毕前后做预处理和善后处理工作。

public interface UserDao {

  void save();
}

publicclassUserDaoImplimplementsUserDao{

  @Override
    public void save() {
        System.out.println("保存数据");
    }
}

1、静态代理

public class UserDaoProxy implements UserDao {
  
    private UserDao target;

    public UserDaoProxy(UserDao target) {
        this.target = target;
    }

    @Override
    public void save() {
        System.out.println("开启事务"); //扩展额外的功能
        target.save();
        System.out.println("提交事务");
    }
}


//测试静态代理
    @Test
    public void testStaticProxy(){

        //目标类
        UserDao dao = new UserDaoImpl();

        //代理对象
        UserDaoProxy proxy = new UserDaoProxy(dao);
        proxy.save();
    }

2、动态代理 2.1 jdk动态代理

Jdk动态代理对象必须实现接口,通过事件处理器 InvocationHandler 调用 invoke 方法;

public class ProxyFactory {

    //维护一个目标对象
    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }

    //为目标对象生成代理对象
    public Object getProxyInstance(){

        return Proxy.newProxyInstance(
                //目标类使用的类加载器
                target.getClass().getClassLoader(),
                //目标对象实现的接口类型
                target.getClass().getInterfaces(),
                new InvocationHandler() { //事件处理器

                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                        System.out.println("开启事务");
                        method.invoke(target,args);
                        System.out.println("提交事务");
                        return null;
                    }
                }
        );
    }
}

 @Test
 public void testJdkProxy(){

        UserDao userDao = new UserDaoImpl();
        System.out.println(userDao.getClass()); //目标对象的信息

        UserDao proxy = (UserDao) new ProxyFactory(userDao).getProxyInstance();//获取代理对象
        System.out.println(proxy.getClass());
        proxy.save();//代理方法
    }

java.lang.reflect

public interface InvocationHandler {

   // 在代理实例上处理方法调用并返回结果。
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

2.2 Cglib代理

cglib动态代理的被代理对象不需要实现接口,代理对象实现 MethodInterceptor 接口,实现回调方法 intercept

publicclassUserServiceImpl{

    //查询功能
    public Integer  findUserList(){

        return 18;
    }
}

public class UserCglibProxy implements MethodInterceptor {

    /**
     * 生成CGLIB动态代理类方法
     * @param target    需要被代理的目标类
     * @return: java.lang.Object  代理类对象
     */
    public Object getCglibProxy(Object target){

        //增强器类,用来创建动态代理类
        Enhancer enhancer = new Enhancer();

        //设置代理类的父类字节码对象
        enhancer.setSuperclass(target.getClass());

        //设置回调
        enhancer.setCallback(this);

        //创建动态代理对象,并返回
        return enhancer.create();
    }

    /**
     * 实现回调方法
     * @param o      代理对象
     * @param method  目标对象中的方法的Method实例
     * @param args      实际参数
     * @param methodProxy  代理类对象中的方法的Method实例
     * @return: java.lang.Object
     */
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

        Calendar instance = Calendar.getInstance();
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        System.out.println(format.format(instance.getTime()) + "[ " +method.getName() +"] 查询信息...");
        return methodProxy.invokeSuper(o, args);
    }
}


@Test
public void testCglibProxy(){

  //目标对象
  UserServiceImpl userService = new UserServiceImpl();
  System.out.println(userService.getClass());

  //代理对象
  UserServiceImpl proxy = (UserServiceImpl) new UserCglibProxy().getCglibProxy(userService);
  System.out.println(proxy.getClass());

  Integer i = proxy.findUserList();
  System.out.println("用户信息: " +i);
}


3、开源实战

mybatis中的代理模式简单分析

publicclassMapperProxy<T>implementsInvocationHandler,Serializable{

  private static final long serialVersionUID = -4724728412955527868L;
  private static final int ALLOWED_MODES = MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
      | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC;
  private static final Constructor<Lookup> lookupConstructor;
  private static final Method privateLookupInMethod;
  // 记录了关联的sqlSession对象
  private final SqlSession sqlSession;
  // mapper接口对应的Class对象
  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethodInvoker> methodCache;

  // 用于缓存MapperMethod对象,其中key是mapper接口中方法对应的Method对象,value是对应的MapperMethod对象,MapperMethod对象会完成参数转换
  // 以及SQL语句的执行功能,需要注意的是,MapperMethod中并不记录任何状态相关的信息,所以可以在多个代理对象之间共享
  public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethodInvoker> methodCache) {
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
  }


  static {
    Method privateLookupIn;
    try {
      privateLookupIn = MethodHandles.class.getMethod("privateLookupIn", Class.class, MethodHandles.Lookup.class);
    } catch (NoSuchMethodException e) {
      privateLookupIn = null;
    }
    privateLookupInMethod = privateLookupIn;


    Constructor<Lookup> lookup = null;
    if (privateLookupInMethod == null) {
      // JDK 1.8
      try {
        lookup = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
        lookup.setAccessible(true);
      } catch (NoSuchMethodException e) {
        throw new IllegalStateException(
            "There is neither 'privateLookupIn(Class, Lookup)' nor 'Lookup(Class, int)' method in java.lang.invoke.MethodHandles.",
            e);
      } catch (Exception e) {
        lookup = null;
      }
    }
    lookupConstructor = lookup;
  }


  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      // 如果目标方法继承自Object,则直接调用目标方法
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else {
        return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }


  private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
    try {
      return MapUtil.computeIfAbsent(methodCache, method, m -> {
        if (m.isDefault()) {
          try {
            if (privateLookupInMethod == null) {
              return new DefaultMethodInvoker(getMethodHandleJava8(method));
            } else {
              return new DefaultMethodInvoker(getMethodHandleJava9(method));
            }
          } catch (IllegalAccessException | InstantiationException | InvocationTargetException
              | NoSuchMethodException e) {
            throw new RuntimeException(e);
          }
        } else {
          return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
        }
      });
    } catch (RuntimeException re) {
      Throwable cause = re.getCause();
      throw cause == null ? re : cause;
    }
  }


非常典型的,该 MapperProxy 类实现了 InvocationHandler 接口,并且实现了该接口的 invoke 方法。

通过这种方式,我们只需要编写 Mapper.java 接口类,当真正执行一个Mapper 接口的时候,就会转发给 MapperProxy.invoke 方法,而该方法则会调用后续的 sqlSession.crud>executor*ex.e**cute>prepareStatement 等一系列方法,完成SQL的执行和返回。