一、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)