spring AOP 4: Detailed explanation of @AspectJ entry point identifier syntax

The AspectJ pointcut indicator (used to indicate the purpose of pointcut expressions) supported by Spring AOP. Currently in Spring AOP, there is only one connection point for the execution method (because Spring is based on a dynamic proxy, Spring only supports method connection points. This is different from some Other AOP frameworks are different, such as AspectJ and JBoss, in addition to method cutpoints, they also provide access points for fields and constructors. Spring lacks support for field connection points and cannot allow us to create granular notifications, such as intercept The modification of the object field. And it does not support the constructor connection point, we can not apply notification when the bean is created. But method interception can meet most of the needs. If you need a connection point interception function other than method interception, then we can Use Aspect to supplement the functionality of Spring AOP).

The AspectJ entry point indicators supported by Spring AOP are as follows:

Method description matching:

execution (): connection point for matching method execution;

  Syntax structure (AspectJ pointcut expression): execution (method modifier method return value method class belongs to matching method name (formal parameter table in method) method declaration exception thrown)

  The red font part can not be omitted, and all parts support wildcard "*" to match all.

Method parameter matching:

args (): used to match the parameters of the currently executed method to the specified type of execution method;

@args: Used to match the execution of the currently executed method with parameters that carry the specified annotation;

The current AOP proxy object type matches:

this (): the execution method used to match the current AOP proxy object type; note that the type matching of the AOP proxy object, which may include the introduction of interfaces and type matching;

Target class matching:

target (): the execution method used to match the type of the current target object; note that the type of the target object is matched, so that it does not include the introduction of interfaces and type matching;

@target: used to match the execution method of the current target object type, where the target object holds the specified annotation;

 within (): used to match the execution of methods within the specified type;

@within: used to match all methods within the specified annotation type;

The methods marked with this annotation match:

 @annotation: used to match the current execution method with the specified annotation method;

Bean object matching a specific name:

bean (): Spring AOP extension, AspectJ has no execution method for the indicator, which is used to match the Bean object with a specific name;

To cite other named entry points:

reference pointcut: means to refer to other named entry points, only supported by @ApectJ style, not by Schema style.

       The pointcut indicators supported by AspectJ pointcuts are: call, get, set, preinitialization, staticinitialization, initialization, handler, adviceexecution, withincode, cflow, cflowbelow, if, @this, @withincode; but Spring AOP does not currently support these instructions Operator, using these indicators will throw an IllegalArgumentException. These indicators Spring AOP may be extended in the future.

1. Naming and anonymous entry point

       Named pointcuts can be referenced by other pointcuts, while anonymous pointcuts are not.

   Only @AspectJ supports named entry points, while the Schema style does not support named entry points.

As shown below, @AspectJ refers to named entry points in the following way:

2. Type matching syntax

First let's understand the wildcards of the AspectJ type matching:

         *: Match any number of characters;

         ..: Match any number of repetitions of characters, such as matching any number of sub-packages in type mode; and matching any number of parameters in method parameter mode.

         +: Match the subtype of the specified type; can only be placed as a suffix after the type pattern.

Examples: 

1、java.lang.String    匹配String类型; 
2、java.*.String        匹配java包下的任何“一级子包”下的String类型; 
   如匹配java.lang.String,但不匹配java.lang.ss.String 
3、java..*             匹配java包及任何子包下的任何类型; 
   如匹配java.lang.String、java.lang.annotation.Annotation 
4、java.lang.*ing      匹配任何java.lang包下的以ing结尾的类型; 
5、java.lang.Number+  匹配java.lang包下的任何Number的自类型; 
                   如匹配java.lang.Integer,也匹配java.math.BigInteger

Next, let's look at the specific type of matching expression:

         Matching type: match as follows

 annotation? Fully qualified name of the class

  • Annotation: optional, the annotations held on the type, such as @Deprecated;
  • Fully qualified name of the class: required, can be the fully qualified name of any class.

         Implementation of the matching method: use the following method to match:

annotation? Modifier? Return value type declaration? Method name (parameter list) Exception list?
  • Annotation: optional, the annotation held on the method, such as @Deprecated;
  • Modifier: optional, such as public, protected;
  • Return value type: required, can be any type mode; "*" means all types;
  • Type declaration: optional, can be any type pattern;
  • Method name: required, you can use "*" for pattern matching;
  • Parameter list: "()" means that the method does not have any parameters; "(..)" means matching methods that accept any number of parameters, and "(.., java.lang.String)" means matching methods that accept java.lang.String types The parameter ends, and the method with any number of parameters can be accepted in front of it; "(java.lang.String, ..)" means that the method that matches the parameter of type java.lang.String is accepted, and the method behind it can accept any number of parameters ; "(*, Java.lang.String)" means that the method that accepts parameters of type java.lang.String ends and it accepts a method with a parameter of any type in front of it;
  • Exception list: optional, declared with "throws exception fully qualified name list", if there are multiple exception fully qualified name lists, separated by "," such as throws java.lang.IllegalArgumentException, java.lang.ArrayIndexOutOfBoundsException.

         Match Bean name: You can use the id or name of the Bean to match, and you can use the wildcard "*";

3. Combined pointcut expressions

       AspectJ uses and (&&), or (||), and not (!) To combine pointcut expressions.

       In the Schema style, because the use of "&&" in XML needs to use the escape character "& amp; & amp;" to replace it, it is very inconvenient, so Spring AOP provides and, or, not to replace &&, ||, ! .

3.1. Examples of entry point usage

   3.1.1, execution () : use "execution (method expression)" matching method to execute;

 

mode

description

public * *(..)

Execution of any public method

* cn.javass..IPointcutService.*()

Any parameterless method in the IPointcutService interface under the cn.javass package and all subpackages

* cn.javass..*.*(..)

any method of any class under the cn.javass package and all subpackages

* cn.javass..IPointcutService.*(*)

The cn.javass package and all subpackages have only one parameter method for any of the IPointcutService interfaces

* (!cn.javass..IPointcutService+).*(..)

Any method other than "cn.javass package and all subpackages IPointcutService interface and subtype"

* cn.javass..IPointcutService+.*()

any parameterless method of IPointcutService interface and subtypes under cn.javass package and all subpackages

* cn.javass..IPointcut*.test*(java.util.Date)

In the cn.javass package and all subpackages, the IPointcut prefix type starts with test and has only one method with a parameter type of java.util.Date. Note that the matching is based on the parameter type of the method signature, not based on execution. The type of the incoming parameter determines

Such as the definition method: public void test (Object obj); even if java.util.Date is passed in during execution, it will not match;

* cn.javass..IPointcut*.test*(..)  throws

IllegalArgumentException, ArrayIndexOutOfBoundsException

Any method of the IPointcut prefix type under the cn.javass package and all subpackages, and throws IllegalArgumentException and ArrayIndexOutOfBoundsException exceptions

* (cn.javass..IPointcutService+

&& java.io.Serializable+).*(..)

Any method that implements the types of the IPointcutService interface and java.io.Serializable interface under the cn.javass package and all subpackages

@java.lang.Deprecated * *(..)

Any method with @ java.lang.Deprecated annotation

@java.lang.Deprecated @cn.javass..Secure  * *(..)

Any method with @ java.lang.Deprecated and @ cn.javass..Secure annotations

@(java.lang.Deprecated || cn.javass..Secure) * *(..)

Any method with @ java.lang.Deprecated or @ cn.javass..Secure annotation

(@cn.javass..Secure  *)  *(..)

Any return value type holding @ cn.javass..Secure method

*  (@cn.javass..Secure *).*(..)

Any type that defines a method holds @ cn.javass..Secure's method

* *(@cn.javass..Secure (*) , @cn.javass..Secure (*))

Any method with two parameters in the signature, and these two parameters are marked by @ Secure,

如public void test(@Secure String str1,

@Secure String str1);

* * ((@ cn.javass..Secure *)) or

* *(@ cn.javass..Secure *)

Any method with one parameter, and the parameter type holds @ cn.javass..Secure;

Such as public void test (Model model); and @Secure annotation on the Model class

* *(

@cn.javass..Secure (@cn.javass..Secure *) ,

@ cn.javass..Secure (@cn.javass..Secure *))

任何带有两个参数的方法,且这两个参数都被@ cn.javass..Secure标记了;且这两个参数的类型上都持有@ cn.javass..Secure;

 

* *(

java.util.Map<cn.javass..Model, cn.javass..Model>

, ..)

任何带有一个java.util.Map参数的方法,且该参数类型是以< cn.javass..Model, cn.javass..Model >为泛型参数;注意只匹配第一个参数为java.util.Map,不包括子类型;

如public void test(HashMap<Model, Model> map, String str);将不匹配,必须使用“* *(

java.util.HashMap<cn.javass..Model,cn.javass..Model>

, ..)”进行匹配;

而public void test(Map map, int i);也将不匹配,因为泛型参数不匹配

* *(java.util.Collection<@cn.javass..Secure *>)

任何带有一个参数(类型为java.util.Collection)的方法,且该参数类型是有一个泛型参数,该泛型参数类型上持有@cn.javass..Secure注解;

如public void test(Collection<Model> collection);Model类型上持有@cn.javass..Secure

* *(java.util.Set<? extends HashMap>)

任何带有一个参数的方法,且传入的参数类型是有一个泛型参数,该泛型参数类型继承与HashMap;

Spring AOP目前测试不能正常工作

* *(java.util.List<? super HashMap>)

任何带有一个参数的方法,且传入的参数类型是有一个泛型参数,该泛型参数类型是HashMap的基类型;如public voi test(Map map);

Spring AOP目前测试不能正常工作

* *(*<@cn.javass..Secure *>)

任何带有一个参数的方法,且该参数类型是有一个泛型参数,该泛型参数类型上持有@cn.javass..Secure注解;

Spring AOP目前测试不能正常工作

 3.1.2、within():使用“within(类型表达式)”匹配指定类型内的方法执行;

模式

描述

within(cn.javass..*)

cn.javass包及子包下的任何方法执行

within(cn.javass..IPointcutService+)

cn.javass包或所有子包下IPointcutService类型及子类型的任何方法

within(@cn.javass..Secure *)

持有cn.javass..Secure注解的任何类型的任何方法

必须是在目标对象上声明这个注解,在接口上声明的对它不起作用

 

 

 

 

 

 

 

 3.1.3、this():使用“this(类型全限定名)”匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这样就可能包括引入接口方法也可以匹配;注意this中使用的表达式必须是类型全限定名,不支持通配符;

模式

描述

this(cn.javass.spring.chapter6.service.IPointcutService)

当前AOP对象实现了 IPointcutService接口的任何方法

this(cn.javass.spring.chapter6.service.IIntroductionService)

当前AOP对象实现了 IIntroductionService接口的任何方法

也可能是引入接口

 

 

 

 

 

 

 

 3.1.4、target():使用“target(类型全限定名)”匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配;注意target中使用的表达式必须是类型全限定名,不支持通配符;

模式

描述

target(cn.javass.spring.chapter6.service.IPointcutService)

当前目标对象(非AOP对象)实现了 IPointcutService接口的任何方法

target(cn.javass.spring.chapter6.service.IIntroductionService)

当前目标对象(非AOP对象) 实现了IIntroductionService 接口的任何方法

不可能是引入接口

 

 

 

 

 

 

 

3.1.5、args():使用“args(参数类型列表)”匹配当前执行的方法传入的参数为指定类型的执行方法;注意是匹配传入的参数类型,不是匹配方法签名的参数类型;参数类型列表中的参数必须是类型全限定名,通配符不支持;args属于动态切入点,这种切入点开销非常大,非特殊情况最好不要使用; 

模式

描述

args (java.io.Serializable,..)

任何一个以接受“传入参数类型为 java.io.Serializable” 开头,且其后可跟任意个任意类型的参数的方法执行,args指定的参数类型是在运行时动态匹配的

 

 

 

 

 3.1.6、@within:使用“@within(注解类型)”匹配所以持有指定注解类型内的方法;注解类型也必须是全限定类型名;

模式

描述

@within cn.javass.spring.chapter6.Secure)

任何目标对象对应的类型持有Secure注解的类方法;

必须是在目标对象上声明这个注解,在接口上声明的对它不起作用

 

 

 

 

 3.1.7、@target:使用“@target(注解类型)”匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解;注解类型也必须是全限定类型名;

模式

描述

@target (cn.javass.spring.chapter6.Secure)

任何目标对象持有Secure注解的类方法;

必须是在目标对象上声明这个注解,在接口上声明的对它不起作用

 

 

 

 

 3.1.8、@args:使用“@args(注解列表)”匹配当前执行的方法传入的参数持有指定注解的执行;注解类型也必须是全限定类型名;

模式

描述

@args (cn.javass.spring.chapter6.Secure)

任何一个只接受一个参数的方法,且方法运行时传入的参数持有注解 cn.javass.spring.chapter6.Secure;动态切入点,类似于arg指示符;

 

 

 

 

 3.1.9、@annotation:使用“@annotation(注解类型)”匹配当前执行方法持有指定注解的方法;注解类型也必须是全限定类型名;

模式

描述

@annotation(cn.javass.spring.chapter6.Secure )

当前执行方法上持有注解 cn.javass.spring.chapter6.Secure将被匹配

 

 

 

 

 3.1.10、bean():使用“bean(Bean id或名字通配符)”匹配特定名称的Bean对象的执行方法;Spring AOP扩展的,在AspectJ中无相应概念;

模式

描述

bean(*Service)

匹配所有以Service命名(id或name)结尾的Bean

 

 

 

3.1.11、reference pointcut:表示引用其他命名切入点,只有@ApectJ风格支持,Schema风格不支持,如下所示:

 

 比如我们定义如下切面:

package cn.javass.spring.chapter6.aop; 
import org.aspectj.lang.annotation.Aspect; 
import org.aspectj.lang.annotation.Pointcut; 
@Aspect 
public class ReferencePointcutAspect { 
    @Pointcut(value="execution(* *())") 
    public void pointcut() {} 
}

可以通过如下方式引用: 

@Before(value = "cn.javass.spring.chapter6.aop.ReferencePointcutAspect.pointcut()") 
public void referencePointcutTest2(JoinPoint jp) {} 

除了可以在@AspectJ风格的切面内引用外,也可以在Schema风格的切面定义内引用,引用方式与@AspectJ完全一样。 

 到此我们切入点表达式语法示例就介绍完了,我们这些示例几乎包含了日常开发中的所有情况,但当然还有更复杂的语法等等,如果以上介绍的不能满足您的需要,请参考AspectJ文档。

 

二、通知参数

      如果想获取被被通知方法参数并传递给通知方法,该如何实现呢?接下来我们将介绍两种获取通知参数的方式。

2.1、使用JoinPoint获取

  Spring AOP提供使用org.aspectj.lang.JoinPoint类型获取连接点数据,任何通知方法的第一个参数都可以是JoinPoint(环绕通知是ProceedingJoinPoint,JoinPoint子类),当然第一个参数位置也可以是JoinPoint.StaticPart类型,这个只返回连接点的静态部分。

1) JoinPoint:提供访问当前被通知方法的目标对象、代理对象、方法参数等数据:

package org.aspectj.lang; 
import org.aspectj.lang.reflect.SourceLocation; 
public interface JoinPoint { 
    String toString();         //连接点所在位置的相关信息 
    String toShortString();     //连接点所在位置的简短相关信息 
    String toLongString();     //连接点所在位置的全部相关信息 
    Object getThis();         //返回AOP代理对象 
    Object getTarget();       //返回目标对象 
    Object[] getArgs();       //返回被通知方法参数列表 
    Signature getSignature();  //返回当前连接点签名 
    SourceLocation getSourceLocation();//返回连接点方法所在类文件中的位置 
    String getKind();        //连接点类型 
    StaticPart getStaticPart(); //返回连接点静态部分 
}

 

2)ProceedingJoinPoint:用于环绕通知,使用proceed()方法来执行目标方法: 

public interface ProceedingJoinPoint extends JoinPoint { 
    public Object proceed() throws Throwable; 
    public Object proceed(Object[] args) throws Throwable; 
} 

 

3) JoinPoint.StaticPart:提供访问连接点的静态部分,如被通知方法签名、连接点类型等: 

public interface StaticPart { 
Signature getSignature();    //返回当前连接点签名 
String getKind();          //连接点类型 
    int getId();               //唯一标识 
String toString();         //连接点所在位置的相关信息 
    String toShortString();     //连接点所在位置的简短相关信息 
    String toLongString();     //连接点所在位置的全部相关信息 
}

 

示例:使用如下方式在通知方法上声明,必须是在第一个参数,然后使用jp.getArgs()就能获取到被通知方法参数: 

@Before(value="execution(* sayBefore(*))") 
public void before(JoinPoint jp) {} 
 
