spring AOP源碼深度解析

由於springAOP會使用springIOC來管理Bean,因此對springIOC不太瞭解的同窗能夠參考我前篇springIOC源碼深度解析php

本文采用的源碼版本是5.2.x。爲了咱們更好地理解springAOP,咱們使用的是xml的方式,實際開發中大部分都是是用註解的方式,經驗告訴我,從理解源碼的角度上來說,xml配置是最好不過了。java

閱讀建議:把spring官網的源碼給拉下來,對照着個人解析看,這樣學習效率是最高的,而不是蜻蜓點水。node

本文分爲兩大部分,第一部分對spring AOP 的一些前置知識進行介紹,這部分的目的是讓對springAOP不太瞭解的讀者能瞭解springAOP的一些基本知識。第二部分就是咱們本文的重點,這部分會對springAOP源碼進行深刻的解析。spring

spring AOP的相關術語介紹

  • Joinpoint(鏈接點)

    所謂的鏈接點是指那些被攔截到的點。在spring中,這些點指的是方法,由於spring只支持方法類型的鏈接點express

    對應的類:編程

    public interface Joinpoint {
    
      // 執行攔截器鏈中的下一個攔截器邏輯
    	Object proceed() throws Throwable;
    
    	Object getThis();
    
    	AccessibleObject getStaticPart();
    
    }
    複製代碼
  • Pointcut(切入點)

    所謂的切入點,是指咱們要對哪些Joinpoint進行攔截的定義緩存

    對應的類:bash

    public interface Pointcut {
    
        //返回一個類型過濾器
        ClassFilter getClassFilter();
    
        // 返回一個方法匹配器
        MethodMatcher getMethodMatcher();
    
        Pointcut TRUE = TruePointcut.INSTANCE;
    }
    複製代碼

    對應的ClassFilter和MethodMatcher接口:app

    public interface ClassFilter {
        boolean matches(Class<?> clazz);
        ClassFilter TRUE = TrueClassFilter.INSTANCE;
    
    }
    
    public interface MethodMatcher {
        boolean matches(Method method, Class<?> targetClass);
        boolean matches(Method method, Class<?> targetClass, Object... args);
        boolean isRuntime();
        MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
    }
    複製代碼

    咱們寫AOP代碼的時候,通常是同切入點表達式進行對鏈接點進行選擇,切入點表達式處理類: AspectJExpressionPointcut,這個類的繼承關係圖以下:框架

能夠看到AspectJExpressionPointcut繼承了ClassFilter和MethodMatcher接口,擁有了matches方法,因此它能夠對鏈接點進行選擇。

  • Advice(通知/加強)

    所謂的通知就是指攔截到Jointpoint以後所要作的事情就是通知,通知分爲前置通知(AspectJMethodBeforeAdvice),後置通知(AspectJAfterReturningAdvice),異常通知(AspectJAfterThrowingAdvice),最終通知(AspectAfterAdvice),環繞通知(AspectJAroundAdvice)

  • Introduction(引介)

    引介是一種特殊的通知在不修改類代碼的前提下,Introduction能夠在運行期爲類動態地添加一些方法或Field。

  • Target(目標對象)

    代理的目標對象,好比UserServiceImpl類

  • Weaving(織入)

    是指把加強應用到目標對象來建立新的代理對象的過程。spring是經過實現後置處理器BeanPostProcessor接口來實現織入的。也就是在bean完成初始化以後,經過給目標對象生成代理對象,並交由springIOC容器來接管,這樣再去容器中獲取到的目標對象就是已經加強過的代理對象。

  • Proxy(代理)

    一個類被AOP織入加強後,就產生一個結果代理類

  • Advisor(通知器、顧問)

    和Aspect很類似

  • Aspect(切面)

    是切入點和通知的結合,對應於spring中的PointcutAdvisor,既能夠獲取到切入點又能夠獲取到通知。以下面類所示:

    public interface Advisor {
    
        Advice getAdvice();
        boolean isPerInstance();
    }
    
    public interface PointcutAdvisor extends Advisor {
    
        Pointcut getPointcut();
    }
    複製代碼

    對上面的知識點,經過下面的圖來總結一下,但願可以幫助讀者記憶。

瞭解AOP、Spring AOP、AspectJ這幾個概念

什麼是AOP

AOP爲Aspect Oriented Programming的縮寫,意爲:面向切面編程。具體的請讀者查資料看相關定義

Spring AOP

  • 基於動態代理實現,若是使用接口,則用JDK的動態代理實現;若是沒有實現接口,則使用CGLIB來實現。
  • 而動態代理是基於反射設計的。
  • Spring提供了AspectJ的支持,實現了一套純的Spring AOP

