除了依赖注入(DI),Spring框架提供的另外一个核心功能是对面向方面的编程(AOP)的支持。 AOP一般被称为实现横切关注点的工具。横切关注点一词是指应用程序中的逻辑不能与应用程序的其他部分分离并有效模块化的地方,而且可能致使代码重复和紧密耦合。经过使用AOP模块化单个逻辑(即关注点),能够将它们应用于应用程序的许多部分,而无需复制代码或建立硬依赖关系。日志记录和安全性是许多应用程序中横切关注点的典型示例。AOP是对面向对象编程(OOP)的补充,而不是与之竞争。 OOP很是擅长解决咱们做为程序员遇到的各类问题。鉴于AOP能够在OOP之上运行,所以几乎不可能单独使用AOP来开发整个应用程序,尽管使用OOP固然能够开发整个应用程序,可是经过使用AOP解决涉及横切逻辑的某些问题,能更本身聪明地进行工做。java
AOP基础知识程序员
AOP的类型spring
Spring AOP体系结构编程
使用Spring AOP安全
AOP框架服务架构
集成AspectJ框架
与大多数技术同样,AOP带有其本身的特定概念和术语集,所以了解它们的含义很重要。如下是AOP的核心概念:ide
链接点(Joinpoint):链接点是在应用程序执行期间定义明确的点。链接点的典型示例包括对方法的调用,方法调用自己,类初始化和对象实例化。链接点是AOP的核心概念,它定义了应用程序中的点,能够在这些点上使用AOP插入其余逻辑。模块化
加强(Advice):在特定联接点执行的代码是Advice,由类中的方法定义。Advice有多种类型,例如以前,在链接点以前执行,以后,在链接点以后执行。(为何不是翻译成通知?过AOP将横切关注功能加到原有的业务逻辑上,这就是对原有业务逻辑的一种加强,这种加强能够是前置加强、后置加强、返回后加强、抛异常时加强和包围型加强。)工具
切点(Pointcut):切入点是用于定义什么时候执行建议的联接点的集合。经过建立切入点,您能够对如何将建议应用于应用程序中的组件进行精细控制。如前所述,典型的链接点是方法调用,或特定类中全部方法调用的集合。一般,您能够在复杂的关系中编写切入点,以在执行建议时进一步限制。
切面(Aspect):切面是封装在类中的建议和切入点的组合。这种结合产生了应包含在应用程序中以及应在何处执行的逻辑的定义。
织入(Weaving):这是在适当的时候将各切面插入应用程序代码中的过程。对于编译时AOP解决方案,这种编织一般是在构建时完成的。一样,对于运行时AOP解决方案,编织过程在运行时动态执行。 AspectJ支持另外一种编织机制,称为加载时间编织(LTW),在该机制中,它拦截底层的JVM类加载器,并在由类加载器加载字节码时提供对字节码的编织。
目标(Target):经过AOP流程修改了执行流程的对象称为目标对象。一般,你会看到目标对象,即建议对象。
引介(Introduction):这是能够经过向对象引入其余方法或字段来修改其结构的过程。可使用引介AOP使任何对象实现特定的接口,而无需对象的类显式实现该接口。这样,即便一个业务类本来没有实现某个接口,经过引介功能,能够动态的为该业务类添加接口的实现逻辑,让业务类成为这个接口的实现类。
AOP有两种不一样的类型:静态和动态。它们之间的区别其实是织造过程的发生点以及该过程的实现方式。
在静态AOP中,编织过程构成了应用程序构建过程当中的另外一步骤。用Java术语,能够经过修改应用程序的实际字节码,并根据须要更改和扩展应用程序代码,从而在静态AOP实现中实现编织过程。这是完成编织过程的一种很好的方式,由于最终结果只是Java字节码,而且您在运行时不会执行任何特殊技巧来肯定什么时候应执行建议。这种机制的缺点是,即便只是想添加另外一个链接点,对切面进行的任何修改都要求你从新编译整个应用程序。 AspectJ的编译时编织是静态AOP实现的一个很好的例子。
诸如Spring AOP之类的动态AOP实现与静态AOP实现的不一样之处在于,编织过程是在运行时动态执行的。如何实现此目标取决于实现,可是正如你将看到的那样,Spring的方法是为全部建议对象建立代理,并容许根据须要调用建议。动态AOP的缺点是,一般它的性能不如静态AOP,可是性能却在稳步提升。动态AOP实现的主要好处是,于是无需修改主应用程序代码便可轻松修改应用程序的整个切面集。
选择使用静态AOP仍是动态AOP是一个至关困难的决定。二者都有其自身的优势,而且你不限于仅使用一种类型。一般,静态AOP实现的时间更长,而且趋向于具备更多功能丰富的实现,而且具备更多的可用链接点。一般,若是性能绝对相当重要,或者你须要在Spring中未实现的AOP功能,则须要使用AspectJ。在大多数其余状况下,Spring AOP是理想的选择。请记住,Spring已经提供了许多基于AOP的解决方案,例如事务管理,所以在滚动本身的框架以前,请检查其框架功能!与往常同样,让应用程序需求来决定您对AOP实施的选择,若是技术组合更适合本身的应用程序,则不要将本身局限于一个实施。一般,Spring AOP不如AspectJ复杂,所以它是理想的首选。
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的核心架构基于代理。使用ProxyFactory是建立AOP代理的纯编程方法,还能够依靠Spring提供的声明式 AOP 配置机制(ProxyFactoryBean类,aop命名空间和@AspectJ样式的注释)来利用声明式代理建立。在运行时,Spring分析为 ApplicationContext 中的bean定义的横切关注点,并动态生成代理bean(包装基础目标bean)。代替直接调用目标bean,调用者被注入代理bean。而后,代理bean分析运行情况(即,链接点,切入点或advice),并相应地编织适当的advice。
当要加强的目标对象实现接口时,Spring将使用JDK动态代理建立目标的代理实例。可是,若是建议的目标对象未实现接口(例如,它是一个具体的类),则CGLIB将用于建立代理实例。
Spring AOP中最明显的最简化的其中一种是仅支持一种链接点类型:方法调用。方法调用联接点是迄今为止可用的最有用的联接点,使用它能够完成许多使AOP在平常编程中有用的任务。
在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,以下表所述: