一、Spring AOP支持的AspectJ切入点指示符

切入点指示符用来指示切入点表达式目的,在Spring AOP中目前只有执行方法这一个连接点,Spring AOP支持的AspectJ切入点指示符如下:

  1. execution用于匹配方法执行的连接点;
  2. within用于匹配指定类型内的方法执行;
  3. this用于匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配;
  4. target用于匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配;
  5. args用于匹配当前执行的方法传入的参数为指定类型的执行方法;
  6. @within用于匹配所以持有指定注解类型内的方法;
  7. @target用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解;
  8. @args用于匹配当前执行的方法传入的参数持有指定注解的执行;
  9. @annotation用于匹配当前执行方法持有指定注解的方法;
  10. beanSpring AOP扩展的,AspectJ没有对于指示符,用于匹配特定名称的Bean对象的执行方法;
  11. reference pointcut表示引用其他命名切入点,只有@ApectJ风格支持,Schema风格不支持;

AspectJ切入点支持的切入点指示符还有: call、get、set、preinitialization、staticinitialization、initialization、handler、adviceexecution、withincode、cflow、cflowbelow、if、@this、@withincode;但Spring AOP目前不支持这些指示符,使用这些指示符将抛出IllegalArgumentException异常。这些指示符Spring AOP可能会在以后进行扩展。

二、命名及匿名切入点

命名切入点可以被其他切入点引用,而匿名切入点是不可以的。只有@AspectJ支持命名切入点,而Schema风格不支持命名切入点。如下所示,@AspectJ使用如下方式引用命名切入点:

三、类型匹配语法

首先让我们来了解下AspectJ类型匹配的通配符:

  1. *:匹配任何数量字符;
  2. ..:匹配任何数量字符的重复,如在类型模式中匹配任何数量子包;而在方法参数模式中匹配任何数量参数;
  3. +匹配指定类型的子类型;仅能作为后缀放在类型模式后边;

示例:

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

具体的匹配表达式类型:

注解? 类的全限定名字  

注解:可选,类型上持有的注解,如@Deprecated;

类的全限定名:必填,可以是任何类全限定名;

匹配方法执行:使用如下方式匹配:

注解? 修饰符? 返回值类型 类型声明?方法名(参数列表) 异常列表?  

注解:可选,方法上持有的注解,如@Deprecated;

修饰符:可选,如public、protected;

返回值类型:必填,可以是任何类型模式;“*”表示所有类型;

类型声明:可选,可以是任何类型模式;

方法名:必填,可以使用“*”进行模式匹配;

参数列表:“()”表示方法没有任何参数;“(..)”表示匹配接受任意个参数的方法,“(..,java.lang.String)”表示匹配接受java.lang.String类型的参数结束,且其前边可以接受有任意个参数的方法;“(java.lang.String,..)” 表示匹配接受java.lang.String类型的参数开始,且其后边可以接受任意个参数的方法;“(*,java.lang.String)” 表示匹配接受java.lang.String类型的参数结束,且其前边接受有一个任意类型参数的方法;

异常列表:可选,以“throws 异常全限定名列表”声明,异常全限定名列表如有多个以“,”分割,如throws java.lang.IllegalArgumentException, java.lang.ArrayIndexOutOfBoundsException;

匹配Bean名称:可以使用Bean的id或name进行匹配,并且可使用通配符“*”;

四、组合切入点表达式

AspectJ使用且(&&)、或(||)、非(!)来组合切入点表达式。

在Schema风格下,由于在XML中使用“&&”需要使用转义字符“&&”来代替之,所以很不方便,因此Spring AOP提供了and、or、not来代替&&、||、!

五、切入点使用示例

1、execution:使用“execution(方法表达式)”匹配方法执行;

模式

描述

public * *(..)

任何公共方法的执行

* cn.javass..IPointcutService.*()

cn.javass包及所有子包下IPointcutService接口中的任何无参方法

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

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

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

cn.javass包及所有子包下IPointcutService接口的任何只有一个参数方法

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

非“cn.javass包及所有子包下IPointcutService接口及子类型”的任何方法

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

cn.javass包及所有子包下IPointcutService接口及子类型的的任何无参方法

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

cn.javass包及所有子包下IPointcut前缀类型的的以test开头的只有一个参数类型为java.util.Date的方法,注意该匹配是根据方法签名的参数类型进行匹配的,而不是根据执行时传入的参数类型决定的

如定义方法:public void test(Object obj);即使执行时传入java.util.Date,也不会匹配的;

* cn.javass..IPointcut*.test*(..)  throws IllegalArgumentException, ArrayIndexOutOfBoundsException

cn.javass包及所有子包下IPointcut前缀类型的的任何方法,且抛出IllegalArgumentException和ArrayIndexOutOfBoundsException异常

* (cn.javass..IPointcutService+&& java.io.Serializable+).*(..)

任何实现了cn.javass包及所有子包下IPointcutService接口和java.io.Serializable接口的类型的任何方法

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

任何持有@java.lang.Deprecated注解的方法

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

任何持有@java.lang.Deprecated和@cn.javass..Secure注解的方法

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

任何持有@java.lang.Deprecated或@ cn.javass..Secure注解的方法

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

任何返回值类型持有@cn.javass..Secure的方法

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

任何定义方法的类型持有@cn.javass..Secure的方法

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

任何签名带有两个参数的方法,且这个两个参数都被@ Secure标记了,

如public void test(@Secure String str1,

@Secure String str1);

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

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

如public void test(Model model);且Model类上持有@Secure注解

* *(@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目前测试不能正常工作

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

模式

描述

within(cn.javass..*)

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

within(cn.javass..IPointcutService+)

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

within(@cn.javass..Secure *)

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

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

3、this:使用“this(类型全限定名)”匹配当前AOP代理对象类型的执行方法;

注意是AOP代理对象的类型匹配,这样就可能包括引入接口方法也可以匹配;

注意this中使用的表达式必须是类型全限定名,不支持通配符;

模式

描述

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

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

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

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

也可能是引入接口

4、target使用“target(类型全限定名)”匹配当前目标对象类型的执行方法;

注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配;

注意target中使用的表达式必须是类型全限定名,不支持通配符;

模式

描述

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

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

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

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

不可能是引入接口

5、args使用“args(参数类型列表)”匹配当前执行的方法传入的参数为指定类型的执行方法;

注意是匹配传入的参数类型,不是匹配方法签名的参数类型;

参数类型列表中的参数必须是类型全限定名,通配符不支持;

args属于动态切入点,这种切入点开销非常大,非特殊情况最好不要使用;

模式

描述

args(java.io.Serializable,..)

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

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

模式

描述

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

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

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

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

模式

描述

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

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

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

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

模式

描述

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

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

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

模式

描述

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

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

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

模式

描述

bean(*Service)

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

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完全一样。

六、通知参数

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

Ⅰ、使用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) {}

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

在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) :

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

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

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

在此有一个问题,参数名注入限制:在class文件中没生成变量调试信息是获取不到方法参数名字的。

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

如果我们通过“argNames”属性指定了参数名,那么就是要我们指定的;

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

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

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

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

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

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

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

以上策略失败将抛出IllegalArgumentException。

下面是示例一下组合情况:

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

执行步骤如图所示:

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

@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) {
  ……
}

自此给通知传递参数已经介绍完毕。

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

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

总结:

1、AspectJ是一个开源项目。

2、AOP是面向方面的编程。

参考:

(官方针对AOP的AspectJ语法)https://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html#aop-ataspectj

以上转自:http://sishuo*.com/forum/posts/list/281.html(把*改成k)

