大家好,我是徒手敲代码。
今天来介绍一下代理模式。
在日常生活中,当需要完成一项专业性强、涉及复杂流程的任务时,比如:买房,我们会选择委托给专业人士------房地产中介。这个中间人扮演的就是代理角色。他凭借专业知识,帮我们处理各种细节,确保任务顺利完成。但是如果我们省去这些代理,直接与多方交涉,可能会面临信息不对称、流程不熟等问题。
像这种专业的事情,交给专业的人去做的原则,就相当于软件范畴中的代理模式。
假设有一个UserService
接口,定义了新增用户insertUser()
和更新用户updateUser()
的方法。实现类UserServiceImpl
,实现了这个接口。
java
public interface UserService {
void insertUser(User user);
void updateUser(User user);
}
public class UserServiceImpl implements UserService {
@Override
public void insertUser(User user) {
// 实现用户插入逻辑
}
@Override
public void updateUser(User user) {
// 实现用户更新逻辑
}
如果我们需要在调用insertUser()
和updateUser()
前打印日志,并且不使用代理模式,直接在方法里面加其他逻辑,就变成这样:
typescript
public class UserServiceImpl implements UserService {
@Override
public void insertUser(User user) {
log("Inserting user...");
// 实现用户插入逻辑
}
@Override
public void updateUser(User user) {
log("Updating user...");
// 实现用户更新逻辑
}
private void log(String message) {
// 打印日志的具体实现
}
}
过了两天,如果又要在那两个方法前,加个事务,那么原来的方法又要再改一次。虽然这样可以实现功能,但是随着时间的推移,这段代码会越来越复杂。显然,是不符合对扩展开放,对修改关闭这个原则。
这个问题的解决方案是,可以加多一个代理类,负责做业务范畴以外的逻辑。比如,创建一个UserServiceProxy
类,同样实现UserService
接口,并持有一个UserService
对象。代理类负责封装日志和事务管理逻辑,将原本直接对UserServiceImpl
的操作转交给代理类
typescript
public class UserServiceProxy implements UserService {
private final UserService target;
public UserServiceProxy(UserService target) {
this.target = target;
}
@Override
public void insertUser(User user) {
beginTransaction();
log("Inserting user...");
target.insertUser(user);
commitTransaction();
}
@Override
public void updateUser(User user) {
beginTransaction();
log("Updating user...");
target.updateUser(user);
commitTransaction();
}
private void log(String message) {...}
private void beginTransaction() {...}
private void commitTransaction() {...}
private void rollbackTransaction() {...}
}
// 使用代理类
UserService userService = new UserServiceProxy(new UserServiceImpl());
userService.insertUser(someUser);
像这种预先定义好代理类结构,并实现相关逻辑的模式,称为静态代理。
显然,这种方式有个缺点,就是当需要为很多个类添加相同额外操作的时候,就要对每个类都写一个代理类,会出现大量重复的代码。
此时此刻,动态代理就诞生了。
Java提供了两种实现动态代理的方式:基于**InvocationHandler
接口的JDK原生动态代理和基于CGLIB**库的字节码生成技术。
基于接口的动态代理:
typescript
public class LoggingAndTransactionalProxy implements InvocationHandler {
private final Object target;
public static <T> T createProxy(T target) {
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new LoggingAndTransactionalProxy(target)
);
}
private LoggingAndTransactionalProxy(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
beginTransaction();
try {
log(method.getName() + " invoked...");
Object result = method.invoke(target, args);
commitTransaction();
return result;
} catch (Exception e) {
rollbackTransaction();
log(method.getName() + " failed.");
throw e;
}
}
private void log(String message) {...}
private void beginTransaction() {...}
private void commitTransaction() {...}
private void rollbackTransaction() {...}
}
// 使用动态代理
UserService userService = LoggingAndTransactionalProxy.createProxy(new UserServiceImpl());
userService.insertUser(someUser);
注意:这种方式,必须要求构造参数的入参对象类型,实现了一个或多个接口。
CGLIB动态代理
typescript
public class LoggingAndTransactionalInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
beginTransaction();
try {
log(method.getName() + " invoked...");
Object result = proxy.invokeSuper(obj, args);
commitTransaction();
return result;
} catch (Exception e) {
rollbackTransaction();
log(method.getName() + " failed.");
}
}
private void log(String message) {...}
private void beginTransaction() {...}
private void commitTransaction() {...}
private void rollbackTransaction() {...}
public static <T> T createProxy(T target) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(new LoggingAndTransactionalInterceptor());
return (T) enhancer.create();
}
// 使用CGLIB动态代理
UserService userService = LoggingAndTransactionalInterceptor.createProxy(new UserServiceImpl());
userService.insertUser(someUser);
}
这两种方式,都是在调用的时候,才传入被代理的对象,所以会灵活很多。
最后,来说一下代理模式和装饰者模式的区别,简单来理解,代理模式,就是多了个外人来增强功能,所以这些功能跟被代理类本身,是没说明关系的。就像房地产中介处理房子买卖、过户很厉害,关你买家什么事,都是中介自己的能力。
而装饰者模式,增强的功能是属于自己的功能,想要更加深入的了解装饰着模式,请期待下一篇的文章。
今天的分享到这里结束了。
关注公众号"徒手敲代码",免费领取腾讯大佬推荐的Java电子书!