代理模式
# 代理模式
代理模式,通过引入一个代理对象来间接访问原始对象,以控制对原始对象的访问。代理对象充当了客户端和原始对象之间的中介,可以在访问原始对象之前或之后执行一些额外的逻辑操作。
# 定义
代理模式,为其他对象提供一种代理(Proxy)以控制对这个对象的访问。代理是指具有与被代理的对象具有相同的接口的类,客户端通过代理与被代理的目标类交互,而代理一般在交互的过程中(交互前后),进行某些特别的处理。
代理模式又分为静态代理和动态代理。动态代理常用 JDK代理和 CGLIB代理。
静态代理: 在编译时就已经将接口、被代理类、代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成。
动态代理:在编译期间无法确定需要代理的类。运行期间动态生成代理类。
JDK动态代理与CGLIB动态代理区别
使用jdk代理的目标类必须实现至少一个接口,因为它是通过java反射实现,基于接口创建代理对象。使用cglib代理的目标类则无需实现,但其不能被final修饰,因为其运用了继承关系。
# 结构
抽象主题(Subject):定义了真实主题(Real Subject)和代理(Proxy)之间的共同接口,客户端通过该接口访问真实主题或代理对象。
真实主题(Real Subject):定义了代理所代表的真实对象,是被代理的具体业务对象。真实主题包含了真正的业务逻辑,代理通过调用真实主题的方法来完成具体的业务。
代理(Proxy):实现了抽象主题接口,同时持有对真实主题的引用。代理对象在客户端和真实主题之间充当了中介的角色,它可以在调用真实主题之前或之后执行一些额外的逻辑操作。
- img: https://bitouyun.com/images/design-pattern/proxy.png
link: https://bitouyun.com/images/design-pattern/proxy.png
name: 代理模式
2
3
# 优点
隐藏对象的具体实现:代理模式可以隐藏原始对象的具体实现细节,使客户端只关注代理对象的接口。这样可以提高系统的安全性和稳定性,防止客户端直接访问和修改原始对象。
访问控制:代理模式可以在代理对象中添加访问控制的逻辑,限制对原始对象的访问。例如,可以在代理对象中检查客户端的权限,只有满足一定条件的客户端才能访问原始对象。
延迟加载:代理对象可以在真正需要时才创建和初始化原始对象,可以避免在一开始就创建代价昂贵的原始对象。
缓存数据:代理对象可以在访问原始对象之前或之后缓存数据,以提高系统的响应速度。例如,代理对象可以缓存一些频繁访问的数据,当下次请求时,可以直接返回缓存的数据,而无需再次访问原始对象。
# 缺点
增加复杂性:引入代理模式会增加系统的复杂性,需要定义和管理额外的代理类。如果系统中有多个原始对象,就需要为每个原始对象定义相应的代理类,增加了代码量和维护成本。
增加间接性:代理模式引入了额外的代理层,导致客户端访问原始对象变得间接,增加了访问的复杂性和开销。
可能影响系统性能:在某些情况下,代理模式可能会对系统性能产生一定的影响。例如,在频繁访问原始对象的情况。
# 应用场景
代理模式在需要控制对原始对象的访问、隐藏实现细节、提高性能或添加额外逻辑时是一种有用的设计模式。但在使用时需要权衡其带来的复杂性和性能影响。
代理模式常用在业务系统中开发一些非功能性需求,比如:监控、统计、鉴权、限流、事务、幂等、日志。我们将这些附加功能与业务功能解耦,放到代理类统一处理,让程序员只需要关注业务方面的开发。除此之外,代理模式还可以用在RPC、缓存等应用场景中。
代理模式与装饰器模式的区别
装饰器模式是动态地给对象添加额外的功能,通过包装器对象来包装原始对象,以扩展其功能。
装饰器模式注重对对象功能的增强和扩展,而代理模式注重对对象的控制访问。
装饰器模式通过包装器对象来包装原始对象,形成一条装饰链;代理模式通过代理对象来控制对原始对象的访问。
装饰器模式保持了原始对象的接口完整性,客户端可以透明地使用装饰后的对象;代理模式可以隐藏原始对象的细节,客户端可能无法直接访问原始对象。
实际上,装饰器模式可以看作是一种特殊的代理模式,其中代理对象的主要职责是增强原始对象的功能。因此,装饰器模式和代理模式可以相互补充,并在某些情况下可以互换使用。
# 示例代码1
场景1
添加用户业务,使用代理模式记录消耗时间日志。
/**
* 抽象主题
* 定义了真实主题(Real Subject)和代理(Proxy)之间的共同接口,
* 在任何需要真实主题的地方都可以使用代理。
*/
public interface UserService {
/**
* 添加用户
* @param username 用户名
*/
void addUser(String username);
}
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 真实主题
* 实现抽象主题,实际业务实现
*/
@Slf4j
public class UserServiceImpl implements UserService{
@Override
public void addUser(String username) {
// 添加用户具体业务实现
log.info("添加用户:{}", username);
}
}
2
3
4
5
6
7
8
9
10
11
12
/**
* 静态代理
* 实现UserService抽象主题接口,并持有一个真实主题的引用。
*/
@Slf4j
public class UserServiceProxy implements UserService {
private UserService realSubject;
public UserServiceProxy(UserService realSubject) {
this.realSubject = realSubject;
}
@Override
public void addUser(String username) {
long startTime = System.currentTimeMillis();
// 调用真实主题,添加用户业务
realSubject.addUser(username);
long endTime = System.currentTimeMillis();
log.info("静态代理添加用户,耗时:{}", endTime - startTime);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* JDK动态代理
*/
@Slf4j
public class UserServiceJDKProxy implements InvocationHandler {
private Object realSubject;
public UserServiceJDKProxy(Object realSubject) {
this.realSubject = realSubject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long startTime = System.currentTimeMillis();
// 调用添加用户业务
Object result = method.invoke(realSubject, args);
long endTime = System.currentTimeMillis();
log.info("jdk动态代理添加用户,耗时:{}", endTime - startTime);
return result;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* CGLIB动态代理
*/
@Slf4j
public class UserServiceCglibProxy implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = proxy.invokeSuper(obj, args);
long endTime = System.currentTimeMillis();
log.info("CGLIB动态代理添加用户,耗时:{}", endTime - startTime);
return result;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import net.sf.cglib.proxy.Enhancer;
import java.lang.reflect.Proxy;
/**
* 测试类
*/
public class Client {
public static void main(String[] args) {
// 静态代理
UserService realSubject = new UserServiceImpl(); // 真实主题
UserService proxy = new UserServiceProxy(realSubject); // 代理
proxy.addUser("Mike");
// JDK动态代理
UserService jdkProxy = (UserService) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(),
realSubject.getClass().getInterfaces(),
new UserServiceJDKProxy(realSubject));
jdkProxy.addUser("Alice");
// CGLIB动态代理(引入cglib库)
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserServiceImpl.class);
enhancer.setCallback(new UserServiceCglibProxy());
UserServiceImpl cglibProxy = (UserServiceImpl) enhancer.create();
cglibProxy.addUser("Bob");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Make sure to add code blocks to your code group
常见问题
- 添加CGLIB依赖
<!-- CGLIB -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
2
3
4
5
6
2.非法反射访问操作
WARNING: An illegal reflective access operation has occurred
IDEA设置VM OPTION:
--add-opens java.base/java.lang=ALL-UNNAMED