Spring源碼解讀(3)AOP-切面類的註解處理

一、概述

    Aop是面向接口的,也便是面向方法的,實現是在IOC的基礎上,Aop能夠攔截指定的方法而且對方法加強,並且無需侵入到業務代碼中,使業務與非業務處理邏輯分離,好比Spring的事務,經過事務的註解配置,Spring會自動在業務方法中開啓、提交業務,而且在業務處理失敗時,執行相應的回滾策略,aop的實現主要包括了兩個部分:java

  • 匹配符合條件的方法(Pointcut)spring

  • 對匹配的方法加強(JDK代理cglib代理)shell

    spring針對xml配置和配置自動代理的Advisor有很大的處理差異,在IOC中主要是基於XML配置分析的,在AOP的源碼解讀中,則主要從自動代理的方式解析,分析完註解的方式,再分析基於xml的方式。express

二、案例準備

    下面是spring aop的用法 也是用於源碼分析的案例緩存

    切面類:TracesRecordAdvisorbash

@Aspect
@Component
public class TracesRecordAdvisor {
    
    @Pointcut("execution(* spring.action.expend.aop.services.*.*(..))")
    public void expression() {
    }

    @Before("expression()")
    public void beforePrint() {
        System.out.println("進入服務,記錄日誌開始....");
    }

    @AfterReturning("expression()")
    public void afterPrint() {
        System.out.println("退出服務,記錄日誌退出.....");
    }
}

複製代碼

    xml配置: aop的註解啓用只須要在xml中配置這段代碼便可,這個是做爲入口app

<aop:aspectj-autoproxy/>
複製代碼

    服務類:PayServiceImpl 使用jdk代理 因此要有一個接口ide

@Service
public class PayServiceImpl implements PayService {
    public void payMoneyService() {
        System.out.println("付款服務正在進行...");
    }
}
複製代碼

    測試方法:函數

@Test
    public void springAopTestService() {
        ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring-aop.xml");
        PayService payService= (PayService) applicationContext.getBean("payServiceImpl");
        payService.payMoneyService();
    }
複製代碼

    執行結果:源碼分析

進入服務,記錄日誌開始....
付款服務正在進行...
退出服務,記錄日誌退出.....
複製代碼

    從上面的執行結果看,payMoneyService方法的確是被加強了。

三、BeanFactoryPostProcessor

    在讀spring源碼時,我想首先來看下BeanFactoryPostProcessorBeanPostProcess,這兩個接口都是在spring經過配置文件或者xml獲取bean聲明生成完BeanDefinition後容許咱們對生成BeanDefinition進行再次包裝的入口。

首先看下BeanFactoryPostProcessor的定義

public interface BeanFactoryPostProcessor {
	void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory); } 複製代碼

    方法postProcessBeanFactory的參數爲ConfigurableListableBeanFactory,前文說過beanFactory用來獲取bean的,而ConfigurableListableBeanFactory繼承接口SingletonBeanRegistryBeanFactroy,因此能夠訪問到已經生成過的BeanDefinitions集合,若是某個類實現該接口,spring會註冊這個類,而後執行這個類的postProcessBeanFactory方法,以便咱們對BeanDefinition進行擴展。

對於BeanFactoryPostProcessor只作簡單的介紹,只是說明在Spring中,咱們能夠修改生成後的BeanDefinition,這裏住下看下Spring是如何註冊BeanFactoryPostProcessor並執行postProcessBeanFactory的。

@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			prepareRefresh();
             //核心方法1
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
			prepareBeanFactory(beanFactory);
			try {
				postProcessBeanFactory(beanFactory);
                 //核心方法2 執行BeanFactoryPostProcessor
				invokeBeanFactoryPostProcessors(beanFactory);
				//核心方法 3 註冊BeanPostProcessor
				registerBeanPostProcessors(beanFactory);
				// Initialize message source for this context.
				initMessageSource();
				// Initialize event multicaster for this context.
				initApplicationEventMulticaster();
				// Initialize other special beans in specific context subclasses.
				onRefresh();
				// Check for listener beans and register them.
				registerListeners();
				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);
				// Last step: publish corresponding event.
				finishRefresh();
            }
			catch (BeansException ex) {
                 ............
				throw ex;
			}
			finally {
                 ............
				resetCommonCaches();
			}
		}
	}
複製代碼

    核心方法1obtainFreshBeanFactory就是前兩篇所說的生成BeanDefinition的入口,invokeBeanFactoryPostProcessors核心方法2就是執行BeanFactoryPostProcessor接口的方法。

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
		PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
	}
複製代碼

    經過方法getBeanFactoryPostProcessors獲取註冊BeanFactoryPostProcessor,而後來看看如何添加一個處理器

