1、概述
????Aop是面向接口的,也即是面向方法的,實現是在IOC的基礎上,Aop可以攔截指定的方法并且對方法增強,而且無需侵入到業務代碼中,使業務與非業務處理邏輯分離,比如Spring的事務,通過事務的注解配置,Spring會自動在業務方法中開啟、提交業務,并且在業務處理失敗時,執行相應的回滾策略,aop的實現主要包括了兩個部分:
匹配符合條件的方法(Pointcut)
對匹配的方法增強(JDK代理cglib代理)
????spring針對xml配置和配置自動代理的Advisor有很大的處理差別,在IOC中主要是基于XML配置分析的,在AOP的源碼解讀中,則主要從自動代理的方式解析,分析完注解的方式,再分析基于xml的方式。
2、案例準備
????下面是spring aop的用法 也是用于源碼分析的案例
????切面類:TracesRecordAdvisor
@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中配置這段代碼即可,這個是作為入口
<aop:aspectj-autoproxy/>
????服務類:PayServiceImpl
使用jdk代理 所以要有一個接口
@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方法的確是被增強了。
3、BeanFactoryPostProcessor
????在讀spring源碼時,我想首先來看下BeanFactoryPostProcessor
和BeanPostProcess
,這兩個接口都是在spring通過配置文件或者xml獲取bean聲明生成完BeanDefinition
后允許我們對生成BeanDefinition
進行再次包裝的入口。
首先看下BeanFactoryPostProcessor
的定義
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory);
}
????方法postProcessBeanFactory
的參數為ConfigurableListableBeanFactory
,前文說過beanFactory
用來獲取bean的,而ConfigurableListableBeanFactory
繼承接口SingletonBeanRegistry
和BeanFactroy
,所以可以訪問到已經生成過的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
。
4、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
,
5、NameSpaceHanlder
????在Spring中,任何的技術都是在IOC的基礎上進行的,Aop也不例外,程序會首先讀取xml配置文件,然后對讀取到的標簽先查找命名空間,然后找對應的NameSpaceHandler,最終調用parse方法解析標簽。
????aop標簽的解析,使用純注解的方式aop:aspectj-autoproxy
和使用aop:config
的配置解析不太一樣,具體表現在生成PointCut
和生成Before
、After
、Around
等切面類時,使用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>
標簽由ConfigBeanDefinitionParse
r處理,<aop:aspectj-autoproxy/>
則由AspectJAutoProxyBeanDefinitionParser
這個類處理,這兩種處理其實對應了自動代理和通過xml配置的處理方式,然后會調用AspectJAutoProxyBeanDefinitionParser
的parse
方法
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
extendBeanDefinition(element, parserContext);
return null;
}
????這個方法其實就是為了注冊一個AnnotationAwareAspectJAutoProxyCreator
類,然后AOP的所有處理邏輯都會交給這個類處理,由于這個類的實現了BeanPostProcessor
,所以這個類的入口就是BeanPostProcessor接口的兩個方法:
- postProcessBeforeInitialization
- postProcessAfterInitialization
6、Spring Aop 源碼解讀前奏
????上面分析了,當spring讀取xml文件遇到<aop:aspectj-autoproxy/>
會找到AopNamespaceHandler
這個處理類,然后這個類又將這個標簽委托給了AspectJAutoProxyBeanDefinitionParser
類,最終調用這個類得parse方法,parse
方法未做分析,其實這個方法的目的很簡單,就是注冊AnnotationAwareAspectJAutoProxyCreator
這個類,這個類實現了BeanPostProcessor
和InstantiationAwareBeanPostProcessor
接口,最終在實例化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代理。
7、注解切面代理類
????上面分析了真正實現AOP功能的是AnnotationAwareAspectJAutoProxyCreator
,由于這個類實現了BeanPostProcessor
和InstantiationAwareBeanPostProcessor
,所以在創建一個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
主要是匹配符合條件的目標類對象,然后生成代理的過程,接下來就按順序分析這兩個方法完成的功能。
8、處理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的基礎類 2、當前類是否應該跳過不生成代理
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)
,即第三個步驟,這個方法將會獲取到切面類的所有切面方法,并封裝成Advisor
,getAdvisors
是一個接口,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
中處理的。
9、獲取切面類的Advisor
????獲取Advisor
類的方法為getAdvisor
,首先來看下這個方法的參數:
//切面類的切面方法 這里可能就是 beforePrint()
Method candidateAdviceMethod
//獲取AspectMetadata的實例工廠(可以獲取切面的類所有信息)
MetadataAwareAspectInstanceFactory aspectInstanceFactory
//切面的排序
int declarationOrderInAspect
//切面類的beanName 這里是tracesRecordAdvisor
String aspectName
????上面的參數中可以獲取到切面類和切面方法,這樣就可以獲得一個Advisor對象,然后還需要一個切入點表達式PointCut
用來匹配符合條件的方法,攔截到目標方法后,就可以執行Adivsor
增強方法了。 來看看創建Advisor
的過程,這里假設Method
是TracesRecordAdvisor
類的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
, After
,AfterReturning
, 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);
}
}
10、為切面方法創建Advice
????上面方法的最后一句instantiateAdvice(this.declaredPointcut)
會創建一個advice
,具體是調用getAdvice
方法獲?。?/p>
@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
, After
,AfterReturning
, 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
之外,都實現了這個接口,所以可以實現鏈式調用,這個邏輯會在創建代理的具體講解,這里只是簡單分析下,這些advice
的invoke
方法規定了切面方法于要增強方法的執行時機。
11、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;
}
????方法很簡單,主要的關注點在getAdvicesAndAdvisorsForBean
和createProxy
上,第一個是獲取能夠匹配目標類方法的Advisor
集合,如果這個集合不為空,則代表該類需要被增強,需要生成代理,如果匹配不到,則表示該類并不需要被增強,無需創建代理。至于createProxy
就很明顯了,就是創建代理,這個方法里面決定了使用jdk代理還是cglib代理,并且用到了前面生成的Advisor實現增強功能。 這部分內容會放到下一篇文章中專門分析。
12、簡單總結
????簡單總結一下,Spring AOP在第一階段完成的主要任務:
????讀取配置文件階段:
讀取xml文件遇到
<aop:aspectj-autoproxy/>
標簽時,找到命名空間處理器AopNamespaceHandler
,然后找到處理該標簽的類AspectJAutoProxyBeanDefinitionParser
通過
AspectJAutoProxyBeanDefinitionParser
的parse
方法,將AspectJAwareAdvisorAutoProxyCreator
注冊到容器的聲明周期中。
????創建bean階段:
執行
AspectJAwareAdvisorAutoProxyCreator
的postProcessBeforeInstantiation
校驗目標類是否是Aspect
類和AOP基礎類以及是否需要跳過不需要執行代理的類獲取
beanDefinitions
中所有使用了Aspect注解的類,然后將切面方法根據使用的注解生成Advisor類放入到緩存(關鍵)調用
AspectJAwareAdvisorAutoProxyCreator
的postProcessAfterInitialization
的方法,對需要增強的類創建代理。
????這個就是Spring AOP在這個階段所完成的工作,下一部分將專門針對Spring如何實現jdk和cglib代理分析。