A03:Spring AOP 切入点表达式格式及常用写法

Spring java框架 spring spring4  admin 浏览 评论

道法自然,佛法精深



   Spring切面编程,一般使用第三方框架 AspectJ 配合Spring使用,Spring的AOP编程也是基于此框架。


一、AspectJ 有自己的切面编程配置,使得Spring的配置有了很大的简化,下面我们来看一下常见写法:

1、任意公共方法的执行:

    execution(public * *(..))

2、任何一个名字以“set”开始的方法的执行:

    execution(* set*(..))

3、AccountService接口定义的任意方法的执行:

    execution(* com.xyz.service.AccountService.*(..))

4、在service包中定义的任意方法的执行:

    execution(* com.xyz.service.*.*(..))

5、在service包或其子包中定义的任意方法的执行:

    execution(* com.xyz.service..*.*(..))

6、在service包中的任意连接点(在Spring AOP中只是方法执行):

    within(com.xyz.service.*)

7、在service包或其子包中的任意连接点(在Spring AOP中只是方法执行):

    within(com.xyz.service..*)

8、实现了AccountService接口的代理对象的任意连接点 (在Spring AOP中只是方法执行):

    this(com.xyz.service.AccountService)

    'this'在绑定表单中更加常用:- 请参见后面的通知一节中了解如何使得代理对象在通知体内可用。

9、实现AccountService接口的目标对象的任意连接点 (在Spring AOP中只是方法执行):

    target(com.xyz.service.AccountService)

    'target'在绑定表单中更加常用:- 请参见后面的通知一节中了解如何使得目标对象在通知体内可用。

10、任何一个只接受一个参数,并且运行时所传入的参数是Serializable 接口的连接点(在Spring AOP中只是方法执行)

    args(java.io.Serializable)

    'args'在绑定表单中更加常用:- 请参见后面的通知一节中了解如何使得方法参数在通知体内可用。

    请注意在例子中给出的切入点不同于 execution(* *(java.io.Serializable)): args版本只有在动态运行时候传入参数是Serializable时才匹配,而execution版本在方法签名中声明只有一个 Serializable类型的参数时候匹配。

11、目标对象中有一个 @Transactional 注解的任意连接点 (在Spring AOP中只是方法执行)

    @target(org.springframework.transaction.annotation.Transactional)

    '@target'在绑定表单中更加常用:- 请参见后面的通知一节中了解如何使得注解对象在通知体内可用。

12、任何一个目标对象声明的类型有一个 @Transactional 注解的连接点 (在Spring AOP中只是方法执行):

    @within(org.springframework.transaction.annotation.Transactional)

    '@within'在绑定表单中更加常用:- 请参见后面的通知一节中了解如何使得注解对象在通知体内可用。

13、任何一个执行的方法有一个 @Transactional 注解的连接点 (在Spring AOP中只是方法执行)

    @annotation(org.springframework.transaction.annotation.Transactional)

    '@annotation'在绑定表单中更加常用:- 请参见后面的通知一节中了解如何使得注解对象在通知体内可用。

14、任何一个只接受一个参数,并且运行时所传入的参数类型具有@Classified 注解的连接点(在Spring AOP中只是方法执行)

    @args(com.xyz.security.Classified)

    '@args'在绑定表单中更加常用:- 请参见后面的通知一节中了解如何使得注解对象在通知体内可用。

15、任何一个在名为'tradeService'的Spring bean之上的连接点 (在Spring AOP中只是方法执行):

    bean(tradeService)

16、任何一个在名字匹配通配符表达式'*Service'的Spring bean之上的连接点 (在Spring AOP中只是方法执行):

    bean(*Service)


二、execution 表达式解析:

表达式可以分为以下五个部分:

 1、execution(): 表达式主体。

 2、第一个*号:表示返回类型,*号表示所有的类型。

 3、包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.sample.service.impl包、子孙包下所有类的方法。

 4、第二个*号:表示类名,*号表示所有的类。

 5、*(..):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数。

下面我们以 execution(* test.jdbc..*.*(..)) 实例分析:

    a、第一个 * 表示切入任何返回类型的方法,也就是返回类型不限制;

 b、表示在 test.jdbc 包下,后面跟着 .. 表示在 test.jdbc 所有包下,包括子包; 

 c、第二个 * 表示所有类名,也就是对类不限制;

 d、第三个 * 表示类下所有方法,也就是对方法不限制;

 e、(..) 括号里两个点,表示对象方法参数不限制;


下面是Spring的AOP基本配置:

<aop:config>

 <aop:aspect ref="dbAspect">

   <aop:pointcut id="dbHander" expression="execution(* test.jdbc..*.*(..))" />

   <aop:before pointcut-ref="dbHander" method="createConn"></aop:before>

   <aop:after pointcut-ref="dbHander" method="closeConn"></aop:after>

 </aop:aspect>

</aop:config>


、AspectJ的Execution表达式详解:execution()是最常用的切点函数,其语法如下所示:

  execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?)   

  除了返回类型模式、方法名模式和参数模式外,其它项都是可选的。 

  与其直接讲解该方法的使用规则,还不如通过一个个具体的例子进行理解。 

下面,我们给出各种使用execution()函数实例。

1)通过方法签名定义切点

 execution(public * *(..))

匹配所有目标类的public方法,但不匹配SmartSeller和protected voidshowGoods()方法。第一个*代表返回类型,第二个*代表方法名,而..代表任意入参的方法;

 execution(* *To(..))