@Override
	public void addBeanFactoryPostProcessor(BeanFactoryPostProcessor beanFactoryPostProcessor) {
		this.beanFactoryPostProcessors.add(beanFactoryPostProcessor);
	}
複製代碼

    對於方法invokeBeanFactoryPostProcessors再也不往下看了,裏面的方法大體先對BeanFactoryPostProcessor進行排序,排序的標準是是否實現了PriorityOrdered,而後根據設置的order大小指定執行順序,生成一個排序集合和一個普通的集合,最後執行invokeBeanFactoryPostProcessors

private static void invokeBeanFactoryPostProcessors( Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {

		for (BeanFactoryPostProcessor postProcessor : postProcessors) {
		     //執行到自定義的BeanFactoryPostProcessor
			postProcessor.postProcessBeanFactory(beanFactory);
		}
	}
複製代碼

    這個方法就會循環先前註冊的BeanFactoryPostProcessor集合,而後執行postProcessBeanFactory

四、BeanPostProcess

    與BeanFactoryPostProcessor相比,BeanPostProcess就重要得多了,由於Spring的註解、AOP等都是經過這個接口的方法攔截執行的,它貫穿了Bean建立過程的整個生命週期,在IOC階段,Spring只註冊BeanPostProcess,執行則放到了Bean的實例化建立階段。

首先看下BeanPostProcessor的接口定義

public interface BeanPostProcessor {
    //在bean建立 屬性賦值以後 Aware接口執行以後執行
	Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
    //在init-method afterPropertiesSet 執行以後執行
	Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

複製代碼

    在bean的聲明週期中,下面的序列是bean建立後要執行的接口和方法順序:

實例化(autowireConstructor或者instantiateBean)---------------屬性初始化(populateBean)-------------Aware接

口(若是bean實現了的話)--------------------------BeanPostProcess.postProcessBeforeInitialization------------------

--PostConstructInitializingBean.afterPropertiesSet-----BeanPostProcess.postProcessAfterInitialization

    其中經過註解引入依賴的方式就是在AutowiredAnnotationBeanPostProcessor這個類中實現的,而接下來要分析的Spring Aop也是從這裏開始的,這個類叫AnnotationAwareAspectJAutoProxyCreator,

五、NameSpaceHanlder

    在Spring中,任何的技術都是在IOC的基礎上進行的,Aop也不例外,程序會首先讀取xml配置文件,而後對讀取到的標籤先查找命名空間,而後找對應的NameSpaceHandler,最終調用parse方法解析標籤。

    aop標籤的解析,使用純註解的方式aop:aspectj-autoproxy和使用aop:config的配置解析不太同樣,具體表如今生成PointCut和生成BeforeAfterAround等切面類時,使用aop:config的方式會爲這些註解生成一個BeanDefinition,而這個BeanDefinition的構造函數是由3個BeanDefinition組成,代表這個類是合成類,即synthetic這個屬性爲true。而後跟解析普通的bean同樣,生成這些實例對象,後面的過程就跟是用純註解的方式相同了,接下來的分析是基於純註解分析的,也就是解析從解析aop:aspectj-autoproxy這個標籤開始。

    前面的xml文件的標籤解析是經過parseDefaultElement方法解析默認的<bean>標籤的,而咱們在配置文件裏面配置了啓動自動代理的方式<aop:aspectj-autoproxy/>,當Spring讀取到這個標籤,則會走parseCustomElement(root)這個方法了,這個方法的源碼再也不解析,主要完成的功能以下:

  • 獲取element的nameSpaceUri,根據nameSpaceUri找到NameSpaceHanlder

  • 調用NameSpaceHanlder的parse方法解析element

    下面是NameSpaceHanlder接口的定義

public interface NamespaceHandler {
	void init();
	BeanDefinition parse(Element element, ParserContext parserContext);
	BeanDefinitionHolder decorate(Node source, BeanDefinitionHolder definition, ParserContext parserContext);
}

複製代碼

    這裏面的init方法是咱們初始化操做的,這裏能夠完成對指定的標籤設置解析器,而後再parse方法裏面找到指定標籤的解析器,而後調用該解析器的parse方法解析標籤,後面會重點看這兩個方法。

    再來看下Spring如何加載NameSpaceHanlder的,Spring首先會取查找項目空間下目錄META-INF/的全部spring.handlers文件,這個文件是在Spring依賴的jar下面,在覈心jar包都會由這個文件,aop的jar包路徑下文件內容爲:spring.handlers

http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler
複製代碼

    發現這裏面存儲的是一個key,value,key是aop的nameSpaceUri,value是AopNamespaceHandler,從這個類名上就能發現該類實現了NamespaceHandler,確定也就實現了init和parse方法,因此解析<aop:aspectj-autoproxy/>的任務就由AopNamespaceHandler的parse完成。

查看AopNamespaceHandler的init方法

@Override
	public void init() {
		// In 2.0 XSD as well as in 2.1 XSD.
		registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
		registerBeanDefinitionParser("aspectj-autoproxy", new                       AspectJAutoProxyBeanDefinitionParser());
		registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
		// Only in 2.0 XSD: moved to context namespace as of 2.1
		registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
	}
複製代碼

上面的代碼就很清晰了,<aop:config>標籤由ConfigBeanDefinitionParser處理,<aop:aspectj-autoproxy/>則由AspectJAutoProxyBeanDefinitionParser這個類處理,這兩種處理其實對應了自動代理和經過xml配置的處理方式,而後會調用AspectJAutoProxyBeanDefinitionParserparse方法

@Override
	public BeanDefinition parse(Element element, ParserContext parserContext) {
		AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
		extendBeanDefinition(element, parserContext);
		return null;
	}
複製代碼

    這個方法其實就是爲了註冊一個AnnotationAwareAspectJAutoProxyCreator類,而後AOP的全部處理邏輯都會交給這個類處理,因爲這個類的實現了BeanPostProcessor,因此這個類的入口就是BeanPostProcessor接口的兩個方法:

  • postProcessBeforeInitialization
  • postProcessAfterInitialization

六、Spring Aop 源碼解讀前奏

    上面分析了,當spring讀取xml文件遇到<aop:aspectj-autoproxy/>會找到AopNamespaceHandler這個處理類,而後這個類又將這個標籤委託給了AspectJAutoProxyBeanDefinitionParser類,最終調用這個類得parse方法,parse方法未作分析,其實這個方法的目的很簡單,就是註冊AnnotationAwareAspectJAutoProxyCreator這個類,這個類實現了BeanPostProcessorInstantiationAwareBeanPostProcessor接口,最終在實例化bean對象也就是執行BeanFactory.getBean(beanName)的過程當中,會調用這兩個接口的方法(執行順序以下):

InstantiationAwareBeanPostProcessor先執行:

postProcessBeforeInstantiation(Class<?> beanClass, String beanName)

postProcessAfterInstantiation(Object bean, String beanName)

BeanPostProcessor再執行:

postProcessBeforeInitialization(Object bean, String beanName)

Object postProcessAfterInitialization(Object bean, String beanName)

    AOP的實現基本上是在這兩個方法中進行的,因此就從這裏來看Spring是如何實現AOP的,Spring的AOP代理目前支持方法的加強,看源碼目前好像也支持了屬性的加強了。

    讀取源碼前首先來分析一下方法加強的原理,有助於咱們讀取源碼時牢牢抓住主線。首先第一個問題,若是咱們想對一個類的方法進行加強,咱們應該怎麼作呢?

    這種業務需求能夠經過代理實現,在方法執行前,攔截這個方法,而且加入要執行加強的邏輯,最後再執行目標方法。下面是Spring用的兩種代理方式:

JDK代理:咱們能夠通Proxy類獲取一個目標類的代理對象,但JDK代理要求被代理的類必須實現接口,因此是基於接口的代理。

cglib代理:若是目標類沒有接口,使用cglib代理,是由asm封裝的,直接操做類得字節碼,效率也很高。

    因爲在生產業務中,咱們不可能對全部的類都執行加強,因此還須要一個選擇器,將符合條件的bean進行加強,Spring使用了PointCut接口,經過該接口的getMethodMatcher方法獲取一個方法匹配器,而後經過matches方法匹配到目標類對象的目標方法執行加強操做。mathcer匹配規則就是經過Spring 配置的expression表達式了。

    因此在分析源碼的時,要圍繞這兩方面進行:

  • 匹配切點方法(構建切入點表達式類和切面類)

  • 建立代理對象

    這兩方面在Spring的實現裏很是複雜,尤爲是第一步匹配切點方法過程,這個過程當中,Spring會將@Aspect註解類的@Before@After@Around@Pointcut等註解都封裝成待執行的切面方法類,而後經過方法匹配器匹配到的要加強的方法先後執行切面方法類,達到方法加強的目的。

    第二階段,建立代理對象默認是經過JDK代理實現配置,<aop:aspectj-autoproxy proxy-target-class="true">這樣配置能夠指定使用cglib代理。

七、註解切面代理類

    上面分析了真正實現AOP功能的是AnnotationAwareAspectJAutoProxyCreator,因爲這個類實現了BeanPostProcessorInstantiationAwareBeanPostProcessor,因此在建立一個bean的時候,會進入到這兩個接口的方法,這兩個接口包含了四個方法,方法執行順序上面已經分析過了,來看看這個類的類圖:

    類圖上比較重要的接口就是右上角實現的兩個接口,在bean建立的生命週期過程當中,會校驗當前容器中是否註冊了實現了這兩個接口的類,若是有則調用接口的方法,前面的分析中在解析<aop:aspectj-autoproxy/>時,將這個類註冊到了容器中,並且上面也羅列了這兩個接口中四個方法的調用順序,在這個類中完成主要功能的2個方法及其執行順序:

InstantiationAwareBeanPostProcessor先執行:

postProcessBeforeInstantiation(Class<?> beanClass, String beanName)

BeanPostProcessor再執行:

Object postProcessAfterInitialization(Object bean, String beanName)

    postProcessBeforeInstantiation方法主要是找出註解了Advice的類,並將Advice的類使用了@Before@After@Around@Pointcut,@AfterThrowing等註解的方法封裝成一個一個類放入到緩存中供匹配到的類生成代理用。postProcessAfterInitialization主要是匹配符合條件的目標類對象,而後生成代理的過程,接下來就按順序分析這兩個方法完成的功能。

八、處理Aspect註解類

public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        //構建一個緩存key
		Object cacheKey = getCacheKey(beanClass, beanName);
		if (beanName == null || !this.targetSourcedBeans.contains(beanName)) {
             //若是當前beanClass的緩存key 存在於Class爲Advise的緩存中,表示當前的beanClass是Adivse類
             //並且不須要生成代理。
			if (this.advisedBeans.containsKey(cacheKey)) {
				return null;
			}
             //核心校驗:1 當前類是不是AOP的基礎類 二、當前類是否應該跳過不生成代理
			if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
				this.advisedBeans.put(cacheKey, Boolean.FALSE);
				return null;
			}
		}
        //這部分主要是用於實現了TargetSource接口的bean,而後從getTarget中獲取對象 建立代理
		if (beanName != null) {
			TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
			if (targetSource != null) {
				this.targetSourcedBeans.add(beanName);
				Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
				Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
				this.proxyTypes.put(cacheKey, proxy.getClass());
				return proxy;
			}
		}
		return null;
	}
