# 【结构型模式】代理模式

# 代理模式的概念

概念(定义):在不改变原始类的情况下,通过引入代理类来给原始类附件功能。

从概念上可以大概知道代理模式的使用场景是在维护旧代码时,一种优秀的代码设计思路。

# 代理模式的使用场景

其实不止在维护旧代码时会使用到代理模式,通过了解实现代码可以发现,代理模式类似于一种包裹原有代码实现的方式,可以在原有代码的前后增加新的逻辑,扩充了原有逻辑的实现。

总结可以使用代理模式的场景有:

  1. A逻辑代码前后存在与A无关的逻辑代码B,将B逻辑作为代理模式去实现(例如,一段代码的性能测试代码,日志代码等)
  2. 业务系统中的非功能性需求的开发:监控、统计、鉴权、限流、事务、幂等、日志等。能用AOP实现的都可以用代理模式,毕竟AOP就是用代理模式实现的。
  3. RPC框架
  4. 缓存框架
  5. 延迟加载

# 代理模式的实现

代理模式也称为静态代理模式,一般实现方法有两种,也对应两种不同的场景:1.实现接口的代理模式 2.继承的代理模式

# 接口代理模式

存在一个业务接口类

public interface IUserService {
    void doing();
}

实现了业务接口类的业务逻辑代码

public class MyUserService implements IUserService {
    @Override
    public void doing() {
        System.out.println("I want doing something...");
    }
}

设计代理类,实现业务接口类,并且定义业务接口类(或实现类)作为属性,提供构造方式或set方式设置业务类属性。同时实现业务接口的方法,在方法中可以做到对原有业务类逻辑前后做各种处理

public class UserServiceProxy implements IUserService {
    public IUserService userService;
    public UserServiceProxy(IUserService userService) {
        this.userService = userService;
    }
    @Override
    public void doing() {
        before();
        userService.doing();
        after();
    }

    public void before() {
        System.out.println("before");
    }

    public void after() {
        System.out.println("after");
    }
}

代理方法使用样例

public static void main(String[] args) {
    IUserService service = new MyUserService();
    // 将自己传入到proxy中,让代理类帮你去做
    UserServiceProxy proxy = new UserServiceProxy(service);
    proxy.doing();
}

# 继承代理模式

如果原始类不是我们自己开发维护的,没有定义接口类,我们可以使用继承来实现代理模式。

定义业务实现类(通常是第三方的代码)

public class SdkService {
    public void doing() {
        System.out.println("sdk service doing something...");
    }
}

继承业务类,定义代理类proxy,在其中定义业务类为属性,并提供设置属性的方法,重载业务类的方法,在方法中利用super.doing()调用业务类,同时可以包装自己需要的代码。

public class SdkServiceProxy extends SdkService {
    private SdkService sdkService;

    public SdkServiceProxy(SdkService service) {
        this.sdkService = service;
    }

    @Override
    public void doing() {
        System.out.println("before");
        super.doing();
        System.out.println("after");
    }
}

执行样例

public static void main(String[] args) {
    SdkServiceProxy proxy = new SdkServiceProxy(new SdkService());
    proxy.doing();
}

# 动态代理

静态代理模式的弊端有

  1. 如果只需要代理一个方法,却要将接口中所有的方法都实现一遍
  2. 若需要添加代理功能的类不止一个,需要为每个接口都实现一个代理类,而添加重复的代理功能

以上问题会导致类成倍增加,同时增加代码的维护成本。解决办法就是动态代理技术。

定义一个InvocationHandler的实现类,调用Proxy类提供的构造代理类的方法,创建出自定义的代理类:

public class JdkDynamicProxy {
    public Object createProxy(Object proxiedObject) {
        Class<?>[] interfaces = proxiedObject.getClass().getInterfaces();
        DynamicProxyHandler handler = new DynamicProxyHandler(proxiedObject);
        return Proxy.newProxyInstance(proxiedObject.getClass().getClassLoader(), interfaces, handler);
    }

    private class DynamicProxyHandler implements InvocationHandler {
        private Object proxiedObject;

        public DynamicProxyHandler(Object proxiedObject) {
            this.proxiedObject = proxiedObject;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("jdk dynamic proxy before");
            Object result = method.invoke(proxiedObject, args);
            System.out.println("jdk dynamic proxy after");
            return result;
        }
    }
}

调用方法:

public static void main(String[] args) {
    JdkDynamicProxy proxy = new JdkDynamicProxy();
    IUserService myProxy = (IUserService) proxy.createProxy(new MyUserService());
    myProxy.doing();
}

# 动态代理的实现库

动态代理包括: JDK动态代理/CGLib/javassist/ASM库. 这几个是常用的实现动态代理的方法. 其中CGLibJavassist是最常用的高级字节码生成工具.

# CGLib
# Javassist
修改于: 8/11/2022, 3:17:56 PM