一、AOP需要程序员参与的三个部分:
1、定义普通业务组件
2、定义切入点,一个切入点可能横切多个业务组件
3、定义曾强处理,增强处理就是AOP框架为普通业务组件织入的处理动作
一旦定义了合适的切入点和增强处理,AOP框架将会自动生成AOP代理,而AOP代理方法大致有如下公式:
代理对象的方法 = 增强处理 + 被代理对象的方法
建议使用AspectJ方式来定义切入点和增强处理,在这种公式下,Spring依然有如下两种选择来定义切入点和增强处理
①基于Annotation的“零配置”方式:使用@Aspect、@Pointcut等Annotation来标注切入点和增强处理
②基于XML配置文件的管理方式:使用SPring配置文件来定义切入点和增强处理
二、Spring依然采用运行时生成动态代理的方式来增强目标对象,所以它不需要增加额外的编译,也不需要AspectJ的织入器支持;而AspectJ在采用编译时增强,所以AspectJ需要使用自己的编译器来编译Java文件,还需要织入器。
为启动Spring对@AspectJ切面配置的支持,并保证Spring容器的目标Bean被一个或多个切面自动增强,必须Spring配置文件中配置如下片段:
bean.xml
<?xml version="1.0" encoding="UTF-8"?>
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"></span><pre name="code" class="html"><!-- Spring配置文件的根元素,使用spring-beans-3.0.xsd语义约束 --> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns="http://www.springframework.org/schema/beans" 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"> <!-- 指定自动搜索Bean组件、自动搜索切面类 --> <context:component-scan base-package="tju.chc.app.service.impl,tju.chc.app.service"> <context:include-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect"/> </context:component-scan> <!-- 启动!AspectJ支持 --> <aop:aspectj-autoproxy/> <bean id="korea" class="tju.chc.app.service.impl.Korea" /> </beans>
TxApect增强处理
public aspect TxAspect { //指定执行Hello.sayHello()方法时执行下面代码块 void around():call(void Hello.sayHello()){ System.out.println("开始事务"); proceed(); System.out.println("事物结束"); } }
BeforeAdvice增强处理
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">定义Before增强处理</span>
package tju.chc.aspectJTest; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; @Aspect public class BeforeAdviceTest { //匹配 包下所有的类的所有方法的执行 作为切入点 @Before("execution(* tju.chc.app.service.impl.*.*(..))") public void authority(){ System.out.println("before 切入"); } }
定义AfterReturning增强处理
package tju.chc.aspectJTest; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; @Aspect public class AfterReturningAdviceTest { //匹配tju.chc.app.service.impl包下所有类的所有方法的执行作为切入点 @AfterReturning(returning="rvt", pointcut="execution(* tju.chc.app.service.impl.*.*(..))") public void log(Object rvt){ System.out.println("获取目标方法返回值:" + rvt); System.out.println("模拟记录日志功能。。。"); } }
输出
开始事务 Hello AspectJ! 事物结束 before 切入 获取目标方法返回值:adfHello , SPring AOP 模拟记录日志功能。。。 adfHello , SPring AOP before 切入 Korea eat 泡菜 获取目标方法返回值:null 模拟记录日志功能。。。
4AfterThrowing增强处理
package tju.chc.aspectJTest; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Aspect; @Aspect public class AfterThrowingAdviceTest { //匹配 包下所有类的所有方法的执行作为切入点 @AfterThrowing(throwing ="ex" ,pointcut="execution(* tju.chc.app.service.impl.*.*(..))") public void doRecoveryActions(Throwable ex){ System.out.println("目标方法中抛出的异常:"+ ex); System.out.println("模拟抛出异常增强处理"); } }
Korea类增加的两个方法
//定义一个sayHi方法 public String sayHi(String name){ try{ System.out.println("start running"); new FileInputStream("a.txt"); }catch(Exception ex){ System.out.println("目标异常处理" + ex.getMessage()); } return name + ", Hi,Spring AOP"; } public void divide(){ int a = 5 / 0; System.out.println("divide compelete"); }
输出
----------------------sayHi
before 切入
start running
目标异常处理a.txt (系统找不到指定的文件。)
获取目标方法返回值:mashang6, Hi,Spring AOP
模拟记录日志功能。。。
----------------------divide
before 切入
目标方法中抛出的异常:java.lang.ArithmeticException: / by zero
模拟抛出异常增强处理
Exception in thread "main" java.lang.ArithmeticException: / by zero
at tju.chc.app.service.impl.Korea.divide(Korea.java:36)
at tju.chc.aspectJTest.Hello.main(Hello.java:25)
5、After增强处理
与AfterReturning增强处理优点类似,但也有区别:
①AfterReturning增强处理只有在目标方法成功完成后才会被织入
②After增强处理不管目标方法如何结束(包括成功完成和遇到异常终止两种情况),它都会被织入
因此After增强处理必须准备处理正常返回和异常返回两种情况,这种增强处理通常用于释放资源。
package tju.chc.aspectJTest; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; @Aspect public class AfterAdviceTest { //匹配包下所有类的所有方法的执行作为切入点 @After("execution(* tju.chc.app.service.impl.*.*(..))") public void release(){ System.out.println("模拟方法结束后的释放资源。。。"); } }
输出
目标方法中抛出的异常:java.lang.ArithmeticException: / by zero 模拟抛出异常增强处理 模拟方法结束后的释放资源。。。 Exception in thread "main" java.lang.ArithmeticException: / by zero at tju.chc.app.service.impl.Korea.divide(Korea.java:36) at tju.chc.aspectJTest.Hello.main(Hello.java:25)
6.Around增强处理
Around 增强处理既可以在执行目标方法之前织入增强动作,也可以执行目标方法之后织入增强动作
Around增强处理可以决定目标方法在什么时候执行,如何执行,甚至可以完全阻止方法的执行
可以改变执行目标方法的参数值,也可以改变执行目标方法之后的返回值
(通常需要在线程安全的环境下使用,因此如果普通的Before增强处理和AfterReturning 增强处理就能解觉得问题,就没必要使用Around)
当定义一个Around增强处理方法时,该方法的第一个形参必须是ProceedingJoinPoint类型(至少包含一个形参),在增强处理方法体内,调用ProceedingJoinPoint的proceed()方法才会执行目标方法(这就是Around可以完全控制目标方法执行时机。如何执行的关键;如果程序没有调用ProceedingJoinPoint的proceed()方法,则目标方法不会执行)
调用ProceedingJoinPoint的proceed()方法时,还可以传入一个Object【】对象,该数组中的值将被出入目标方法作为执行方法的实参。(用于改变目标方法的的参数)
切面定义如下
package tju.chc.aspectJTest; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; @Aspect public class AroundAdviceTest { //匹配包下的所有类的所有方法的执行作为切入点 @Around("execution(* tju.chc.app.service.impl.*.*(..))") public Object processTx(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("z执行目标方法之前,模拟开始事务"); //执行目标方法,并保存目标方法执行后的返回值 Object ret = pjp.proceed(new String[]{"被改变的参数"}); System.out.println("z执行目标方法之后,模拟结束事务。。。"); //该变目标方法的返回值 return ret + " new added content"; } }
输出
before 切入 z执行目标方法之前,模拟开始事务 获取目标方法返回值:被改变的参数Hello , SPring AOP 模拟记录日志功能。。。 z执行目标方法之后,模拟结束事务。。。 模拟方法结束后的释放资源。。。 被改变的参数Hello , SPring AOPnew added content before 切入 z执行目标方法之前,模拟开始事务 Korea eat 被改变的参数 获取目标方法返回值:null 模拟记录日志功能。。。 z执行目标方法之后,模拟结束事务。。。 模拟方法结束后的释放资源。。。 ----------------------sayHi before 切入 z执行目标方法之前,模拟开始事务 start running 目标异常处理a.txt (系统找不到指定的文件。) 获取目标方法返回值:被改变的参数, Hi,Spring AOP 模拟记录日志功能。。。 z执行目标方法之后,模拟结束事务。。。 模拟方法结束后的释放资源。。。 ----------------------divide before 切入 z执行目标方法之前,模拟开始事务 目标方法中抛出的异常:java.lang.ClassCastException: java.lang.String cannot be cast to org.aspectj.lang.JoinPoint 模拟抛出异常增强处理 模拟方法结束后的释放资源。。。 Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to org.aspectj.lang.JoinPoint at tju.chc.app.service.impl.Korea$AjcClosure9.run(Korea.java:1) at org.aspectj.runtime.reflect.JoinPointImpl.proceed(JoinPointImpl.java:221) at tju.chc.aspectJTest.AroundAdviceTest.processTx(AroundAdviceTest.java:16) at tju.chc.app.service.impl.Korea.divide(Korea.java:35) at tju.chc.aspectJTest.Hello.main(Hello.java:25)
7、访问目标函数的参数
在定义增强处理方法时将第一个参数定义为JoinPoint类型,当该类型增强处理方法被调用时,该JoinPoint参数就代表了织入曾强处理的连接点。JoinPoint中几个常用的方法
Object[] getArgs():返回执行目标方法时的参数
Signature getSignature() 放回被增强的方法的相关信息
Object getTarget() :返回被织入增强处理的目标对象
Object getThis() : 返回AOP框架为目标对象生成的代理对象
@Aspect public class FourAdviceTest { // 匹配包下的所有类的所有方法的执行作为切入点 @Around("execution(* tju.chc.app.service.impl.*.*(..))") public Object processTx(ProceedingJoinPoint pjp) throws Throwable { System.out.println("Around增强:z执行目标方法之前,模拟开始事务"); //访问执行目标方法的参数 Object[] args = pjp.getArgs(); //当执行目标方法的参数存在且第一个参数是字符串参数 if(args != null && args.length > 0 && args[0].getClass() == String.class){ //改变第一个目标方法的第一个参数 args[0] = "被改变参数4"; } // 执行目标方法,并保存目标方法执行后的返回值 Object ret = pjp.proceed(args); System.out.println("Around增强:z执行目标方法之后,模拟结束事务。。。"); // 该变目标方法的返回值 return ret + " new added content"; } //Before增强处理 匹配 包下所有的类的所有方法的执行 作为切入点 @Before("execution(* tju.chc.app.service.impl.*.*(..))") public void authority(JoinPoint jp){ System.out.println("Before 增强处理:模拟执行权限检查!"); //返回被织入增强处理目标方法 System.out.println("Before增强:被织入增强处理目标方法为:" + jp.getSignature().getName()); //访问执行目标方法的参数 System.out.println("Before增强:目标方法的参数为:" + Arrays.toString(jp.getArgs())); //访问被增强处理的目标对象 System.out.println("Before增强:被织入增强处理的目标对象为:" + jp.getTarget()); } // After 增强处理执行 匹配 包下所有类的所有方法的执行作为切入点 @After("execution(* tju.chc.app.service.impl.*.*(..))") public void release(JoinPoint jp) { System.out.println("After 增强处理:模拟方法结束后的释放资源。。。"); //返回被织入增强处理目标方法 System.out.println("After 增强:被织入增强处理目标方法为:" + jp.getSignature().getName()); //访问执行目标方法的参数 System.out.println("After 增强:目标方法的参数为:" + Arrays.toString(jp.getArgs())); //访问被增强处理的目标对象 System.out.println("Atfer 增强:被织入增强处理的目标对象为:" + jp.getTarget()); } //定义AfterReturning增强处理执行 匹配tju.chc.app.service.impl包下所有类的所有方法的执行作为切入点 @AfterReturning(returning="rvt", pointcut="execution(* tju.chc.app.service.impl.*.*(..))") public void log(JoinPoint jp, Object rvt){ System.out.println("AfterReturning 增强处理:模拟记录日志功能。。。"); //返回被织入增强处理目标方法 System.out.println("AfterReturning 增强:被织入增强处理目标方法为:" + jp.getSignature().getName()); //访问执行目标方法的参数 System.out.println("AfterReturning 增强:目标方法的参数为:" + Arrays.toString(jp.getArgs())); //访问被增强处理的目标对象 System.out.println("AfterReturning 增强:被织入增强处理的目标对象为:" + jp.getTarget()); } }
输出:
before 切入 z执行目标方法之前,模拟开始事务 Around增强:z执行目标方法之前,模拟开始事务 Before 增强处理:模拟执行权限检查! Before增强:被织入增强处理目标方法为:sayHello Before增强:目标方法的参数为:[adf] Before增强:被织入增强处理的目标对象为:tju.chc.app.service.impl.Korea@55d96549 Around增强:z执行目标方法之后,模拟结束事务。。。 After 增强处理:模拟方法结束后的释放资源。。。 After 增强:被织入增强处理目标方法为:sayHello After 增强:目标方法的参数为:[adf] Atfer 增强:被织入增强处理的目标对象为:tju.chc.app.service.impl.Korea@55d96549 AfterReturning 增强处理:模拟记录日志功能。。。 AfterReturning 增强:被织入增强处理目标方法为:sayHello AfterReturning 增强:目标方法的参数为:[adf] AfterReturning 增强:被织入增强处理的目标对象为:tju.chc.app.service.impl.Korea@55d96549 获取目标方法返回值:被改变参数4Hello , SPring AOP new added content 模拟记录日志功能。。。 z执行目标方法之后,模拟结束事务。。。 模拟方法结束后的释放资源。。。 被改变参数4Hello , SPring AOP new added content new added content before 切入 z执行目标方法之前,模拟开始事务 Around增强:z执行目标方法之前,模拟开始事务 Before 增强处理:模拟执行权限检查! Before增强:被织入增强处理目标方法为:eat Before增强:目标方法的参数为:[泡菜] Before增强:被织入增强处理的目标对象为:tju.chc.app.service.impl.Korea@55d96549 Korea eat 被改变参数4 Around增强:z执行目标方法之后,模拟结束事务。。。 After 增强处理:模拟方法结束后的释放资源。。。 After 增强:被织入增强处理目标方法为:eat After 增强:目标方法的参数为:[泡菜] Atfer 增强:被织入增强处理的目标对象为:tju.chc.app.service.impl.Korea@55d96549 AfterReturning 增强处理:模拟记录日志功能。。。 AfterReturning 增强:被织入增强处理目标方法为:eat AfterReturning 增强:目标方法的参数为:[泡菜] AfterReturning 增强:被织入增强处理的目标对象为:tju.chc.app.service.impl.Korea@55d96549 获取目标方法返回值:null 模拟记录日志功能。。。 z执行目标方法之后,模拟结束事务。。。 模拟方法结束后的释放资源。。。 ----------------------sayHi before 切入 z执行目标方法之前,模拟开始事务 Around增强:z执行目标方法之前,模拟开始事务 Before 增强处理:模拟执行权限检查! Before增强:被织入增强处理目标方法为:sayHi Before增强:目标方法的参数为:[mashang6] Before增强:被织入增强处理的目标对象为:tju.chc.app.service.impl.Korea@55d96549 start running 目标异常处理a.txt (系统找不到指定的文件。) Around增强:z执行目标方法之后,模拟结束事务。。。 After 增强处理:模拟方法结束后的释放资源。。。 After 增强:被织入增强处理目标方法为:sayHi After 增强:目标方法的参数为:[mashang6] Atfer 增强:被织入增强处理的目标对象为:tju.chc.app.service.impl.Korea@55d96549 AfterReturning 增强处理:模拟记录日志功能。。。 AfterReturning 增强:被织入增强处理目标方法为:sayHi AfterReturning 增强:目标方法的参数为:[mashang6] AfterReturning 增强:被织入增强处理的目标对象为:tju.chc.app.service.impl.Korea@55d96549 获取目标方法返回值:被改变参数4, Hi,Spring AOP new added content 模拟记录日志功能。。。 z执行目标方法之后,模拟结束事务。。。 模拟方法结束后的释放资源。。。 ----------------------divide before 切入 z执行目标方法之前,模拟开始事务 模拟方法结束后的释放资源。。。 Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to org.aspectj.lang.JoinPoint at tju.chc.app.service.impl.Korea$AjcClosure19.run(Korea.java:1) at org.aspectj.runtime.reflect.JoinPointImpl.proceed(JoinPointImpl.java:221) at tju.chc.aspectJTest.AroundAdviceTest.processTx(AroundAdviceTest.java:15) at tju.chc.app.service.impl.Korea.divide(Korea.java:35) at tju.chc.aspectJTest.Hello.main(Hello.java:25)