Spring AOP原始碼解析(二)獲取增強器
一、方法入口
上一節中,Spring會建立兩個工廠來完成獲取增強方法的功能:
AspectMetadata amd = new AspectMetadata(beanType, beanName); if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) { MetadataAwareAspectInstanceFactory factory = new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName); //獲取增強方法 List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory); //下面是快取功能的實現 if (this.beanFactory.isSingleton(beanName)) { this.advisorsCache.put(beanName, classAdvisors); } else { this.aspectFactoryCache.put(beanName, factory); } advisors.addAll(classAdvisors); }
ReflectiveAspectJAdvisorFactory.getAdvisors:
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) { //獲取標註有@Aspect註解的類 Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass(); //切面名稱(beanName) String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName(); //校驗,主要是對@Aspect註解資訊的校驗 validate(aspectClass); // 將MetadataAwareAspectInstanceFactory封裝起來,對建立的增強器進行快取,避免多次例項化 MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory = new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory); List<Advisor> advisors = new LinkedList<Advisor>(); //獲取類中所有的增強方法(排除帶有@Pointcut註解的方法) for (Method method : getAdvisorMethods(aspectClass)) { //建立增強器 Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName); if (advisor != null) { advisors.add(advisor); } } // 如果增強器不為空,並且配置了增強延遲初始化,則需要在頭部加入同步例項化增強器 if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) { Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory); advisors.add(0, instantiationAdvisor); } // 獲取DeclareParents註解,並建立增強器 for (Field field : aspectClass.getDeclaredFields()) { Advisor advisor = getDeclareParentsAdvisor(field); if (advisor != null) { advisors.add(advisor); } } return advisors; }
方法中首先完成普通增強器的獲取,然後完成對延遲初始化增強的支援,最後是對@DeclareParents註解的支援
二、獲取普通增強器
獲取普通增強器是通過getAdvisor實現,包括對切點的註解的獲取以及根據註解資訊生成增強器:
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrderInAspect, String aspectName) { //驗證@Aspect註解 validate(aspectInstanceFactory.getAspectMetadata().getAspectClass()); //獲取切點資訊 AspectJExpressionPointcut expressionPointcut = getPointcut( candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass()); if (expressionPointcut == null) { return null; } //根據切點資訊,生成增強器 return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod, this, aspectInstanceFactory, declarationOrderInAspect, aspectName); }
1、獲取切點資訊
getPointcut方法實現:
private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
//獲取方法上的註解
AspectJAnnotation<?> aspectJAnnotation =
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
if (aspectJAnnotation == null) {
return null;
}
//使用AspectJExpressionPointcut封裝切點資訊
AspectJExpressionPointcut ajexp =
new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);
//獲取註解中的表示式,如:"execytion(* *.test(..))"
ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
ajexp.setBeanFactory(this.beanFactory);
return ajexp;
}
a、獲取註解資訊:
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;
}
//獲取方法上指定的註解,並使用AspectJAnnotation封裝
private static <A extends Annotation> AspectJAnnotation<A> findAnnotation(Method method, Class<A> toLookFor) {
A result = AnnotationUtils.findAnnotation(method, toLookFor);
if (result != null) {
return new AspectJAnnotation<A>(result);
}
else {
return null;
}
}
從上面程式碼可以看出,Spring會嘗試獲取方法上Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class註解,如果存在的話,會使用AspectJAnnotation封裝,AspectJAnnotation會解析註解的型別、pointcutExpression和argumentNames的值並儲存起來。
b、封裝切點資訊
Spring使用AspectJExpressionPointcut類,將註解中pointcutExpression和BeanFactory封裝起來,用於後面切點表示式匹配。
2、生成增強
所有的增強都有Advisor的實現類InstantiationModelAwarePointcutAdvisorImpl統一封裝:
public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut,
Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory,
MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
//切點資訊
this.declaredPointcut = declaredPointcut;
//宣告@Aspect註解的類
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;
if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
// Static part of the pointcut is a lazy type.
Pointcut preInstantiationPointcut = Pointcuts.union(
aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);
this.pointcut = new PerTargetInstantiationModelPointcut(
this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory);
this.lazy = true;
}
else {
// A singleton aspect.
this.pointcut = this.declaredPointcut;
this.lazy = false;
this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
}
}
在封裝過程中,只是簡單的賦值,並在最後一步完成增強的初始化:
instantiateAdvice:
private Advice instantiateAdvice(AspectJExpressionPointcut pcut) {
return this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pcut,
this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
}
ReflectiveAspectJAdvisorFactory中的實現:
public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
//宣告註解的所在類
Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
validate(candidateAspectClass);//又又又驗證了一次
//獲取方法上的註解(上面已經獲取過了一次,這裡重複獲取了)
AspectJAnnotation<?> aspectJAnnotation =
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
if (aspectJAnnotation == null) {
return null;
}
// 檢查宣告類上是否有@Aspect註解
if (!isAspect(candidateAspectClass)) {
throw new AopConfigException("Advice must be declared inside an aspect type: " +
"Offending method '" + candidateAdviceMethod + "' in class [" +
candidateAspectClass.getName() + "]");
}
if (logger.isDebugEnabled()) {
logger.debug("Found AspectJ method: " + candidateAdviceMethod);
}
AbstractAspectJAdvice springAdvice;
//根據不同註解,生成不同的增強器
switch (aspectJAnnotation.getAnnotationType()) {
case AtBefore:
springAdvice = new AspectJMethodBeforeAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtAfter:
springAdvice = new AspectJAfterAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtAfterReturning:
springAdvice = new AspectJAfterReturningAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterReturningAnnotation.returning())) {
springAdvice.setReturningName(afterReturningAnnotation.returning());
}
break;
case AtAfterThrowing:
springAdvice = new AspectJAfterThrowingAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
}
break;
case AtAround:
springAdvice = new AspectJAroundAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtPointcut:
if (logger.isDebugEnabled()) {
logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
}
return null;
default:
throw new UnsupportedOperationException(
"Unsupported advice type on method: " + candidateAdviceMethod);
}
// Now to configure the advice...
springAdvice.setAspectName(aspectName);
springAdvice.setDeclarationOrder(declarationOrder);
String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
if (argNames != null) {
springAdvice.setArgumentNamesFromStringArray(argNames);
}
springAdvice.calculateArgumentBindings();
return springAdvice;
}
程式碼流程:
- 獲取增強方法上的註解資訊
- 驗證
- 根據不同註解生成不同的增強器
增強器型別:
- Befor --> AspectJMethodBeforeAdvice
- After --> AspectJAfterAdvice
- AfterReturning --> AspectJAfterReturningAdvice
- AfterThrowing --> AspectJAfterThrowingAdvice
- Around --> AspectJAroundAdvice
三、@DeclareParents功能
前getAdvisors方法中,Spring完成了普通增強器的獲取後還會繼續獲取@DeclareParents註解,並建立對應的增強器DeclareParentsAdvisor進行封裝,下面是getAdvisors部分程式碼:
// 遍歷Field物件
for (Field field : aspectClass.getDeclaredFields()) {
//建立對應的增強器
Advisor advisor = getDeclareParentsAdvisor(field);
if (advisor != null) {
advisors.add(advisor);
}
}
return advisors;
建立增強器:
private Advisor getDeclareParentsAdvisor(Field introductionField) {
DeclareParents declareParents = introductionField.getAnnotation(DeclareParents.class);
if (declareParents == null) {
// Not an introduction field
return null;
}
//未配置defaultImpl
if (DeclareParents.class == declareParents.defaultImpl()) {
throw new IllegalStateException("'defaultImpl' attribute must be set on DeclareParents");
}
return new DeclareParentsAdvisor(
introductionField.getType(), declareParents.value(), declareParents.defaultImpl());
}
四、篩選匹配到的增強器
findAdvisorsThatCanApply:
protected List<Advisor> findAdvisorsThatCanApply(
List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {
ProxyCreationContext.setCurrentProxiedBeanName(beanName);
try {
//匹配增強器的功能交由AopUtils完成
return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
}
finally {
ProxyCreationContext.setCurrentProxiedBeanName(null);
}
}
接著檢視findAdvisorsThatCanApply:
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
if (candidateAdvisors.isEmpty()) {
return candidateAdvisors;
}
List<Advisor> eligibleAdvisors = new LinkedList<Advisor>();
//首先處理引介增強
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
eligibleAdvisors.add(candidate);
}
}
boolean hasIntroductions = !eligibleAdvisors.isEmpty();
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor) {
// 引介增強已處理
continue;
}
//其他增強器的處理
if (canApply(candidate, clazz, hasIntroductions)) {
eligibleAdvisors.add(candidate);
}
}
return eligibleAdvisors;
}
程式碼中最重要的函式就是canApply方法,下面是其原始碼實現:
public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
//引介增強處理
if (advisor instanceof IntroductionAdvisor) {
return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
}
else if (advisor instanceof PointcutAdvisor) {
PointcutAdvisor pca = (PointcutAdvisor) advisor;
return canApply(pca.getPointcut(), targetClass, hasIntroductions);
}
else {
// It doesn't have a pointcut so we assume it applies.
return true;
}
}
普通增強器的處理:
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
Assert.notNull(pc, "Pointcut must not be null");
if (!pc.getClassFilter().matches(targetClass)) {
return false;
}
MethodMatcher methodMatcher = pc.getMethodMatcher();
if (methodMatcher == MethodMatcher.TRUE) {
return true;
}
IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
}
//遍歷所有宣告的Method方法物件(包括介面中定義的)
Set<Class<?>> classes = new LinkedHashSet<Class<?>>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
classes.add(targetClass);
for (Class<?> clazz : classes) {
Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
for (Method method : methods) {
//只要存在方法匹配切點中的表示式
if ((introductionAwareMethodMatcher != null &&
introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||
methodMatcher.matches(method, targetClass)) {
return true;
}
}
}
return false;
}
總結:
- Spring會解析出標註@Aspect註解的類下帶有Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class註解的方法,並生成不同型別的增強器
- 獲取類中標註有@DeclareParents註解的Field物件,並生成DeclareParentsAdvisor增強器
- 從所有增強器中篩選出匹配的增強器