跳到主要内容

AspectJ形式AOP使用

本文相关代码(来自官方源码spring-test模块)请参见spring-framework org.springframework.mylearntest包下。

无需像1.0那样实现相应的接口,唯一要做的就是在这个Aspect类上加一个@Aspect注解。这样就可以判断ClassPath中哪些类是我们要找的Aspect定义。通过@Pointcut定义Pointcut,通过Around等注解来指定哪些方法定义了相应的Advice逻辑。

PerformanceTraceAspect
package org.springframework.mylearntest.aop2.aspectj;

import org.apache.commons.lang3.time.StopWatch;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

/**
* @Author: whalefall
* @Date: 2020/7/28 22:46
* Spring 2.0 之后无需实现接口定义pointcut
*/
@Aspect
public class PerformanceTraceAspect {
private final Log logger = LogFactory.getLog(PerformanceTraceAspect.class);

@Pointcut("execution(public void *.method1()) || execution(public void *.method2())")
public void pointcutName() {}

@Around("pointcutName()")
public Object performanceTrace(ProceedingJoinPoint joinPoint) throws Throwable {
StopWatch sw = new StopWatch();
try {
sw.start();
return joinPoint.proceed();
} finally {
System.out.println("pt in method["
+ joinPoint.getSignature().getName()
+ "]>>>>>>" + sw.toString());
}
}
}

两种织入Aspect方法

假如我们有一个目标对象Foo,有两种方式将Aspect定义织入这个目标对象类,,实现对其符合Pointcut定义的Joinpoint进行拦截。

Foo
public class Foo {
public void method1() {
System.out.println("method1 executed");
}

public void method2() {
System.out.println("method2 executed");
}
}

编程方式织入

通过AspectJProxyFactory实现
public class Test4AspectJProxyFactory {
public static void main(String[] args) {
AspectJProxyFactory weaver = new AspectJProxyFactory();
weaver.setProxyTargetClass(true);
weaver.setTarget(new Foo());
weaver.addAspect(PerformanceTraceAspect.class);
Object proxy = weaver.getProxy();
((Foo)proxy).method1();
((Foo)proxy).method2();
}
}

通过自动代理织入

针对@AspectJ风格的AOP,Spring AOP专门提供了一个AutoProxyCreator实现类进行自动代理,以免去过多的编码和配置工作,它是在AbstractAdvisorAutoProxyCreator基础上的一个扩展类。

与AutoProxyCreator一样,我们需要在IoC容器的配置文件中注册一下AnnotationAwareAspectJAutoProxyCreator就可以了。

xml 配置
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

<aop:aspectj-autoproxy proxy-target-class="true"/>
<!-- 等同于上面一行-->
<!--<bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator">
<property name="proxyTargetClass" value="true"/>
</bean>-->

<bean id="performanceAspect" class="org.springframework.mylearntest.aop2.aspectj.PerformanceTraceAspect"/>

<bean id="target" class="org.springframework.mylearntest.aop2.aspectj.Foo"/>
</beans>
TestAutoAspectJ
public class Test4AutoAspectJ {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("annotationawareaspectJautoproxycreator\\annotationawareaspectJautoproxycreator.xml");
Object proxy = context.getBean("target");
((Foo)proxy).method1();
((Foo)proxy).method2();
}
}

如果把target作为依赖对象注入其他的bean定义,那么依赖的主体对象在持有的也是被代理过的目标对象。

Tip

在使用@AspectJ形式的AOP的时候,应该尽量使用容器内的自动代理支持,通常,只有处于测试目的才会使用编程的方式直接织入操作,在使用过程中,你会发现,实际上这两种方式是有差异的,一些行为并不统一。

使用@Aspect形式的AOP,需要引入aspectjweaver.jar和aspectjrt.jar。

@AspectJ形式的Pointcut

在Spring框架发布 2.0 版本之前,Spring AOP没有像AspectJ那样的正式的Pointcut描述语言,而且也只支持方法级别的拦截。所以通常情况下,简单的方法名指定以及正则表达式两种,基本上可以很好地达到目的。

