Spring源碼系列:SpringAop中的NameMatchMethodPointcutAdvisor和RegexpMethodPointcutAdvisor

上一節我們講到了SpringAop基於註解版本的織入和創建代理的流程。這一節我們來講兩個切面類。從前面知道 一個切面是有兩部分構成 增強切點 。上節我們也列舉了一些常用的切面 下面我們就來
介紹兩個功能比較相近的切面類。

NameMatchMethodPointcutAdvisor的使用

啓動配置類


@SpringBootApplication
public class App {
  public static void main(String[] args) {
    SpringApplication application = new SpringApplicationBuilder(App.class).application();
    ApplicationContext context = application.run(args);
    UserService userServiceImpl = (UserService)context.getBean("userServiceImpl");
    userServiceImpl.findUser();
    
  }
  @Configuration
  public static class AppConfig{
    /**
     * 定義切面
     * @return
     */
    @Bean
    public MethodBeforeAdvice methodBeforeAdvice(){
      return new com.coledy.aop.advice.MethodBeforeAdvice();
    }
    /**
     * 創建一個NameMatchMethodPointcutAdvisor 切面
     * @return
     */
    @Bean
    public NameMatchMethodPointcutAdvisor nameMatchMethodPointcutAdvisor(){
      NameMatchMethodPointcutAdvisor matchMethodPointcutAdvisor = new NameMatchMethodPointcutAdvisor();
      matchMethodPointcutAdvisor.setAdvice(methodBeforeAdvice());
      matchMethodPointcutAdvisor.setMappedNames("find*");
      return matchMethodPointcutAdvisor;
    }
  }
}

被代理對象 和增強

public interface UserService {
  void findUser();
}

@Component
public class UserServiceImpl implements UserService {
  @Override
  public void findUser() {
    System.out.println("findUser....");
  }
}

public class MethodBeforeAdvice implements org.springframework.aop.MethodBeforeAdvice {
  @Override
  public void before(Method method, Object[] args, Object target) throws Throwable {
    System.out.println("我是一個前置增強 ");
  }
}

啓動app 輸出
我是一個前置增強
findUser…

可以看到到這裏 我們看到了我們想要看到的結果。對象被代理了。值得注意的是NameMatchMethodPointcutAdvisor這個切面類 的setMappedNames 需要配置的是方法名字。支持通配符。


RegexpMethodPointcutAdvisor的使用

我們只在上面的例子的基礎上加一個後置增強 和一個切面配置

@SpringBootApplication
//@Import(RegisterAspectJAutoProxyRegisterar.class)
public class App {

  public static void main(String[] args) {
    SpringApplication application = new SpringApplicationBuilder(App.class).application();
    ApplicationContext context = application.run(args);
    UserService userServiceImpl = (UserService)context.getBean("userServiceImpl");
    userServiceImpl.findUser();
  }
  @Configuration
  public static class AppConfig{
 
   @Bean
    public MethodBeforeAdvice methodBeforeAdvice(){
      return new com.coledy.aop.advice.MethodBeforeAdvice();
    }
    @Bean
    public RegexpMethodPointcutAdvisor regexpMethodPointcutAdvisor(){
      RegexpMethodPointcutAdvisor regexpMethodPointcutAdvisor = new RegexpMethodPointcutAdvisor();
      regexpMethodPointcutAdvisor.setPatterns("*.findGoods");
      regexpMethodPointcutAdvisor.setAdvice(methodBeforeAdvice());
      return regexpMethodPointcutAdvisor;
    }
  }
}

public class CommonMethodAfterAdvice implements AfterReturningAdvice {

  @Override
  public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
    System.out.println("common after return");

  }
}

同樣也達到了代理的目的。下面我們就看一下爲什麼我們只是簡單的在容器中定義了一個切面 Spring就幫我們織入了被代理的對象裏面。

SpringAop非註解式的源碼分析

