The most complete SpringAOP aspect expression

Related articles in the past :
AOP core concepts and SpringAOP aspects
10-minute introduction to SpringAOP

preface

What is PCD

PCD (pointcut designators) is the pointcut expression of SpringAOP. SpringAOP's PCD is fully compatible with AspectJ, and there are 10 types in total.

PCD at a glance

image-20210108140219721

user's guidance

Spring AOP is implemented based on dynamic proxy, the following is used to 目标对象represent the proxy bean, and the proxy object represents the bean constructed by AOP. The target method represents the method being proxied.

execution

Execution is the most commonly used PCD. Its matching template is shown below:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)
          throws-pattern?)
execution(修饰符匹配式? 返回类型匹配式 类名匹配式? 方法名匹配式(参数匹配式) 异常匹配式?)

The ?signed matching formulas in the code block are all optional, and there execution PCDare only three essential ones:

  1. Return value matching value
  2. Method name matching
  3. Parameter matching

Example analysis: execution(public * ServiceDemo.*(..))match the public modifier, the return value is *, that is , any return value type is OK, ServiceDemothe class name matching type does not have to be the full path, as long as the global visibility is unique , it .*is the method name matching type, matching all methods, ..yes Parameter matching type, a method of matching any number and any type of parameters.

Some other examples:

  • execution(* com.xyz.service..*.*(..)): Match any method under com.xyz.service and its sub-packages.

  • execution(* joke(Object+))): Match any jokemethod whose name is , and its dynamic input parameter is Object type or a subclass of this class.

  • execution(* joke(String,..)): Match jokethe method with any name , one input parameter of this method is String (cannot be a subclass), and there can be any number of input parameters and the input parameter type is unlimited

  • execution(* com..*.*Dao.find*(..)): Match the method at the beginning of find under the specified package

  • execution(* com.baobaotao.Waiter+.*(..)): Match Waiterall methods under the com.baobaotao package and its subclasses.

within

Filter out all classes under a certain package, and pay attention to them *.

  • within(com.xyz.service.*)Classes under the com.xyz.service package, excluding sub-packages

  • within(com.xyz.service..*)Classes under the com.xyz.service package and its subpackages

this

Often used 命名绑定模式. Filter by the type of proxy object.

If the target class is implemented based on an interface, the full path name this()can be filled 该接口in. Otherwise, because the non-interface implementation is based on CGLIB, 目标类the full path name can be filled in this .

this(com.xyz.service.AccountService): The proxy class is com.xyz.service.AccountService or its subclasses.

Use @EnableAspectJAutoProxy(proxyTargetClass = true)can force the use of CGLIB. Otherwise, the jdk dynamic proxy is used by default first, and CGLIB will only be used if jdk is not proxy.

target

This acts on the proxy object, and target acts on the target object.

target(com.xyz.service.AccountService): The proxy class (target object) is com.xyz.service.AccountService or its subclasses

args

It is often used to match the parameters of the target method. Generally not used alone, but used in conjunction with other PCD. args can use 命名绑定modes, as shown below:

@Aspect // 切面声明
@Component // 注入IOC
@Slf4j
class AspectDemo {
    
    
    @Around("within(per.aop.*) && args(str)") // 在per.aop包下,且被代理方法的只有一个参数,参数类型是String或者其子类
    @SneakyThrows
    public Object logAspect(ProceedingJoinPoint pjp, String str) {
    
     
        String signature = pjp.getSignature().toString();
        log.info("{} start,param={}", signature, pjp.getArgs());
        Object res = pjp.proceed();
        log.info("{} end", signature);
        return res;
    }
}
  1. If yes in args, 参数名use the advice method to determine the type of method parameter to be matched.
  2. If args is a type, for example @Around("within(per.aop.*) && args(String)”), you don't need to use the aspect method to determine the type, but you can't use parameter binding at this time . See below .

Although symbols are args()supported +, this province args()supports sub-category wildcarding.

executionDifference with parameter matching

For example: args(com.xgj.Waiter)equivalent to execution(* *(com.xgj.Waiter+)). And execution cannot support advice with parameters.

@target

Examples of usage scenarios: When a Service has multiple subclasses, some subclasses need to log, and some subclasses do not need to log, which can be handled as follows (with java polymorphism):

Filter out whether the proxy object with the given annotation is an object or not a class, and @target is dynamic . Customize an annotation as follows LogAble:

//全限定名: annotation.LogAble
@Target({
    
    ElementType.TYPE,ElementType.PARAMETER}) // 支持在方法参数、类上注
@Retention(RetentionPolicy.RUNTIME)
public @interface LogAble {
    
    
}

If you need to " publiclog the methods of all classes with this annotation ", then the log logic needs to be customized . You can write PCD as follows. Of course, the corresponding method bean must be injected into the SpringIOC container:

@Around("@target(annotation.LogAble) && execution(public * *.*(..))")
// 自定义日志逻辑

@args

There 运行时类型must be @argsspecified annotations for the target method parameters . It is the type of the method parameter that has a specified annotation, not the annotation on the method parameter.

Usage scenario: If the parameter type has multiple subclasses, only a certain subclass can match the PCD.

  • @args(com.ms.aop.jargs.demo1.Anno1): Match 1 parameter, and Anno1 annotation is required when the first parameter is running

  • @args(com.ms.aop.jargs.demo1.Anno1,..)To match one or more parameters, the first parameter needs to be annotated with Anno1 when running.

  • @args(com.ms.aop.jargs.demo1.Anno1,com.ms.aop.jargs.demo1.Anno2): One parameter matches Anno1, two parameters matches Annno2.

@within

Non 运行时类型的of @target. @target focuses on the object being called, and @within focuses on the class of the called method.

The difference between @target and @within:

@target(Annotation A): Determine 目标对象whether annotation A is declared in the called , if there is, it will be intercepted

@within(Annotation A): Determine whether annotation A is declared in the method to which the called method belongs . If there is, it will be intercepted

@annotation

Match the method with specified annotation (annotation acts on the method)

bean

According to beanNam to match. Support *wildcards.

bean(*Service) // 匹配所有Service结尾的Service

other

Combined use

&& || !Three types of operators are supported between PCD . The && operator is used in the above example. ||Means or (not short-circuited or). !Represents non.

Named binding mode

The @Around("within(per.aop.*) && args(str)")example above is to use the named binding mode, write the variable name in the PCD, and limit the type of the variable name in the method.

@Around("within(per.aop.*) && args(str)")
public Object logAspect(ProceedingJoinPoint pjp, String str) {
    
     ...}

As the example above, if str is of type String or its subclass, and the method can only have one parameter.

name binding only allowed in target, this, and args pcds

The named binding mode only supports target、this、argsthree PCDs.

argNames

Observing the source code, we can find that all Advice annotations have argNamesfields, such as @Around:

@Retention(RetentionPolicy.RUNTIME)
@Target({
    
    ElementType.METHOD})
public @interface Around {
    
    
    String value();
    String argNames() default "";
}

Under what circumstances will this attribute be used, the following example explains:

@Around(value = "execution(* TestBean.paramArgs(..))  && args(decimal,str,..)&& target(bean)", argNames = "pjp,str,decimal,bean")
@SneakyThrows // proceed会抛受检异常
Object aroundArgs(ProceedingJoinPoint pjp,/*使用命名绑定模式*/ String str, BigDecimal decimal, Object bean) {
    
    
    // 在方法执行前做一些操作
	return  pjp.proceed();
}

argnames must be used with args, target, this tags. Although it is not necessary in actual operation, the official recommendation is to bring all parameters with parameters for the following reasons:

Therefore, if the'argernames' attribute is not specified, Spring AOP will look at the debugging information of the class and try to determine the parameter name from the local variable table. This message will appear as long as the class is compiled with debugging information (at least'-g: vars'). The result of compiling with this flag is:

(1) Your code will be easier to reverse engineer)

(2) The class file size will be very large (usually irrelevant)

(3) The optimization of deleting unused local variables will not be applied by the compiler.

In addition, if the compiled code does not have the necessary debugging information, Spring AOP will try to infer the pairing of bind variables and parameters. If the binding of the variable is ambiguous in the available information, then an AmbiguousBindingException will be thrown. If the above strategies fail, then an IllegalArgumentException will be thrown.

It is recommended to include all advice comments, argNamesand idea will remind you anyway.

Reference article

  1. Spring documentation
  2. SpringAOP Point expression

Guess you like

Origin blog.csdn.net/qq_38619183/article/details/112359456