死磕Spring AOP系列5:设计模式在AOP中的使用

时间:2024-03-14 15:55:17

通过前面的死磕,应该对AOP的原理都掌握了。annotation配置AOP,再没有讲的必要了。annotation和xml schema两种方式,仅仅是声明方式不同而已,其他的都一样。

Spring 作为一个流行的框架技术,它的代码设计是非常值得借鉴的。从编码风格到设计模式,有很多我们学习的点。本篇重点分析一下设计模式在AOP框架的使用,捎带着回顾下几个经典的设计模式。

使用设计模式,无外乎就是复用+解耦。引用鲁迅先生的一句话,“世间本没有模式,用的人多了,也便有了模式”。在学习模式的过程中,我经常学了忘,忘了学,费了不少劲,一直不得要领。最后,随着经验的积累,有了自己的一点认识。刚开始,我把重点放在类图,和角色的记忆,感觉挺简单的,感慨经过简单的组合搭配,实现系统解耦,可扩展性,对设计模式的作用有了认识,但这样学容易忘。慢慢的,我发现其实模式的学习,是场景与模式的匹配过程,需要我们去衡量,往往一个功能,有多个模式都能实现,这就要考验我们思维了。其实,往往很多优秀的框架技术,也会发生模式变化的,这都是一个道理。

主要内容包括

  1. 策略模式在AOP中的使用

  2. 模板模式在AOP中的使用

  3. 责任链模式在AOP中的使用

  4. 适配器模式在AOP中的使用

  5. 桥接模式在AOP中的使用



1策略模式在AOP的使用

1.1模式结构

死磕Spring AOP系列5:设计模式在AOP中的使用

  • Context: 环境类

  • Strategy: 抽象策略类

  • ConcreteStrategy: 具体策略类


1.2 spring中的使用

死磕Spring AOP系列5:设计模式在AOP中的使用


使用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%

  • 复杂度      :中低

  • 变化点      :算法骨架内各个步骤的具体实现

  • 选择关键点      :算法骨架是否牢固

  • 逆鳞      :无

死磕Spring AOP系列5:设计模式在AOP中的使用

角色

抽象类(AbstractClass):实现了模板方法,定义了算法的骨架。

具体类(ConcreteClass):实现抽象类中的抽象方法,已完成完整的算法。

2.2模式总结
优点
模板方法模式通过把不变的行为搬移到超类,去除了子类中的重复代码。
子类实现算法的某些细节,有助于算法的扩展。
通过一个父类调用子类实现的操作,通过子类扩展增加新的行为,符合“开放-封闭原则”。
缺点
每个不同的实现都需要定义一个子类,这会导致类的个数的增加,设计更加抽象。
适用场景
在某些类的算法中,用了相同的方法,造成代码的重复。
控制子类扩展,子类必须遵守算法规则。

2.3在spring中的使用

死磕Spring AOP系列5:设计模式在AOP中的使用

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%
    复杂度 :中
    变化点 :处理链的长度与次序
    选择关键点 :对于每一次请求是否每个处理的对象都需要一次处理机会
    逆鳞 :无

死磕Spring AOP系列5:设计模式在AOP中的使用

   抽象处理者(Handler)角色:定义出一个处理请求的接口。如果需要,接口可以定义 出一个方法以设定和返回对下家的引用。这个角色通常由一个Java抽象类或者Java接口实现。上图中Handler类的聚合关系给出了具体子类对下家的引用,抽象方法handleRequest()规范了子类处理请求的操作。

具体处理者(ConcreteHandler)角色:具体处理者接到请求后,可以选择将请求处理掉,或者将请求传给下家。由于具体处理者持有对下家的引用,因此,如果需要,具体处理者可以访问下家。
优点:实现了请求者与处理者代码分离

缺点:了解处理流程困难

3.2 在AOP中的使用

死磕Spring AOP系列5:设计模式在AOP中的使用

其实,说这个不是责任链模式,也说得过去。因为这些Handler角色之间没有关系,他们之间的顺序是通过List管理的,也不太符合Handler"要么处理要么转发"这个特点。但,从使用者角度出发,这确实是个不折不扣的责任链模式。

4.适配器模式在AOP中的使用

4.1适配器模式结构

对象适配器:

死磕Spring AOP系列5:设计模式在AOP中的使用

类适配器

死磕Spring AOP系列5:设计模式在AOP中的使用

适配器模式包含如下角色:

  • Target:目标抽象类

  • Adapter:适配器类

  • Adaptee:适配者类

  • Client:客户类


适配器模式优点
    将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,而无须修改原有代码。
    增加了类的透明性和复用性,将具体的实现封装在适配者类中,对于客户端类来说是透明的,而且提高了适配者的复用性。
    灵活性和扩展性都非常好,通过使用配置文件,可以很方便地更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,完全符合“开闭原则”。

类适配器模式还具有如下优点:
    由于适配器类是适配者类的子类,因此可以在适配器类中置换一些适配者的方法,使得适配器的灵活性更强。
对象适配器模式还具有如下优点:
    一个对象适配器可以把多个不同的适配者适配到同一个目标,也就是说,同一个适配器可以把适配者类和它的子类都适配到目标接口。 

4.2适配器模式在Spring中的使用

死磕Spring AOP系列5:设计模式在AOP中的使用

这张图里还有另外一个模式。只需要关注MethodBeforeAdviceAdapter->MethodBeforeAdviceInterceptor-->MethodBeforeAdvice即可。

5.桥接模式在AOP中的使用

5.1模式结构

死磕Spring AOP系列5:设计模式在AOP中的使用

桥接模式包含如下角色:

  • Abstraction:抽象类

  • RefinedAbstraction:扩充抽象类

  • Implementor:实现类接口

  • ConcreteImplementor:具体实现类

桥接模式的优点:

    分离抽象接口及其实现部分。
    桥接模式有时类似于多继承方案,但是多继承方案违背了类的单一职责原则(即一个类只有一个变化的原因),复用性比较差,而且多继承结构中类的个数非常庞大,桥接模式是比多继承方案更好的解决方法。
    桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。
    实现细节对客户透明,可以对用户隐藏实现细节。
5.2桥接模式在Spring中的使用

死磕Spring AOP系列5:设计模式在AOP中的使用

严格来说,RefinedAbstraction有一个,这个设计图中基本每个Advisor都有对应的PointCut,和桥接模的设计这一点差别很大。还是那句话,从使用者角度来说,这应该也算桥接模式的一个范例。


总之,其实模式的落地应用与理论上,多多少少会存在出入。在不断深入了解过程中,慢慢补充吧。




本文转自 randy_shandong 51CTO博客,原文链接:http://blog.51cto.com/dba10g/1786250,如需转载请自行联系原作者