Spring3之AOP
AOP(Aspect Oriented Programming),即面向方面编程,是通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。它以”方面“的方式对原有的模块进行重组,抽取那些与业务无关却为整个系统所通用的功能,并将其最终封装在一起。
在java世界里,AOP的应用已经走向成熟,从AOP体现的能力上来说,AspectJ、Spring已经渐趋成熟。面向方面编程的价值主要体现在事务处理、日志管理、权限控制等于业务无关却为业务模块所共同调用的逻辑或责任上,而这些所谓的“方面”,恰恰是在企业应用时所必须的。因此,与其说AOP是一种编程的技术,毋宁说AOP是一种企业的“设计模式”。
AOP的开发包括3个过程,首先是分离关注点,然后是实现关注点与核心功能,,最后再是组合关注点与核心功能,形成整个完整的系统,如图所示:
分解关注点:分解需求提取出横切关注点和一般关注点。可以把核心模块级关注点和系统级的横切关注点分离开来。
实现关注点:各自独立地实现这些关注点。
组合关注点:方面集成器通过创建一个模块单元方面来指定重组的规则。重组过程(也叫织入或结合)则使用这些信息来构建最终系统。
下面来讲述,AOP技术的专用名字:
关注点(Concern):一个关注点就是一个特定的目的,一块我们感兴趣的区域。从技术的角度来说,一个典型的软件系统包含一些核心的关注点和系统级的关注点。举个例子来说,一个信用卡处理系统的核心关注点是借贷/存入处理,而系统级的关注点则是日志、事务完整性、授权、安全及性能问题等。许多关注点--我们叫它横切关注点,会在多个模块中出现,使用现有的编程方法,横切关注点会横越多个模块,结果是使系统难以设计、理解、实现和演进。
切面(Aspect):如果一个关注点模块化,则这个关注点可能会横切多个对象。事务管理是JavaEE应用中一个关于横切关注点的很好的例子。在Spring Aop中,切面可以使用通用类(基于模式的风格)或者在普通类中以@Aspect注解(@AspectJ)风格来实现。
连接点(JoinPoint):在程序执行过程中的某个特定的点,比如在某方法调用的时候或者处理异常的时候。在Spring AOP中,一个连接点总是代表一个方法的执行。通过声明一个org.aspectj.lang.JoinPoint类型的参数可以使通知(Advice)的主体获得连接点信息。连接点表示“在哪里干?”
通知(Advice):在切面的某个特定的连接点上执行的动作。通知有各种类型,其中包括“around”,“before”和“after”等。通知的类型将在后面部分进行讨论。许多AOP框架,包括Spring,都以拦截器做通知类型,并维护一个以连接点为中心的拦截器连接。通知表示“要干什么?”
切入点(Pointcut):匹配连接点的断链。通知和一个切入点表达式关联,并在满足这个切入点的连接点上运行(例如,当执行某个特定名称的方法时)。切入点表达式及如何与连接点匹配是AOP的核心,Spring默认使用AspectJ切入点语法。切入点表示“在哪里干的集合”
引入(Introduction):也被称为内部类型声明(Inter Type Declaration),用来声明额外的方法或者某个类型的字段。Spring允许引入新的接口(及一个对应的实现)到任何被代理的对象。例如,你可以使用一个引入来使Bean实现IsModified接口,以便简化缓存机制。引入表示“干什么(引入什么)”
目标对象(Target Object):被一个或者多个切面(Aspect)所通知(Advise)的对象。也有人把它叫做被通知(Advised)对象。既然Spring AOP是通过在运行时代理实现的,那么这个对象永远是一个被代理(Proxied)对象。
AOP代理(AOP Proxy):AOP框架创建的对象,用来实现切面契约(Aspect Contract,包括通知方法执行等功能)。在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。
织入(Weaving):把切面(Aspect)连接到其他的应用程序类型或者对象上,并创建一个被通知(Advised)的对象。这些可以在编译时(例如使用AspectJ编译器),类加载时和运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。
在AOP中,通过切入点选择目标对象的连接点,然后在目标对象的相应连接点处织入通知,而切入点和通知就是切面(横切关注点),而在目标对象连接点处应用切面的实现方式是通过AOP代理对象。
在AOP中有个几种通知类型如下:
前置通知(Before Advice):在某个连接点(Join Point)之前执行的通知,但这个通知不能阻止连接点前的执行(除非它抛出一个异常)。
返回通知(After Returning Advice):在某连接点(Join Point)正常完成后执行的通知。例如,一个方法没有抛出任何异常,正常返回。
异常通知(After Throwing Advice):在方法抛出异常,退出时执行的通知。
后通知(After Finally Advice):当某连接点推出的时候执行的通知(不论是正常返回还是异常退出)。
环绕通知(Around Advice):包围一个连接点(Join Point)的通知,如方法调用,这是最强大的一个通知类型。环绕通知可以在方法调用前后完成自定义的行为。它也会选择是继续执行连接点,还是直接返回它们自己的返回值或通过抛出异常来结束执行。
在AOP框架中,JDK动态代理和字节码处理是最流行的组织AOP的策略。Spring为AOP提供了一组简单易用的API,并且和其他AOP框架集成的很好。但是Spring并不是要尝试提供最完整的AOP实现,相反地,它其实侧重于提供一种AOP实现和Spring IoC容器的整合。本博客讲述三种基于Spring的三种开发方式:
一、基于@Aspect注释符
1.首先启用Spring对@Aspectj的支持,使用<aop:aspectj-autoproxy/>,如果是使用DTD的话,则可以在Application Context中添加如下定义:
<bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"/>
2.声明一个切面类似:
@Aspect
3.声明一个切入点类似:
@Pointcut("exection(public * * (..))")
@Pointcut("within(com.demo.spring..*)")
public void bbb(){}
关于切入点的指定者可以使用多种方式有 execution,within,this,target,args,@target,@args,@within,@annotation等,经常会使用execution表达式,该执行表达式的格式为:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-patterm) throws-pattern?)
当然可以同时定义表达式和参数,使用方式为:
@Pointcut(value="",argNames="")
public void PointcutClassName{}
4. 定义通知类似:
@BeforeAdvice(value="",argNames="")
还可以加上pointcut属性,其就是执行表达式。如果是返回通知或者异常通知,通常情况下还要加上 returning,throwing属性等。
下面一个完整的实例:
二、基于Schema模式
基于Schema的格式需要将applicationContext.xml改为基于Schema的格式,然后添加spring-aop的命名空间,在该模式下aop的配置都是<aop:config>的元素下,一个applicationContext的内部可以包括多个<aop:config>。一个<aop:config>可以包含Pointcut和Aspect元素,使用<aop:aspect>来申明一个切面,使用<aop:pointcut>来申明一个切入点,两者的顺序如下:
<aop:config>
</aop:pointcut>
<aop:aspect>
</aop:aspect>
</aop:config>
也可以将<aop:pointcut>放在<aop:aspect>的内部。
1.申明一个切面如下:
<bean id="myAspect" class="com.demo.spring.aop.MyAspect" />
<aop:aspect id="aopAspect" ref="myAspect" />
</aop:config>
2.申明一个切入点如下:
<aop:config>
</aop:config>
上述是共享的切入点,如果针对某个切面来说,则需要这样配置:
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.demo.spring.test.HelloWorld.execute(..))" />
</aop:aspect>
</aop:config>
3.申明一个通知如下:
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* execute(..)) and target(com.demo.spring.test.HelloWorld)" />
<aop:before pointcut="pointcut" method="beforeExecute" />
<aop:around pointcut="pointcut" method="userOperate"/>
<aop:after pointcut="pointcut" method="afterExecute" />
</aop:aspect>
</aop:config>
三、基于Spring API方式
Spring自己也提供了API,来实现基于AOP的功能。
1.我们先创建自定义的通知,在Spring中需要实现几种接口MethodIntercepter, MethodBeforeAdvice,AfterReturningAdvice,ThrowsAdvice等,这些接口中对应相关的方法,可以访问返回值、被调用方法、方法参数等,其中MethodInterceptor接口的有个invoke函数,其参数MethodInvocation参数也是暴露了被调用的方法、目标连接点、AOP代理以及传递给方法的参数。其返回调用的结果,即连接点的返回值。下面举例其中一个的使用:
public class MyAroundAdvice implements MethodInterceptor{
System.out.println("executing..");
return arg0.proceed();
}
}
2.要实现对Advice的绑定,还需要配置Advisor来实现对目标类的匹配,Advisor是一个仅仅包含一个同志对象和与之关联的切入点表达式的切面,任何Advisor都可以和任何通知一起工作。Spring Aop最底层的切面封装是Advisor接口,其下面还有两个接口IntroductionAdvisor(引介增强器)及PointcutAdvisor(切入点增强器),它们有几下几种实现:
配置一个Advisor元素,使用上述的几种实现,它的属性advice指向上面配置的某个通知。
3.Spring AOP是基于代理实现的,代理工厂用来创建代理对象,实现通知与目标类的织入。Spring的AOP提供了以下几个代理工厂实现。
这些代理实现,各自有各自不同的属性,使用时可以通过提示使用。
无论是哪种形式,无非是定义最开始讲述的几种概念,上面只是简单的使用,只要理清思路,上手会很快。