Spring的AOP AspectJ切入点语法详解(转)的更多相关文章

  1. Spring 之AOP AspectJ切入点语法详解

    记录一下,以后学习 https://blog.csdn.net/zhengchao1991/article/details/53391244

  2. spring AspectJ切入点语法详解 记录以便查阅

    AspectJ切入点语法详解 6.5.1  Spring AOP支持的AspectJ切入点指示符 切入点指示符用来指示切入点表达式目的,,在Spring AOP中目前只有执行方法这一个连接点,Spri ...

  3. 开涛spring3(6.5) - AOP 之 6.5 AspectJ切入点语法详解

    6.5.1  Spring AOP支持的AspectJ切入点指示符 切入点指示符用来指示切入点表达式目的,,在Spring AOP中目前只有执行方法这一个连接点,Spring AOP支持的Aspect ...

  4. Spring AspectJ切入点语法详解

    1.Spring AOP支持的AspectJ切入点指示符 切入点指示符用来指示切入点表达式目的,,在Spring AOP中目前只有执行方法这一个连接点,Spring AOP支持的AspectJ切入点指 ...

  5. Spring AspectJ 切入点语法详解(7)

    1.Spring AOP支持的AspectJ切入点指示符 切入点指示符用来指示切入点表达式目的,,在Spring AOP中目前只有执行方法这一个连接点,Spring AOP支持的AspectJ切入点指 ...

  6. AspectJ切入点语法详解

    在看这篇文章前,建议首先看下 spring aop与aspectj的区别 aop是对oop的补充. 参阅:https://blog.csdn.net/column/details/aspectj.ht ...

  7. spring AOP 之四:@AspectJ切入点标识符语法详解

    @AspectJ相关文章 <spring AOP 之二:@AspectJ注解的3种配置> <spring AOP 之三:使用@AspectJ定义切入点> <spring ...

  8. springboot aop的execution 表达式详解

    Aspectj切入点语法定义 在使用spring框架配置AOP的时候,不管是通过XML配置文件还是注解的方式都需要定义pointcut"切入点" 例如定义切入点表达式  execu ...

  9. 【Spring】——声明式事务配置详解

    项目中用到了spring的事务: @Transactional(rollbackFor = Exception.class, transactionManager = "zebraTrans ...

随机推荐

  1. 50个Bootstrap扩展插件

    Bootstap这个框架本身已经包含了开发网页的众多要素,包括了常用的工具以及扩展组件,如果你在开发页面时觉得在某些方面还不够的话,不妨看看最新收集的50个Bootstrap扩展插件,这些插件在我们平 ...

  2. 为什么ABAP整型的1转成string之后,后面会多个空格

    有同事问这个问题:lv_s是从int4转过来的,长度为2,和硬编码的lv_s2(长度为1)相比,后面多了个空格. 为什么?查SAP ABAP的编程帮助F1 help. 帮助文档说的很清楚了:如果赋值操 ...

  3. Python 学习日志9月21日

    9月21日 周四 今天是个特殊的日子吗,总感觉9月21这个日子听着怪怪的. 今天早晨看<Head First HTML and CSS>第13章节“表格和更多列表”,内容不多,看完并做了详 ...

  4. exit - 使程序正常中止

    SYNOPSIS 总览 #include <stdlib.h> void exit(int status); DESCRIPTION 描述 函数 exit() 使得程序正常中止,statu ...

  5. activitymq 集群构建

    测试zk是否正常 [root@node2 bin]# cd /zk/1/zookeeper-3.4.10/bin/ [root@node2 bin]# ./zkCli.sh -server 10.50 ...

  6. centos下安装nodejs的三种种方式

    方法一:源码包安装 官网下载 centos下载最新版10.9 https://nodejs.org/dist/v10.9.0/node-v10.9.0-linux-x64.tar.xz mkdir / ...

  7. Java代理模式精讲之静态代理,动态代理,CGLib代理

    代理(Proxy)是一种设计模式,通俗的讲就是通过别人达到自己不可告人的目的(玩笑). 如图: 代理模式的关键点是:代理对象与目标对象.代理对象是对目标对象的扩展,并会调用目标对象 这三个代理模式,就 ...

  8. 阿里云人脸比对API封装

    这是根据封装是根据阿里云官方给的Demo进行修改的,当时是因为编写微信小程序云函数需要使用到阿里云人脸比对接口,才对其进行封装的. 记录下来,方便下次使用. 复制下来可以直接使用. 用到的依赖如下: ...

  9. Bootstrap 3 Glyphicons are not working

    Bootstrap 3 Glyphicons are not working 解答1 Note to readers: be sure to read @user2261073's comment a ...

  10. spring boot 自动生成mybatis代码

    1)在pom.xml中增加generator插件 <!--自动生成mybaits--> <plugin> <groupId>org.mybatis.generato ...