@AspectJ形式声明的所有的Pointcut表达式,在SpringAop内部都会通过解析,转化为具体的Pointcut对象。Spring Aop只是接了AspectJ的“外衣”,外衣下面的实现(解析方式及其解析后的语义都是由SpringAop识别的)。AspectJ Pointcut扩展类图(图片截自Spring揭秘):
Pointcut层次结构回顾(来自Spring揭秘):
在AspectJProxyFactory或者AnnotationAwareAspectJAutoProxyCreator通过反射获取了Aspect中的@Pointcut定义的AspectJ形式的Pointcut定义之后,在SpringAop框架内部都会构造一个对应的AspectJExpressionPointcut对象实例。AspectJExpression内部持有通过反射获取的Pointcut表达式。
@AspectJ形式的Advice包括:
@Before,@AfterReturning,@AfterThrowing(相当于在SpringAop中称为Throws Advice那种类型的Advice),@After(用于标注After(finally)Advice定义所在的地方,SpringAop中没有对应的Advice接口或者实现),@Around(拦截器类型的Advice),@DeclareParents(用于标注Intruduction类型的Advice,但是该注解对应标注对象的域而不是方法);除了@DeclareParents类型的Advice是属性级别的,其他的Advice类型都是方法级别的。
某些情况下,我么需要在Advice中访问Joinpoint处的参数,
1.在Spring1.x中可以通过MethodBeforeAdvice的before方法传入Object[]数组访问相应的方法参数,
2.但是现在可以通过org.aspectj.lang.Joinpoint,定义BeforeAdvice的方法可以将第一个参数设置为org.aspectj.lang.Joinpoint类型,通过org.aspectj.lang.Joinpoint的getArgs()方法,访问相应的Joinpoint处的参数值,还可以使用getThis()取得当前的代理对象,getTarget()取得当前的目标对象,getSignature()取得当前Joinpoint处的方法签名;
3.通过args标识符绑定:当args标识符接受到的不是具体的类型的,而是某个参数的名称的时候,他会将这个参数值绑定到对Advice方法的调用,args的参数必须与方法中声明的参数相同:
当Advice引用具体的Pointcut时的定义方式:
当然后面的两种方式可以结合使用,但是Joinpoint类型的参数永远处于第一个参数的位置。
注意:
1.不只是@BeforeAdvice可以这么声明第一个参数是Joinpoint类型,实际上除了AroundAdvice和Introduction类型的Advice之外,其他的Advice都可以这么声明;
2.除了execution类型的标志符不是直接指定对象类型之外,this,target,args,@within,@target,@annotation,@args都是直接指定对象类型的,但是与args一样,当他们的参数不是具体的对象类型时,而是具体的参数名称时,所起的作用与args标识符起的参数绑定的作用是一致的:
@AfterThrowingAdvice使用的方法和BeforeAdvice差不了太多,但是他有一个独有的属性:可以像[throwing=”e”]这种形式限定方法的参数名,并将其绑定到具体的参数上:
@AfterReturning Advice使用的方式和AfterThrowing Advice相似,他也有一个独有属性:当需要访问到目标对象的方法返回的参数时就一个使用renturning属性:[returning=”returnValue”]:
@After Advice:不管对象的方法是正常的执行结束,还是说异常结束,@After所定义的方法都是必须要执行的,一般来说@After用于释放系统中的某些资源,比如临时文件的删除,网络资源的释放,数据库连接的释放;
@Around Advice相较于其他类型的Advice所标示的方法的第一个参数是Joinpoint(可选参数),Around的第一个参数必须是org.aspectj.lang.ProceedingJoinpoint类型,它属于Joinpoint的子类型,我么需要通过ProceedingJoinpoint的proceed()方法继续调用链的执行;我们还可以在调用proceed()方法时传入一个Object[]数组代表方法参数列表,通常在修改方法参数调用值的时候,会使用带Object[]参数的proceed()方法;若有多个参数,应该按照方法定义的顺序指定,以便定义的Around Advice可以在SpringAop和AspectJ之间通用;
@Introduction Advice需要添加新的行为逻辑,并且以新的接口定义增加到目标对象上,其使用方法下:
@Aspect
public class IntroductionAspect{
@DeclareParents(
value="...MockTask",
defaultImpl="CounterImpl.class"
)
public ICounter counter;
}
需要注意的是:
1.@DeclareParents所归属的域定义类型为ICounter,也就是将为目标对象新增加的对象类型;
2.通过@DeclareParents的value属性,可以指定将要应用到的目标对象,可以只指定单一的目标对象,也可以指定一批目标对象;
@DeclareParents(
value="cn.spring21.unveilspring.service.*",
defaultImpl=CounterImpl.class
)
public ICounter counter;
通过@DeclareParents的defaultImpl属性,我们可以指定新增加的接口的实现类;
值得注意的是Introduction属于per-instance类型的Advice,所以目标对象的scope通常设置为prototype!!!当然也是存在scope为singleton的情况。