天天看點

Spring AOP原理學習-看書記錄筆記1. 整體2. 重點

1. 整體

2. 重點

2.1 重要概念

  • 切點
  • 通知
  • 代理
  • 攔截
  • 增強

2.2 動态代理原理

記住一點,AOP的核心技術就是動态代理

AOP用的是代理,管理代理。java有自帶的代理機制,還有Cglb代理機制。這兩種機制Aop都在應用,Java代理主要運用于接口,而Cglb主要是類。

很重要的一段話

Spring AOP原理學習-看書記錄筆記1. 整體2. 重點

了解一下ProxyCreatorSupport

Spring AOP原理學習-看書記錄筆記1. 整體2. 重點

AOP的具體實作就是在這個類中。

都是從DefaultAopProxyFactory中的createAopProxy()開始下面操作的。

  • 生成代理對象
  • 執行攔截器。

JdkDynamicAopProxy攔截器

從JdkDynamicAopProxy的invoke()方法,到ReflectiveMethodInvocation的process()

CglibAopProxy攔截器

intercept()方法,攔截器不是憑空來的,需要進行攔截器注冊才能儲存到攔截器鍊中,在攔截器注冊之間有一個:從攔截器注冊器中進行适配,确定哪些攔截器是該代理目标的攔截器的。

ReflectiveMethodInvocation反射

這裡通過反射調用目标方法。 process()方法中實作,這裡也是執行matches()的地方。

2.3 invoke()方法邏輯分析

代理中最主要的方法就是invoke()方法了,那就介紹一下該方法的邏輯。

  • 擷取目标源TargetSource
  • 指派代理目标對象Target
  • 擷取代理對象類
  • 擷取代理對象參數,用數組來存放
  • 通過反射調用目标方法
  • 擷取目标方法執行後傳回資訊

2.4 關于切點

知道切點都是到,就是增強的目标方法,一般是方法。但是Aop怎麼知道對那些方法進行操作,這就要在配置的時候告訴Aop需要操作哪些方法。

先看一下Pointcut的整體架構:

Spring AOP原理學習-看書記錄筆記1. 整體2. 重點

這裡每一個實作類都要實作一個方法matchs(),至于不同的切點比對規則不一樣。通常用到的

  • 方法名
  • 注解
  • 正規表達式

2.4 Advisor通知器

将增強設計,也就是Advice和切點(Pointcut)結合起來。 也就是哪個切點上使用哪個通知。

2.4.1 DefaultPointcutAdvisor預設切點通知器

也就是當通知器為空的時候,就是用預設的。 預設就是對任何方法的比對都成功。

TrueMethodMatcher這個方法的比對對任何方法的比對都會成功

總結

  1. 關于Spring AOP的介紹

    AOP是基于代理對象的,,通過ProxyCreateSuport來設定建立代理對象的通用操作,在這個類中主要就是擷取代理目标對象的SourceTarget,擷取代理對象的資訊。

  2. 再通過DefaultAopProxyFactory确定AopProxy代理對象的生成政策,生成政策的配置主要實在createAopProxy()方法中,其實也就是确定是有Java Proxy還是CGLIB第三來生成AopProxy代理對象。

    在确定了生成政策之後,真正的Proxy實在ObjenesisCglibAopProxy和JdkDynamicAopProxy類中生成的。

  3. 代理對象生成之後,在ObjenesisCglibAopProxy-(intercept)和JdkDynamicAopProxy-(invoke)都有自己的回調函數,回調函數的作用就是對代理的方法進行各種增強操作。
  4. 回調函數的作用是,擷取目标對象(包括目标方法,方法參數),攔截器鍊,建立ReflectiveMethodInvocation的process()方法來完成Aop的各種增強處理。這裡的攔截器鍊其實就是通過周遊連接配接器鍊,用一個一個的攔截器來執行攔截增強。
  5. 這裡有一個問題,當攔截器為空的時候,直接使用反射來調用目标方法,不需要建立ReflectiveMethodInvocation。這也是我之前運用AOP的一個例子,沒有用到攔截器,擷取了代理的目标對象方法之後,通過目标對象方法調用invoke(target, args)來執行方法。

    這裡記住一點這些操作是在ObjenesisCglibAopProxy-(intercept)和JdkDynamicAopProxy-(invoke)完成的,執行的方法是括号的方法。

  6. 攔截器分類兩種JdkDynamicAopProxy攔截器和CglibAopProxy攔截器,其實不難了解,既然是對AopProxy代理對象的攔截,針對AopProxy兩種生成政策,肯定也有兩種攔截器。上面已經講過了,當攔截器鍊不為空的時候,這兩種連接配接器都是通過ReflectiveMethodInvocation的process()方法進行代理目标函數的增強的。
  7. 是以接下來的重心就是ReflectiveMethodInvocation的process()方法,該方法中中實作了具體的增強邏輯。貼一個代碼吧。代碼的核心是從interceptorsAndDynamicMethodMatchers集合中擷取攔截器或者攔截通知,然後執行增強操作。這裡的interceptorsAndDynamicMethodMatchers連接配接器接口是從前面提到的方法中傳過來的,那就傳回擷取看看。
