通过前面的死磕,应该对AOP的原理都掌握了。annotation配置AOP,再没有讲的必要了。annotation和xml schema两种方式,仅仅是声明方式不同而已,其他的都一样。
Spring 作为一个流行的框架技术,它的代码设计是非常值得借鉴的。从编码风格到设计模式,有很多我们学习的点。本篇重点分析一下设计模式在AOP框架的使用,捎带着回顾下几个经典的设计模式。
使用设计模式,无外乎就是复用+解耦。引用鲁迅先生的一句话,“世间本没有模式,用的人多了,也便有了模式”。在学习模式的过程中,我经常学了忘,忘了学,费了不少劲,一直不得要领。最后,随着经验的积累,有了自己的一点认识。刚开始,我把重点放在类图,和角色的记忆,感觉挺简单的,感慨经过简单的组合搭配,实现系统解耦,可扩展性,对设计模式的作用有了认识,但这样学容易忘。慢慢的,我发现其实模式的学习,是场景与模式的匹配过程,需要我们去衡量,往往一个功能,有多个模式都能实现,这就要考验我们思维了。其实,往往很多优秀的框架技术,也会发生模式变化的,这都是一个道理。
主要内容包括
-
策略模式在AOP中的使用
-
模板模式在AOP中的使用
-
责任链模式在AOP中的使用
-
适配器模式在AOP中的使用
-
桥接模式在AOP中的使用
1策略模式在AOP的使用
1.1模式结构
-
Context: 环境类
-
Strategy: 抽象策略类
-
ConcreteStrategy: 具体策略类
1.2 spring中的使用
使用rose7画的图,接口实现的线有点问题。
策略模式的优点
-
策略模式提供了对“开闭原则”的完美支持,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为。
-
策略模式提供了管理相关的算法族的办法。
-
策略模式提供了可以替换继承关系的办法。
-
使用策略模式可以避免使用多重条件转移语句。
通过使用策略模式,比较容易灵活的实现了代理生成方式的替换。
简单看下环境类DefaultAopProxyFactory的代码片段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class targetClass = config.getTargetClass();
if (targetClass == null ) {
throw new AopConfigException( "TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation." );
}
if (targetClass.isInterface()) {
return new JdkDynamicAopProxy(config);
}
return CglibProxyFactory.createCglibProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
} |
1.3总结
-
在策略模式中定义了一系列算法,将每一个算法封装起来,并让它们可以相互替换。策略模式让算法独立于使用它的客户而变化,也称为政策模式。策略模式是一种对象行为型模式。
-
策略模式包含三个角色:环境类在解决某个问题时可以采用多种策略,在环境类中维护一个对抽象策略类的引用实例;抽象策略类为所支持的算法声明了抽象方法,是所有策略类的父类;具体策略类实现了在抽象策略类中定义的算法。
-
策略模式是对算法的封装,它把算法的责任和算法本身分割开,委派给不同的对象管理。策略模式通常把一个系列的算法封装到一系列的策略类里面,作为一个抽象策略类的子类。
-
策略模式主要优点在于对“开闭原则”的完美支持,在不修改原有系统的基础上可以更换算法或者增加新的算法,它很好地管理算法族,提高了代码的复用 性,是一种替换继承,避免多重条件转移语句的实现方式;其缺点在于客户端必须知道所有的策略类,并理解其区别,同时在一定程度上增加了系统中类的个数,可 能会存在很多策略类。
-
策略模式适用情况包括:在一个系统里面有许多类,它们之间的区别仅在于它们的行为,使用策略模式可以动态地让一个对象在许多行为中选择一种行为; 一个系统需要动态地在几种算法中选择一种;避免使用难以维护的多重条件选择语句;希望在具体策略类中封装算法和与相关的数据结构。
2.模板模式在AOP中的使用
2.1 模板方法模式
-
设计原则 :破坏里氏替换,体现功能复用
-
常用场景 :一批子类的功能有可提取的公共算法骨架
-
使用概率 :80%
-
复杂度 :中低
-
变化点 :算法骨架内各个步骤的具体实现
-
选择关键点 :算法骨架是否牢固
-
逆鳞 :无
角色
抽象类(AbstractClass):实现了模板方法,定义了算法的骨架。
具体类(ConcreteClass):实现抽象类中的抽象方法,已完成完整的算法。
2.2模式总结
优点
模板方法模式通过把不变的行为搬移到超类,去除了子类中的重复代码。
子类实现算法的某些细节,有助于算法的扩展。
通过一个父类调用子类实现的操作,通过子类扩展增加新的行为,符合“开放-封闭原则”。
缺点
每个不同的实现都需要定义一个子类,这会导致类的个数的增加,设计更加抽象。
适用场景
在某些类的算法中,用了相同的方法,造成代码的重复。
控制子类扩展,子类必须遵守算法规则。
2.3在spring中的使用
AOP相关的代理BeanPostProcessor。子类实现分别提供Annotation,schema等,实现模板是AbstractAutoProxyCreator.getAdvicesAndAdvisorsForBea和AbstractAdvisorAutoProxyCreator.findEligibleAdvisor 等处地方。
以AbstractAdvisorAutoProxyCreator.findEligibleAdvisor代码为例
1
2
3
4
5
6
7
8
9
|
protected List<Advisor> findEligibleAdvisors(Class beanClass, String beanName) {
List<Advisor> candidateAdvisors = findCandidateAdvisors();
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors); //子类可以覆盖
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors); //子类可以覆盖
}
return eligibleAdvisors;
} |
经过分析代码,模板模式在AbstractAutoProxyCreator结构树中的使用有多处地方使用了模板模式。
3.责任链模式在AOP中的使用
3.1 职责链模式结构
设计原则 :遵循迪米特
常用场景 :一个请求的处理需要多个对象当中的一个或几个协作处理
使用概率 :15%
复杂度 :中
变化点 :处理链的长度与次序
选择关键点 :对于每一次请求是否每个处理的对象都需要一次处理机会
逆鳞 :无
抽象处理者(Handler)角色:定义出一个处理请求的接口。如果需要,接口可以定义 出一个方法以设定和返回对下家的引用。这个角色通常由一个Java抽象类或者Java接口实现。上图中Handler类的聚合关系给出了具体子类对下家的引用,抽象方法handleRequest()规范了子类处理请求的操作。
具体处理者(ConcreteHandler)角色:具体处理者接到请求后,可以选择将请求处理掉,或者将请求传给下家。由于具体处理者持有对下家的引用,因此,如果需要,具体处理者可以访问下家。
优点:实现了请求者与处理者代码分离
缺点:了解处理流程困难
3.2 在AOP中的使用
其实,说这个不是责任链模式,也说得过去。因为这些Handler角色之间没有关系,他们之间的顺序是通过List管理的,也不太符合Handler"要么处理要么转发"这个特点。但,从使用者角度出发,这确实是个不折不扣的责任链模式。
4.适配器模式在AOP中的使用
4.1适配器模式结构
对象适配器:
类适配器
适配器模式包含如下角色:
-
Target:目标抽象类
-
Adapter:适配器类
-
Adaptee:适配者类
-
Client:客户类
适配器模式优点
将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,而无须修改原有代码。
增加了类的透明性和复用性,将具体的实现封装在适配者类中,对于客户端类来说是透明的,而且提高了适配者的复用性。
灵活性和扩展性都非常好,通过使用配置文件,可以很方便地更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,完全符合“开闭原则”。
类适配器模式还具有如下优点:
由于适配器类是适配者类的子类,因此可以在适配器类中置换一些适配者的方法,使得适配器的灵活性更强。
对象适配器模式还具有如下优点:
一个对象适配器可以把多个不同的适配者适配到同一个目标,也就是说,同一个适配器可以把适配者类和它的子类都适配到目标接口。
4.2适配器模式在Spring中的使用
这张图里还有另外一个模式。只需要关注MethodBeforeAdviceAdapter->MethodBeforeAdviceInterceptor-->MethodBeforeAdvice即可。
5.桥接模式在AOP中的使用
5.1模式结构
桥接模式包含如下角色:
-
Abstraction:抽象类
-
RefinedAbstraction:扩充抽象类
-
Implementor:实现类接口
-
ConcreteImplementor:具体实现类
桥接模式的优点:
分离抽象接口及其实现部分。
桥接模式有时类似于多继承方案,但是多继承方案违背了类的单一职责原则(即一个类只有一个变化的原因),复用性比较差,而且多继承结构中类的个数非常庞大,桥接模式是比多继承方案更好的解决方法。
桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。
实现细节对客户透明,可以对用户隐藏实现细节。
5.2桥接模式在Spring中的使用
严格来说,RefinedAbstraction有一个,这个设计图中基本每个Advisor都有对应的PointCut,和桥接模的设计这一点差别很大。还是那句话,从使用者角度来说,这应该也算桥接模式的一个范例。
总之,其实模式的落地应用与理论上,多多少少会存在出入。在不断深入了解过程中,慢慢补充吧。