本文解析源碼時基於springAOP進行解析的。

原理以下圖所示:

可能這樣講仍是有些抽象,下面寫個例子輔助理解:

建立一個Person接口:

public interface Person {

    public void eat();
}
複製代碼

建立接口實現類:PersonImpl

public class PersonImpl implements Person {

    @Override
    public void eat() {
        System.out.println("我要吃飯");
    }
}
複製代碼

小時候媽媽告訴咱們吃飯前要洗手,怎麼使用代碼進行實現呢?

定義一個EatHandler,實現 InvocationHandler

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class EatHandler implements InvocationHandler {

    // 要代理的目標對象
    private Object target;

    public EatHandler(Object target) {
        this.target = target;
    }
    public Object proxyInstance() {
        // 第一個參數就是要加載代理對象的類加載器,
        // 第二個參數就是要代理類實現的接口
        // 第三個參數就是EatHandler
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("吃飯前要洗手");
        Object result = method.invoke(this.target, args);
        System.out.println("吃飯後要洗碗");
        return result;
    }
}
複製代碼

來個測試類:

public class ProxyMain {
    public static void main(String[] args) {
        EatHandler eatHandler = new EatHandler(new PersonImpl());
        Person person = (Person) eatHandler.proxyInstance();
        person.eat();
    }
}
複製代碼

輸出:

吃飯前要洗手
我要吃飯
吃飯後要洗碗
複製代碼

能夠看到,在「我要吃飯」先後咱們已經插入了「吃飯前要洗手」,「吃飯後要洗碗」的功能

是怎麼插入的呢?

原理就是JDK幫咱們建立了目標對象(EatHandler)的代理類,這個代理類插入了咱們想要的功能,當咱們調用person.eat();時,操做的是代理類而不是原對象,從而實現了插入咱們想要功能的需求。這樣應該能夠理解了。下面貼下生成的代理對象:

import java.lang.reflect.UndeclaredThrowableException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 
// Decompiled by Procyon v0.5.36
// 

public final class PersonImpl$Proxy1 extends Proxy implements Person {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;
    
    public PersonImpl$Proxy1(final InvocationHandler h) {
        super(h);
    }
   
    public final boolean equals(final Object o) {
        try {
            return (boolean)super.h.invoke(this, PersonImpl$Proxy1.m1, new Object[] { o });
        }
        catch (Error | RuntimeException error) {
            throw;
        }
        catch (Throwable undeclaredThrowable) {
            throw new UndeclaredThrowableException(undeclaredThrowable);
        }
    }
    
    public final void eat() {
        try {
            super.h.invoke(this, PersonImpl$Proxy1.m3, null);
        }
        catch (Error | RuntimeException error) {
            throw;
        }
        catch (Throwable undeclaredThrowable) {
            throw new UndeclaredThrowableException(undeclaredThrowable);
        }
    }
    
    public final String toString() {
        try {
            return (String)super.h.invoke(this, PersonImpl$Proxy1.m2, null);
        }
        catch (Error | RuntimeException error) {
            throw;
        }
        catch (Throwable undeclaredThrowable) {
            throw new UndeclaredThrowableException(undeclaredThrowable);
        }
    }
    
    public final int hashCode() {
        try {
            return (int)super.h.invoke(this, PersonImpl$Proxy1.m0, null);
        }
        catch (Error | RuntimeException error) {
            throw;
        }
        catch (Throwable undeclaredThrowable) {
            throw new UndeclaredThrowableException(undeclaredThrowable);
        }
    }
    
    static {
        try {
            PersonImpl$Proxy1.m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
          // 利用反射獲取目標對象的Method方法
            PersonImpl$Proxy1.m3 = Class.forName("Person").getMethod("eat", (Class<?>[])new Class[0]);
            PersonImpl$Proxy1.m2 = Class.forName("java.lang.Object").getMethod("toString", (Class<?>[])new Class[0]);
            PersonImpl$Proxy1.m0 = Class.forName("java.lang.Object").getMethod("hashCode", (Class<?>[])new Class[0]);
        }
        catch (NoSuchMethodException ex) {
            throw new NoSuchMethodError(ex.getMessage());
        }
        catch (ClassNotFoundException ex2) {
            throw new NoClassDefFoundError(ex2.getMessage());
        }
    }
}
複製代碼

咱們看到JDK確實是爲咱們生成了一個代理對象,這個代理對象繼承了Proxy對象,實現了Person接口。咱們看到代理對象中生成了equals()、toString()、hashCode()以及咱們的eat()方法。看到eat方法中有這樣的代碼super.h.invoke(this, PersonImpl$Proxy1.m3, null); 其中,h就是咱們定義的EatHandle。咱們看到當咱們調用person.eat()的時候,其實是調用這個代理對象的eat()方法,先是調用EatHandle中的invoke方法,這裏有咱們加入的加強代碼,這就實現了代理的做用了。這樣應該懂了吧。

