# 【结构型模式】代理模式
# 代理模式的概念
概念(定义):在不改变原始类的情况下,通过引入代理类来给原始类附件功能。
从概念上可以大概知道代理模式的使用场景是在维护旧代码时,一种优秀的代码设计思路。
# 代理模式的使用场景
其实不止在维护旧代码时会使用到代理模式,通过了解实现代码可以发现,代理模式类似于一种包裹原有代码实现的方式,可以在原有代码的前后增加新的逻辑,扩充了原有逻辑的实现。
总结可以使用代理模式的场景有:
- A逻辑代码前后存在与A无关的逻辑代码B,将B逻辑作为代理模式去实现(例如,一段代码的性能测试代码,日志代码等)
- 业务系统中的非功能性需求的开发:监控、统计、鉴权、限流、事务、幂等、日志等。能用AOP实现的都可以用代理模式,毕竟AOP就是用代理模式实现的。
- RPC框架
- 缓存框架
- 延迟加载
# 代理模式的实现
代理模式也称为静态代理模式,一般实现方法有两种,也对应两种不同的场景: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();
}
# 动态代理
静态代理模式的弊端有
- 如果只需要代理一个方法,却要将接口中所有的方法都实现一遍
- 若需要添加代理功能的类不止一个,需要为每个接口都实现一个代理类,而添加重复的代理功能
以上问题会导致类成倍增加,同时增加代码的维护成本。解决办法就是动态代理技术。
定义一个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库. 这几个是常用的实现动态代理的方法. 其中CGLib和Javassist是最常用的高级字节码生成工具.