public Object proceed() throws Throwable {
		// We start with an index of -1 and increment early.
		if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
			return invokeJoinpoint();
		}

		Object interceptorOrInterceptionAdvice =
				this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
		if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
			// Evaluate dynamic method matcher here: static part will already have
			// been evaluated and found to match.
			InterceptorAndDynamicMethodMatcher dm =
					(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
			Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
			if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
				return dm.interceptor.invoke(this);
			}
			else {
				// Dynamic matching failed.
				// Skip this interceptor and invoke the next in the chain.
				return proceed();
			}
		}
		else {
			// It's an interceptor, so we just invoke it: The pointcut will have
			// been evaluated statically before this object was constructed.
			return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
		}
	}
           
  1. 關于通知或者攔截器來源我們在JdkDynamicAopProxy的invoke方法中看到了下面這段代碼,跟進去發現是在AdvisedSupport擷取的。代碼如第二段代碼,該類中用了一個Map來緩存所有代理方法的攔截器,

    Map定義:

    private transient Map<MethodCacheKey, List<Object>> methodCache;

    從代碼中可以看到,真正擷取攔截器的方法是:

    this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice( this, method, targetClass);

    方法。跟進去看看。

    這裡有一個問題,通過advisorChainFactory工程來擷取攔截器,advisorChainFactory是該類的一個屬性,看代碼發現,該屬性的具體執行個體被配置成DefaultAdvisorChainFactory類

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
		MethodCacheKey cacheKey = new MethodCacheKey(method);
		List<Object> cached = this.methodCache.get(cacheKey);
		if (cached == null) {
			// 真正擷取攔截器
			cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
					this, method, targetClass);
			this.methodCache.put(cacheKey, cached);
		}
		return cached;
	}
           
  1. 進入了

    this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice( this, method, targetClass);

    方法。發現該方法的具體實作是在DefaultAdvisorChainFactory中。這裡算是找到了擷取攔截器鍊的源頭了。
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
			Advised config, Method method, @Nullable Class<?> targetClass) {

		// This is somewhat tricky... We have to process introductions first,
		// but we need to preserve order in the ultimate list.
		AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
		Advisor[] advisors = config.getAdvisors();
		List<Object> interceptorList = new ArrayList<>(advisors.length);
		Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
		Boolean hasIntroductions = null;

		for (Advisor advisor : advisors) {
			if (advisor instanceof PointcutAdvisor) {
				// Add it conditionally.
				PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
				if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
					MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
					boolean match;
					if (mm instanceof IntroductionAwareMethodMatcher) {
						if (hasIntroductions == null) {
							hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
						}
						match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
					}
					else {
						match = mm.matches(method, actualClass);
					}
					if (match) {
						MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
						if (mm.isRuntime()) {
							// Creating a new object instance in the getInterceptors() method
							// isn't a problem as we normally cache created chains.
							for (MethodInterceptor interceptor : interceptors) {
								interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
							}
						}
						else {
							interceptorList.addAll(Arrays.asList(interceptors));
						}
					}
				}
			}
			else if (advisor instanceof IntroductionAdvisor) {
				IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
				if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
					Interceptor[] interceptors = registry.getInterceptors(advisor);
					interceptorList.addAll(Arrays.asList(interceptors));
				}
			}
			else {
				Interceptor[] interceptors = registry.getInterceptors(advisor);
				interceptorList.addAll(Arrays.asList(interceptors));
			}
		}

		return interceptorList;
	}
           
  1. 接下來就來看看這個方法的邏輯,該方法就是用一個單例類來注冊通知-攔截器。首先通過擷取所有的advisors,然後周遊獲得所有advisiors,在比對代理目标方法切點是否有該攔截器,如果有,就将該攔截器注冊,儲存到一個List攔截器集合中。 這裡基本是了解了,但是有個問題,advisior是怎麼讀入系統的呢? 這将是整個Aop的最後一個環節了。
  2. advisior真正注冊的地方就是在ProxyFacrotyBean中的initializeAdvisorChain()。之前在代碼中有這一行代碼。

    Advisor[] advisors = config.getAdvisors();

    ,那個時候我就在這裡的Advisor[] 從哪裡來的,經過一番檢視,就是從ProxyFacrotyBean中的initializeAdvisorChain()配置的。 這樣這個Aop的邏輯就很清晰了。

AopProxy代理類的擷取,Advisior的注冊,執行增強操作。 這一套都能聯系起來。其實Aop的邏輯元件确實就這三個,了解深刻了就會化繁就簡。