Spring:面向切面编程的AOP

1、前言

  除了依赖注入(DI),Spring框架提供的另外一个核心功能是对面向方面的编程(AOP)的支持。 AOP一般被称为实现横切关注点的工具。横切关注点一词是指应用程序中的逻辑不能与应用程序的其他部分分离并有效模块化的地方,而且可能致使代码重复和紧密耦合。经过使用AOP模块化单个逻辑(即关注点),能够将它们应用于应用程序的许多部分,而无需复制代码或建立硬依赖关系。日志记录和安全性是许多应用程序中横切关注点的典型示例。AOP是对面向对象编程(OOP)的补充,而不是与之竞争。 OOP很是擅长解决咱们做为程序员遇到的各类问题。鉴于AOP能够在OOP之上运行,所以几乎不可能单独使用AOP来开发整个应用程序,尽管使用OOP固然能够开发整个应用程序,可是经过使用AOP解决涉及横切逻辑的某些问题,能更本身聪明地进行工做。java

  • AOP基础知识程序员

  • AOP的类型spring

  • Spring AOP体系结构编程

  • Spring AOP中的代理
  • 使用Spring AOP安全

  • 切入点的高级用法
  • AOP框架服务架构

  • 集成AspectJ框架

2、AOP基础知识

  与大多数技术同样,AOP带有其本身的特定概念和术语集,所以了解它们的含义很重要。如下是AOP的核心概念:ide

  • 链接点(Joinpoint):链接点是在应用程序执行期间定义明确的点。链接点的典型示例包括对方法的调用,方法调用自己,类初始化和对象实例化。链接点是AOP的核心概念,它定义了应用程序中的点,能够在这些点上使用AOP插入其余逻辑。模块化

  • 加强(Advice):在特定联接点执行的代码是Advice,由类中的方法定义。Advice有多种类型,例如以前,在链接点以前执行,以后,在链接点以后执行。(为何不是翻译成通知?过AOP将横切关注功能加到原有的业务逻辑上,这就是对原有业务逻辑的一种加强,这种加强能够是前置加强、后置加强、返回后加强、抛异常时加强和包围型加强。)工具

  • 切点(Pointcut):切入点是用于定义什么时候执行建议的联接点的集合。经过建立切入点,您能够对如何将建议应用于应用程序中的组件进行精细控制。如前所述,典型的链接点是方法调用,或特定类中全部方法调用的集合。一般,您能够在复杂的关系中编写切入点,以在执行建议时进一步限制。

  • 切面(Aspect):切面是封装在类中的建议和切入点的组合。这种结合产生了应包含在应用程序中以及应在何处执行的逻辑的定义。

  • 织入(Weaving):这是在适当的时候将各切面插入应用程序代码中的过程。对于编译时AOP解决方案,这种编织一般是在构建时完成的。一样,对于运行时AOP解决方案,编织过程在运行时动态执行。 AspectJ支持另外一种编织机制,称为加载时间编织(LTW),在该机制中,它拦截底层的JVM类加载器,并在由类加载器加载字节码时提供对字节码的编织。

  • 目标(Target):经过AOP流程修改了执行流程的对象称为目标对象。一般,你会看到目标对象,即建议对象。

  • 引介(Introduction):这是能够经过向对象引入其余方法或字段来修改其结构的过程。可使用引介AOP使任何对象实现特定的接口,而无需对象的类显式实现该接口。这样,即便一个业务类本来没有实现某个接口,经过引介功能,能够动态的为该业务类添加接口的实现逻辑,让业务类成为这个接口的实现类。

3、AOP的类型

  AOP有两种不一样的类型:静态和动态。它们之间的区别其实是织造过程的发生点以及该过程的实现方式。

① 使用静态AOP

  在静态AOP中,编织过程构成了应用程序构建过程当中的另外一步骤。用Java术语,能够经过修改应用程序的实际字节码,并根据须要更改和扩展应用程序代码,从而在静态AOP实现中实现编织过程。这是完成编织过程的一种很好的方式,由于最终结果只是Java字节码,而且您在运行时不会执行任何特殊技巧来肯定什么时候应执行建议。这种机制的缺点是,即便只是想添加另外一个链接点,对切面进行的任何修改都要求你从新编译整个应用程序。 AspectJ的编译时编织是静态AOP实现的一个很好的例子。

② 使用动态AOP

  诸如Spring AOP之类的动态AOP实现与静态AOP实现的不一样之处在于,编织过程是在运行时动态执行的。如何实现此目标取决于实现,可是正如你将看到的那样,Spring的方法是为全部建议对象建立代理,并容许根据须要调用建议。动态AOP的缺点是,一般它的性能不如静态AOP,可是性能却在稳步提升。动态AOP实现的主要好处是,于是无需修改​​主应用程序代码便可轻松修改应用程序的整个切面集。

