要在 Spring 中声明 AspectJ 切面, 只需要在 IOC 容器中将切面声明为 Bean 实例. 当在 Spring IOC 容器中初始化 AspectJ 切面之后, Spring IOC 容器就会为那些与 AspectJ 切面相匹配的 Bean 创建代理,AOP的原理其实就是利用了动态代理,将动态代理进行了封装。

在 AspectJ 注解中, 切面 只是一个带有 @Aspect 注解的 Java 类,通知 则是标注有某种注解的简单的 Java 方法。

AspectJ 支持 5 种类型的通知注解:
@Before: 前置通知, 在方法执行之前执行
@After: 后置通知, 在方法执行之后执行
@AfterRunning: 返回通知, 在方法返回结果之后执行
@AfterThrowing: 异常通知, 在方法抛出异常之后
@Around: 环绕通知, 围绕着方法执行

前置通知

前置通知:在目标 方法开始之前进行执行的通知。
前置通知使用 @Before 注解, 并将 切入点表达式的值作为注解值。
value属性值:切入点表达式,匹配与之对应的目标。利用【】可以进行那匹配不同的目标,参数只需要传入类型即可。
方法体* JoinPoint 参数
:用来连接当前连接点的连接细节,一般包括方法名和参数值。【org.aspectj.lang.JoinPoint】包

  1. @Before( value="execution(public int com.wf.springaopImpl.ArithmeticCalculatorImpl.*(int , int))")
  2. public void brforelogging(JoinPoint joinPoint){ //注意 JoinPoint 的包
  3. String methodName = joinPoint.getSignature().getName();
  4. List<Object> args = Arrays.asList(joinPoint.getArgs());
  5. System.out.println("The brforelogging methor"+ methodName+" begin with"+args);
  6. }

后置通知

后置通知: 在目标方法执行之后,无论是否发生异常,都进行执行的通知。
后置通知使用@After注解, 并将切入点表达式的值作为注解值。
在后置通知中,不能访问目标方法的执行结果。原因可能在执行过程中发生异常而无法得到结果。

  1. // @After("execution(public int com.wf.springaopImpl.ArithmeticCalculatorImpl.*(int , int)))")
  2. public void afterlogging(JoinPoint joinPoint){
  3. String methodName = joinPoint.getSignature().getName();
  4. System.out.println("The afterlogging method "+methodName+" end");
  5. }

返回通知

返回通知: 在目标方法 正常结束时,才执行的通知
返回通知使用@AfterReturning注解,并将切入点表达式的值作为注解值。
返回通知可以访问到方法的返回值。
returning属性值: 声明该方法可以存在返回值,该属性的值即为用来传入返回值的参数名称。
方法体Object参数 : 需要使用与returning同名参数名称,用来接收方法的返回值。

  1. @AfterReturning(value="execution(public int com.wf.springaopImpl.ArithmeticCalculatorImpl.*(int , int)))",returning="result")
  2. public void afterreturninglogging(JoinPoint joinPoint,Object result){
  3. String methodName = joinPoint.getSignature().getName();
  4. System.out.println("The afterreturninglogging method "+methodName+" end with "+result);
  5. }

异常通知

异常通知 :在目标方法出现异常时才会进行执行的代码。
异常通知使用@AfterThrowing注解,并将切入点表达式的值作为注解值。
throwing属性: 访问连接点抛出的异常。
方法体Exception参数: 用来接收连接点抛出的异常。Exception类匹配所有的异常,可以指定为特定的异常 例如NullPointerException类等

  1. @AfterThrowing(value="execution(public int com.wf.springaopImpl.ArithmeticCalculatorImpl.*(int , int)))",throwing="ex")
  2. public void afterthrowinglogging(JoinPoint joinPoint, Exception ex){
  3. String methodName = joinPoint.getSignature().getName();
  4. System.out.println("The afterthrowinglogging method "+methodName+" occurs excetion: "+ex);
  5. }

环绕通知

环绕通知是所有通知类型中功能最为强大的, 能够全面地控制连接点。甚至可以控制是否执行连接点。类似于动态代理。
环绕通知 连接点的参数类型必须是 ProceedingJoinPoint ,它是 JoinPoint 的子接口,,允许控制何时执行, 是否执行连接点。
环绕通知中需要明确调用 ProceedingJoinPoint 的 proceed() 方法来执行被代理的方法,如果忘记这样做就会导致通知被执行了,但目标方法没有被执行。
环绕通知的方法需要有返回值,返回目标方法执行之后的结果, 即调用 joinPoint.proceed() 的返回值, 否则会出现空指针异常。
环绕通知虽然功能最为强大的,但是一般我们都不使用这个。

  1. @Around(value="execution(public int com.wf.springaopImpl.ArithmeticCalculatorImpl.*(int , int)))")
  2. public Object aroundlogging(ProceedingJoinPoint pjd){
  3. Object result =null;
  4. String methodName = pjd.getSignature().getName();
  5. List<Object> args = Arrays.asList(pjd.getArgs());
  6. try {
  7. // 前置通知
  8. System.out.println("The aroundlogging methor"+ methodName+" begin with"+args);
  9. result = pjd.proceed();
  10. // 返回通知
  11. System.out.println("The aroundlogging method "+methodName+" end with "+result);
  12. } catch (Throwable e) {
  13. System.out.println("The aroundlogging method "+methodName+" occurs excetion: "+e);
  14. // 异常通知
  15. e.printStackTrace();
  16. }
  17. //后置通知
  18. System.out.println("The aroundlogging method "+methodName+" end");
  19. return result;
  20. }