看過上一篇文章的應該有印象 getAdvicesAndAdvisorsForBean 這個方法是AbstractAutoProxyCreator類唯一一個留給子類必須實現的方法 (獲取所有的切面由於不同子類的實現 獲取切面的方式不同 所以這裏交給了子類實現 模板方法。如BeanNameAutoProxyCreator就和我們經常使用的AnnotationAwareAspectJAutoProxyCreator 這個織入類的實現方式不一樣)。

	protected Object[] getAdvicesAndAdvisorsForBean(
			Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
		
		List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
		if (advisors.isEmpty()) {
			return DO_NOT_PROXY;
		}
		return advisors.toArray();
	}

跟進findEligibleAdvisors


	protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
		List<Advisor> candidateAdvisors = findCandidateAdvisors();
		List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
		extendAdvisors(eligibleAdvisors);
		if (!eligibleAdvisors.isEmpty()) {
			eligibleAdvisors = sortAdvisors(eligibleAdvisors);
		}
		return eligibleAdvisors;
	}


//這裏爲了節省篇幅 直接跳到findCandidateAdvisors這個方法裏面來 我們上一節說過 AnnotationAwareAspectJAutoProxyCreator 重寫了這個方法
//但是在子類的方法有行代碼很關鍵List<Advisor> advisors = super.findCandidateAdvisors();
//先回去父類獲取Advisor 然後再用自己的邏輯獲取 父類是獲取實現了Advisor接口的切面 
//子類是獲取有AspectJ註解的並且方法中有@Pointcut的方法 封裝成切面。
	protected List<Advisor> findCandidateAdvisors() {
		Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available");
		return this.advisorRetrievalHelper.findAdvisorBeans();
	}

下面我們繼續看看 父類獲取Advisor的邏輯。其實和子類的獲取邏輯很類似。父類的這個更簡單因爲本來就是Advisor不需要解析包裝。

public List<Advisor> findAdvisorBeans() {
		String[] advisorNames = this.cachedAdvisorBeanNames;
		if (advisorNames == null) {
		    //獲取beanFactory中實現了Advisor接口的實例
			advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
					this.beanFactory, Advisor.class, true, false);
			this.cachedAdvisorBeanNames = advisorNames;
		}
		if (advisorNames.length == 0) {
			return new ArrayList<>();
		}

		List<Advisor> advisors = new ArrayList<>();
		for (String name : advisorNames) {
			if (isEligibleBean(name)) {
   			     //判斷當前bean是不是正在創建。 正在創建則不做任何處理。這裏需要注意的是 即使當前正在
   			     //創建的bean是Advisor的子類 也不必擔心 這個類無法加入到advisors中因爲這個方法不止調用一次。每一個bean實例化的時候都會調用。
				if (this.beanFactory.isCurrentlyInCreation(name)) {
					if (logger.isTraceEnabled()) {
						logger.trace("Skipping currently created advisor '" + name + "'");
					}
				}
				else {
					try {
					   //這裏如果沒有創建就從beanFactory獲取bean 如果沒創建會創建 繼而加入進緩存中
					   //bean的創建流程請參考https://blog.csdn.net/xiaomujiang_dafangzi/article/details/105183005
						advisors.add(this.beanFactory.getBean(name, Advisor.class));
					}
					catch (BeanCreationException ex) {
						Throwable rootCause = ex.getMostSpecificCause();
						if (rootCause instanceof BeanCurrentlyInCreationException) {
							BeanCreationException bce = (BeanCreationException) rootCause;
							String bceBeanName = bce.getBeanName();
							if (bceBeanName != null && this.beanFactory.isCurrentlyInCreation(bceBeanName)) {
								if (logger.isTraceEnabled()) {
									logger.trace("Skipping advisor '" + name +
											"' with dependency on currently created bean: " + ex.getMessage());
								}
								// Ignore: indicates a reference back to the bean we're trying to advise.
								// We want to find advisors other than the currently created bean itself.
								continue;
							}
						}
						throw ex;
					}
				}
			}
		}
		return advisors;
	}

到這裏查找的過程就完了、、其實還是很簡單的,就是如果有實現Advisor接口的類。至於 是如何織入到代理類中的請參考上一篇https://blog.csdn.net/xiaomujiang_dafangzi/article/details/105217189

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章