AspectJ

  • AspectJ是一個Java實現的AOP框架,它可以對java代碼進行AOP編譯(通常在編譯期進行),讓 java代碼具備AspectJ的AOP功能(固然須要特殊的編譯器)
  • 能夠這樣說AspectJ是目前實現AOP框架中最成熟,功能最豐富的語言。更幸運的是,AspectJ與 java程序徹底兼容,幾乎是無縫關聯,所以對於有java編程基礎的工程師,上手和使用都很是容易。
  • ApectJ採用的就是靜態織入的方式。ApectJ主要採用的是編譯期織入,在這個期間使用AspectJ的 acj編譯器(相似javac)把aspect類編譯成class字節碼後,在java目標類編譯時織入,即先編譯 aspect類再編譯目標類。

以下圖所示:

源碼解析

有了前面基礎知識的鋪墊,如今終於能夠進入到源碼解析階段了。讓咱們來驗證一下咱們的想法吧。請讀者打開spring源碼。

這個階段分爲三個步驟:

  • AOP配置文件的解析流程分析
  • 代理對象的建立分析
  • 代理對象執行流程分析

AOP配置文件的解析流程分析

咱們要完成下面配置文件的解析:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

	<!-- 配置目標對象 -->
	<bean class="com.sjc.spring.aop.target.UserServiceImpl"></bean>
	<!-- 配置通知類 -->
	<bean id="myAdvice" class="com.sjc.spring.aop.advice.MyAdvice"></bean>
	<!-- AOP配置 -->
	<aop:config>
	
		<!-- <aop:advisor advice-ref="" /> -->
		<aop:pointcut expression="" id="" />
		<aop:aspect ref="myAdvice">
			<aop:before method="before" pointcut="execution(* *..*.*ServiceImpl.*(..))" />
			<aop:after method="after" pointcut="execution(* *..*.*ServiceImpl.*(..))" />
		</aop:aspect>
	</aop:config>

</beans>

複製代碼

入口:DefaultBeanDefinitionDocumentReader#parseBeanDefinitions

咱們在上一篇解析SpringIOC源碼的時候也也分析過這一步, 讓咱們進入到