複製代碼

    這個方法主要是先爲beanClass生成一個緩存的key,這個beanClass若是是FactoryBean,則按照工廠類的命名規則命名,不然用beanName命名,而後用剛纔生成的key判斷beanClass是否已經存在於Advice的緩存集合中,若是已經存在則表明該類是切面類並且已經被處理過了,後續處理不會爲該類生成代理,若是沒有沒處理過,則會調用下面的方法校驗該類是不是AOP的基礎類 ,總之這個方法做用就是將AOP相關操做的切面類和基礎類放入到緩存中,當爲bean生成代理的時候,忽略advice緩存中的AOP切面類和基礎類,下面是具體校驗過程:

AnnotationAwareAspectJAutoProxyCreator重寫了該方法

@Override
protected boolean isInfrastructureClass(Class<?> beanClass) {
//調用父類的isInfrastructureClass判斷是不是aop基礎類
//校驗當前類是否使用@Aspect註解
return (super.isInfrastructureClass(beanClass) || this.aspectJAdvisorFactory.isAspect(beanClass));
}
複製代碼

父類的isInfrastructureClass方法

protected boolean isInfrastructureClass(Class<?> beanClass) {
		boolean retVal = Advice.class.isAssignableFrom(beanClass) ||
				Advisor.class.isAssignableFrom(beanClass) ||
				AopInfrastructureBean.class.isAssignableFrom(beanClass);
		if (retVal && logger.isTraceEnabled()) {
			logger.trace("Did not attempt to auto-proxy infrastructure class [" + beanClass.getName() + "]");
		}
		return retVal;
	}