③ 选择AOP类型

  选择使用静态AOP仍是动态AOP是一个至关困难的决定。二者都有其自身的优势,而且你不限于仅使用一种类型。一般,静态AOP实现的时间更长,而且趋向于具备更多功能丰富的实现,而且具备更多的可用链接点。一般,若是性能绝对相当重要,或者你须要在Spring中未实现的AOP功能,则须要使用AspectJ。在大多数其余状况下,Spring AOP是理想的选择。请记住,Spring已经提供了许多基于AOP的解决方案,例如事务管理,所以在滚动本身的框架以前,请检查其框架功能!与往常同样,让应用程序需求来决定您对AOP实施的选择,若是技术组合更适合本身的应用程序,则不要将本身局限于一个实施。一般,Spring AOP不如AspectJ复杂,所以它是理想的首选。

4、Spring AOP体系结构

  Spring的AOP实施能够看做是两个逻辑部分。 第一部分是AOP核心,它提供彻底解耦的纯编程AOP功能(也称为Spring AOP API)。 AOP实现的第二部分是框架服务集,这些框架服务使AOP易于在您的应用程序中使用。 最重要的是,Spring的其余组件(例如事务管理器和EJB帮助器类)提供了基于AOP的服务,以简化应用程序的开发。

  AOP Alliance(http://aopalliance.sourceforge.net/)是许多开源AOP项目的表明之间的共同努力,旨在为AOP实现定义标准的接口集。只要适用,Spring都会使用AOP Alliance接口,而不是定义本身的接口。这使您能够在支持AOP Alliance接口的多个AOP实现中重用某些建议。

  先看一个AOP的例子,编写一个名为Agent的类来打印“Bond”:

1 package com.apress.prospring5.ch5;
2 public class Agent {
3      public void speak() {
4          System.out.print("Bond");
5      }
6 }

  而后咱们加强该方法,按AOP的属于来讲,就是想该方法添加Advice,在该方法执行前打印“James ”,在该方法指向后打印“!”

 1 package com.apress.prospring5.ch5;
 2 import org.aopalliance.intercept.MethodInterceptor;
 3 import org.aopalliance.intercept.MethodInvocation;
 4 
 5 public class AgentDecorator implements MethodInterceptor {
 6      public Object invoke(MethodInvocation invocation) throws Throwable {
 7          System.out.print("James ");
 8          Object retVal = invocation.proceed();
 9          System.out.println("!");
10          return retVal;
11      }
12 }

  MethodInterceptor 接口是一个标准的 AOP Alliance接口,用于为方法调用链接点实施环绕加强。MethodInvocation对象表示正在建议的方法调用,而且使用此对象,咱们控制什么时候容许进行方法调用。可以在调用方法以前,以后以及返回以前执行操做。在这段代码中,将“James ”为控制台输出,经过调用 invocation.proceed() 来调用该方法(指 speak()),而后打印"!"。

  下面是一个测试实例,建立目标实例和用代理工厂建立代理进行编织:

 1 package com.apress.prospring5.ch5;
 2 import org.springframework.aop.framework.ProxyFactory;
 3 
 4 public class AgentAOPDemo {
 5      public static void main(String... args) {
 6          Agent target = new Agent();
 7          ProxyFactory pf = new ProxyFactory();
 8          pf.addAdvice(new AgentDecorator());
 9          pf.setTarget(target);
10          Agent proxy = (Agent) pf.getProxy();
11          target.speak();
12          System.out.println("");
13          proxy.speak();
14      }
15 }

  使用 ProxyFactory 类建立目标对象的代理,同时编织advice。经过调用addAdvice() 将 AgentDecorator 建议传递给 ProxyFactory,并经过调用 setTarget() 来指定编织目标,调用getProxy() 生成代理,这里调用了原始目标和代理的speak方法。

输出:

  代理的 speak() 调用会致使 AgentDecorator 中的代码执行,从而输出所需的"James Bond!"。

Spring AOP 架构

  Spring AOP的核心架构基于代理。使用ProxyFactory是建立AOP代理的纯编程方法,还能够依靠Spring提供的声明式 AOP 配置机制(ProxyFactoryBean类,aop命名空间和@AspectJ样式的注释)来利用声明式代理建立。在运行时,Spring分析为 ApplicationContext 中的bean定义的横切关注点,并动态生成代理bean(包装基础目标bean)。代替直接调用目标bean,调用者被注入代理bean。而后,代理bean分析运行情况(即,链接点,切入点或advice),并相应地编织适当的advice。

  当要加强的目标对象实现接口时,Spring将使用JDK动态代理建立目标的代理实例。可是,若是建议的目标对象未实现接口(例如,它是一个具体的类),则CGLIB将用于建立代理实例。

Spring中的链接点

  Spring AOP中最明显的最简化的其中一种是仅支持一种链接点类型:方法调用。方法调用联接点是迄今为止可用的最有用的联接点,使用它能够完成许多使AOP在平常编程中有用的任务。

Spring中的切面

  在Spring AOP中,切面由实现Advisor接口的类的实例表示。Spring提供了便捷的Advisor的实现,能够在应用程序中重复使用它们。从而消除了必定要本身建立自定义Advisor实施的须要。Advisor 有两个子接口:PointcutAdvisor 和 IntroductionAdvisor。 PointcutAdvisor 接口继承了 Advisor 接口并使用使用切入点管理应用于链接点的advice。 在Spring中,引介被视为特殊的加强,经过使用 IntroductionAdvisor 接口,能够操做,引介所适用的类。

关于代理工厂

  ProxyFactory类控制Spring AOP中的编织和代理建立过程。 在建立代理以前,必须指定建议对象或目标对象。 您能够经过使用setTarget() 方法来执行此操做。 在内部,ProxyFactory 将代理建立过程委托给DefaultAopProxyFactory的一个实例,该实例又委托给Cglib2AopProxy或JdkDynamicAopProxy。

 1 public class ProxyFactory extends ProxyCreatorSupport {
 2     ……
 3     public Object getProxy() {
 4         return createAopProxy().getProxy();
 5     }
 6     ……
 7         
 8 }
 9 10 
11 public class ProxyCreatorSupport extends AdvisedSupport {
12      ……
13     /**
14      * Create a new ProxyCreatorSupport instance.
15      */
16     public ProxyCreatorSupport() {
17         this.aopProxyFactory = new DefaultAopProxyFactory();
18     }
19     ……
20     /**
21      * Subclasses should call this to get a new AOP proxy. They should <b>not</b>
22      * create an AOP proxy with {@code this} as an argument.
23      */
24     protected final synchronized AopProxy createAopProxy() {
25         if (!this.active) {
26             activate();
27         }
28         return getAopProxyFactory().createAopProxy(this);
29     }
30     ……
31 }
32     

DefaultAopProxyFactory.java

 1 public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
 2 
 3     @Override
 4     public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
 5         if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
 6             Class<?> targetClass = config.getTargetClass();
 7             if (targetClass == null) {
 8                 throw new AopConfigException("TargetSource cannot determine target class: " +
 9                         "Either an interface or a target is required for proxy creation.");
10             }
11             if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
12                 return new JdkDynamicAopProxy(config);
13             }
14             return new ObjenesisCglibAopProxy(config);
15         }
16         else {
17             return new JdkDynamicAopProxy(config);
18         }
19     }
20 
21     ……
22 
23 }