delegate.parseCustomElement(ele);

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
		// 加載的Document對象是否使用了Spring默認的XML命名空間(beans命名空間)
		if (delegate.isDefaultNamespace(root)) {
			// 獲取Document對象根元素的全部子節點(bean標籤、import標籤、alias標籤和其餘自定義標籤context、aop等)
			NodeList nl = root.getChildNodes();
			for (int i = 0; i < nl.getLength(); i++) {
				Node node = nl.item(i);
				if (node instanceof Element) {
					Element ele = (Element) node;
					// bean標籤、import標籤、alias標籤,則使用默認解析規則
					if (delegate.isDefaultNamespace(ele)) {
						parseDefaultElement(ele, delegate);
					}
					else { //像context標籤、aop標籤、tx標籤,則使用用戶自定義的解析規則解析元素節點
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
			delegate.parseCustomElement(root);
		}
	}
複製代碼

咱們進入到:BeanDefinitionParserDelegate#parseCustomElement

這個方法主要作兩件事:

  • 獲取命名空間URI(就是獲取beans標籤的xmlns:aop或者xmlns:context屬性的值)
  • 根據namespaceUri獲取對應的處理類handler
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
		// 獲取命名空間URI(就是獲取beans標籤的xmlns:aop或者xmlns:context屬性的值)
		// http://www.springframework.org/schema/aop
		String namespaceUri = getNamespaceURI(ele);
		if (namespaceUri == null) {
			return null;
		}
		// 根據不一樣的命名空間URI,去匹配不一樣的NamespaceHandler(一個命名空間對應一個NamespaceHandler)
		// 此處會調用DefaultNamespaceHandlerResolver類的resolve方法
		// 兩步操做:查找NamespaceHandler 、調用NamespaceHandler的init方法進行初始化(針對不一樣自定義標籤註冊相應的BeanDefinitionParser)
		NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
		if (handler == null) {
			error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
			return null;
		}
		return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
	}

複製代碼

咱們進入到NamespaceHandlerSupport#parse

這個方法咱們主要關心的是parser.parse()方法,findParserForElement能夠做爲一個分支去了解,若是讀者感興趣的話。

public BeanDefinition parse(Element element, ParserContext parserContext) {
		// NamespaceHandler裏面初始化了大量的BeanDefinitionParser來分別處理不一樣的自定義標籤
		// 從指定的NamespaceHandler中,匹配到指定的BeanDefinitionParser
		BeanDefinitionParser parser = findParserForElement(element, parserContext);
		// 調用指定自定義標籤的解析器,完成具體解析工做
		return (parser != null ? parser.parse(element, parserContext) : null);
	}
複製代碼

BeanDefinitionParser有不少的實現類,這裏咱們配置文件是由ConfigBeanDefinitionParser來實現的。

咱們進入到ConfigBeanDefinitionParser#parse();

這個方法主要產生三個咱們比較關心的分支:

  • configureAutoProxyCreator(parserContext, element);

    主要是生成AspectJAwareAdvisorAutoProxyCreator類的BeanDefinition,並註冊到IOC容器,這個類用來建立AOP代理對象

  • parsePointcut(elt, parserContext);

    產生一個AspectJExpressionPointcut的BeanDefinition對象,並註冊。這個AspectJExpressionPointcut用來解析咱們的切入點表達式,讀者能夠看開頭咱們介紹的AspectJExpressionPointcut類關係圖。

  • parseAspect(elt, parserContext);

    這一步主要是將解析的aop:aspect標籤進行進行封裝。咱們主要來看看這個分支。

public BeanDefinition parse(Element element, ParserContext parserContext) {
		CompositeComponentDefinition compositeDef =
				new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
		parserContext.pushContainingComponent(compositeDef);

		// 向IoC容器中註冊 AspectJAwareAdvisorAutoProxyCreator 類的BeanDefinition:(用於建立AOP代理對象的)
		// BeanPostProcessor能夠對實例化以後的bean進行一些操做
		// AspectJAwareAdvisorAutoProxyCreator 實現了BeanPostProcessor接口,能夠對目標對象實例化以後,建立對應的代理對象
		configureAutoProxyCreator(parserContext, element);

		// 獲取<aop:config>標籤的子標籤<aop:aspect>、<aop:advisor> 、<aop:pointcut>
		List<Element> childElts = DomUtils.getChildElements(element);
		for (Element elt: childElts) {
			// 獲取子標籤的節點名稱或者叫元素名稱
			String localName = parserContext.getDelegate().getLocalName(elt);
			if (POINTCUT.equals(localName)) {
				// 解析<aop:pointcut>標籤
				// 產生一個AspectJExpressionPointcut的BeanDefinition對象,並註冊
				parsePointcut(elt, parserContext);
			}
			else if (ADVISOR.equals(localName)) {
				// 解析<aop:advisor>標籤
				// 產生一個DefaultBeanFactoryPointcutAdvisor的BeanDefinition對象,並註冊
				parseAdvisor(elt, parserContext);
			}
			else if (ASPECT.equals(localName)) {
				// 解析<aop:aspect>標籤
				// 產生了不少BeanDefinition對象
				// aop:after等標籤對應5個BeanDefinition對象
				// aop:after標籤的method屬性對應1個BeanDefinition對象
				// 最終的AspectJPointcutAdvisor BeanDefinition類
				parseAspect(elt, parserContext);
			}
		}

		parserContext.popAndRegisterContainingComponent();
		return null;
	}
複製代碼

咱們進入到ConfigBeanDefinitionParser#parseAspect

這個方法開始解析aop:aspect標籤,

咱們主要關心parseAdvice方法

private void parseAspect(Element aspectElement, ParserContext parserContext) {
		// 獲取<aop:aspect>標籤的id屬性值
		String aspectId = aspectElement.getAttribute(ID);
		// 獲取<aop:aspect>標籤的ref屬性值,也就是加強類的引用名稱
		String aspectName = aspectElement.getAttribute(REF);

		try {
			this.parseState.push(new AspectEntry(aspectId, aspectName));
			List<BeanDefinition> beanDefinitions = new ArrayList<>();
			List<BeanReference> beanReferences = new ArrayList<>();
			// 處理<aop:aspect>標籤的<aop:declare-parents>子標籤
			List<Element> declareParents = DomUtils.getChildElementsByTagName(aspectElement, DECLARE_PARENTS);
			for (int i = METHOD_INDEX; i < declareParents.size(); i++) {
				Element declareParentsElement = declareParents.get(i);
				beanDefinitions.add(parseDeclareParents(declareParentsElement, parserContext));
			}

			// We have to parse "advice" and all the advice kinds in one loop, to get the
			// ordering semantics right.
			// 獲取<aop:aspect>標籤的全部子標籤
			NodeList nodeList = aspectElement.getChildNodes();
			boolean adviceFoundAlready = false;
			for (int i = 0; i < nodeList.getLength(); i++) {
				Node node = nodeList.item(i);
				// 判斷是不是<aop:before>、<aop:after>、<aop:after-returning>、<aop:after-throwing method="">、<aop:around method="">這五個標籤
				if (isAdviceNode(node, parserContext)) {
					if (!adviceFoundAlready) {
						adviceFoundAlready = true;
						if (!StringUtils.hasText(aspectName)) {
							parserContext.getReaderContext().error(
									"<aspect> tag needs aspect bean reference via 'ref' attribute when declaring advices.",
									aspectElement, this.parseState.snapshot());
							return;
						}
						beanReferences.add(new RuntimeBeanReference(aspectName));
					}
					// 解析<aop:before>等五個子標籤
					// 方法主要作了三件事:
					// 一、根據織入方式(before、after這些)建立RootBeanDefinition,名爲adviceDef即advice定義
					// 二、將上一步建立的RootBeanDefinition寫入一個新的RootBeanDefinition,構造一個新的對象,名爲advisorDefinition,即advisor定義
					// 三、將advisorDefinition註冊到DefaultListableBeanFactory中
					AbstractBeanDefinition advisorDefinition = parseAdvice(
							aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences);
					beanDefinitions.add(advisorDefinition);
				}
			}

			AspectComponentDefinition aspectComponentDefinition = createAspectComponentDefinition(
					aspectElement, aspectId, beanDefinitions, beanReferences, parserContext);
			parserContext.pushContainingComponent(aspectComponentDefinition);

			List<Element> pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT);
			for (Element pointcutElement : pointcuts) {
				parsePointcut(pointcutElement, parserContext);
			}

			parserContext.popAndRegisterContainingComponent();
		}
		finally {
			this.parseState.pop();
		}
	}
