aop原始碼解析二:postProcessBeforeInstantiation
我們就不再從bean
的載入過程開始看了 在aop原始碼解析一:註冊BPP中 介紹到了一個後置處理器 AnnotationAwareAspectJAutoProxyCreator
我們就從這個類入手aop
先看postProcessBeforeInstantiation
很顯然 是bean例項化前呼叫的 裡面好像是處理了一些事情
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
Object cacheKey = getCacheKey(beanClass, beanName);
if (beanName == null || !this.targetSourcedBeans.containsKey(beanName)) {
if (this.advisedBeans.containsKey(cacheKey)) {
return null;
}
//這裡需要注意的是 shouldSkip方法被子類覆蓋了 這個很容易忽略
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
this .advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}
}
// Create proxy here if we have a custom TargetSource.
// Suppresses unnecessary default instantiation of the target bean:
// The TargetSource will handle target instances in a custom fashion.
if (beanName != null) {
//todo 這裡返回null!!!
TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
if (targetSource != null) {
this.targetSourcedBeans.put(beanName, Boolean.TRUE);
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
}
return null;
}
這裡shouldSkip(beanClass, beanName)
方法是一個很容易忽略的方法 可能是方法名字的原因或者複雜的繼承關係吧 但在這個方法的實現過程中 已經把所有的切面載入完成了
AspectJAwareAdvisorAutoProxyCreator##shouldSkip
protected boolean shouldSkip(Class beanClass, String beanName) {
// TODO: Consider optimization by caching the list of the aspect names
List<Advisor> candidateAdvisors = findCandidateAdvisors();
for (Advisor advisor : candidateAdvisors) {
if (advisor instanceof AspectJPointcutAdvisor) {
if (((AbstractAspectJAdvice) advisor.getAdvice()).getAspectName().equals(beanName)) {
return true;
}
}
}
return super.shouldSkip(beanClass, beanName);
}
首先找出所有的advisor
如果要載入的這個bean
是一個advisor
的話 當然就不會代理了
AnnotationAwareAspectJAutoProxyCreator##findCandidateAdvisors()
protected List<Advisor> findCandidateAdvisors() {
// Add all the Spring advisors found according to superclass rules.
// 尋找bean的型別為Aspect.class的 我們更關心註解的形式 這裡不過多的講解
List<Advisor> advisors = super.findCandidateAdvisors();
// Build Advisors for all AspectJ aspects in the bean factory.
// 處理註解(@Aspect)的形式
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
return advisors;
}
public List<Advisor> buildAspectJAdvisors() {
List<String> aspectNames = null;
synchronized (this) {
aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
List<Advisor> advisors = new LinkedList<Advisor>();
aspectNames = new LinkedList<String>();
// 根據型別取出beanFactory中所有的bean 包括父容器 然後迴圈判斷
// todo 這樣就可能造成重複載入
String[] beanNames =
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);
for (String beanName : beanNames) {
// 提供可供擴充套件的模式匹配
if (!isEligibleBean(beanName)) {
continue;
}
// We must be careful not to instantiate beans eagerly as in this
// case they would be cached by the Spring container but would not
// have been weaved
Class beanType = this.beanFactory.getType(beanName);
if (beanType == null) {
continue;
}
// 帶有@Aspect 註解 並且不是由ajc編譯生成 code-style方式的切面spring aop不處理(在前面已經處理過了)
if (this.advisorFactory.isAspect(beanType)) {
aspectNames.add(beanName);
//包裝類
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);
}
else {
// Per target or per this.
if (this.beanFactory.isSingleton(beanName)) {
throw new IllegalArgumentException("Bean with name '" + beanName +
"' is a singleton, but aspect instantiation model is not singleton");
}
MetadataAwareAspectInstanceFactory factory =
new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
this.aspectFactoryCache.put(beanName, factory);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
}
this.aspectBeanNames = aspectNames;
return advisors;
}
}
if (aspectNames.isEmpty()) {
return Collections.EMPTY_LIST;
}
List<Advisor> advisors = new LinkedList<Advisor>();
for (String aspectName : aspectNames) {
List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
if (cachedAdvisors != null) {
advisors.addAll(cachedAdvisors);
}
else {
MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
return advisors;
}
對於bean工廠中的所有的bean
判斷是否有Aspect
註解 並且不是由ajc
編譯 code-style
程式設計的方式 再前面已經處理過了 這裡就不再處理 對於下面部分的程式碼閱讀來說 有些讀者可能會覺得困難 看著這麼多陌生的名字很長的類就覺得好難 其實這些類只是一些包裝util而已 為的就是不會在一個方法裡面程式碼過多顯得臃腫 AspectMetadata
MetadataAwareAspectInstanceFactory
都是起到這樣的作用 下面的部分就是本文的重頭戲了 尋找advisor
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory maaif) {
final Class<?> aspectClass = maaif.getAspectMetadata().getAspectClass();
final String aspectName = maaif.getAspectMetadata().getAspectName();
validate(aspectClass);
// We need to wrap the MetadataAwareAspectInstanceFactory with a decorator
// so that it will only instantiate once.
final MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
new LazySingletonAspectInstanceFactoryDecorator(maaif);
final List<Advisor> advisors = new LinkedList<Advisor>();
// 迴圈所有的切面方法
for (Method method : getAdvisorMethods(aspectClass)) {
// 將切面方法轉化為advisor
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.
if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
advisors.add(0, instantiationAdvisor);
}
// Find introduction fields.
for (Field field : aspectClass.getDeclaredFields()) {
Advisor advisor = getDeclareParentsAdvisor(field);
if (advisor != null) {
advisors.add(advisor);
}
}
return advisors;
}
我們這裡先列舉一個切面的例子 這樣可能更有助於讀者閱讀 這個例子可以從github-springmvcdemo中clone
下來
@Component
@Aspect
public class LogTipAspect {
@Around("@annotation(com.sunmingshuai.annotation.LogTip)")
public void aroundEntry(ProceedingJoinPoint pjp){
System.out.print("before aroundEntry");
try {
pjp.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.print("before aroundEntry");
}
@Before("@annotation(com.sunmingshuai.annotation.LogTip)")
public void beforeEntry(){
System.out.print("beforeEntry");
}
@After("@annotation(com.sunmingshuai.annotation.LogTip)")
public void afterEntry(){
System.out.print("afterEntry");
}
@AfterThrowing("@annotation(com.sunmingshuai.annotation.LogTip)")
public void afterThrowing(){
System.out.print("afterEntry");
}
@AfterReturning("@annotation(com.sunmingshuai.annotation.LogTip)")
public void afterReturning(){
System.out.print("afterReturning");
}
}
getAdvisorMethods(aspectClass)
方法就是找出切面類中的所有的切面方法 看下這裡的實現
private List<Method> getAdvisorMethods(Class<?> aspectClass) {
final List<Method> methods = new LinkedList<Method>();
// 篩選出沒有Pointcut註解的方法 其實就是為了想獲取所有的@Before @After @Around等註解標註的方法
// 這裡的實現是直接排除Pointcut註解 這樣程式碼更簡潔些吧
ReflectionUtils.doWithMethods(aspectClass, new ReflectionUtils.MethodCallback() {
public void doWith(Method method) throws IllegalArgumentException {
// Exclude pointcuts
if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) {
methods.add(method);
}
}
});
/**
* 多個comparator的排序: 多個comparator組成一個排序鏈 程式執行這個排序鏈 有一個comparator返回非0數字則返回 或者直到執行完排序鏈 返回0
* 有點類似與sql中多個列的排序演算法
* 最後排序結果 Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class
*/
Collections.sort(methods, METHOD_COMPARATOR);
return methods;
}
程式碼裡面的註釋已經很清晰了 找出被切面所註釋的方法 注意在方法返回前對方法進行了排序 這個排序演算法閱讀起來不太容易 主要是因為有多重排序 讀者可以學習一下這個方法 也許在工作中能用的到 最後的排序順序是
Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class
這個順序是很重要的 影響到後面方法呼叫過程中執行鏈的順序
然後再把找到的方法轉化成advisor
儲存 我們可以想象的出來 這樣一個advisor
儲存了非常多的資訊在裡面
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aif,
int declarationOrderInAspect, String aspectName) {
validate(aif.getAspectMetadata().getAspectClass());
// 包裝類: 拆解出切面的切點表示式
AspectJExpressionPointcut ajexp =
getPointcut(candidateAdviceMethod, aif.getAspectMetadata().getAspectClass());
if (ajexp == null) {
return null;
}
//從建構函式中可以看出Advisor的組成結構 pointcut advice
return new InstantiationModelAwarePointcutAdvisorImpl(
this, ajexp, aif, candidateAdviceMethod, declarationOrderInAspect, aspectName);
}
對於切面來說 肯定要指定切點的 也就是這個方法在什麼地點執行? getPointcut
方法就比較簡單了 首先檢視是否有切面方法註解 然後根據切點構造AspectJExpressionPointcut
private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
AspectJAnnotation<?> aspectJAnnotation =
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
if (aspectJAnnotation == null) {
return null;
}
AspectJExpressionPointcut ajexp =
new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class[0]);
ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
return ajexp;
}
然後再看最後一步構造advisor
可以看到是直接呼叫建構函式構造了一個實現類InstantiationModelAwarePointcutAdvisorImpl
public InstantiationModelAwarePointcutAdvisorImpl(AspectJAdvisorFactory af, AspectJExpressionPointcut ajexp,
MetadataAwareAspectInstanceFactory aif, Method method, int declarationOrderInAspect, String aspectName) {
this.declaredPointcut = ajexp;
this.method = method;
this.atAspectJAdvisorFactory = af;
this.aspectInstanceFactory = aif;
this.declarationOrder = declarationOrderInAspect;
this.aspectName = aspectName;
if (aif.getAspectMetadata().isLazilyInstantiated()) {
// Static part of the pointcut is a lazy type.
Pointcut preInstantiationPointcut =
Pointcuts.union(aif.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, aif);
this.lazy = true;
}
else {
// A singleton aspect.
this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
this.pointcut = declaredPointcut;
this.lazy = false;
}
}
方法的內部就是各種賦值操作 但有一個方法需要看一下 instantiateAdvice(this.declaredPointcut)
我們往下追蹤
public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut ajexp,
MetadataAwareAspectInstanceFactory aif, int declarationOrderInAspect, String aspectName) {
Class<?> candidateAspectClass = aif.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() + "]");
}
if (logger.isDebugEnabled()) {
logger.debug("Found AspectJ method: " + candidateAdviceMethod);
}
AbstractAspectJAdvice springAdvice;
switch (aspectJAnnotation.getAnnotationType()) {
case AtBefore:
springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, ajexp, aif);
break;
case AtAfter:
springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, ajexp, aif);
break;
case AtAfterReturning:
springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, ajexp, aif);
AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterReturningAnnotation.returning())) {
springAdvice.setReturningName(afterReturningAnnotation.returning());
}
break;
case AtAfterThrowing:
springAdvice = new AspectJAfterThrowingAdvice(candidateAdviceMethod, ajexp, aif);
AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
}
break;
case AtAround:
springAdvice = new AspectJAroundAdvice(candidateAdviceMethod, ajexp, aif);
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(declarationOrderInAspect);
// argNames
String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
if (argNames != null) {
springAdvice.setArgumentNamesFromStringArray(argNames);
}
springAdvice.calculateArgumentBindings();
return springAdvice;
}
可以看到生成了對應的advice
下面又對方法的引數進行處理
argNames指的是註解的argNames
引數提供的值 這裡會考慮JoinPoint
ProceedingJoinPoint
特殊引數 這些引數是框架預設支援提供的 一般約定放到引數的第一位
帶了讀者繞了這麼久 讀者可能已經不知道我們是從哪裡進來的 進來的目的又是什麼了 讓我們再回到最開始的程式碼 我們進行了這麼多步的操作其實就是為了要驗證是不是應該要跳過這個bean
如果這個bean
本身就是切面類的話 那麼就不會被代理了
繼續往下執行 如果沒有beanclass
所對應的targetsource
的話 也一樣不會代理的 targetsource
相關的知識不再介紹 建立代理的地方其實是在postProcessAfterInitialization
中
預知後事如何 且聽下回分析…