切入点表达式

在切入点表达式中我们可以利用【*】去进行模糊匹配。
最典型的就是下面的这个,我们可以根据这个进行一系列的变形。

  1. execution(* com.wf.springaopImpl.ArithmeticCalculatorImpl.*(..))
  2. //第一个 * 代表任意修饰符及任意返回值. 第二个 * 代表任意方法. .. 匹配任意数量的参数.
  3. //若目标类与接口与该切面在同一个包中, 可以省略包名.
  4. executionpublic * ArithmeticCalculator.*(..))
  5. // 匹配 ArithmeticCalculator 接口的所有公有方法.
  6. execution (public double ArithmeticCalculator.*(..))
  7. //匹配 ArithmeticCalculator 中返回 double 类型数值的方法
  8. execution (public double ArithmeticCalculator.*(double, ..))
  9. // 匹配第一个参数为 double 类型的方法, .. 匹配任意数量任意类型的参数

切面优先级

当在同一个方法中存在多个切面时,我们可以在注解切面的类中添加新的注解@Order,可以给我们的切面进行优先级排序,@Order(index) 其中index的值越小,所对应的优先级就越高。
image.png

重用切点表达式

有时候一个切点可以有多个通知,就像上面,如果我们将切点表达式写在每一个通知中,会有大量的重复代码。因此我们可以重用切点表达式。

// 定义一个方法,利用注解@Pointcut用于声明切入点达式,然后在其中引入方法名即可。
// 我们可以利用ValidateAspect.declareJoinPointExpression()进行跨类声明【类名.方法名】。
// 甚至我们可以利用【类的全路径.方法名】使切点表达式作用于不同的包中。

  1. public class ValidateAspect {
  2. @Pointcut(value="execution(public int com.wf.springaopImpl.ArithmeticCalculatorImpl.*(int , int))")
  3. public void declareJoinPointExpression(){
  4. }
  5. @Before("declareJoinPointExpression()")
  6. public void brforeValidate(JoinPoint joinPoint){ //注意 JoinPoint 的包
  7. String methodName = joinPoint.getSignature().getName();
  8. List<Object> args = Arrays.asList(joinPoint.getArgs());
  9. System.out.println("----------The Validate methor"+ methodName+" begin with"+args);
  10. }
  11. @After("declareJoinPointExpression()")
  12. public void afterValidate(JoinPoint joinPoint){
  13. String methodName = joinPoint.getSignature().getName();
  14. System.out.println("The afterValidate method "+methodName+" end");
  15. }
  16. }

基于xml的配置通知

对于没有注解的,我们可以将对于切面的相关配置写在我们的配置文件中。具体如下:
1、配置基本bean
2、配置切面bean
3、配置AOP
在配置AOP时,我们进行属性配置
A、配置切面表达式
B、配置切面以及通知
注意:记得要引入aop命名空间。

  1. <!-- 配置基本bean -->
  2. <bean id="arithmeticCalculator" class="com.wf.springaopImplxml.ArithmeticCalculatorImpl"></bean>
  3. <!-- 配置切面bean -->
  4. <bean id="loggingAspect" class="com.wf.springaopImplxml.LoggingAspect"></bean>
  5. <bean id="validateAspect" class="com.wf.springaopImplxml.ValidateAspect"></bean>
  6. <!-- 配置AOP -->
  7. <aop:config>
  8. <!-- 配置切点表达式 -->
  9. <aop:pointcut expression="execution(public int com.wf.springaopImplxml.ArithmeticCalculatorImpl.*(int , int))"
  10. id="pointcut"/>
  11. <!-- 配置切面及通知 -->
  12. <aop:aspect ref="loggingAspect" order="2">
  13. <aop:before method="brforelogging" pointcut-ref="pointcut"/>
  14. <aop:after method="afterlogging" pointcut-ref="pointcut"/>
  15. <aop:after-returning method="afterreturninglogging" pointcut-ref="pointcut" returning="result"/>
  16. <aop:after-throwing method="afterthrowinglogging" pointcut-ref="pointcut" throwing="ex"/>
  17. <!-- <aop:around method="aroundlogging" pointcut-ref="pointcut"/> -->
  18. </aop:aspect>
  19. <!-- 配置切面及通知 -->
  20. <aop:aspect ref="validateAspect" order="0">
  21. <aop:before method="brforeValidate" pointcut-ref="pointcut"/>
  22. </aop:aspect>
  23. </aop:config>

[

](https://blog.csdn.net/sinat_28978689/article/details/62215513)