複製代碼

咱們進入到ConfigBeanDefinitionParser#parseAdvice

這個方法主要作的是:解析aop:before等五個子標籤

一、根據織入方式(before、after這些)建立RootBeanDefinition,名爲adviceDef即advice定義 二、將上一步建立的RootBeanDefinition寫入一個新的RootBeanDefinition,構造一個新的對象,名爲advisorDefinition,即advisor定義 三、將advisorDefinition註冊到DefaultListableBeanFactory中

private AbstractBeanDefinition parseAdvice( String aspectName, int order, Element aspectElement, Element adviceElement, ParserContext parserContext, List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) {

		try {
			this.parseState.push(new AdviceEntry(parserContext.getDelegate().getLocalName(adviceElement)));

			// create the method factory bean
			// 建立方法工廠Bean的BeanDefinition對象:用於獲取Advice加強類的Method對象, <aop:brefore method="before">中的method
			RootBeanDefinition methodDefinition = new RootBeanDefinition(MethodLocatingFactoryBean.class);
			// 設置MethodLocatingFactoryBean的targetBeanName爲advice類的引用名稱,也就是<aop:aspect ref="myAdvice">中的myAdvice
			methodDefinition.getPropertyValues().add("targetBeanName", aspectName);
			// 設置MethodLocatingFactoryBean的methodName爲<aop:after>標籤的method屬性值(也就是method="before"中的before,做爲advice方法名稱)
			methodDefinition.getPropertyValues().add("methodName", adviceElement.getAttribute("method"));
			methodDefinition.setSynthetic(true);

			// create instance factory definition
			// 建立實例工廠BeanDefinition:用於建立加強類的實例,也就是<aop:aspect ref="myAdvice">中的myAdvice
			RootBeanDefinition aspectFactoryDef =
					new RootBeanDefinition(SimpleBeanFactoryAwareAspectInstanceFactory.class);
			// 設置SimpleBeanFactoryAwareAspectInstanceFactory的aspectBeanName爲advice類的引用名稱
			aspectFactoryDef.getPropertyValues().add("aspectBeanName", aspectName);
			aspectFactoryDef.setSynthetic(true);

			//以上的兩個BeanDefinition的做用主要是經過反射調用Advice對象的指定方法
			// method.invoke(obj,args)

			// register the pointcut
			// 通知加強類的BeanDefinition對象(核心),也就是一個<aop:before>等對應一個adviceDef
			AbstractBeanDefinition adviceDef = createAdviceDefinition(
					adviceElement, parserContext, aspectName, order, methodDefinition, aspectFactoryDef,
					beanDefinitions, beanReferences);

			// configure the advisor
			// 通知器類的BeanDefinition對象, 對應<aop:aspect>
			RootBeanDefinition advisorDefinition = new RootBeanDefinition(AspectJPointcutAdvisor.class);
			advisorDefinition.setSource(parserContext.extractSource(adviceElement));
			// 給通知器類設置Advice對象屬性值
			advisorDefinition.getConstructorArgumentValues().addGenericArgumentValue(adviceDef);
			if (aspectElement.hasAttribute(ORDER_PROPERTY)) {
				advisorDefinition.getPropertyValues().add(
						ORDER_PROPERTY, aspectElement.getAttribute(ORDER_PROPERTY));
			}

			// register the final advisor
			// 將advisorDefinition註冊到IoC容器中
			parserContext.getReaderContext().registerWithGeneratedName(advisorDefinition);

			return advisorDefinition;
		}
		finally {
			this.parseState.pop();
		}
	}
