1. 什么是AOP
AOP(Aspect Oriented Programming)面向切面编程。通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。简单的说它就是把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改源码的基础上,对我们的已有方法进行增强。
AOP相关术语:
- Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点。
- Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义。
- Advice(通知/增强):所谓通知是指拦截到Joinpoint之后所要做的事情就是通知。通知的类型:前置通知、后置通知、异常通知、最终通知、环绕通知。
- Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下,Introduction可以在运行期为类动态地添加一些方法或Field。
- Target(目标对象):代理的目标对象。
- Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。
- Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类。
- Aspect(切面):是切入点和通知(引介)的结合。
2. 基于XML的AOP配置
public class AdviceTest {
public void beforeAdvice() {
System.out.println("前置通知");
}
public void afterAdvice() {
System.out.println("后置通知");
}
// proceedingJoinPoint是一个切入点,被增强的方法
public void aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕通知开始");
proceedingJoinPoint.proceed();
System.out.println("环绕通知结束");
}
public void finalAdvice() {
System.out.println("最终通知");
}
public void execptionAdvice() {
System.out.println("异常通知");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 配置通知 -->
<bean id="adviceTest" class="com.joker.service.AdviceTest"/>
<!-- aop:config标签:声明 aop配置 -->
<aop:config>
<!-- aop:pointcut标签:用于配置切入点表达式。就是指定对哪些类的哪些方法进行增强
* id:用于给切入点表达式提供一个唯一标识
* expression:用于定义切入点表达式
-->
<aop:pointcut id="pointcut1" expression="execution(* com.joker.service.*.*(..))"/>
<!-- aop:pointcut标签:用于配置切面
* id:给切面提供一个唯一标识
* ref:引用配置好的通知类 bean的 id
-->
<aop:aspect id="aspect" ref="adviceTest">
<!-- aop:before:前置通知,在切入点方法之前执行
aop:after-returning:后置通知,切入点方法正常执行之后,它和异常通知只能有一个执行
aop:after-throwing:异常通知,切入点方法执行产生异常后执行,它和后置通知只能有一个执行
aop:before:最终通知,无论切入点方法执行时是否有异常,它都会在其后面执行
aop:around:环绕通知,在切入点方法前后执行
* method:用于指定通知类中的增强方法名称
* ponitcut-ref:用于指定切入点的表达式的引用
* poinitcut:用于指定切入点表达式
-->
<aop:before method="beforeAdvice" pointcut-ref="pointcut1"/>
<aop:after-returning method="afterAdvice" pointcut-ref="pointcut1"/>
<aop:after-throwing method="afterAdvice" pointcut-ref="pointcut1"/>
<aop:after method="afterAdvice" pointcut-ref="pointcut1"/>
<aop:around method="aroundAdvice" pointcut-ref="pointcut1"/>
</aop:aspect>
</aop:config>
</beans>
切入点表达式说明:execution:匹配方法的执行。表达式语法:execution([修饰符] 返回值类型 包名.类名.方法名(参数))
- 全匹配方式:public void com.joker.service.AdviceTest.beforeAdvice()
- 访问修饰符可以省略:void com.joker.service.AdviceTest.beforeAdvice()
- 返回值可以使用*号,表示任意返回值:* com.joker.service.AdviceTest.beforeAdvice()
- 包名可以使用*号,表示任意包,但是有几级包,需要写几个:* *.*.*.AdviceTest.beforeAdvice()
- 使用 . . 来表示当前包,及其子包:* com. .AdviceTest.beforeAdvice()
- 类名可以使用*号,表示任意类:* com. .*.beforeAdvice()
- 方法名可以使用*号,表示任意方法:* com. .*.*()
- 参数列表可以使用*,表示参数可以是任意数据类型,但是必须有参数:* com. .*.*(*)
- 参数列表可以使用. .表示有无参数均可,有参数可以是任意类型:* com. .*.*(. .)
- 全通配方式:* *. .*.*(. .)
3. 基于注解的AOP配置
@Aspect
@Component("adviceTest")
public class AdviceTest {
// 切入点表达式注解,value指定表达式的内容
@Pointcut(value = "execution(* com.joker.service.*.*(..))")
public void pointCut() {
System.out.println("切入点");
}
@Before("execution(* com.joker.service.*.*(..))")
// @Before("pointCut()") 若使用切入点表达式注解,注意千万别忘了写括号
public void beforeAdvice() {
System.out.println("前置通知");
}
@After("pointCut()")
public void afterAdvice() {
System.out.println("后置通知");
}
@Around("pointCut()")
public void aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕通知开始");
proceedingJoinPoint.proceed();
System.out.println("环绕通知结束");
}
@AfterReturning("pointCut()")
public void finalAdvice() {
System.out.println("最终通知");
}
@AfterThrowing("pointCut()")
public void execptionAdvice() {
System.out.println("异常通知");
}
}
<!-- 开启 spring对注解 AOP的支持 -->
<aop:aspectj-autoproxy />
可以不使用XML的配置方式开启spring对注解AOP的支持,使用@EnableAspectJAutoProxy注解可以代替,如下所示:
@Configuration
@ComponentScan(basePackages="com.joker")
@EnableAspectJAutoProxy
public class SpringConfiguration {
}