一、装饰模式介绍
修饰符模式定义:不改变原始对象的附加函数比生成子类更灵活。
适用场景:动态的给一个对象添加或者撤销功能。
优点:它能够在不改变原有对象的情况下动态扩展函数,使扩展函数按照期望的顺序执行,达到不同的效果。
缺点:更多的类,使程序复杂
类型:结构型。
类图:
源码分析中的典型应用
- Java I/O 中的装饰者模式
- Spring Session 中的装饰者模式
- Mybatis 缓存中的装饰者模式
二、给系统添加日志,安全、限流示例
可以抽取出通用系统的安全性、日志、当前限制等独立于业务的代码,在控制器转换前后使用模板法模式可以部分解决上述问题。
public abstract class BaseAspect {
Logger logger = LoggerFactory.getLogger(BaseCommand.class);
public void execute(){
//记录日志
logger.debug("..start..");
//过滤跨站脚本攻击
paramXssAspect();
//限制速率
doRateLimit();
doBusiness();
logger.debug("..end..");
}
public abstract void doBusiness();
}
class PlaceOrderAspect extends BaseAspect {
@Override
public void doBusiness() {
//下单操作
}
}
class PayOrderAspect extends BaseAspect {
@Override
public void doBusiness() {
//支付操作
}
}
在父类中,已经编写了“杂乱”的非业务代码,只剩下一个抽象的方法和其他子类来实现,子类变得非常清新,只关注业务逻辑。
这种方法的最大缺点是父类定义了一切:为了执行那些非业务代码,按照什么顺序等等,子类只能被无条件地接受。如果有一个子类不限制速率,那么它就无法摆脱它。
通过使用装饰器模型,我们可以灵活地处理上述问题。
//最高层抽象组件
interface IAspect {
String doHandlerAspect();
}
//基本被装饰类,做一些公共处理
class AspectImpl implements IAspect{
@Override
public String doHandlerAspect() {
return "裸跑代码.";
}
}
abstract class AbstractDecorator implements IAspect{
//很重要,组合抽象构件到自己的类中
private IAspect aspect;
public AbstractDecorator(IAspect aspect) {//通过IAspect构造自己
this.aspect = aspect;
}
@Override
public String doHandlerAspect() {
return this.aspect.doHandlerAspect();
}
}
附加记录日志,安全,限流功能:
class LoggerAspect extends AbstractDecorator{
public LoggerAspect(IAspect aspect){
super(aspect);
}
@Override
public String doHandlerAspect() {
return super.doHandlerAspect()+"+记录日志.";
}
}
class ParamXssAspect extends AbstractDecorator{
public ParamXssAspect(IAspect aspect){
super(aspect);
}
@Override
public String doHandlerAspect() {
return super.doHandlerAspect()+"+过滤危险字符.";
}
}
class LimitAspect extends AbstractDecorator{
public LimitAspect(IAspect aspect){
super(aspect);
}
@Override
public String doHandlerAspect() {
return super.doHandlerAspect()+"+限流.";
}
}
测试一下:
public class Test {
public static void main(String[] args) {
IAspect aspect = new LimitAspect(new ParamXssAspect(new LoggerAspect(new AspectImpl())));
System.out.println(aspect.doHandlerAspect());
}
}
运行结果:
------
裸跑代码.+记录日志.+过滤危险字符.+限流.
------
从上面可以看出,装饰器模式可以按任意顺序组装函数,是否非常灵活?此外,上述三个函数还可以封装到注释@Log、@ParamXss、@AccessLimit中以实现可插拔性。
三、源码中的装饰者模式
3.1、Java IO中是体现最明显的装饰者模式。
它是.stream(InputStream/OutputStream),bytestream(Reader/Writer)是类,InputStream中的输入,ReaderClass:
这里总结几种常用流的应用场景:
3.2、Spring Session中的ServletRequestWrapper(Response也一样)的装饰者模式。
public class ServletRequestWrapper implements ServletRequest {
private ServletRequest request;//组合抽象接口到自己的类中
public ServletRequestWrapper(ServletRequest request) {//可以构造自己
if(request == null) {
throw new IllegalArgumentException("Request cannot be null");
} else {
this.request = request;
}
}
public ServletRequest getRequest() {
return this.request;
}
public void setRequest(ServletRequest request) {
if(request == null) {
throw new IllegalArgumentException("Request cannot be null");
} else {
this.request = request;
}
}
//省略...
}
3.3、Spring Cache中的TransactionAwareCacheDecorator的装饰者模式。
其实从类名就可以看出。
public class TransactionAwareCacheDecorator implements Cache {
private final Cache targetCache;//把Cache组合到自己类中
public TransactionAwareCacheDecorator(Cache targetCache) {//通过Cache构造自己
Assert.notNull(targetCache, "Target Cache must not be null");
this.targetCache = targetCache;
}
public <T> T get(Object key, Class<T> type) {
return this.targetCache.get(key, type);
}
public void put(final Object key, final Object value) {
// 判断是否开启了事务
if (TransactionSynchronizationManager.isSynchronizationActive()) {
// 将操作注册到 afterCommit 阶段
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
public void afterCommit() {
TransactionAwareCacheDecorator.this.targetCache.put(key, value);
}
});
} else {
this.targetCache.put(key, value);
}
}
// ...省略...
}
3.4、Mybatis中的装饰者。
Cache是一个抽象的组件类,PerpetualCache是一个具体的组件类,decorators包下的类是装饰类。没有抽象装饰类。