複製代碼

至此對標籤的解析完畢

代理對象的建立分析

分析AOP代理對象建立以前,咱們先來看下類的繼承結構圖:

咱們來看AbstractAutoProxyCreator類,裏面有這些方法:

postProcessBeforeInitialization

 postProcessAfterInitialization----AOP功能入口

 postProcessBeforeInstantiation 

postProcessAfterInstantiation 

postProcessPropertyValues
複製代碼

咱們找到分析入口:AbstractAutoProxyCreator#postProcessAfterInitialization

@Override
	public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
		if (bean != null) {
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			if (this.earlyProxyReferences.remove(cacheKey) != bean) {
				// 使用動態代理技術,產生代理對象
				// bean : 目標對象
				// beanName :目標對象名稱
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}
複製代碼

進入AbstractAutoProxyCreator#wrapIfNecessary

這個方法主要作三件事:

  1. 查找代理類相關的advisor對象集合,從IOC中查找,由於上一步咱們解析了配置文件,並將相關信息封裝了幾個對象,並保存到了IOC容器中。
  2. 經過jdk動態代理或者cglib動態代理,產生代理對象
  3. 將代理類型放入緩存中
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
		if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
			return bean;
		}
		if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
			return bean;
		}
		// Advice/Pointcut/Advisor/AopInfrastructureBean接口的beanClass不進行代理以及對beanName爲aop內的切面名也不進行代理
		// 此處可查看子類複寫的shouldSkip()方法
		if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
			this.advisedBeans.put(cacheKey, Boolean.FALSE);
			return bean;
		}

		// Create proxy if we have advice.
		// 查找對代理類相關的advisor對象集合,此處就與point-cut表達式有關了
		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
		// 對相應的advisor不爲空才採起代理
		if (specificInterceptors != DO_NOT_PROXY) {
			this.advisedBeans.put(cacheKey, Boolean.TRUE);
			// 經過jdk動態代理或者cglib動態代理,產生代理對象
			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;
	}
複製代碼

咱們接着進入AbstractAutoProxyCreator#createProxy

protected Object createProxy(Class<?> beanClass, @Nullable String beanName, @Nullable Object[] specificInterceptors, TargetSource targetSource) {

		if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
			AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
		}

		// 建立代理工廠對象
		ProxyFactory proxyFactory = new ProxyFactory();
		proxyFactory.copyFrom(this);

		//若是沒有使用CGLib代理
		if (!proxyFactory.isProxyTargetClass()) {
			// 是否可能使用CGLib代理
			if (shouldProxyTargetClass(beanClass, beanName)) {
				proxyFactory.setProxyTargetClass(true);
			}
			else {
				// 查看beanClass對應的類是否含有InitializingBean.class/DisposableBean.class/Aware.class接口
				// 無則採用JDK動態代理,有則採用CGLib動態代理
				evaluateProxyInterfaces(beanClass, proxyFactory);
			}
		}
		// 得到全部關聯的Advisor集合(該分支待補充)
		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
		proxyFactory.addAdvisors(advisors);
		// 此處的targetSource通常爲SingletonTargetSource
		proxyFactory.setTargetSource(targetSource);
		// 空的實現
		customizeProxyFactory(proxyFactory);

		proxyFactory.setFrozen(this.freezeProxy);
		// 是否設置預過濾模式,此處針對本文爲true
		if (advisorsPreFiltered()) {
			proxyFactory.setPreFiltered(true);
		}
		// 獲取使用JDK動態代理或者cglib動態代理產生的對象
		return proxyFactory.getProxy(getProxyClassLoader());
	}
複製代碼

咱們進入ProxyFactory#getProxy

public Object getProxy(@Nullable ClassLoader classLoader) {
		// 一、建立JDK方式的AOP代理或者CGLib方式的AOP代理
		// 二、調用具體的AopProxy來建立Proxy代理對象
		return createAopProxy().getProxy(classLoader);
	}
複製代碼

咱們選擇jdk方式建立代理,由於咱們是經過接口實現代理的。

咱們進入:JdkDynamicAopProxy#getProxy

這個方法主要作兩件事:

  1. 獲取全部的代理接口
  2. 調用JDK動態代理