複製代碼

    裏面isAssignableFrom表示當前類是否容許被設置爲beanClass類對象,能夠以此判斷beanClass是不是Advice類,因此這個方法的校驗目的就是判斷當前正在建立目標類是不是AOP的基礎類,即該類是不是Advice,Advisor或者實現了AopInfrastructureBean接口。該方法調用父類的isInfrastructureClass判斷是不是aop基礎類,而後再校驗當前類是否使用@Aspect註解,目的只有一個,若是是Advice切面相關的類不作任何處理,直接放入advice緩存便可。

而後再來看shouldSkip(beanClass, beanName)

@Override
	protected boolean shouldSkip(Class<?> beanClass, String beanName) {
		//查找當前已經生成的全部Advisor切面類 不展開分析
		List<Advisor> candidateAdvisors = findCandidateAdvisors();
		for (Advisor advisor : candidateAdvisors) {
			if (advisor instanceof AspectJPointcutAdvisor) {
				if (((AbstractAspectJAdvice) advisor.getAdvice()).getAspectName().equals(beanName)) {
					return true;
				}
			}
		}
		return super.shouldSkip(beanClass, beanName);
	}
複製代碼

    這個方法主要是校驗當前正在建立bean的beanName是否屬於已經建立好的切面類緩存中,若是是則加入到advices緩存中,再也不處理。其中findCandidateAdvisors()會查找當前容器中生成的全部實現了Advisor的類,Spring會將@Before@After@Around等生成一個繼承了Advisor類對象存儲到緩存中供後續使用,這一部分時Spring AOP前半段的核心內容,後續都會圍繞着如何將切面類的註解生成Adisor類探索。

