• Spring AOP aspect切面指北


    Spring Aspect cheatsheet

    放一些简单易用的spring aspect 语法手册。

    使用

    完整代码:spring-aop

    1. 引入库, 参考build.gradle
    2. 使用@Aspect@Configuration来标记Aspect。参考LogingAspect

    Why?

    就像之前写的btrace手册一样, 为了方便查找和实际操作,想分享一些关于写Aspect相关的cheatsheet。

    概念

    1. Aspect

      作为某个关注点、横切面在许多类的模块化集合。比如事务管理@Transactional就是在企业应用中事务相关的的横切面。或者某个用来记录日志的切面。一般情况下切面会包含一个完整
      的有意义的集合。一般会包含Pointcut定义和Advice定义。
    2. Join Point

      代表某个程序执行的点。在spring中代表某个方法的执行点。
    3. Advice

      在某个特殊的Join Point,Aspect采取的操作。包含不同的类型(around,before,after)。许多aop框架包括
      spring用拦截器来实现Advice,并且维护一个拦截器链来处理某个JointPoint的多个Advice。
    4. Pointcut

      定义满足某些定义条件的方法切面。Advice将在所有满足Pointcut的JoinPoint执行。比如Pointcut可以定义为
      在所有的Controller方法上执行(within(com.example.controller.*))。本文也将提供更多的例子方便查找。
    5. Introduction

      以类为基础去添加新的方法或者属性。比如可以为某些bean引入一个属性IsModified来方便实现缓存。
    6. Target object

      等着被aspect增强的类对象(也叫advised object)。因为spring用代理来实现的,所以这个也叫被代理对象。
    7. AOP proxy
      `
      为了实现aspect,通过JDK动态代理,或者cglib代理实现的增强过的对象。
    8. Weaving

      将多个aspect和真正的应用内组合在一起的过程, 可以在编译时(aspectj compiler),加载时,运行时(spring aop)实现。

    Advice类型

    1. Before
    2. AfterReturning
    3. AfterThrowing
    4. Around

      around advice会有一个ProceedingJoinPoint参数.这是最强的一类advice。它提供proceed方法来
      调用原有逻辑。其他的几个Advie都是类似于只读型的, 只提供了一个JoinPoint类型的参数。

    PointCut常用示例

    示例解释
    within(com.xyz.service.*)执行com.xyz.service(不包括子包)的所有类的方法
    within(com.xyz.service..*)执行com.xyz.service(包括子包)的所有类的方法
    this(com.xyz.service.AccountService)执行实现了AccountService的接口的类
    execution(* com.example.springaop.controller.*.*(..))执行
    execution(public * *(..))执行所有public方法
    execution(* set*(..))执行所有方法名以set开始的方法
    execution(* com.xyz.service.AccountService.*(..))执行所有在com.xyz.service.AccountService类里面的方法。
    execution(* com.xyz.service.*.*(..))执行所有在com.xyz.service里面的方法(不包括子包)
    execution(* com.xyz.service..*.*(..))执行所有在com.xyz.service里面的方法(包括子包)
    @within(org.springframework.transaction.annotation.Transactional)某个类具有@Transactional注解
    @annotation(org.springframework.transaction.annotation.Transactional)某个方法具有@Transactional注解

    具体语法:
    execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)

    Advice使用示例

    1. Before
      方法开始执行前,可以使用JoinPoint获取当前执行方法的状态。后续几个都可以获取该参数。
        @Before("execution(* com.xyz.myapp.dao.*.*(..))")
        public void doAccessCheck(JoinPoint jp) {
        
        }
    
    • 1
    • 2
    • 3
    • 4
    1. AfterReturning

      正常返回后:

      @AfterReturning(
           pointcut="com.xyz.myapp.CommonPointcuts.dataAccessOperation()",
           returning="retVal")
       public void doAccessCheck(Object retVal) {
           // ...
       }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
    2. AfterThrowing

      抛异常后:

      @AfterThrowing(
          pointcut="com.xyz.myapp.CommonPointcuts.dataAccessOperation()",
          throwing="ex")
      public void doRecoveryActions(DataAccessException ex) {
          // ...
      } 
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
    3. Arround
      环绕通知可以使用ProceedingJoinPoint.proceed 来调用原始的实现。

       @Around("com.xyz.myapp.CommonPointcuts.businessService()")
       public Object timing(ProceedingJoinPoint jp) throws Throwable {
            // start stopwatch
            Object retVal = jp.proceed(); 
            // stop stopwatch
            return retVal;
       }   
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    1. 给Advice传参数。
      除了上面的JoinPoint可以拿到参数外,还可以用如下的方式拿到:
    @Before("com.xyz.myapp.CommonPointcuts.dataAccessOperation() && args(account,..)")
    public void validateAccount(Account account) {
        // ...
    }
    
    • 1
    • 2
    • 3
    • 4

    上面例子中account是第一个参数。 当然dataAccessOperation指定的PointCut必须保证有至少一个参数。

    1. Advice的顺序
      按照下面顺序执行:
    @Around, @Before, @After, @AfterReturning, @AfterThrowing
    
    • 1

    不同Aspect的还可以通过@Order指定顺序

    一个完整的例子

    用来统计运行时长和记录请求的例子:

    package com.example.springaop;
    
    import org.aspectj.lang.*;
    import org.aspectj.lang.annotation.*;
    import org.springframework.context.annotation.*;
    
    import java.util.*;
    
    @Configuration
    @Aspect
    public class LogingAspect {
    
        @Pointcut("within(com.example.springaop.controller.*)")
        public void ctrlMethod() {
    
        }
    
        @Pointcut("execution(public * *(..)) ")
        public void publicMethod() {}
    
        @Around("ctrlMethod() && publicMethod()")
        public Object loggingAndTiming(ProceedingJoinPoint jp) throws Throwable {
            long t1 = System.currentTimeMillis();
            String className = jp.getSignature().getDeclaringTypeName();
            String methodName = jp.getSignature().getName();
            String args = Arrays.toString(jp.getArgs());
            try {
                return jp.proceed();
            } catch (Throwable e) {
                throw e;
            }
            finally {
                System.out.println(String.format("%s.%s with args: %s , cost time: %d",
                        className, methodName, args, System.currentTimeMillis() - t1));
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    参考

    1. spring官网文档
  • 相关阅读:
    Android学习笔记 25. Activity与Fragment通信
    7 行代码搞崩溃 B 站,原因令人唏嘘!
    【python 】pygame制作简单的游戏移动操作
    监听问卷网答题事件并自动执行其它操作
    超好用R包(grafify)
    【ETL工具】Datax-ETL-SqlServerToHDFS
    SnapHelper翻页效果两行代码带你实现不一样的RecyclerView
    【Windows】磁盘管理无法删除卷
    pip安装sentence transformer失败原因解析
    Rockland丨GOAT TRUEBLOT抗山羊IGG HRP说明书
  • 原文地址:https://blog.csdn.net/scugxl/article/details/128151039