public Object getProxy(@Nullable ClassLoader classLoader) {
		if (logger.isTraceEnabled()) {
			logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
		}
		// 獲取完整的代理接口
		Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
		findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
		// 調用JDK動態代理方法
		return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
	}
複製代碼

至今生成代理類對象分析完畢

代理對象執行流程分析

咱們來看一下JdkDynamicAopProxy,它實現了InvocationHandler,因此代理對象執行流程分析的入口咱們已經找到了

入口:JdkDynamicAopProxy#invoke

這個方法主要作如下事情:

  1. 獲取針對該目標對象的全部加強器(advisor)

    這些Advisor都是有順序的,他們會按照順序進行鏈式調用

  2. 若是調用鏈爲空,則直接經過反射調用目標對象的方法,也就是不對方法進行任何的加強

  3. 建立ReflectiveMethodInvocation實例對調用鏈進行調用,開始執行AOP的攔截過程

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		Object oldProxy = null;
		boolean setProxyContext = false;

		TargetSource targetSource = this.advised.targetSource;
		Object target = null;

		try {
			
      //...省略若干代碼

			Object retVal;

			if (this.advised.exposeProxy) {
				// Make invocation available if necessary.
				oldProxy = AopContext.setCurrentProxy(proxy);
				setProxyContext = true;
			}

			// Get as late as possible to minimize the time we "own" the target,
			// in case it comes from a pool.
			// 獲取目標對象
			target = targetSource.getTarget();
			// 獲取目標對象的類型
			Class<?> targetClass = (target != null ? target.getClass() : null);

			// Get the interception chain for this method.
			// 獲取針對該目標對象的全部加強器(advisor), 這些advisor都是有順序的,他們會按照順序進行鏈式調用
			List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

			// Check whether we have any advice. If we don't, we can fallback on direct
			// reflective invocation of the target, and avoid creating a MethodInvocation.
			// 檢查是否咱們有一些通知。若是咱們沒有,咱們能夠直接對目標類進行反射調用,避免建立MethodInvocation類

			// 調用目標類的方法
			if (chain.isEmpty()) {
				// We can skip creating a MethodInvocation: just invoke the target directly
				// Note that the final invoker must be an InvokerInterceptor so we know it does
				// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
				Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
				// 經過反射調用目標對象的方法
				retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
			}
			else {
				// We need to create a method invocation...
				//咱們須要建立一個方法調用
				// proxy:生成的動態代理對象
				// target:目標方法
				// args: 目標方法參數
				// targetClass:目標類對象
				// chain: AOP攔截器執行鏈,是一個MethodInterceptor的集合
				MethodInvocation invocation =
						new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
				// Proceed to the joinpoint through the interceptor chain.
				// 經過攔截器鏈進入鏈接點
				// 開始執行AOP的攔截過程
				retVal = invocation.proceed();
			}

			// Massage return value if necessary.
			Class<?> returnType = method.getReturnType();
			if (retVal != null && retVal == target &&
					returnType != Object.class && returnType.isInstance(proxy) &&
					!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
				// Special case: it returned "this" and the return type of the method
				// is type-compatible. Note that we can't help if the target sets
				// a reference to itself in another returned object.
				retVal = proxy;
			}
			else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
				throw new AopInvocationException(
						"Null return value from advice does not match primitive return type for: " + method);
			}
			return retVal;
		}
		finally {
			if (target != null && !targetSource.isStatic()) {
				// Must have come from TargetSource.
				targetSource.releaseTarget(target);
			}
			if (setProxyContext) {
				// Restore old proxy.
				AopContext.setCurrentProxy(oldProxy);
			}
		}
複製代碼

咱們來看獲取調用鏈的過程:

進入到:AdvisedSupport#getInterceptorsAndDynamicInterceptionAdvice

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
		// 建立以方法爲單位的緩存key
		MethodCacheKey cacheKey = new MethodCacheKey(method);
		// 從緩存中獲取指定方法的advisor集合
		List<Object> cached = this.methodCache.get(cacheKey);
		if (cached == null) {
			// 獲取目標類中指定方法的MethodInterceptor集合,該集合是由Advisor轉換而來
			cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
					this, method, targetClass);
			this.methodCache.put(cacheKey, cached);
		}
		return cached;
	}
複製代碼

咱們進入獲取MethodInterceptor集合:

DefaultAdviceChainFactory#getInterceptorsAndDynamicInterceptionAdvice

這裏主要作:

  1. 建立DefaultAdvisorAdapterRegistry實例,並建立MethodBeforeAdviceAdapter、AfterReturningAdviceAdapter、ThrowsAdviceAdapter適配器

    這裏爲啥須要適配器呢?咱們來看下面的類關係圖:

有些Advice和攔截器MethodInterceptor根本就沒有關係,此時就須要適配器將他們之間產生關係。這裏用了適配器模式,打個比方,咱們買了好多型號的電腦,使用的電壓都不同,咱們家的電壓是220v,那怎麼樣才能使咱們的電腦能充上電呢,納米就須要電源適配器了。這樣講應該懂了吧

2.變量全部的Advisor集合,使用Pointcut類的ClassFilter().matches()進行匹配,還有使用Pointcut().getMethodMatcher()進行匹配,若是匹配上則將advisor轉成MethodInterceptor,

3.若是須要根據參數動態匹配(好比重載)則攔截器鏈中新增InterceptorAndDynamicMethodMatcher

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.
		// advice適配器註冊中心
		// MethodBeforeAdviceAdapter:將Advisor適配成MethodBeforeAdvice
		// AfterReturningAdviceAdapter:將Advisor適配成AfterReturningAdvice
		// ThrowsAdviceAdapter: 將Advisor適配成ThrowsAdvice
		AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
		Advisor[] advisors = config.getAdvisors();
		// 返回值集合,裏面裝的都是Interceptor或者它的子類接口MethodInterceptor
		List<Object> interceptorList = new ArrayList<>(advisors.length);
		// 獲取目標類的類型
		Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
		// 是否有引介
		Boolean hasIntroductions = null;

		// 去產生代理對象的過程當中,針對該目標方法獲取到的全部合適的Advisor集合
		for (Advisor advisor : advisors) {
			if (advisor instanceof PointcutAdvisor) {
				// Add it conditionally.
				PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
				// 若是該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) {
						// 將advisor轉成MethodInterceptor
						MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
						// MethodMatcher接口經過重載定義了兩個matches()方法
						// 兩個參數的matches() 被稱爲靜態匹配,在匹配條件不是太嚴格時使用,能夠知足大部分場景的使用
						// 稱之爲靜態的主要是區分爲三個參數的matches()方法須要在運行時動態的對參數的類型進行匹配
						// 兩個方法的分界線就是boolean isRuntime()方法
						// 進行匹配時先用兩個參數的matches()方法進行匹配,若匹配成功,則檢查boolean isRuntime()的返回值若爲
						// true, 則調用三個參數的matches()方法進行匹配(若兩個參數的都匹配不中,三個參數的一定匹配不中)

						// 須要根據參數動態匹配(好比重載)
						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;
	}
複製代碼

咱們返回到JdkDynamicAopProxy#invoke

進入到ReflectiveMethodInvocation#proceed()中:

這個是調用鏈的執行過程

  1. 若是執行到鏈條的末尾,則直接調用鏈接點,即直接調用目標方法

  2. 最終會調用MethodInterceptor的invoke方法,本文配置文件會調用AspectJAfterAdvice、MethodBeforeAdviceInterceptor

    咱們來看AspectJAfterAdvice:裏面的方法調用是這樣,又繼續調用調用鏈

    public Object invoke(MethodInvocation mi) throws Throwable {
    		try {
    			return mi.proceed();
    		}
    		finally {
    			invokeAdviceMethod(getJoinPointMatch(), null, null);
    		}
    	}
    複製代碼

咱們看:MethodBeforeAdviceInterceptor(前置通知攔截器)

public Object invoke(MethodInvocation mi) throws Throwable {
		this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
		return mi.proceed();
	}
複製代碼

是先執行目標方法而後再調用下一個調用鏈

這就保證了調用的順序符號前置通知在目標方法前調用,最終通知是在目標方法執行後通知,同時也說明了配置文件中<aop:before />

、<aop:after method="after"/>的順序不會影響最終執行的順序,順序是經過調用鏈來保證的。

public Object proceed() throws Throwable {
		// We start with an index of -1 and increment early.
		// 若是執行到鏈條的末尾, 則直接調用鏈接點方法 即直接調用目標方法
		if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
			return invokeJoinpoint();
		}

		// 獲取集合中的MethodInterceptor
		Object interceptorOrInterceptionAdvice =
				this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
		// 若是是InterceptorAndDynamicMethodMatcher類型(動態匹配)
		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)) {
				// 若是匹配則直接調用MethodInterceptor的invoke方法
				// 注意這裏傳入的參數是this,咱們下面看一下ReflectiveMethodInvocation的類型
				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.
			// 說明是適用於此目標方法的,直接調用MethodInterceptor的invoke方法
			// 傳入this即ReflectiveMethodInvocation實例
			// 傳入this進入 這樣就能夠造成一個調用的鏈條了
			return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
		}
	}
複製代碼

通過長久的書寫,終於成文,因爲水平有限,不能詳盡各個方面,忘讀者見諒,能夠和我交流。

相關文章
相關標籤/搜索