Spring基於註解形式的 AOP的原理流程及原始碼解析(三)
此篇部落格主要講解Spring如何驗證將要例項化的Bean是否應該被代理,生成代理物件的時機問題。
在第二篇部落格中,Spring對容器內所有的標識了@Aspect註解的的類的切面方法(標識了@Around, @Before, @After, @AfterReturning, @AfterThrowing的方法)生成切面Advisor,切面由切入點Pointcut和通知組成,切入點一般是ExpressionPointcut,及帶表示式的切入點,由註解上的value或pointcut屬性來指定;通知一般就是被織入的方法,就是註解標識的方法。
Spring代理Bean預設是singleton形式,即多個織入方法織入同一個代理物件,而不是每個方法生成一個物件。傳統的AOP是每個方法生成一個代理物件。詳情可檢視@Aspect的value屬性。如果被代理物件的scope不是singleton,則需要在value屬性上指定代理的型別,比如是perTarget等。
基於註解形式的AOP的代理物件生成的時機一般是在每個Bean初始化之後,及執行BeanPostProcessor的postProcessAfterInitialization方法,可看AbstractAutoProxyCreator的方法結構。如果物件的scope非singleton,那麼生成代理物件的時機肯定在postProcessAfterInitialization;如果物件的scope是singleton,那麼可能在postProcessAfterInitialization,也可能在getEarlyBeanReference方法。如果被代理的Bean觸發了迴圈引用circular references,即Bean A注入了Bean B,而B內也注入了A,那麼在解析B的時候會觸發A的getEarlyBeanReference方法,如果A符合被代理的條件,則會直接生產代理物件,將代理物件注入B的屬性中。在這種情況下有危險,B本來想注入A,但是結果注入的是A的代理物件,此時A還是一個原始的物件,還未注入屬性及執行初始化方法,所以有很大程度上會報異常。結論 : 動態代理scope = singleton時請注意避免迴圈引用。
接下來將生成代理物件的程式碼:
程式碼塊1
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
//如果scope = singleton的Bean觸發circular references,會在此步生成代理例項,生成時機比正常的早得多
public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
this.earlyProxyReferences.add(cacheKey);
}
return wrapIfNecessary(bean, beanName, cacheKey);
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
//大部分的動態代理物件均在此步驟生成,此步驟是Bean例項化的最後一步
if (!this.earlyProxyReferences.contains(cacheKey)) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// Create proxy if we have advice.
//校驗此類是否應該被代理,也就是將此類和所有的ExpressionPointcut匹配,匹配程式碼解析看下一塊(程式碼塊2)
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
}
驗證在例項化的Bean是否應該被代理,如果應該則發揮那些符合條件的待織入切面Advisor:
程式碼塊2
public abstract class AbstractAdvisorAutoProxyCreator extends AbstractAutoProxyCreator {
protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) {
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
//獲取容器中的所有切面
List<Advisor> candidateAdvisors = findCandidateAdvisors();
//驗證beanClass是否該被代理,如果應該,則返回所有符合條件的待織入切面
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
protected List<Advisor> findAdvisorsThatCanApply(
List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {
ProxyCreationContext.setCurrentProxiedBeanName(beanName);
try { //匹配過程,看下一塊程式碼(程式碼塊3)
return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
}
finally {
ProxyCreationContext.setCurrentProxiedBeanName(null);
}
}
}
AopUtils工具類迴圈的將每個Advisor與BeanClass匹配,如果匹配上就方法返回的集合中,
驗證步驟:
- 在類上做精確匹配,如果匹配規則定義在方法上,則返回maybetrue,如果規則定義在類上,不匹配則直接返回false
- 當類上返回的結果為true或者maybetrue,再進行方法的匹配。方法的匹配也是先做模糊匹配,如果返回的為nevermatch或者alwaysmatche,則得到了結果。
- 如果返回的結果不能確定為true或false,則需要根據實際的去驗證,比如args(…)則驗證方法的引數是否有指定的型別,@args則驗證方法的引數是否標識了指定的註解
- 在方法上做模糊匹配的原因是一個類從父類繼承,實現介面及自身的方法很多,如果能提前做一個大致的預估,如果預估的結果是肯定不匹配或不匹配,則可以省去對其所有方法的匹配解析。
程式碼塊3
public abstract class AopUtils {
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) {
//基於註解形式生成的Advisor均是PointcutAdvisor,所以條件均不符合
if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
eligibleAdvisors.add(candidate);
}
}
boolean hasIntroductions = !eligibleAdvisors.isEmpty();
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor) {
// already processed
continue;
}
if (canApply(candidate, clazz, hasIntroductions)) {
eligibleAdvisors.add(candidate);
}
}
return eligibleAdvisors;
}
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;
//pca.getPointcut()返回的是一個AspectJExpressionPointcut型別物件
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");
//先對類做匹配,如果結果為false則說明不匹配,程式碼詳解看程式碼塊4
if (!pc.getClassFilter().matches(targetClass)) {
return false;
}
//再對方法做匹配,有些匹配規則只作用在類上,那麼方法的匹配確定返回true,
//很多規則是針對方法的代理,所以需要繼續對合適的類裡的所有方法做驗證
MethodMatcher methodMatcher = pc.getMethodMatcher();
//methodMatcher 一般是AspectJExpressionPointcut物件自身,所有此步不符合
if (methodMatcher == MethodMatcher.TRUE) {
// No need to iterate the methods if we're matching any method anyway...
return true;
}
IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
}
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;
}
public boolean matches(Method method, Class<?> targetClass) {
return matches(method, targetClass, false);
}
public boolean matches(Method method, Class<?> targetClass, boolean beanHasIntroductions) {
checkReadyToMatch();
Method targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);
//getShadowMatch方法的呼叫繁雜,有點類似Class的匹配,最終也會呼叫到Pointcut的match(Shadow shadow)方法
//Pointcut對方法的匹配看下一個方法
ShadowMatch shadowMatch = getShadowMatch(targetMethod, method);
// Special handling for this, target, @this, @target, @annotation
// in Spring - we can optimize since we know we have exactly this class,
// and there will never be matching subclass at runtime.
//this, target, @this, @target, @annotation這些匹配規則都是匹配類的,當類的匹配通過時,方法均通過
if (shadowMatch.alwaysMatches()) {
return true;
}
else if (shadowMatch.neverMatches()) {
return false;
}
else {
// the maybe case
if (beanHasIntroductions) {
return true;
}
//如果FuzzyBoolean 即不確定錯誤,也不確定是正確的,那麼就需要實際的驗證,
//比如args(...)則驗證方法的引數是否有指定的型別,@args則驗證方法的引數是否標識了指定的註解
// A match test returned maybe - if there are any subtype sensitive variables
// involved in the test (this, target, at_this, at_target, at_annotation) then
// we say this is not a match as in Spring there will never be a different
// runtime subtype.
RuntimeTestWalker walker = getRuntimeTestWalker(shadowMatch);
return (!walker.testsSubtypeSensitiveVars() || walker.testTargetInstanceOfResidue(targetClass));
}
}
public abstract class Pointcut extends PatternNode {
public final FuzzyBoolean match(Shadow shadow) {
if (shadow.shadowId == lastMatchedShadowId) {
return lastMatchedShadowResult;
}
FuzzyBoolean ret;
// this next test will prevent a lot of un-needed matching going on....
if (shadow.getKind().isSet(couldMatchKinds())) {
//不同的Pointcut例項對此方法做了不同的實現,比如args,this,target等等,
//不是實實在在的根據表示式去驗證方法,而僅僅只是粗略的返回一個模糊的結果,比如可能為真,也可能為假
ret = matchInternal(shadow);
} else {
ret = FuzzyBoolean.NO;
}
lastMatchedShadowId = shadow.shadowId;
lastMatchedShadowResult = ret;
return ret;
}
}
}
表示式形式的切入點對類的匹配驗證:
程式碼塊4
public class AspectJExpressionPointcut extends AbstractExpressionPointcut
implements ClassFilter, IntroductionAwareMethodMatcher, BeanFactoryAware {
public boolean matches(Class<?> targetClass) {
checkReadyToMatch(); //確保生成表示式解析的Context環境
try {
try { //根據表示式的解析例項,驗證此類是否匹配,程式碼解析看下一塊程式碼(程式碼塊6)
return this.pointcutExpression.couldMatchJoinPointsInType(targetClass);
}
catch (ReflectionWorldException ex) {
logger.debug("PointcutExpression matching rejected target class - trying fallback expression", ex);
// Actually this is still a "maybe" - treat the pointcut as dynamic if we don't know enough yet
PointcutExpression fallbackExpression = getFallbackPointcutExpression(targetClass);
if (fallbackExpression != null) {
return fallbackExpression.couldMatchJoinPointsInType(targetClass);
}
}
}
catch (Throwable ex) {
logger.debug("PointcutExpression matching rejected target class", ex);
}
return false;
}
private void checkReadyToMatch() {
if (getExpression() == null) {
throw new IllegalStateException("Must set property 'expression' before attempting to match");
}
if (this.pointcutExpression == null) {
this.pointcutClassLoader = determinePointcutClassLoader();
//這一步很重要,根據切面註解的value屬性,生成切入點表示式例項,程式碼解析看下一塊程式碼(程式碼塊5)
this.pointcutExpression = buildPointcutExpression(this.pointcutClassLoader);
}
}
}
根據切面註解的value值,比如@After(“@args(….)”),生成不同例項的切入點表示式例項,即Pointcut實現類,切入點的實現類有很多,但Spring並不是每種都支援,支援的型別可以在AspectJExpressionPointcut的SUPPORTED_PRIMITIVES屬性中看到。
程式碼塊5
public class AspectJExpressionPointcut extends AbstractExpressionPointcut
implements ClassFilter, IntroductionAwareMethodMatcher, BeanFactoryAware {
private void checkReadyToMatch() {
if (getExpression() == null) {
throw new IllegalStateException("Must set property 'expression' before attempting to match");
}
if (this.pointcutExpression == null) {
this.pointcutClassLoader = determinePointcutClassLoader();
//第一步
this.pointcutExpression = buildPointcutExpression(this.pointcutClassLoader);
}
}
private PointcutExpression buildPointcutExpression(ClassLoader classLoader) {
PointcutParser parser = initializePointcutParser(classLoader);
PointcutParameter[] pointcutParameters = new PointcutParameter[this.pointcutParameterNames.length];
for (int i = 0; i < pointcutParameters.length; i++) {
pointcutParameters[i] = parser.createPointcutParameter(
this.pointcutParameterNames[i], this.pointcutParameterTypes[i]);
}
//第二步
return parser.parsePointcutExpression(replaceBooleanOperators(getExpression()),
this.pointcutDeclarationScope, pointcutParameters);
}
}
public class PointcutParser {
public PointcutExpression parsePointcutExpression(String expression, Class inScope, PointcutParameter[] formalParameters)
throws UnsupportedPointcutPrimitiveException, IllegalArgumentException {
PointcutExpressionImpl pcExpr = null;
try {
//第三步
Pointcut pc = resolvePointcutExpression(expression, inScope, formalParameters);
pc = concretizePointcutExpression(pc, inScope, formalParameters);
validateAgainstSupportedPrimitives(pc, expression); // again, because we have now followed any ref'd pcuts
pcExpr = new PointcutExpressionImpl(pc, expression, formalParameters, getWorld());
} catch (ParserException pEx) {
throw new IllegalArgumentException(buildUserMessageFromParserException(expression, pEx));
} catch (ReflectionWorld.ReflectionWorldException rwEx) {
throw new IllegalArgumentException(rwEx.getMessage());
}
return pcExpr;
}
protected Pointcut resolvePointcutExpression(String expression, Class inScope, PointcutParameter[] formalParameters) {
try {
PatternParser parser = new PatternParser(expression);
parser.setPointcutDesignatorHandlers(pointcutDesignators, world);
Pointcut pc = parser.parsePointcut(); //第四步
validateAgainstSupportedPrimitives(pc, expression);
IScope resolutionScope = buildResolutionScope((inScope == null ? Object.class : inScope), formalParameters);
pc = pc.resolve(resolutionScope);
return pc;
} catch (ParserException pEx) {
throw new IllegalArgumentException(buildUserMessageFromParserException(expression, pEx));
}
}
public Pointcut parsePointcut() {
//第五步,最關鍵的一步
Pointcut p = parseAtomicPointcut();
if (maybeEat("&&")) {
p = new AndPointcut(p, parseNotOrPointcut());
}
if (maybeEat("||")) {
p = new OrPointcut(p, parsePointcut());
}
return p;
}
//根據切面註解的value屬性的值上的特殊字串,解析成不同的Pointcut的例項
private Pointcut parseAtomicPointcut() {
if (maybeEat("!")) { //解析字串以"!"開頭
int startPos = tokenSource.peek(-1).getStart();
Pointcut p = new NotPointcut(parseAtomicPointcut(), startPos);
return p;
}
if (maybeEat("(")) { //解析value值以"("開頭
Pointcut p = parsePointcut();
eat(")");
return p;
}
if (maybeEat("@")) { //解析value值以"@"開頭
int startPos = tokenSource.peek().getStart();
Pointcut p = parseAnnotationPointcut();
int endPos = tokenSource.peek(-1).getEnd();
p.setLocation(sourceContext, startPos, endPos);
return p;
}
int startPos = tokenSource.peek().getStart();
Pointcut p = parseSinglePointcut(); //如果均不是
int endPos = tokenSource.peek(-1).getEnd();
p.setLocation(sourceContext, startPos, endPos);
return p;
}
//解析value值以"@"開頭
public Pointcut parseAnnotationPointcut() {
int start = tokenSource.getIndex();
IToken t = tokenSource.peek();
String kind = parseIdentifier();
IToken possibleTypeVariableToken = tokenSource.peek();
String[] typeVariables = maybeParseSimpleTypeVariableList();
if (typeVariables != null) {
String message = "(";
assertNoTypeVariables(typeVariables, message, possibleTypeVariableToken);
}
tokenSource.setIndex(start);
if (kind.equals("annotation")) {
return parseAtAnnotationPointcut();
} else if (kind.equals("args")) {
return parseArgsAnnotationPointcut();
} else if (kind.equals("this") || kind.equals("target")) {
return parseThisOrTargetAnnotationPointcut();
} else if (kind.equals("within")) {
return parseWithinAnnotationPointcut();
} else if (kind.equals("withincode")) {
return parseWithinCodeAnnotationPointcut();
}
throw new ParserException("pointcut name", t);
}
//解析普通的單例的切入點例項
public Pointcut parseSinglePointcut() {
int start = tokenSource.getIndex();
IToken t = tokenSource.peek();
Pointcut p = t.maybeGetParsedPointcut();
if (p != null) {
tokenSource.next();
return p;
}
String kind = parseIdentifier();
// IToken possibleTypeVariableToken = tokenSource.peek();
// String[] typeVariables = maybeParseSimpleTypeVariableList();
if (kind.equals("execution") || kind.equals("call") || kind.equals("get") || kind.equals("set")) {
p = parseKindedPointcut(kind);
} else if (kind.equals("args")) {
p = parseArgsPointcut();
} else if (kind.equals("this")) {
p = parseThisOrTargetPointcut(kind);
} else if (kind.equals("target")) {
p = parseThisOrTargetPointcut(kind);
} else if (kind.equals("within")) {
p = parseWithinPointcut();
} else if (kind.equals("withincode")) {
p = parseWithinCodePointcut();
} else if (kind.equals("cflow")) {
p = parseCflowPointcut(false);
} else if (kind.equals("cflowbelow")) {
p = parseCflowPointcut(true);
} else if (kind.equals("adviceexecution")) {
eat("(");
eat(")");
p = new KindedPointcut(Shadow.AdviceExecution, new SignaturePattern(Member.ADVICE, ModifiersPattern.ANY,
TypePattern.ANY, TypePattern.ANY, NamePattern.ANY, TypePatternList.ANY, ThrowsPattern.ANY,
AnnotationTypePattern.ANY));
} else if (kind.equals("handler")) {
eat("(");
TypePattern typePat = parseTypePattern(false, false);
eat(")");
p = new HandlerPointcut(typePat);
} else if (kind.equals("lock") || kind.equals("unlock")) {
p = parseMonitorPointcut(kind);
} else if (kind.equals("initialization")) {
eat("(");
SignaturePattern sig = parseConstructorSignaturePattern();
eat(")");
p = new KindedPointcut(Shadow.Initialization, sig);
} else if (kind.equals("staticinitialization")) {
eat("(");
TypePattern typePat = parseTypePattern(false, false);
eat(")");
p = new KindedPointcut(Shadow.StaticInitialization, new SignaturePattern(Member.STATIC_INITIALIZATION,
ModifiersPattern.ANY, TypePattern.ANY, typePat, NamePattern.ANY, TypePatternList.EMPTY, ThrowsPattern.ANY,
AnnotationTypePattern.ANY));
} else if (kind.equals("preinitialization")) {
eat("(");
SignaturePattern sig = parseConstructorSignaturePattern();
eat(")");
p = new KindedPointcut(Shadow.PreInitialization, sig);
} else if (kind.equals("if")) {
// - annotation style only allows if(), if(true) or if(false)
// - if() means the body of the annotated method represents the if expression
// - anything else is an error because code cannot be put into the if()
// - code style will already have been processed and the call to maybeGetParsedPointcut()
// at the top of this method will have succeeded.
eat("(");
if (maybeEatIdentifier("true")) {
eat(")");
p = new IfPointcut.IfTruePointcut();
} else if (maybeEatIdentifier("false")) {
eat(")");
p = new IfPointcut.IfFalsePointcut();
} else {
if (!maybeEat(")")) {
throw new ParserException(
"in annotation style, if(...) pointcuts cannot contain code. Use if() and put the code in the annotated method",
t);
}
// TODO - Alex has some token stuff going on here to get a readable name in place of ""...
p = new IfPointcut("");
}
} else {
boolean matchedByExtensionDesignator = false;
// see if a registered handler wants to parse it, otherwise
// treat as a reference pointcut
for (PointcutDesignatorHandler pcd : pointcutDesignatorHandlers) {
if (pcd.getDesignatorName().equals(kind)) {
p = parseDesignatorPointcut(pcd);
matchedByExtensionDesignator = true;
}
}
if (!matchedByExtensionDesignator) {
tokenSource.setIndex(start);
p = parseReferencePointcut();
}
}
return p;
}
}
切入點表示式例項即PointcutExpression驗證一個類是否匹配表示式,主要是根據其內部持有的Pointcut例項來匹配驗證,而Pointcut例項則根據切面註解的value值如@After(“@args(…..)”)來生成相應的Pointcut實現類,以這個實現類來驗證:
程式碼塊6
public class PointcutExpressionImpl implements PointcutExpression {
private Pointcut pointcut;
private String expression;
public boolean couldMatchJoinPointsInType(Class aClass) {
ResolvedType matchType = world.resolve(aClass.getName());
if (matchType.isMissing() && (world instanceof ReflectionWorld)) {
// Class is a generated class that cannot be 'looked up' via getResource.
// For example a proxy or lambda.
// Use the class itself in this case
matchType = ((ReflectionWorld)world).resolveUsingClass(aClass);
}
ReflectionFastMatchInfo info = new ReflectionFastMatchInfo(matchType, null, this.matchContext, world);
//關鍵看這步,不同的實現類返回不同的值,maybeTrue代表是可能為真,比如很多適配的原則是在方法上的,那麼這步就會返回true
//有些規則是要在類上做匹配的,則會在此步驟做驗證,通過後在方法上的驗證直接返回true
boolean couldMatch = pointcut.fastMatch(info).maybeTrue();
if (MATCH_INFO) {
System.out.println("MATCHINFO: fast match for '" + this.expression + "' against '" + aClass.getName() + "': "
+ couldMatch);
}
return couldMatch;
}
}