• AspectJ切面自定义注解实现参数分组校验——基础概念(2)


    一、环境

    maven需要引入依赖,aspectjweaver。

    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.5</version>
    </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    二、创建AspectJ

    2-1.基础概念

    AspectJ提供了面向切面编程的实现,AspectJ有三个核心概念:

    Join Point
    Advice
    Pointcut
    
    • 1
    • 2
    • 3

    Aspect(切面): 通过横切多个对象来做模块化的关联,每个切面侧重一个特定的横切功能。

    Join point(执行点): 脚本执行过程中的一个点,例如方法的执行或属性的访问。

    Advice(通知): 切面在特定执行点采取的行动。

    Pointcut(与执行点相切的点): 使用规则表达式与执行点相切的点。advice通过pointcut的规则表达式,可在任意与执行点相切的点运行。

    2-2.Pointcut规则表达式

    Pointcut注解的源码,

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface Pointcut {
    
        /**
         * @return the pointcut expression
         * We allow "" as default for abstract pointcut
         */
        String value() default "";
        
        /**
         * When compiling without debug info, or when interpreting pointcuts at runtime,
         * the names of any arguments used in the pointcut are not available.
         * Under these circumstances only, it is necessary to provide the arg names in 
         * the annotation - these MUST duplicate the names used in the annotated method.
         * Format is a simple comma-separated list.
         * 
         * @return argNames the argument names (should match those in the annotated method)
         */
        String argNames() default "";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    Pointcut规则表达式可以是@pointcut注解的value。

    2-3.切点标志符pointcut designator (PCD)

    1)execution

    最主要的Spring PCD是execution,通过method的执行点切入。

    @Pointcut("execution(public String com.pingpongx.pointcutadvice.dao.InboundDao.findById(Long))")
    
    • 1

    上述的示例,InboundDao类的findById方法作为切点。但是这种方式不够灵活。

    可以使用通配符,使规则表达式能表达的Pointcut更灵活。

    @Pointcut("execution(* com.pingpongx.pointcutadvice.dao.InboundDao.*(..))")
    
    • 1

    第一个通配符*表示任意的return的结果。

    第二个通配符*表示任意的方法名称。

    第三个(..)表示任意个数的参数(包含0个)。

    2)within

    通过某种type的执行点切入。

    @Pointcut("within(com.pingpongx.pointcutadvice.dao.InboundDao)")
    
    • 1

    当然也可以是某个package或者sub-package里面的任意类型。

    @Pointcut("within(com.pingpongx..*)")
    
    • 1

    3)this和target

    this limits matching to join points where the bean reference is an instance of the given type, while target limits matching to join points where the target object is an instance of the given type.
    
    • 1

    this在Spring AOP创建基于cglib的代理时有效,target在创建基于jdk的代理时使用。

    假设targetXxxDao实现了一个接口

    public class XxxDao implements YyyDao {
        ...
    }
    
    • 1
    • 2
    • 3

    上述例子,Spring AOP会使用基于jdk代理,因此需要使用target,因为代理的对象会是Proxy类的实例,且实现了YyyDao接口。

    @Pointcut("target(com.pingpongx.pointcutadvice.dao.YyyDao)")
    
    • 1

    换言之,如果XxxDao没有实现任何接口,或者proxyTargetClass的属性是true,那么被代理的对象会是XxxDao的子类,需要使用this

    @Pointcut("this(com.pingpongx.pointcutadvice.dao.XxxDao)")
    
    • 1

    4)args

    通过特定方法的参数作为执行点切入。

    @Pointcut("execution(* *..find*(Long))")
    
    • 1

    如果需要任意个数参数,但保证一个参数是Long类型。

    @Pointcut("execution(* *..find*(Long,..))")
    
    • 1

    5)@target

    执行切入点是执行对象的类具有给定类型的注解。

    @Pointcut("@target(org.springframework.stereotype.Repository)")
    
    • 1

    6)@args

    执行切入点是传递的实际参数是运行时类型,且具有给定类型的注释。

    比如,追溯所有方法,方法满足接收的bean参数具备@Entity注解。

    @Pointcut("@args(com.pingpongx.pointcutadvice.annotations.Entity)")
    public void methodsAcceptingEntities() {}
    
    • 1
    • 2

    要访问参数,我们应该为通知提供一个 JoinPoint 参数。

    @Before("methodsAcceptingEntities()")
    public void logMethodAcceptionEntityAnnotatedBean(JoinPoint jp) {
        logger.info("Accepting beans with @Entity annotation: " + jp.getArgs()[0]);
    }
    
    • 1
    • 2
    • 3
    • 4

    7)@within

    执行切入点是具有给定注解的类型内的类型。

    @Pointcut("@within(org.springframework.stereotype.Repository)")
    
    • 1

    等价于

    @Pointcut("within(@org.springframework.stereotype.Repository *)")
    
    • 1

    8)@annotation

    执行切入点是执行点的主体具有给定的注解。

    @Pointcut("@annotation(com.pingpongx.pointcutadvice.annotations.Loggable)")
    public void loggableMethods() {}
    
    • 1
    • 2
    @Before("loggableMethods()")
    public void logMethod(JoinPoint jp) {
        String methodName = jp.getSignature().getName();
        logger.info("Executing method: " + methodName);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2-4.Pointcut规则表达式合并符号

    支持&&||操作符。

    @Pointcut("@target(org.springframework.stereotype.Repository)")
    public void repositoryMethods() {}
    
    @Pointcut("execution(* *..create*(Long,..))")
    public void firstLongParamMethods() {}
    
    @Pointcut("repositoryMethods() && firstLongParamMethods()")
    public void entityCreationMethods() {}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    2-5.Advice通知

    Advicearoundbeforeafter

    1)启用Advice通知

    对于configuration类使用@EnableAspectJAutoProxy注解,对于component类使用@Aspect注解。

    2)Before通知

    通知执行在执行点之前。除非抛出异常,否则它不会阻止它通知的方法的继续执行。

    3)After通知

    在切点的方法执行后执行,无论是否引发异常。

    4)Around通知

    ProceedingJoinPoint参数控制before和after功能效果,pjp.proceed()执行前是before,pjp.proceed()执行后是after,Object retval = pjp.proceed();return retval;是结束(或者抛出异常)。如果没有进行retval接收,直接return pjp.proceed();,就没有after了。

  • 相关阅读:
    VBA技术资料MF52:VBA_在Excel中突出显示前 10 个值
    登录安全分析报告:小米官网注册
    Golang Map 基本原理
    MariaDB MaxScale实现mysql8读写分离
    2-35 基于matlab的四足液压机器人设计程序
    聚乙烯亚胺偶联乳清白蛋白/肌白蛋白/豆清白蛋白/蓖麻蛋白/豌豆白蛋白1b ( PA1b)科研试剂
    摆脱障碍,通过技术实现企业财务数字化新高度
    读懂MCU产品选型表
    利用R语言进行生态环境数据的可视化分析:方法和实践
    Matlab:数组索引
  • 原文地址:https://blog.csdn.net/weixin_36894490/article/details/125610487