AnnotationAwareAspectJAutoProxyCreator重寫了findCandidateAdvisors方法,因此會執行到該方法:

@Override
protected List<Advisor> findCandidateAdvisors() {
   //經過父類的方法查找全部容器中的Advisor類,也就是基於xml配置的<aop:before/>生成的
   List<Advisor> advisors = super.findCandidateAdvisors();
   //查找經過註解的方式生成Advisor類
   advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
   return advisors;
}
複製代碼

    這個方法會首先調用父類的findCandidateAdvisors方法用於獲取經過xml文件配置生成的Advisor,也就是經過<aop:before>,<aop:after>等生成的,而後調用經過註解方式即@Before@After@Around@Pointcut,@AfterThrowing生成的advisor,能夠說,這兩個方法分別處理了基於xml配置文件的方式和基於註解的配置方式,由於全部的分析都是基於AnnotationAwareAspectJAutoProxyCreator這個類進行的,因此在這個地方會先獲取配置文件的,再生成基於註解類的Advisor,這樣就將基於xml配置的和基於註解的配置都會解析到。

看下this.aspectJAdvisorsBuilder.buildAspectJAdvisors()

public List<Advisor> buildAspectJAdvisors() {
		List<String> aspectNames = null;
		synchronized (this) {
			aspectNames = this.aspectBeanNames;
			if (aspectNames == null) {
				List<Advisor> advisors = new LinkedList<Advisor>();
				aspectNames = new LinkedList<String>();
                //從beanDefinitions中獲取全部的beanName
				String[] beanNames =
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);
				for (String beanName : beanNames) {
                      //若是beanName不符合配置的 <aop:include name="***"/>
                      //忽略這個bean上全部的切面方法
					if (!isEligibleBean(beanName)) {
						continue;
					}
					Class<?> beanType = this.beanFactory.getType(beanName);
					if (beanType == null) {
						continue;
					}
                      //若是當前beanType是一個切面類 則將該切面類相關信息封裝起來
					if (this.advisorFactory.isAspect(beanType)) {
						aspectNames.add(beanName);
						AspectMetadata amd = new AspectMetadata(beanType, beanName);
						if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {                   
//將切面信息放入到分裝到MetadataAwareAspectInstanceFactory 生成一個AspectMetadata
MetadataAwareAspectInstanceFactory factory =new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
//獲取容器中全部Advisor類 須要進入這個方法詳細分析
List<Advisor> classAdvisors=this.advisorFactory.getAdvisors(factory);
							if (this.beanFactory.isSingleton(beanName)) {
                                   //單例加入緩存
								this.advisorsCache.put(beanName, classAdvisors);
							}
							else {
                                   //非單例 將工廠加入緩存
								this.aspectFactoryCache.put(beanName, factory);
                            }
							advisors.addAll(classAdvisors);
						}
						else {
							// 非單例 將生成Advisor的工廠類加入到緩存
							if (this.beanFactory.isSingleton(beanName)) {
								throw new IllegalArgumentException("Bean with name '" + beanName +
										"' is a singleton, but aspect instantiation model is not singleton");
							}
							MetadataAwareAspectInstanceFactory factory =
									new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
							this.aspectFactoryCache.put(beanName, factory);
							advisors.addAll(this.advisorFactory.getAdvisors(factory));
						}
					}
				}
				this.aspectBeanNames = aspectNames;
				return advisors;
			}
		}
        ..............
	}
複製代碼

    這個方法主要的任務其實就是獲取類得類型爲Aspect的切面類,而後獲取切面類方法的全部註解並將註解轉換成Advisor類返回,主要步驟爲:

  • 獲取容器中全部的BeanDefinition的beanName

  • 根據beanName,或者beanClass,匹配符合規則的Aspect切面類,經過<aop:include>配置的規則

  • 獲取Aspect切面類的全部切面方法封裝成Advisor對象返回。

  • 將獲取到的全部Advisor放入到緩存中。

    這個方法代碼雖然不少,可是核心的是this.advisorFactory.getAdvisors(factory),即第三個步驟,這個方法將會獲取到切面類的全部切面方法,並封裝成AdvisorgetAdvisors是一個接口,ReflectiveAspectJAdvisorFactory實現了這個接口,下面代碼是其實現邏輯:

@Override
	public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
         //獲取切面類Class
		Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
        //獲取切面類的beanName
		String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
		validate(aspectClass);
	    //進一步對AspectMetadata封裝 裏面包含了切面類的信息
		MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
				new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
		List<Advisor> advisors = new LinkedList<Advisor>();
         //獲取切面類中沒有使用Pointcut註解的方法
		for (Method method : getAdvisorMethods(aspectClass)) {
             //檢查該方法是不是切面方法, 若是是成Advisor類返回
			Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
			if (advisor != null) {
				advisors.add(advisor);
			}
		}
		//若是沒有切面方法 設置一個空的
		if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
			Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
			advisors.add(0, instantiationAdvisor);
		}
		//處理屬性字段 Spring支持到了屬性的加強
		for (Field field : aspectClass.getDeclaredFields()) {
			Advisor advisor = getDeclareParentsAdvisor(field);
			if (advisor != null) {
				advisors.add(advisor);
			}
		}
		return advisors;
	}
複製代碼

    這個方法首先已經將切面類信息封裝到AspectMetadata的類再次封裝到MetadataAwareAspectInstanceFactory,而後獲取切面類的全部沒有使用Pointcut註解的方法,調用getAdvisor獲取這個方法使用的切面註解,生成對應的Advisor類。 至於PointCut的處理則是再後面的getAdvisor中處理的。

九、獲取切面類的Advisor

    獲取Advisor類的方法爲getAdvisor,首先來看下這個方法的參數:

//切面類的切面方法 這裏可能就是 beforePrint()
Method  candidateAdviceMethod 
//獲取AspectMetadata的實例工廠(能夠獲取切面的類全部信息)
MetadataAwareAspectInstanceFactory aspectInstanceFactory
//切面的排序
int declarationOrderInAspect
//切面類的beanName 這裏是tracesRecordAdvisor
 String aspectName
複製代碼

    上面的參數中能夠獲取到切面類和切面方法,這樣就能夠得到一個Advisor對象,而後還須要一個切入點表達式PointCut用來匹配符合條件的方法,攔截到目標方法後,就能夠執行Adivsor加強方法了。 來看看建立Advisor的過程,這裏假設MethodTracesRecordAdvisor類的beforePrint方法,也就是咱們測試案例中建立使用了@Before註解的切面方法:

@Override
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrderInAspect, String aspectName) {

   validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
   //獲取pointCut,這裏實際上得到的是 expression()這個方法對應了pointCut的內容
   AspectJExpressionPointcut expressionPointcut = getPointcut(
         candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
   if (expressionPointcut == null) {
      return null;
   }
    //建立advisor
   return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
         this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}
複製代碼

    看看getPointCut方法如何獲取到exression過程須要嵌套不少步驟,這裏不展開了,簡單看下如何將查找到的值設置到表達式中的:

private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
   //
   AspectJAnnotation<?> aspectJAnnotation =
         AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
   if (aspectJAnnotation == null) {
      return null;
   }

   AspectJExpressionPointcut ajexp =
         new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);
   //將上面生成的AspectJAnnotation 解析出的expression方法放入到表達式中
   //
   ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
   return ajexp;
}
複製代碼

    這裏須要關注下上面的findAspectJAnnotationOnMethod方法:

protected static AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) {
    //看到了咱們熟悉的切面方法註解,這裏的beforePrint使用@Before註解
   Class<?>[] classesToLookFor = new Class<?>[] {
         Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class};
   for (Class<?> c : classesToLookFor) {
      AspectJAnnotation<?> foundAnnotation = findAnnotation(method, (Class<Annotation>) c);
      if (foundAnnotation != null) {
         return foundAnnotation;
      }
   }
   return null;
}
複製代碼

    這個方法就是查找切面方法是否使用了Before, Around, AfterAfterReturning, AfterThrowing,Pointcut註解,若是使用了,則返回一個AspectJAnnotation對象,裏面有一個annotation的泛型對象,這個泛型對象就是被設置爲這些註解的值,並且還會得到這些註解裏面配置的pointcut表達式內容,若是是引用的表達式方法,則將方法參數設置到pointcutExpression這個屬性中。

    解析完切面方法的註解後如今再回過頭來看看如何建立一個advisor實例:

public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut, Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
   this.declaredPointcut = declaredPointcut;
   this.aspectJAdviceMethod = aspectJAdviceMethod;
   this.aspectJAdvisorFactory = aspectJAdvisorFactory;
   this.aspectInstanceFactory = aspectInstanceFactory;
   this.declarationOrder = declarationOrder;
   this.aspectName = aspectName;
    //切面類是不是懶加載
   if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
      // Static part of the pointcut is a lazy type.
      Pointcut preInstantiationPointcut = Pointcuts.union(
            aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);

      // Make it dynamic: must mutate from pre-instantiation to post-instantiation state.
      // If it's not a dynamic pointcut, it may be optimized out
      // by the Spring AOP infrastructure after the first evaluation.
      this.pointcut = new PerTargetInstantiationModelPointcut(
            this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory);
      this.lazy = true;
   }
   else {
      this.pointcut = this.declaredPointcut;
      this.lazy = false;
       //最終會執行到這裏獲取一個advice
      this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
   }
}
複製代碼