在Spring发布 2.0 版本之后,Spring AOP 框架集成了AspectJ的部分功能,这其中就包括AspectJ的Pointcut语言支持。从DTD过渡到XSD时代。

@AspectJ形式Pointcut声明方式

@AspectJ形式的Pointcut声明,依附在@ApectJ,通过使用org.aspectj.lang.annotation.Pointcut这个注解,指定AspectJ形式的Pointcut 表达式之后,将这个指定了相应表达式的注解标注到Aspect定义类的某个方法上即可。

@AspectJ形式的Pointcut声明包含如下两个部分。

  • Pointcut Expression

Pointcut Expression的载体为@Pointcut,该注解是方法级别的注解,所以Pointcut Expression不能脱离某个方法单独声明

表达式由两部分组成,分别是Pointcut标识符,和表达式匹配模式。

  • Pointcut Signature

它是一个方法的定义,作为Pointcut Expression的载体。Pointcut Signature所在的方法定义,除了返回类型必须是void之外,没有其他的限制。方法修饰符所起到的作用于java语言中语义相同,public型的Pointcut Signature可以在其他Aspect定义中引用,private则只能在当前Aspect定义中引用。可以将Pointcut Signature作为相应Pointcut Expression的标识符,在Pointcut Expression的定义中取代重复的Pointcut表达式定义。

YourAspect
@Aspect
public class YourAspect {
@Pointcut("execution(void method1())")
public void method1Execution() {}

@Pointcut("method1Execution()")
private void stillMethod1Execution() {}

// ...
}

AspectJ形式Pointcut表达式的标识符

execution

使用它将帮助我们匹配拥有指定方法签名的Joinpoint,使用格式如下

execution(modifiers-pattern ? ret-type-pattern declaring-type-pattern ? name-pattern(param-pattern) throws-pattern?)

其中方法的返回类型、方法名、以及参数部分匹配模式时必须指定的,其他部分的匹配模式可以省略。

Foo Example
public class Foo {
public void doSomething(String arg) {
// ...
}
}

那么可以指定如下Pointcut表达式来匹配Foo的doSomething方法

execution(public void Foo.doSomething(String))

execution(void Foo.doSomething(String))

*可以用于任何部分的匹配模式中,可以匹配相邻的多个字符*****

execution(* *(String))

execution(* *(*))

**..**通配符可以在两个位置使用

  1. declaring-type-pattern处使用指定多个层次的类型声明
