javascript
Spring系列之AOP分析之为目标类挑选合适的Advisor(五)
我們在之前的文章中分析了Advisor的生成過程以及在Advisor中生成Advise的過程。在這一篇文章中我們說一下為目標類挑選合適的Advisor的過程。通過之前的分析我們知道,一個切面類可以生成多個Advisor(多個切面類的話那就更多多的Advisor了),這些Advisor是否都能適用于我們的目標類呢?這就需要通過Advisor中所擁有的Pointcut來進行判斷了。先回到我們最開始的例子:
//手工創建一個實例 AspectJService aspectJService = new AspectJServiceImpl(); //使用AspectJ語法 自動創建代理對象 AspectJProxyFactory aspectJProxyFactory = new AspectJProxyFactory(aspectJService); 復制代碼我們在我們的AspectJProxyFactory中傳入了我們的目標對象。我們再回到AspectJProxyFactory的addAdvisorsFromAspectInstanceFactory方法中。
private void addAdvisorsFromAspectInstanceFactory(MetadataAwareAspectInstanceFactory instanceFactory) {//獲取Advisor的過程我們在之前分析了List<Advisor> advisors = this.aspectFactory.getAdvisors(instanceFactory);//這句代碼的意思是為我們的目標類挑選合適的Advisor也是我們這一次要分析的內容advisors = AopUtils.findAdvisorsThatCanApply(advisors, getTargetClass());AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(advisors);//為Advisor進行排序AnnotationAwareOrderComparator.sort(advisors);addAdvisors(advisors);} 復制代碼在AspectJProxyFactory中是通過調用AopUtils中的findAdvisorsThatCanApply方法來為目標類挑選合適的Advisor的或者是進判斷哪些Advisor可以作用于目標類。在這個方法中傳入了兩個參數,一個參數是Advisor的集合,一個參數是目標類Class。我們看一下getTargetClass()這個方法的內容: AdvisedSupport#getTargetClass
@Overridepublic Class<?> getTargetClass() {//直接調用targetSource的getTargetClass方法//其實也是相當于調用target.getClass()return this.targetSource.getTargetClass();} 復制代碼OK,下面我們進入到AopUtils#findAdvisorsThatCanApply中看一下這個方法的內容
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {//如果傳入的Advisor集合為空的話,直接返回這個空集合//這里沒有判斷candidateAdvisors不為null的情況 因為在獲取Advisor的地方是先創建一個空的集合,再進行添加Advisor的動作//不過還是加一下不為null的判斷更好一點if (candidateAdvisors.isEmpty()) {return candidateAdvisors;}//創建一個合適的Advisor的集合 eligibleList<Advisor> eligibleAdvisors = new LinkedList<Advisor>();//循環所有的Advisorfor (Advisor candidate : candidateAdvisors) {//如果Advisor是IntroductionAdvisor 引介增強 可以為目標類 通過AOP的方式添加一些接口實現//引介增強是一種比較我們接觸的比較少的增強 我們可以在以后的文章單獨做個分析if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {eligibleAdvisors.add(candidate);}}//是否有引介增強boolean hasIntroductions = !eligibleAdvisors.isEmpty();for (Advisor candidate : candidateAdvisors) {//如果是IntroductionAdvisor類型的話 則直接跳過if (candidate instanceof IntroductionAdvisor) {// already processedcontinue;}//判斷此Advisor是否適用于targetif (canApply(candidate, clazz, hasIntroductions)) {eligibleAdvisors.add(candidate);}}return eligibleAdvisors;} 復制代碼canApply方法的內容
public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {//如果是IntroductionAdvisor的話,則調用IntroductionAdvisor類型的實例進行類的過濾//這里是直接調用的ClassFilter的matches方法if (advisor instanceof IntroductionAdvisor) {return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);}//通常我們的Advisor都是PointcutAdvisor類型else if (advisor instanceof PointcutAdvisor) {PointcutAdvisor pca = (PointcutAdvisor) advisor;//這里從Advisor中獲取Pointcut的實現類 這里是AspectJExpressionPointcutreturn canApply(pca.getPointcut(), targetClass, hasIntroductions);}else {// It doesn't have a pointcut so we assume it applies.return true;}} 復制代碼重載canApply方法的內容。
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {Assert.notNull(pc, "Pointcut must not be null");//進行切點表達式的匹配最重要的就是 ClassFilter 和 MethodMatcher這兩個方法的實現。//MethodMatcher中有兩個matches方法。一個參數是只有Method對象和targetclass,另一個參數有//Method對象和targetClass對象還有一個Method的方法參數 他們兩個的區別是://兩個參數的matches是用于靜態的方法匹配 三個參數的matches是在運行期動態的進行方法匹配的//先進行ClassFilter的matches方法校驗//首先這個類要在所匹配的規則下if (!pc.getClassFilter().matches(targetClass)) {return false;}//再進行 MethodMatcher 方法級別的校驗MethodMatcher methodMatcher = pc.getMethodMatcher();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);//只要有一個方法能匹配到就返回true//這里就會有一個問題:因為在一個目標中可能會有多個方法存在,有的方法是滿足這個切點的匹配規則的//但是也可能有一些方法是不匹配切點規則的,這里檢測的是只有一個Method滿足切點規則就返回true了//所以在運行時進行方法攔截的時候還會有一次運行時的方法切點規則匹配for (Method method : methods) {if ((introductionAwareMethodMatcher != null &&introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||methodMatcher.matches(method, targetClass)) {return true;}}}return false;} 復制代碼從上面的代碼來看,這次我們要分析的重點就在AspectJExpressionPointcut這個類中了。在AspectJExpressPointcut中預先初始化了這些內容:你能看出來這是什么內容嗎?
static {//executionSUPPORTED_PRIMITIVES.add(PointcutPrimitive.EXECUTION);//argsSUPPORTED_PRIMITIVES.add(PointcutPrimitive.ARGS);SUPPORTED_PRIMITIVES.add(PointcutPrimitive.REFERENCE);//thisSUPPORTED_PRIMITIVES.add(PointcutPrimitive.THIS);//targetSUPPORTED_PRIMITIVES.add(PointcutPrimitive.TARGET);//withinSUPPORTED_PRIMITIVES.add(PointcutPrimitive.WITHIN);//@annotationSUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ANNOTATION);SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_WITHIN);SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ARGS);SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_TARGET);} 復制代碼我們來看一下這段代碼,這是要從AspectJExpressPointcut中獲取ClassFilter
if (!pc.getClassFilter().matches(targetClass)) {return false;} 復制代碼 public ClassFilter getClassFilter() {checkReadyToMatch();return this;}private void checkReadyToMatch() {//如果沒有expression的值的話 直接拋出異常if (getExpression() == null) {throw new IllegalStateException("Must set property 'expression' before attempting to match");}if (this.pointcutExpression == null) {//選擇類加載器this.pointcutClassLoader = determinePointcutClassLoader();//構建PointcutExpression的實例this.pointcutExpression = buildPointcutExpression(this.pointcutClassLoader);}}//下面的這一段邏輯完全就是aspectj這個jar中的內容了 很復雜不多說了。。。。private PointcutExpression buildPointcutExpression(ClassLoader classLoader) {//初始化一個PointcutParser的實例 PointcutParser aspectj中提供的類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);}//這個方法 將表達式中的 and 替換為 && or 替換為 || not 替換為 !private String replaceBooleanOperators(String pcExpr) {String result = StringUtils.replace(pcExpr, " and ", " && ");result = StringUtils.replace(result, " or ", " || ");result = StringUtils.replace(result, " not ", " ! ");return result;} 復制代碼由于我們在項目開發中使用SpringAOP基本上都是用的AspectJ的注解的形式。AspectJ對于切點的匹配規則解析是一個比較復雜的過程,所以我們在使用的AspectJ中的切點表達式的時候要按照它的一個規則來進行書寫。
轉載于:https://juejin.im/post/5b61c566e51d4535a65af79a
總結
以上是生活随笔為你收集整理的Spring系列之AOP分析之为目标类挑选合适的Advisor(五)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: String比较? 用==判断两个字符串
- 下一篇: maven中spring-boot-de