示例代码
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.junit.runner.RunWith;
import org.springframework.aop.aspectj.annotation.AspectJProxyFactory;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.ReflectionUtils;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Aspect
class AopAdviceConfig {
@Before("@annotation(Test)")
public void beforeAdvice(JoinPoint joinPoint) {
System.out.println("我是前置通知....");
}
@Around("@annotation(Test)")
public String around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("开启....");
Object n = joinPoint.proceed();
System.out.println("关闭...." + n);
return "!!!";
}
@After("@annotation(Test)")
public void afterAdvice() throws Throwable {
System.out.println("后置事务……");
}
@AfterReturning(value = "@annotation(Test)", returning = "r")
public Object afterReturning(Object r) {
System.out.println(r);
return r + "!!";
}
@AfterThrowing(value = "@annotation(Test)")
public void afterThrowing() {
System.out.println("目标方法报错");
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Test{
}
//定义一个接口
interface AspectJService {
/**
* 测试前置通知
*/
int beforeAdvice();
/**
* 测试后置通知
* @return
*/
String afterAdvice();
}
//实现类
class AspectJServiceImpl implements AspectJService {
@Override
public int beforeAdvice() {
System.out.println("测试前置通知,我是第一个Service。。。。。。");
return 1;
}
/**
* 测试后置通知
* @return
*/
@Override
@Test
public String afterAdvice() {
System.out.println("测试AspectJ后置通知。。。。");
System.out.println("continue");
return "true";
}
}
@SpringBootTest
@RunWith(SpringRunner.class)
public class AopTest1 {
public static void main(String[] args) throws NoSuchMethodException {
//进行方法调用
System.out.println(new AopTest1().test1());
}
public Object test1() {
//手工创建一个实例
AspectJService aspectJService = new AspectJServiceImpl();
//使用AspectJ语法 自动创建代理对象
AspectJProxyFactory aspectJProxyFactory = new AspectJProxyFactory(aspectJService);
//添加切面和通知类
aspectJProxyFactory.addAspect(AopAdviceConfig.class);
//创建代理对象
AspectJService proxyService = aspectJProxyFactory.getProxy();
Object o = proxyService.afterAdvice();
return o;
}
}
输出结果
开启....
我是前置通知....
测试AspectJ后置通知。。。。
continue
true
后置事务……
关闭....true
!!!
执行优先级:
根据上面的代码的输出结果可以看出,如果一个类被多个AOP注解增强,按照Around》Before》目标类》【AfterThrowing》AfterReturning 】》After》Around
执行
afterThrowing与afterReturning执行注意事项
afterThrowing与afterReturning冲突,因为after Returning只有在正常执行是织入,而afterThrowing则在出异常后织入。
关于Around注解的目标类带返回值报错问题:Null return value from advice does not match primitive return type for:
因为Aop的底层是采用的ReflectionUtils.accessibleConstructor()根据Class对象创建对应的实例的,而如果使用基本类作为返回值类型,这个方法是会报错的。所以如果有返回类型,可以采用包装类。
注解使用
Around:相当于是一个顺序执行,在执行ProceedingJoinPoint#proceed方法后,会按顺序触发其他的AOP注解直到执行完成才会进行执行之后的方法。
- 在增强的方法上
@Around("execution(* 包名.*(..))")
或使用切点@Around("pointcut()")
- 接收参数类型为
ProceedingJoinPoint
,必须有这个参数在切面方法的入参第一位 - 返回值可为任意包装类
- 需要执行ProceedingJoinPoint对象的proceed方法,在这个方法前与后面做环绕处理,可以决定何时执行与完全阻止方法的执行
- 返回proceed方法的返回值
- @Around相当于@Before和@AfterReturning功能的总和
- 可以改变方法参数,在proceed方法执行的时候可以传入Object[]对象作为参数,作为目标方法的实参使用。
- 如果传入Object[]参数与方法入参数量不同或类型不同,会抛出异常
- 通过改变proceed()的返回值来修改目标方法的返回值
Before:在目标方法执行之前执行。
- 在增强的方法上
@Before("execution(* 包名.*.*(..))")
- 上述表达式可使用pointcut或切入表达式,效果一致,之后不再赘述
- 切点方法没有形参与返回值
After:在目标方法执行完成后执行。
- 用法同@Before
AfterThrowing:在目标方法执行完,如果被After增强的话就在After执行完毕后执行
- 与
@AfterReturning
类似,同样有一个切点和一个定义参数名的参数——throwing - 同样可以通过切面方法的入参进行限制切面方法的执行,e.g. 只打印IOException类型的异常, 完全不限制可以使用Throwable类型
- pointcut使用同
@AfterReturning
- 还有一个默认的value参数,如果指定了pointcut则会覆盖value的值
- 如果目标方法中的异常被try catch块捕获,此时异常完全被catch块处理,如果没有另外抛出异常,那么还是会正常运行,不会进入AfterThrowing切面方法
AfterReturning:在目标方法执行完,如果被After增强的话就在After执行完毕后执行。如果目标方法报错不会织入,
- 和上边的方法不同的地方是该注解除了切点,还有一个返回值的对象名
- 不同的两个注解参数:returning与pointcut,其中pointcut参数可以为切面表达式,也可为切点
- returning定义的参数名作为切面方法的入参名,类型可以指定。如果切面方法入参类型指定Object则无限制,如果为其它类型,则当且仅当目标方法返回相同类型时才会进入切面方法,否则不会
- 还有一个默认的value参数,如果指定了pointcut则会覆盖value的值
- 与@After类似,但@AfterReturning只有方法成功完成才会被织入,而@After不管结果如何都会被织入
- 不能改变返回的值