@Before(value="execution(* sayBefore(*))") 
public void before(JoinPoint.StaticPart jp) {} 

2.2、自动获取:通过切入点表达式可以将相应的参数自动传递给通知方法,例如前边章节讲过的返回值和异常是如何传递给通知方法的。

在Spring AOP中,除了execution和bean指示符不能传递参数给通知方法,其他指示符都可以将匹配的相应参数或对象自动传递给通知方法。

示例:

@Before(value="execution(* test(*)) && args(param)", argNames="param") 
public void before1(String param) { 
    System.out.println("===param:" + param); 
} 

 切入点表达式execution(* test(*)) && args(param) :

1)首先execution(* test(*))匹配任何方法名为test,且有一个任何类型的参数;

2)args(param)将首先查找通知方法上同名的参数,并在方法执行时(运行时)匹配传入的参数是使用该同名参数类型,即java.lang.String;如果匹配将把该被通知参数传递给通知方法上同名参数。

其他指示符(除了execution和bean指示符)都可以使用这种方式进行参数绑定。

在此有一个问题,即前边提到的类似于【3.1.2构造器注入】中的参数名注入限制:在class文件中没生成变量调试信息是获取不到方法参数名字的。

所以我们可以使用策略来确定参数名:

  1. 如果我们通过“argNames”属性指定了参数名,那么就是要我们指定的;
@Before(value=" args(param)", argNames="param") //明确指定了 
public void before1(String param) { 
    System.out.println("===param:" + param); 
} 

2、如果第一个参数类型是JoinPoint、ProceedingJoinPoint或JoinPoint.StaticPart类型,应该从“argNames”属性省略掉该参数名(可选,写上也对),这些类型对象会自动传入的,但必须作为第一个参数; 

@Before(value=" args(param)", argNames="param") //明确指定了 
public void before1(JoinPoint jp, String param) { 
    System.out.println("===param:" + param); 
} 

3、如果“class文件中含有变量调试信息”将使用这些方法签名中的参数名来确定参数名; 

@Before(value=" args(param)") //不需要argNames了 
public void before1(JoinPoint jp, String param) { 
    System.out.println("===param:" + param); 
} 

4、如果没有“class文件中含有变量调试信息”,将尝试自己的参数匹配算法,如果发现参数绑定有二义性将抛出AmbiguousBindingException异常;对于只有一个绑定变量的切入点表达式,而通知方法只接受一个参数,说明绑定参数是明确的,从而能配对成功。 

@Before(value=" args(param)")  
public void before1(JoinPoint jp, String param) { 
    System.out.println("===param:" + param); 
}

5、以上策略失败将抛出IllegalArgumentException。

接下来让我们示例一下组合情况吧:

@Before(args(param) && target(bean) && @annotation(secure)",  
        argNames="jp,param,bean,secure") 
public void before5(JoinPoint jp, String param, 
IPointcutService pointcutService, Secure secure) { 
…… 
}

该示例的执行步骤如图6-5所示。

除了上边介绍的普通方式,也可以对使用命名切入点自动获取参数:

@Pointcut(value="args(param)", argNames="param") 
private void pointcut1(String param){} 
@Pointcut(value="@annotation(secure)", argNames="secure") 
private void pointcut2(Secure secure){} 
     
@Before(value = "pointcut1(param) && pointcut2(secure)", 
argNames="param, secure") 
public void before6(JoinPoint jp, String param, Secure secure) { 
…… 
}

 自此给通知传递参数已经介绍完了,示例代码在cn.javass.spring.chapter6.ParameterTest文件中。 

在Spring配置文件中,所以AOP相关定义必须放在<aop:config>标签下,该标签下可以有<aop:pointcut>、<aop:advisor>、<aop:aspect>标签,配置顺序不可变。

  • <aop:pointcut>:用来定义切入点,该切入点可以重用;
  • <aop:advisor>:用来定义只有一个通知和一个切入点的切面;
  • <aop:aspect>:用来定义切面,该切面可以包含多个切入点和通知,而且标签内部的通知和切入点定义是无序的;和advisor的区别就在此,advisor只包含一个通知和一个切入点。

 

 

发布了203 篇原创文章 · 获赞 6 · 访问量 4504

Guess you like

Origin blog.csdn.net/weixin_42073629/article/details/105212525