34--SpringAop获取增强(二)
引
在上一篇结尾,我们得到了增强的提取工作交给了List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
。接着分析。
1. getAdvisors获取增强简析
@Override
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
// 1、预处理工作,包括获取切面类,名称,验证等
Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
validate(aspectClass);
// 我们需要用一个装饰器包装MetadataAwareAspectInstanceFactory,这样它只会实例化一次。
MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
// 2、提取增强
// 2.1、获取切面类的所有方法,循环判断提取合适的切入点,并创建增强
List<Advisor> advisors = new ArrayList<>();
for (Method method : getAdvisorMethods(aspectClass)) {
// 获取增强
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
if (advisor != null) {
advisors.add(advisor);
}
}
// If it's a per target aspect, emit the dummy instantiating aspect.
// 2.2、处理perthis和pertarget切面类
if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
// 创建SyntheticInstantiationAdvisor实例
Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
// 将SyntheticInstantiationAdvisor实例加入到advisors集合首位,注意:不是替换
advisors.add(0, instantiationAdvisor);
}
// Find introduction fields.
// 2.3、处理引入,获取所有的引入并循环创建DeclareParentsAdvisor
for (Field field : aspectClass.getDeclaredFields()) {
Advisor advisor = getDeclareParentsAdvisor(field);
if (advisor != null) {
advisors.add(advisor);
}
}
return advisors;
}
在该方法中提取工作一共分为了三步:提取普通增强、处理处理perthis和pertarget、引介增强。篇幅有限,只分析普通增强的处理过程,该过程也是大家最关心的过程。来看代码:
@Override
@Nullable
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
int declarationOrderInAspect, String aspectName) {
// 1、验证
validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
// 2、提取切入点
AspectJExpressionPointcut expressionPointcut = getPointcut(
candidateAdviceMethod,
aspectInstanceFactory.getAspectMetadata().getAspectClass());
if (expressionPointcut == null) {
return null;
}
// 3、创建增强
return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}
在该方法中,终于看到了核心的切点提取和创建增强。下面分别来看这两步是如何实现的。
2.提取切入点
@Nullable
private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
// 1、从候选切入点上找出增强表达式
AspectJAnnotation<?> aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
if (aspectJAnnotation == null) {
return null;
}
// 2、创建AspectJExpressionPointcut对象
AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);
ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
if (this.beanFactory != null) {
ajexp.setBeanFactory(this.beanFactory);
}
return ajexp;
}
2.1 从候选切入点上找出增强表达式
/**
* Find and return the first AspectJ annotation on the given method
* (there <i>should</i> only be one anyway...).
*/
@SuppressWarnings("unchecked")
@Nullable
protected static AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) {
Class<?>[] classesToLookFor = new Class<?>[] {
Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class};
for (Class<?> c : classesToLookFor) {
AspectJAnnotation<?> foundAnnotation = findAnnotation(method, (Class<Annotation>) c);
if (foundAnnotation != null) {
return foundAnnotation;
}
}
return null;
}
该过程主要通过findAnnotation方法完成,但是该方法调用较深,也不属于我们分析的范畴,感兴趣的同学可以自己跟踪调试。
2.2 创建AspectJExpressionPointcut对象
/**
* Create a new AspectJExpressionPointcut with the given settings.
* @param declarationScope the declaration scope for the pointcut
* @param paramNames the parameter names for the pointcut
* @param paramTypes the parameter types for the pointcut
*/
public AspectJExpressionPointcut(Class<?> declarationScope, String[] paramNames, Class<?>[] paramTypes) {
this.pointcutDeclarationScope = declarationScope;
if (paramNames.length != paramTypes.length) {
throw new IllegalStateException(
"Number of pointcut parameter names must match number of pointcut parameter types");
}
this.pointcutParameterNames = paramNames;
this.pointcutParameterTypes = paramTypes;
}
该创建过程比较简单,将提取的切点表达式的信息实例化为AspectJExpressionPointcut对象即可。
3.创建增强
public InstantiationModelAwarePointcutAdvisorImpl(
AspectJExpressionPointcut declaredPointcut,
Method aspectJAdviceMethod,
AspectJAdvisorFactory aspectJAdvisorFactory,
MetadataAwareAspectInstanceFactory aspectInstanceFactory,
int declarationOrder,
String aspectName) {
this.declaredPointcut = declaredPointcut;
this.declaringClass = aspectJAdviceMethod.getDeclaringClass();
this.methodName = aspectJAdviceMethod.getName();
this.parameterTypes = aspectJAdviceMethod.getParameterTypes();
this.aspectJAdviceMethod = aspectJAdviceMethod;
this.aspectJAdvisorFactory = aspectJAdvisorFactory;
this.aspectInstanceFactory = aspectInstanceFactory;
this.declarationOrder = declarationOrder;
this.aspectName = aspectName;
// 1、延迟初始化
// 在Spring AOP中,切面类的实例只有一个,比如前面我们一直使用的MyAspect类,
// 假设我们使用的切面类需要具有某种状态,以适用某些特殊情况的使用,比如多线程环境,此时单例的切面类就不符合我们的要求了。
// 在Spring AOP中,切面类默认都是单例的,但其还支持另外两种多例的切面实例的切面,即perthis和pertarget,
// 需要注意的是perthis和pertarget都是使用在切面类的@Aspect注解中的。
// 这里perthis和pertarget表达式中都是指定一个切面表达式,其语义与前面讲解的this和target非常的相似,
// perthis表示如果某个类的代理类符合其指定的切面表达式,那么就会为每个符合条件的目标类都声明一个切面实例;
// pertarget表示如果某个目标类符合其指定的切面表达式,那么就会为每个符合条件的类声明一个切面实例。
// 从上面的语义可以看出,perthis和pertarget的含义是非常相似的。如下是perthis和pertarget的使用语法:
// perthis(pointcut-expression)
// pertarget(pointcut-expression)
if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
// Static part of the pointcut is a lazy type.
Pointcut preInstantiationPointcut = Pointcuts.union(
aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(),
this.declaredPointcut);
// Make it dynamic: must mutate from pre-instantiation to post-instantiation state.
// If it's not a dynamic pointcut, it may be optimized out
// by the Spring AOP infrastructure after the first evaluation.
this.pointcut = new PerTargetInstantiationModelPointcut(
this.declaredPointcut,
preInstantiationPointcut,
aspectInstanceFactory);
this.lazy = true;
}
// 2、立刻初始化
else {
// A singleton aspect.
this.pointcut = this.declaredPointcut;
this.lazy = false;
// 初始化增强
this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
}
}
创建过程中又涉及到perthis和pertarget,无需理会,还是先看单例模式下的创建过程:
/**
* 根据pointcut初始化增强
* @param pointcut
* @return
*/
private Advice instantiateAdvice(AspectJExpressionPointcut pointcut) {
Advice advice = this.aspectJAdvisorFactory.getAdvice(
this.aspectJAdviceMethod, pointcut,
this.aspectInstanceFactory,
this.declarationOrder,
this.aspectName);
return (advice != null ? advice : EMPTY_ADVICE);
}
@Override
@Nullable
public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
// 1、获取增强之前的处理
Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
validate(candidateAspectClass);
AspectJAnnotation<?> aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
if (aspectJAnnotation == null) {
return null;
}
// If we get here, we know we have an AspectJ method.
// Check that it's an AspectJ-annotated class
if (!isAspect(candidateAspectClass)) {
throw new AopConfigException("Advice must be declared inside an aspect type: " +
"Offending method '" + candidateAdviceMethod + "' in class [" +
candidateAspectClass.getName() + "]");
}
// 2、针对各种不同的增强,做不同的处理
AbstractAspectJAdvice springAdvice;
switch (aspectJAnnotation.getAnnotationType()) {
// 1、前置增强
case AtBefore:
springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
// 2、后置增强
case AtAfter:
springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
// 3、后置返回增强
case AtAfterReturning:
springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterReturningAnnotation.returning())) {
springAdvice.setReturningName(afterReturningAnnotation.returning());
}
break;
// 4、后置异常增强
case AtAfterThrowing:
springAdvice = new AspectJAfterThrowingAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
}
break;
// 5、环绕增强
case AtAround:
springAdvice = new AspectJAroundAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
// 6、如果是Pointcut,则不做处理
case AtPointcut:
return null;
// 7、未能满足case条件,抛出异常
default:
throw new UnsupportedOperationException("Unsupported advice type on method: " + candidateAdviceMethod);
}
// 3、获取增强方法之后,对增强方法进行配置
springAdvice.setAspectName(aspectName);
springAdvice.setDeclarationOrder(declarationOrder);
String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
if (argNames != null) {
springAdvice.setArgumentNamesFromStringArray(argNames);
}
springAdvice.calculateArgumentBindings();
return springAdvice;
}
看到这里,是不是有种豁然开朗的感觉,Spring针对各种不同的增强创建过程,做了不同的处理。到这里Spring创建增强的过程就完成了,对于不同的增强类型创建,大家可以自己debug跟踪,这里不一一赘述了。
4.关于Advisor的疑问
这两篇在介绍获取增强,但是最受获取到的并不是Advice而是Advisor,可能大家会有所疑问。举例说明一下:
如果我们定义了一个DogAspect类,并用@AspectJ对其进行注解,那么该类仅仅代表一个切面类,会被Spring扫描并解析,仅此而已,该类不代表SpringAop概念中的切面。那么Spring如果通过解析该类得到具体的切面呢?
首先,关于SpringAop中的切面概念,可以理解为 切面=连接点+增强
其次,而标记了@AspectJ注解的类在被Spring解析的时候,
- 提取该类的方法上的切点表达式注解:例如-->@Pointcut("execution(* com.lyc.cn.v2.day07..(..))"),解析之后,就可以的到具体的切点.
- 提取该类的方法上的增强注解:例如:@Before("test()")解析之后,就可以得到具体的增强代码
最后,通过第一步和第二步的操作,就可以得到切点+增强,那么自然就构成了一个切面
但是Advisor接口里只包含了一个Advice,并且Advisor一般不直接提供给用户使用,所以这里也可以理解为获取增强,当然如果理解为切面也是没有问题的。