代理模式 Proxy

发布时间 2023-12-21 08:54:44作者: 梅丹隆

一、定义

  • 为其他对象提供一种代理,以控制对这个对象的访问
  • 代理对象在客户端和目前对象之间起到中介的作用

二、适用场景

  • 保护目标对象
  • 增强目标对象

三、优缺点

1、优点

  1. 代理模式能将代理对象与真实被调用的目标对象分离
  2. 一定程度上降低了系统的耦合度,扩展性好
  3. 保护目标对象
  4. 增强目标对象

2、缺点

  1. 代理模式会造成系统设计中类的数目增加
  2. 在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢
  3. 增加系统的复杂度

四、代码实现

https://github.com/Meidanlong/all-in-one/tree/master/design/src/main/java/com/mdl/design/pattern/structural/proxy

1、静态代理

image.png

2、动态代理

2.1、JDK

public class OrderServiceDynamicProxy implements InvocationHandler {

    private Object target;

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

    public Object bind(){
        Class cls = target.getClass();
        return Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object argObject = args[0];
        beforeMethod(argObject);
        Object object = method.invoke(target,args);
        afterMethod();
        return object;
    }

    private void beforeMethod(Object obj){
        int userId = 0;
        System.out.println("动态代理 before code");
        if(obj instanceof Order){
            Order order = (Order)obj;
            userId = order.getUserId();
        }
        int dbRouter = userId % 2;
        System.out.println("动态代理分配到【db"+dbRouter+"】处理数据");

        //todo 设置dataSource;
        DataSourceContextHolder.setDBType("db"+String.valueOf(dbRouter));
    }

    private void afterMethod(){
        System.out.println("动态代理 after code");
    }
}

2.2、Cglib

:::warning
基于继承
:::

public class CglibProxy implements MethodInterceptor {

    /**
     * 被代理对象
     */
    private Object target;

    /**
     * 利用构造函数设置被代理对象
     *
     * @param target
     */
    public CglibProxy(Object target) {
        this.target = target;
    }

    /**
     * 创建代理对象
     *
     * @return
     */
    public Object newProxyInstance() {
        /**
         * 创建Enhancer实例
         */
        Enhancer enhancer = new Enhancer();
        /**
         * 设置被代理类
         */
        enhancer.setSuperclass(this.target.getClass());
        /**
         * 设置回调
         */
        enhancer.setCallback(this);
        /**
         * 创建代理
         */
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        before();
        Object result = methodProxy.invokeSuper(o, objects);
        after();
        return new Dog();
    }

    private void before(){
        System.out.println("Cglib动态代理-方法前调用");
    }

    private void after(){
        System.out.println("Cglib动态代理-方法后调用");
    }
}

2.3、两种动态代理性能比较

JDK7/8的动态代理大约比Cglib快20%左右

2.4、Spring代理的选择

  • 当Bean有实现接口时,Spring就会用JDK的动态代理
  • 当Bean没有实现接口时,Spring适用Cglib
  • 可以强制使用Cglib
    • 在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>