十、爲切面方法建立Advice

    上面方法的最後一句instantiateAdvice(this.declaredPointcut)會建立一個advice,具體是調用getAdvice方法獲取:

@Override
	public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
         //獲取切面類對象,這裏是TracesRecordAdvisor
		Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
		validate(candidateAspectClass);
          //核心點1:獲取切面註解,這裏得方法是 beforePrint 使用了@Before註解
		AspectJAnnotation<?> aspectJAnnotation =
				AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
		if (aspectJAnnotation == null) {
			return null;
		}
            ....................
		AbstractAspectJAdvice springAdvice;
        //核心點2:根據註解轉換後的 將註解生成不一樣的Advice類。
		switch (aspectJAnnotation.getAnnotationType()) {
			case AtBefore:
				springAdvice = new AspectJMethodBeforeAdvice(
						candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
				break;
			case AtAfter:
				springAdvice = new AspectJAfterAdvice(
						candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
				break;
			case AtAfterReturning:
				springAdvice = new AspectJAfterReturningAdvice(
						candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
				AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
				if (StringUtils.hasText(afterReturningAnnotation.returning())) {
					springAdvice.setReturningName(afterReturningAnnotation.returning());
				}
				break;
			case AtAfterThrowing:
				springAdvice = new AspectJAfterThrowingAdvice(
						candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
				AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
				if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
					springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
				}
				break;
			case AtAround:
				springAdvice = new AspectJAroundAdvice(
						candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
				break;
			case AtPointcut:
                //這裏對PointCut不作處理
				if (logger.isDebugEnabled()) {
					logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
				}
				return null;
			default:
				throw new UnsupportedOperationException(
						"Unsupported advice type on method: " + candidateAdviceMethod);
		}

		// 將切面類信息配置到SpringAdvice中
		springAdvice.setAspectName(aspectName);
		springAdvice.setDeclarationOrder(declarationOrder);
		String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
		if (argNames != null) {
			springAdvice.setArgumentNamesFromStringArray(argNames);
		}
		springAdvice.calculateArgumentBindings();
		return springAdvice;
	}
複製代碼

    首先來看看核心點1,上面其實已經看過了, 可是上面的方法做用僅僅是爲了獲取註解上的exression表達式的,這裏再調用一遍就是爲註解生成Advice類的,目的就是獲取切面註解與AspectJAnnotation的映射類。

protected static AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) {
    //看到了咱們熟悉的切面方法註解,這裏的beforePrint使用@Before註解
   Class<?>[] classesToLookFor = new Class<?>[] {
         Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class};
   for (Class<?> c : classesToLookFor) {
      AspectJAnnotation<?> foundAnnotation = findAnnotation(method, (Class<Annotation>) c);
      if (foundAnnotation != null) {
         return foundAnnotation;
      }
   }
   return null;
}
複製代碼

    這個方法就是查找切面方法是否實現了Before, Around, AfterAfterReturning, AfterThrowing,Pointcut註解,若是實現了,則返回一個AspectJAnnotation對象,裏面有一個annotation的泛型對象,這個泛型對象就是被設置爲這些註解的值。最終這些對象會被轉換成下面的對象存入AspectJAnnotation中:

static {
   //會將註解轉換成後面的AspectJAnnotationType枚舉的類。 
   annotationTypes.put(Pointcut.class,AspectJAnnotationType.AtPointcut);
   annotationTypes.put(After.class,AspectJAnnotationType.AtAfter);
   annotationTypes.put(AfterReturning.class,AspectJAnnotationType.AtAfterReturning);
   annotationTypes.put(AfterThrowing.class,AspectJAnnotationType.AtAfterThrowing);
   annotationTypes.put(Around.class,AspectJAnnotationType.AtAround);
   annotationTypes.put(Before.class,AspectJAnnotationType.AtBefore);
}
複製代碼

    經過核心點1,Spring已經將註解@Before對應轉換爲AtBefore@After轉換成AtAfter,以此類推,都會一一映射到了核心點2的switch的條件類了,在覈心點2中,會爲對應的切面註解類生成Advice類。 全部的註解切面類具體實現都是由AbstractAspectJAdvice這個抽象類實現的,這個類的構造函數有三個參數:

//切面方法 這裏多是beforePrint
Method aspectJAroundAdviceMethod
//切入點表達式匹配器 這裏指封裝了exression的匹配器
AspectJExpressionPointcut pointcut
//切面類 這裏指TracesRecordAdvisor
AspectInstanceFactory aif
複製代碼

    下面是Spring爲對應註解生成對應的Advice類

註解類 Advice 顧問方法
AtBefore AspectJMethodBeforeAdvice
AtAfter AspectJAfterAdvice
AtAfterReturning AspectJAfterReturningAdvice
AtAfterThrowing AspectJAfterThrowingAdvice
AtAround AspectJAroundAdvice

    各個註解會在不一樣的實際執行自身加強方法,這個部分只是生成Advice類,然會放入到緩存中,等真正生成代理時就會調用這些方法。這個在建立代理的時候須要具體拆開說,至此,Spring將使用了@Aspect註解的切面類的切面方法,都轉換成了對應的Adivsor類,這個類包含了切面方法,封裝後的切點匹配器PointCut以及生成切面類的實例對象,經過這個類就能夠匹配到符合條件的目標類的目標方法,而後執行加強操做了。

    由切面註解生成的Advice類,最終會放入到一個緩存中,當生成目標bean的時候,會將全部因此可以匹配到目標bean的advice放入到集合中,由一個實現了MethodInvocation的類統一管理調用過程,這個類後面會詳細說到,這裏簡單分析下AspectJAfterAdvice的invoke方法,看看它的調用過程

@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		try {
             //調用是實現了MethodInvocation方法的類 這個實際上是個鏈式調用
			return mi.proceed();
		}
		finally {
            //最終執行後置加強方法
			invokeAdviceMethod(getJoinPointMatch(), null, null);
		}
	}
複製代碼

    上面的invoke方法須要一個MethodInvocation的參數,上面的Advice類除了AspectJMethodBeforeAdvice以外,都實現了這個接口,因此能夠實現鏈式調用,這個邏輯會在建立代理的具體講解,這裏只是簡單分析下,這些adviceinvoke方法規定了切面方法於要加強方法的執行時機。

十一、AOP代理初窺

    上面一部分操做主要是處理使用了@Aspect註解的切面類,而後將切面類的全部切面方法根據使用的註解生成對應的Advisor的過程,這個Advisor包含了切面方法,切入點匹配器和切面類,也就是準好了要加強的邏輯,接下來就是要將這些邏輯注入到合適的位置進行加強,這部分的操做就是由老生常談的代理實現的了。

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
   if (bean != null) {
      Object cacheKey = getCacheKey(bean.getClass(), beanName);
       //若是要建立的類不是提早暴露的代理 則進入下面的方法
      if (!this.earlyProxyReferences.contains(cacheKey)) {
         return wrapIfNecessary(bean, beanName, cacheKey);
      }
   }
   return bean;
}
複製代碼

    建立代理前,須要先校驗bean是否須要建立代理

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    //若是bean是經過TargetSource接口獲取 則直接返回
   if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
      return bean;
   }
    //若是bean是切面類 直接返回
   if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
      return bean;
   }
    //若是bean是Aspect 並且容許跳過建立代理, 加入advise緩存 返回
   if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
      this.advisedBeans.put(cacheKey, Boolean.FALSE);
      return bean;
   }
   //若是前面生成的advisor緩存中存在可以匹配到目標類方法的Advisor 則建立代理
   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;
}
複製代碼

    方法很簡單,主要的關注點在getAdvicesAndAdvisorsForBeancreateProxy上,第一個是獲取可以匹配目標類方法的Advisor集合,若是這個集合不爲空,則表明該類須要被加強,須要生成代理,若是匹配不到,則表示該類並不須要被加強,無需建立代理。至於createProxy就很明顯了,就是建立代理,這個方法裏面決定了使用jdk代理仍是cglib代理,而且用到了前面生成的Advisor實現加強功能。 這部份內容會放到下一篇文章中專門分析。

十二、簡單總結

    簡單總結一下,Spring AOP在第一階段完成的主要任務:

    讀取配置文件階段:

  • 讀取xml文件遇到 <aop:aspectj-autoproxy/>標籤時,找到命名空間處理器AopNamespaceHandler,而後找處處理該標籤的類AspectJAutoProxyBeanDefinitionParser

  • 經過AspectJAutoProxyBeanDefinitionParserparse方法,將AspectJAwareAdvisorAutoProxyCreator註冊到容器的聲明週期中。

    建立bean階段:

  • 執行AspectJAwareAdvisorAutoProxyCreatorpostProcessBeforeInstantiation校驗目標類是不是Aspect類和AOP基礎類以及是否須要跳過不須要執行代理的類

  • 獲取beanDefinitions中全部使用了Aspect註解的類,而後將切面方法根據使用的註解生成Advisor類放入到緩存(關鍵)

  • 調用AspectJAwareAdvisorAutoProxyCreatorpostProcessAfterInitialization的方法,對須要加強的類建立代理。

    這個就是Spring AOP在這個階段所完成的工做,下一部分將專門針對Spring如何實現jdk和cglib代理分析。

相關文章
相關標籤/搜索