在 Spring 中的 advice

  Spring支持六种 advice,以下表所述:

  1. Before:org.springframework.aop.MethodBeforeAdvice,使用BeforeAdvice,能够在链接点执行以前执行自定义的处理。于Spring中的联接点始终是方法调用,能够在方法执行以前执行预处理。
  2. After-Returning:org.springframework.aop.AfterReturningAdvice,返回点建议在链接点处的方法调用完成执行并返回值后执行。 AfterReturningAdvice能够访问调用的目标方法,传递给该方法的参数以及返回值。
  3. After(finally):org.springframework.aop.AfterAdvice,仅在加强的方法正常执行后才执行AfterReturningAdvice。 可是,不管所加强方法的结果如何,都将执行以后(最终)建议。 即便被加强的方法失败而且抛出异常,该advice也会被执行。
  4. Around:org.aopalliance.intercept.MethodInterceptor,在Spring中,围绕加强使用方法拦截器的AOP Alliance标准进行建模。你的advice被容许在方法调用以前和以后执行,而且你能够控制容许方法调用进行的时间点。 
  5. Throws:org.springframework.aop.ThrowsAdvice,在方法调用返回后,将执行引起advice,但前提是该调用引起了异常。 抛出advice可能仅捕获特定的异常,若是选择这样作,则能够访问引起异常的方法,传递给调用的参数以及调用的目标。
  6. Introduction:org.springframework.aop.IntroductionInterceptor,Spring将引介建模为特殊类型的拦截器。 使用引介拦截器,能够经过该advice给加强的目标指定某种方法实现。
相关文章
相关标签/搜索