匹配目标类所有以To为后缀的方法。它匹配NaiveWaiter和NaughtyWaiter的greetTo()和serveTo()方法。第一个*代表返回类型,而*To代表任意以To为后缀的方法;


2)通过类定义切点

 execution(*com.baobaotao.Waiter.*(..))

匹配Waiter接口的所有方法,它匹配NaiveWaiter和NaughtyWaiter类的greetTo()和serveTo()方法。第一个*代表返回任意类型,com.baobaotao.Waiter.*代表Waiter接口中的所有方法;

 execution(*com.baobaotao.Waiter+.*(..))

匹配Waiter接口及其所有实现类的方法,它不但匹配NaiveWaiter和NaughtyWaiter类的greetTo()和serveTo()这两个Waiter接口定义的方法,同时还匹配NaiveWaiter#smile()和NaughtyWaiter#joke()这两个不在Waiter接口中定义的方法。


3)通过类包定义切点

在类名模式串中,“.*”表示包下的所有类,而“..*”表示包、子孙包下的所有类。

 execution(* com.baobaotao.*(..))

匹配com.baobaotao包下所有类的所有方法;

 execution(* com.baobaotao..*(..))

匹配com.baobaotao包、子孙包下所有类的所有方法,如com.baobaotao.dao,com.baobaotao.servier以及com.baobaotao.dao.user包下的所有类的所有方法都匹配。“..”出现在类名中时,后面必须跟“*”,表示包、子孙包下的所有类;

 execution(* com..*.*Dao.find*(..))

匹配包名前缀为com的任何包下类名后缀为Dao的方法,方法名必须以find为前缀。如com.baobaotao.UserDao#findByUserId()、com.baobaotao.dao.ForumDao#findById()的方法都匹配切点。


4)通过方法入参定义切点

切点表达式中方法入参部分比较复杂,可以使用“*”和“..”通配符,其中“*”表示任意类型的参数,而“..”表示任意类型参数且参数个数不限。

 execution(* joke(String,int))

匹 配joke(String,int)方法,且joke()方法的第一个入参是String,第二个入参是int。它匹配NaughtyWaiter#joke(String,int)方法。如果方法中的入参类型是java.lang包下的类,可以直接使用类名,否则必须使用全限定类名,如joke(java.util.List,int);

 execution(* joke(String,*))

匹 配目标类中的joke()方法,该方法第一个入参为String,第二个入参可以是任意类型,如joke(Strings1,String s2)和joke(String s1,double d2)都匹配,但joke(String s1,doubled2,String s3)则不匹配;

 execution(* joke(String,..))

匹配目标类中的joke()方法,该方法第 一个入参为String,后面可以有任意个入参且入参类型不限,如joke(Strings1)、joke(String s1,String s2)和joke(String s1,double d2,Strings3)都匹配。

 execution(* joke(Object+))

匹 配目标类中的joke()方法,方法拥有一个入参,且入参是Object类型或该类的子类。它匹配joke(Strings1)和joke(Client c)。如果我们定义的切点是execution(*joke(Object)),则只匹配joke(Object object)而不匹配joke(Stringcc)或joke(Client c)。


四、AspectJ 表达式图表详解:


类别函数入参说明
方法切点函数execution()方法

表示满足某一匹配模式的所有目标类方法连接点。

如execution(* greetTo(..))表示所有目标类中的greetTo()方法。

匹配模式串

@annotation()方法注

表示标注了特定注解的目标方法连接点。

如@annotation(com.baobaotao.anno.NeedTest)表示任何标注了@NeedTest注解的目标类方法。

解类名
方法入参切点函数args()类名

通过判别目标类方法运行时入参对象的类型定义指定连接点。

如args(com.baobaotao.Waiter)表示所有有且仅有一个按类型匹配于Waiter的入参的方法。


@args()类型注

通过判别目标方法的运行时入参对象的类是否标注特定注解来指定连接点。

如@args(com.baobaotao.Monitorable)表示任何这样的一个目标方法:

它有一个入参且入参对象的类标注@Monitorable注解。

解类名
目标类切点函数within()类名匹配串

   表示特定域下的所有连接点。

如within(com.baobaotao.service.*)表示com.baobaotao.service包中的所有连接点,

也即包中所有类的所有方法,

而within(com.baobaotao.service.*Service)表示在com.baobaotao.service包中,

所有以Service结尾的类的所有连接点。


target()类名

   假如目标类按类型匹配于指定类,则目标类的所有连接点匹配这个切点。

如通过target(com.baobaotao.Waiter)定义的切点,Waiter、以及Waiter实现类NaiveWaiter中所有连接点都匹配该切点。


@within()类型注解类名

   假如目标类按类型匹配于某个类A,且类A标注了特定注解,

则目标类的所有连接点匹配这个切点。

   如@within(com.baobaotao.Monitorable)定义的切点,

假如Waiter类标注了@Monitorable注解,

则Waiter以及Waiter实现类NaiveWaiter类的所有连接点都匹配。


@target()类型注解类名

   目标类标注了特定注解,则目标类所有连接点匹配该切点。

如@target(com.baobaotao.Monitorable),

假如NaiveWaiter标注了@Monitorable,则NaiveWaiter所有连接点匹配切点。

代理类切点函数this()类名

 代理类按类型匹配于指定类,则被代理的目标类所有连接点匹配切点。

这个函数比较难理解,这里暂不举例,留待后面详解。





























五、AspectJ 有自己的切面编程配置,使得Spring的配置有了很大的简化,下面我们来看一下常见写法