一、原理
AOP即
Aspect-OrientedProgramming的缩写,中文意思是面向切面(或方面)编程。
AOP实际上是一种编程思想,可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种思想。
在传统的面向对象(Object-Oriented Programming,OOP)编程中,我们总是按照某种特定的执行顺序来实现业务流程,各个执行步骤之间是相互衔接、相互耦合的,对垂直切面关注度很高,横切面关注却很少,也很难关注。那么怎样可以解决这个问题呢?我们需要AOP,关注系统的“切面”,在适当的时候“拦截”程序的执行流程,把程序的预处理和后处理交给某个拦截器来完成。这样,业务流程就完全的从其它无关的代码中解放出来,各模块之间的分工更加明确,程序维护也变得容易多了。
正如上所说,AOP不是一种技术,而是编程思想。凡是符合AOP思想的技术,都可以看成是AOP的实现。目前的AOP实现有AspectJ、JBoss4.0、nanning、spring等项目。其中Spring对AOP进行了很好的实现,同时SpringAOP也是Spring的两大核心一。
AOP的发展目前已经历了两个阶段:第一阶--静态AOP和第二阶段—动态AOP。静态AOP阶段,相应的横切关注点以Aspect形式实现之后,会通过特定的编译器,将实现后的Aspect编译并织入到系统的静态类中。比如AspectJ会使用ajc编译器将各个Aspect以Java字节码的形式编译到系统的各个功能模块中,已达到融合Aspect和Class的目的。动态AOP阶段,AOP的织入过程在系统运行开始之后进行,而不是预先编译到系统中,可以在调整织入点以及织入逻辑单元的同时,不必变更系统其他的模块,甚至在系统运行的时候,也可以动态更改织入逻辑。这两个阶段也为我们提供了通过预编译方式和运行期动态代理方式,实现了在不修改源代码的情况下给程序统一添加功能的目的。
AOP主要的意图是:允许通过分离应用的业务逻辑与系统级服务进行内聚性的开发。应用对象只实现业务逻辑即可,并不负责其它的系统级关注点。
AOP主要应用场景是:日志记录、跟踪、监控和优化,性能统计、优化,安全、权限控制,应用系统的异常捕捉及处理,事务处理,缓存,持久化,懒加载(Lazyloading),内容传递,调试,资源池,同步等等。
二、基本概念
切面(Aspect) :官方的抽象定义为“一个关注点的模块化,这个关注点可能会横切多个对象”
连接点(Joinpoint):程序执行过程中明确的点,如方法的调用或特定的异常被抛出。
通知(Advice):在特定的连接点,AOP框架执行的动作。各种类型的通知包括“around”、“before”和“throws”通知。通知类型将在下面讨论。许多AOP框架包括Spring都是以拦截器做通知模型,维护一个“围绕”连接点的拦截器链。
切入点(Pointcut):指定一个通知将被引发的一系列连接点的集合。AOP框架必须允许开发者指定切入点,例如,使用正则表达式。例如:execution(*com.spring.service.*.*(..))
引入(Introduction):匹配连接点的断言,在AOP中通知和一个切入点表达式关联。添加方法或字段到被通知的类。Spring允许引入新的接口到任何被通知的对象。例如,你可以使用一个引入使任何对象实现IsModified接口,来简化缓存。
目标对象(TargetObject):被一个或者多个切面所通知的对象,包含连接点的对象,也被称作被通知或被代理对象。
AOP代理(AOPProxy):AOP框架创建的对象,包含通知。在Spring中,AOP代理可以是JDK动态代理或CGLIB代理。
Around通知:包围一个连接点的通知,如方法调用。这是最强大的通知。Aroud通知在方法调用前后完成自定义的行为,它们负责选择继续执行连接点或通过返回它们自己的返回值或抛出异常来短路执行。
Before通知:在一个连接点之前执行的通知,但这个通知不能阻止连接点前的执行(除非它抛出一个异常)。
Throws通知:在方法抛出异常时执行的通知。Spring提供强制类型的Throws通知,因此你可以书写代码捕获感兴趣的异常(和它的子类),不需要从Throwable或Exception强制类型转换。
Afterreturning通知:在连接点正常完成后执行的通知,例如,一个方法正常返回,没有抛出异常。
三、示例
1、使用注解的配置文件 applicationContext-aop.xml
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
2、定义一个切面
@Aspect
@Component
public class AnnoAsect {
@Pointcut("execution(* com.user.service.*.*(..))")
public voidanyMethod(){}
@Around("execution(* com.user.service.*.*(..))")
publicObject doAround(ProceedingJoinPoint pjp)throws Throwable{
//环绕通知(Around advice):包围一个连接点的通知
//类似Web中Servlet规范中的Filter的doFilter方法。可以在方法的调用前后完成自定义的行为,
//也可以选择不执行。ApplicationContext中在里面使用元素进行声明。
//例如,TestAspect中的doAround方法。
//执行真正调用的方法
ObjectretVal = pjp.proceed();
returnretVal;
}
@Before(value="anyMethod()")
public voiddobefore(JoinPoint jp){
//前置通知(Before advice):在某连接点(JoinPoint)之前执行的通知
//这个通知不能阻止连接点前的执行。ApplicationContext中在里面使用
//元素进行声明。例如,TestAspect中的doBefore方法
System.out.println("前置通知!");
//获取请求参数
Object[]args = jp.getArgs();
if(args.length > 0){
for (Objectobject : args) {
System.out.println("参数[" + object + "]");
Useruser=(User)object;
if("".equals(user.getUsername())){
System.out.println("---------------");
}
}
}
}
@After("anyMethod()")
public voiddoAfter(JoinPoint jp) {
// 后通知(Afteradvice):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。
//ApplicationContext中在里面使用元素进行声明。例如,TestAspect中的doAfter方法,所以AOPTest中调用BServiceImpl.barB抛出异常时,doAfter方法仍然执行
System.out.println("后通知(After advice)类型");
}
@AfterReturning(pointcut="anyMethod()")
public voiddoAfterReturn(JoinPoint jp) {
//返回后通知(After return advice):在某连接点正常完成后执行的通知
//不包括抛出异常的情况。ApplicationContext中在里面使用元素进行声明。
System.out.println("返回后通知(After return advice)类型");
}
@AfterThrowing(throwing="ex", pointcut="anyMethod()")
public voiddoThrowing(JoinPoint jp, Throwable ex) {
//抛出异常后通知(After throwing advice) : 在方法抛出异常退出时执行的通知。
//ApplicationContext中在里面使用元素进行声明。例如,TestAspect中的doThrowing方法。
System.out.println("方法抛出异常后到了这里抛出异常");
}
}