// 只能定位到cn.spring21这一层下的所有类型
execution(void cn.spring21.*.doSomething(*)

// 可以匹配到所有的cn.spring21包下的包括子孙包
execution(void cn.spring21..*.doSomething(*)
  1. 用于方法列表匹配位置,则表示该方法可以有0到多个参数,参数类型不限。如果..换成*,则只能匹配一个参数。
execution(void *.doSomething(..))

// 表示第一个参数为String,第二个参数为任意类型
execution(void doSomething(String,*))

// 表示不限参数格式,不限类型,但是最后一个参数类型必须是String
execution(void doSomething(..,String))

within

within标识符只接受类类型的声明,它将会匹配指定类型下所有的Joinpoint方法。

within切点表达式写法

within(cn.spring21.aop.target.MockTarget)
// 匹配target包下所有类内部的方法级别的Joinpoint
within(cn.spring21.aop.target.*)
// 匹配aop包下以及子孙包下所有类内部方法级别的Joinpoint
within(cn.spring21.aop..*)

within切点表达式使用

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface AnyJoinpointAnnotation {}
@AnyJoinpointAnnotation
public class Foo{
public void method1(){}
public void method2(){}
}

当使用@within(AnyJoinpointAnnotation)的Pointcut表达式时,Foo类中method1、method2等方法将全部被Pointcu表达式所匹配。因为该类标注了@AnyJoinpointAnnotation,其他类如果也标注了该注解,也会被匹配到。

注意@within只接受注解类型,并对被指定注解类型所标注的类生效

this和target

在AspectJ中,this指代调用方法一方所在的对象,target指代被调用方法所在的对象,这样通常可以同时使用这两个标识符限定方法的调用关系。比如,如果Object1、Object2都会调用Object3 对象的某个方法,那么,Pointcut表达式定义this(Object2) && target(Object3) 只会当Object2调用Object3上的方法的时候才会匹配,而Object1调用Object3上的方法的时候不会被匹配。

在Spring AOP中的this和target表示语义不同于AspectJ中,this指代的是目标对象的代理对象,而target是目标对象。

不论使用基于接口的代理方式还是基于类的代理方式,this(ProxyInterface) 和 target(ProxyInterface)起到的效果差不多。

如果指定具体类型this(TargetFoo)和target(TargetFoo),target(TargetFoo)匹配到目标对象中所有的Joinpoint,因为目标对象确实是TargetFoo类型,而this(TargetFoo)则不可以。

this和target标识符都是在Pointcut表达式中与其他标识符结合使用,以进一步加强匹配的限定规则

execution(void cn.spring21.*.doSomething(*)) && this(TargetFoo)

execution(void cn.spring21.*.doSomething(*)) && targe(ProxyInterface)

// 匹配 同时实现ProxyInterface 和 ProxyInterface2
this(ProxyInterface) && target(proxyInterface2)

在Spring AOP中 @within 和 @target没有太大的区别。只不过@within属于静态匹配,而@target则是在运行时动态匹配Joinpoint。

args

该标识符的作用是,帮助我们捕捉拥有指定参数类型、指定参数数据量的方法级Joinpoint,而不管该方法在什么类型中被声明。例如,args(cn.spring21.unveilspring.domain.User)那么下面方法签名都会被匹配到

public class Foo {
public boolean login(User user) {
// ...
}
}

public class Bar {
public boolean isLogin(User user) {
// ...
}
}

要想捕捉public boolean login(Object user) 使用类似于execution(* *(User))这样的表达式无法捕捉到(因为它是静态的Pointcut),需要使用args的标识符。

使用@args标识符的Pointcut表达式会检查当前方法级的Joinpoint的方法参数类型,如果该次传入的参数类型拥有@args所制定的注解,当前Joinpoint将被匹配。

@args会尝试对系统中所有对象的每次方法执行的参数,都进行指定的注解动态检查。只要参数的类型标注有@args指定的注解类型,当前方法执行就将匹配。

annotation

使用annotation标识符的Pointcut表达式,将会尝试检查系统中所有对象的所有方法级别Joinpoint。如果被检测的方法标注有@annotation标识符所指定的注解类型,那么当前方法所在的Joinpoint将被Pointcut表达式所匹配。

想要下面方法支持事务机制,只需要在相应的方法上一个注解@Transactional,然后使用Pointcut表达式指定@annotation(org.springframework.transaction.annotation.Transactional)注意@Transactional注解被定义为@Target(ElementType.METHOD)

即使在Spring AOP 中可以使用AspectJ中的Pointcut表达式标识符,它们的语义跟AspectJ中的最终语义会有差别。所以,与其说Spring AOP现在可以使用AspectJ的Pointcut表达语言,不如说Spring AOP 借用了AspectJ的Pointcut表达语言的“外衣”,而实际上底层的语义和最终匹配,却还是沿用Spring AOP最初的机制。另外Spring AOP还可能在原来表达式的基础上增加新的标志符,如bean(..)。AspectJ扩展了Java语言,现在Spring AOP的Pointcut表达式则要扩展AspectJ的Pointcut。

@AspectJ形式的Pointcut在Spring AOP中的真实面目

实际上,@AspectJ形式声明的所有Pointcut表达式,在Spring AOP内部都会通过解析,转化为具体的Pointcut对象。因为Spring AOP有自己的Pointcut定义结构,所以@AspectJ形式声明这些Pointcut表达式,最终会转化为一个专门面向AspectJ的Pointcut实现。

org.springframework.aop.aspectj.AspectJExpressionPointcut代表Spring AOP中面向AspectJ的Pointcut具体实现。虽然它使用AspectJ 的相应支持,但是依然遵循Spring AOP的Pointcut定义。

定义ExpressionPointcut和AbstractExpressionPointcut主要为了以后的扩展性。如果还有AspectJ的Pointcut描述语言之外的形式,我们可以在这两个基础上进行集成。

在AspectJProxyFactory或者AnnotationAwareAspectJAutoProxyCreator通过反射获取了AspectJ中的@Pointcut定义的AspectJ形式的定义之后,在Spring AOP框架内部都会构造一个对应的AspectJExpressionPointcut对象实例。AspectJExpressionPointcut内部持有的通过反射获得的Pointcut表达式。

AspectJExpressionPointcut 属于Spring AOP的Pointcut定义之一,Spring AOP框架内部处理Pointcut匹配的逻辑不需要改变,依然使用原来的匹配机制,即通过ClassFilter和MethodMatcher进行具体Joinpoint的匹配工作。不过,AspectJExpressionPointcut在实现ClassFilter和MethodMatcher相应的方法逻辑的时候,会委托AspectJ类库的相关类做具体的工作。AspectJExpressionJoinpoint会委托AspectJ类库中的PointcutParser来对它所持有的AspectJ形式的Pointcut表达式进行解析。PointcutParser解析完成之后会返回一个PointcutExpression对象(依然是AspectJ类库中的类),之后匹配与否就直接委托这个PointcutExpression对象的相关方法进行处理了。

AspectJExpressionPointcut属于面向AspectJ的Pointcut实现,我们可以像Spring AOP中其他各种Pointcut实现类那样来使用它。只不过,构造完成后,不为它设置方法名或者正则表达式,而是如下所示设置@AspectJ形式的Pointcut

AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution( * someMethodName(..))");

@AspectJ形式的Advice

@AspectJ形式的定义,实际上就是使用@AspectJ标注的AspectJ定义类中的普通方法,只不过这些方法需要针对不同的Advice类型对应的注解进行标注。

使用 Pointcut 设置 BeforeAdvice
/**
* 使用AspectJ形式
*/
@Aspect
public class ResourceSetupAspectJ {
private Resource resource;

@Before("execution(boolean *.execute())")
public void setupResourcesBefore() throws Throwable {
if(!getResource().exists())
FileUtils.forceMkdir(getResource().getFile());
}
// 省略getter setter
}

使用Pointcut Signature引用Pointcut

Advice中参数传递

某些情况下,我们可能需要在Advice定义中访问Joinpoint处的方法参数,在1.x的Spring AOP中,我们可以通过MethodBeforeAdvice方法传入的Object数组,访问相应的方法参数。现在有两种方法可以达到相同的目的。

  • 通过org.aspectj.lang.JoinPoint。在@AspectJ形式的Aspect中,定义Before Advice的方法可以将第一个参数声明为org.aspectj.lang.JoinPoint类型,通过Joinpoint我们可以借助它的getArgs()方法,访问相应的Joinpoint方法的参数值,另外我们可以使用getThis()获取当前代理对象,getTarget()取得目标对象。

  • 通过args标志符绑定。前面已经介绍过了args标志符,我们可以通过为其制定相应的对象类型,来进一步限定Pointcut定义。当args标识符接受的不是具体的对象类型,而是某个参数名称的时候它会将这个参数名称对应的参数值绑定到对Advice方法的调用。

注意args指定的参数名称必须与Advice定义所在方法的参数名称相同。在这里args指定的值和setupResourceBefore方法的参数名都是taskName。如果Advice引用的是独立的Pointcut定义,使用args绑定的形式也是大同小异的。

Advice引用的是独立的Pointcut定义
@Pointcut("execution(boolean *.execute(String,..)) && args(taskName)")
private void resourceSetupJoinpoints(String taskName) {}

@Before(value="resourceSetupJoinpoints(taskName)")
public void setupResourcesBefore(String taskName) throws Throwable{}
同时使用JoinPoint以及使用args参数名称绑定功能
@Before(value="execution(boolean *.execute(String,..)) && args(taskName)")
public void setupResourcesBefore1(JoinPoint joinpoint, String taskName) throws Throwable {
// ...
}

针对Before Advice访问方法参数的两种方式

在AspectJ形式的Advice声明方式中,不只Before Advice方法第一个参数可以声明为org.aspectj.lang.JoinPoint类型,实际上,除了Around Advice和Introduction不可以用之外,剩余的Advice类型的方法声明都遵循这个规则

不只可以使用args标识符来绑定参数声明到方法,实际上Pointcut标识符中,除了execution标识符不会直接指定对象类型之外,其他像this、target、@within、@target、@annotation、@args等原本都是指定对象类型的。他们与args一样,在这样的场合下,如果它们的指定的是参数名称

Spring 2.0 基于XSD各种Advice的配声明方式

AspectAdvice
package org.springframework.mylearntest.aop2.schemaapsect;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.io.Resource;
import org.springframework.util.StopWatch;

/**
* @Author: WhaleFall541
* @Date: 2020/8/13 20:56
*/
@Aspect
public class AspectAdvice {
private Resource resource;

public Resource getResource() {
return resource;
}

public void setResource(Resource resource) {
this.resource = resource;
}

@AfterThrowing(pointcut = "execution(boolean *.execute(String, ..))", throwing = "e")
public void afterThrowing(RuntimeException e) {
final String exceptionMsg = ExceptionUtils.getStackTrace(e);
// ...
}

@AfterReturning(value = "execution(boolean *.execute(String, ..))",returning = "retValue")
public void taskExceptionCompleted(JoinPoint jp,boolean retValue) {
//Class clazz = jp.getTarget().getClass();
//getSqlMapClientTemplate().insert("BATCH.insertTaskStatus", clazz.getName());
}

@After("execution(boolean *.execute(String, ..))")
public void cleanUpResourcesIfNecessary() throws Throwable {
if (getResource().exists())
FileUtils.forceMkdir(getResource().getFile());
}

@Around("execution(boolean *.execute(String, ..))")
public Object performanceTrace(ProceedingJoinPoint pjp) throws Throwable {
StopWatch watch = new StopWatch();
try {
watch.start();
return pjp.proceed();
} finally {
watch.stop();
System.out.println("watch.toString() = " + watch.toString());
}
}
}

Introduction

在Spring中,Introduction的实现是通过将需要添加的新的行为逻辑,以新的接口定义的增加到目标对象上。要以@AspectJ形式声明Introduction,我们需要在Aspect中声明一个示例变量,它的类型对应的就是新增加的接口类型,然后使用org.aspectj.lang.annotatioin.DeclareParents对其进行标注。通过@DeclareParents指定新接口定义的实现类以及将要加诸其上的目标对象。

IntroductionAspect
package org.springframework.mylearntest.aop2.introduction;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclareParents;
import org.springframework.mylearntest.aop1.weaver.proxyfactorybean.CounterImpl;
import org.springframework.mylearntest.aop1.weaver.proxyfactorybean.ICounter;

/**
* @Author: WhaleFall541
* @Date: 2020/8/9 16:50
* 将ICounter行为introduce附加到ITask之上
*/
@Aspect
public class IntroductionAspect {
// value属性指定将要应用到的一个对象,也可以某个包下所有类value="cn.spring21.unveilspring.service.*"
// 通过defaultImpl指定新增加的接口定义的实现类,在这里ICounter的实现类为org.springframework.mylearntest.aop1.weaver.proxyfactorybean.CounterImpl
@DeclareParents(value = "org.springframework.mylearntest.aop1.weaver.baseoninterface.MockTask", defaultImpl =
CounterImpl.class)
public ICounter counter;// ICounter即为将要为目标对象鞥家的对象类型
}
xml配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation=
"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"
xmlns:aop="http://www.springframework.org/schema/aop">

<aop:aspectj-autoproxy proxy-target-class="true"/>

<bean id="task" class="org.springframework.mylearntest.aop1.weaver.baseoninterface.MockTask" scope="prototype"/>

<bean id="counterIntroduction" class="org.springframework.mylearntest.aop2.introduction.IntroductionAspect"/>
</beans>
Test4IntroductionAspect
package org.springframework.mylearntest.aop2.introduction;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.mylearntest.aop1.weaver.proxyfactorybean.ICounter;

/**
* @Author: WhaleFall541
* @Date: 2020/8/9 17:10
*/
public class Test4IntroductionAspect {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("introductionaspect/introductionaspect.xml");
Object task1 = context.getBean("task");
Object task2 = context.getBean("task");

System.out.println(((ICounter)task1).getCounter()); // 1
System.out.println(((ICounter)task1).getCounter()); // 2
System.out.println(((ICounter)task2).getCounter()); // 1
}
}

注意 Introduction属于per-instance类型的Advice,所以不要忘记目标对象的scope通常情况下应该设置为prototype

Advice的执行顺序

如果匹配同一个Joinpoint的多个Advice都声明在同一个Aspect定义中,那么这些Advice执行顺序,由他们在Aspect中的声明顺序决定。最先声明的Advice拥有最高的优先级。对于Before Advice来说,拥有最高优先级的最先运行,而对于AfterReturningAdvice,拥有最高优先级的则是最后运行。

MultiAdviceAspect
package org.springframework.mylearntest.aop2.priorityofadvice;

import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

/**
* @Author: WhaleFall541
* @Date: 2020/8/9 18:00
* 如果将该AspectJ织入目标对象,那么可以得到结果为
* before one
* before two
* after returning two
* after returning one
*/
@Aspect
public class MultiAdviceAspect {
@Pointcut("execution(boolean *.execute(String, ..))")
public void taskExecution(){}

@Before("taskExecution()")
public void beforeOne() {
System.out.println("before one");
}

@Before("taskExecution()")
public void beforeTwo() {
System.out.println("before two");
}

@AfterReturning("taskExecution()")
public void afterReturningOne() {
System.out.println("after retuning one");
}

@AfterReturning("taskExecution()")
public void afterReturningTwo() {
System.out.println("after retuning two");
}
}

当这些Advice声明在不同的Aspect内的时候。如果多个Advice声明所对应的Pointcut定义匹配同一个Joinpoint,但它们又不是声明在同一个Aspect内的话。我们需要用到Spring的org.springframework.core.Ordered 接口,只需要让相应的Aspect定义实现Ordered接口即可,否则Advice执行顺序是不确定的。

Note: 如果通过编程方式来使用这些Aspect,Aspect内的Advice执行顺序完全由添加到AspectJProxyFactory的顺序决定,而不是Ordered接口所规定的顺序。,如果采用了如下代码的顺序的话,那么AnotherAspect内的Advice要比MultiAdviceAspect内的Advice优先执行。

Aspect的实例化模式

对于注册到容器的各个Aspect,它默认的实例化模式采用的是singleton,在容器中会实例化并持有每个Aspect定义的单一实例(行为上恰好与容器singleton型scope的行为相吻合)

除了实例化模式,AspectJ还支持perthis、pertarget、percflow、percflowbelow以及perwithin等实例化模式,不过Spring2.0之后AOP只支持默认的singleton、perthis和pertarget三种实例化模式。

要想指定相应的Aspect的实例化模式,可以通过@Aspect指定perthis或者pertarget语句。如果想让MultiAdviceAspect的实例化模式从默认的singleton变成perthis。可以如下指定

@Aspect("perthis(execution(boolean *.execute(String, ..)))")
public class MultiAdviceAspect {

@Pointcut("execution(boolean *.execute(String, ..))")
public void taskExecution(){}

// ..

}

在perthis指定的Pointcut定义匹配之后,会为相应的代理对象实例化各自的Aspect实例。对于pertarget来说,则是为了匹配的单独目标对象实例化相应的Aspect。不过,使用了perthis或者pertarget指定了Aspect实例化模式之后,将这些Aspect注册到容器时,不能为其bean定义指定singleton的scope,否则会出现异常。

参考资料:

  1. 书籍 王福强-Spring揭秘