Spring AOP源碼分析(一)

1 Spring AOP源碼分析(一)

關於Spring AOP的幾個重要概念:java

1.鏈接點(Joinpoint)node

​ 衆所周知,AOP是做用在方法級別上的,因此說對於鏈接點,簡單點理解,就是應用程序中類的方法(普通方法和構造方法)。目的是用於方法的執行。spring

2.切點(Pointcut)express

​ 對於切點也就是須要攔截的方法,也就是在哪裏執行。它是一段表達式,一般包含四種:方法切點函數(execution()@annotation())、方法入參切點函數(args()@args())、目標類切點函數(within()target()@within()@target())、代理類切點函數(this())。app

3.通知(Advice)ide

​ 通知表示了執行的順序以及具體的執行邏輯,也就是在何時執行以及作什麼。通知目前有五種:前置通知、後置通知、環繞通知、後置返回通知、後置異常通知。函數

4.切面(Advisor)源碼分析

​ 切面是由切點和通知組成。ui

5.織入(weaving)this

​ 織入就是爲切面和目標類生成一個代理類。

對於非或標籤,Spring都是由對應的NameSpaceHandler進行處理的,AOP也不例外,使用的是AopNameSpaceHandler

public class AopNamespaceHandler extends NamespaceHandlerSupport {
    @Override
    public void init() {
        registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
        registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
        registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
    }
}
複製代碼

具體AopNameSpaceHandler是由Spring在解析XML文件的時候調用(org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseCustomElement),來分別解析三個對應的頂層標籤。對於這種Parse類,主要關注其中的parse方法便可,解析標籤的入口就是這裏。

applicationContext.xml

<bean id="helloImpl" class="com.ly.aop.HelloImpl"/>
<!--必須配置,由於被代理的對象必須是在容器中-->
<bean id="xmlAop" class="com.ly.aop.XmlAop"/>
<aop:config>
    <aop:pointcut id="txPointcut" expression="execution(* com.ly.aop.HelloImpl.hello(..))"/>

    <!--該標籤一般和Advice的子接口配合使用,例如配合事務<tx:advice/>-->
    <aop:advisor advice-ref="transactionInterceptor" pointcut-ref="txPointcut"/>

    <aop:aspect ref="xmlAop">
        <aop:pointcut id="pointcut" expression="execution(* com.ly.aop.HelloImpl.hello(..))"/>
        <aop:before method="before" pointcut-ref="pointcut"/>
        <aop:after method="after" pointcut-ref="pointcut"/>
        <aop:around method="around" pointcut-ref="pointcut"/>
        <aop:after-returning method="afterReturn" pointcut-ref="pointcut" returning="res"/>
        <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="exception"/>
    </aop:aspect>
    
</aop:config>
<tx:advice id="transactionInterceptor" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="get*" read-only="false" propagation="REQUIRED" isolation="DEFAULT"/>
    </tx:attributes>
</tx:advice>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"/>
複製代碼

1.aop:config

@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
    //1.<aop:config/>配置class爲AspectJAwareAdvisorAutoProxyCreator的BeanDefinition
    //而後設置兩個屬性:proxy-target-class和expose-class
    //proxy-target-class表明是否使用CGLIB代理,默認false,使用JDK
    //expose-class表示是否暴露代理類,能夠經過AopContext獲取當前代理類
    configureAutoProxyCreator(parserContext, element);

    //2.解析<aop:config/>的子標籤
    List<Element> childElts = DomUtils.getChildElements(element);
    for (Element elt: childElts) {
        String localName = parserContext.getDelegate().getLocalName(elt);
        //3.BeanDefinition的class是AspectjExpressionPointcut,保存id和expression,
        // 若是id爲空,則自動生成,而且是prototype
        if (POINTCUT.equals(localName)) {
            parsePointcut(elt, parserContext);
        }
        //4.BeanDefinition的class是DefaultBeanFactoryPointcutAdvisor,
        // 若是是pointcut,則是AspectJExpressPointcut,
        // 若是是pointcut-ref,則是RuntimeBeanNameReference
        // 事務管理的時候用過
        else if (ADVISOR.equals(localName)) {
            parseAdvisor(elt, parserContext);
        }
        //5.會被組裝爲AspectJPointcutAdvisor爲class的BeanDefinition,
        // 這個BeanDefinition還存儲了建立Method的工廠Bean
        // 和建立Aspect Instance的BeanFactory
        // 以及AspectJExpressionPointcut或RuntimeBeanNameReference
        //而<aop:aspect/>會被轉爲AspectComponentBeanDefinition
        //<aop:declare-parents/>會被轉爲DeclareParentsAdvisor
        else if (ASPECT.equals(localName)) {
            parseAspect(elt, parserContext);
        }
    }
}
複製代碼

上述源碼只貼了關鍵代碼,主要分爲2步:1.解析<aop:config/>標籤;2.解析<aop:config/>的三個子標籤。

1.解析<aop:config/>標籤

public static void registerAspectJAutoProxyCreatorIfNecessary( ParserContext parserContext, Element sourceElement) {

    //1.注入BeanDefinition,class爲AspectJAwareAdvisorAutoProxyCreator
    BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAutoProxyCreatorIfNecessary(
        parserContext.getRegistry(), parserContext.extractSource(sourceElement));
    //2.設置proxy-target-class和expose-class屬性
    useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
    registerComponentIfNecessary(beanDefinition, parserContext);
}
複製代碼
public static BeanDefinition registerAspectJAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
		return registerOrEscalateApcAsRequired(AspectJAwareAdvisorAutoProxyCreator.class, registry, source);
}
複製代碼

建立classAspectJAwareAutoProxyCreatorBeanDefinition相對簡單,就是新建了一個BeanDefinition對象,而後設置了一些基礎屬性就完成了。

private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, Element sourceElement) {
    if (sourceElement != null) {
        boolean proxyTargetClass = Boolean.parseBoolean(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));
        if (proxyTargetClass) {
            AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
        }
        boolean exposeProxy = Boolean.parseBoolean(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));
        if (exposeProxy) {
            AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
        }
    }
}
複製代碼

proxy-target-classexpose-proxy這兩個屬性仍是很重要的,proxy-target-class表明的使用何種方式來代理目標類,若是proxy-target-class爲true,則使用CGLIB動態代理;若是爲false,則使用JDK動態代理,默認狀況爲false。expose-proxy表明是否須要暴露當前的代理對象,默認不暴露,若是暴露,可使用AopContext獲取當前代理對象,能夠用來解決在一個類中一個方法調用另外一個方法,切面無效的問題。

2.解析<aop:pointcut/>標籤

private AbstractBeanDefinition parsePointcut(Element pointcutElement, ParserContext parserContext) {
    String id = pointcutElement.getAttribute(ID);
    String expression = pointcutElement.getAttribute(EXPRESSION);
    
    AbstractBeanDefinition pointcutDefinition = null;

    //1.建立AspectJExpressionPointcut
    pointcutDefinition = createPointcutDefinition(expression);
    pointcutDefinition.setSource(parserContext.extractSource(pointcutElement));

    //2.若是名稱未設置,則自動生成
    String pointcutBeanName = id;
    if (StringUtils.hasText(pointcutBeanName)) {
        parserContext.getRegistry().registerBeanDefinition(pointcutBeanName, pointcutDefinition);
    }
    else {
        pointcutBeanName = parserContext.getReaderContext().registerWithGeneratedName(pointcutDefinition);
    }

}
複製代碼

上述代碼只貼了關鍵性代碼。

protected AbstractBeanDefinition createPointcutDefinition(String expression) {
    RootBeanDefinition beanDefinition = new RootBeanDefinition(AspectJExpressionPointcut.class);
    beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
    beanDefinition.setSynthetic(true);
    beanDefinition.getPropertyValues().add(EXPRESSION, expression);
    return beanDefinition;
}
複製代碼

由上述代碼能夠看出來,對於<aop:pointcut/>標籤是使用AspectJExpressionPointcut

注意:它的ScopePrototype,這也就意味着若是有其餘的類引用了Pointcut,則每次都一個新的對象,至於表達式是做爲屬性值保存。

public interface Pointcut {
    /** * 返回一個類型過濾器 */
    ClassFilter getClassFilter();

    /** * 返回一個方法匹配器 */
    MethodMatcher getMethodMatcher();
}
複製代碼

Pointcut接口提供了兩個核心方法,這兩個核心方法分別返回兩個重要的類:ClassFilterMethodMatcherClassFilter用於根據expression過濾ClassMethodMatcher用於根據expression過濾method

3.解析<aop:advisor/>標籤

private void parseAdvisor(Element advisorElement, ParserContext parserContext) {
    //1.建立BeanDefinition,class是DefaultBeanFactoryPointcutAdvisor
    //若是pointcut-ref存在, 則建立RuntimeBeanNameReference
    AbstractBeanDefinition advisorDef = createAdvisorBeanDefinition(advisorElement, parserContext);
    String id = advisorElement.getAttribute(ID);

    try {
        this.parseState.push(new AdvisorEntry(id));
        //2.設置名稱,若是名稱不存在,就自動生成
        String advisorBeanName = id;
        if (StringUtils.hasText(advisorBeanName)) {
            parserContext.getRegistry().registerBeanDefinition(advisorBeanName, advisorDef);
        }
        else {
            advisorBeanName = parserContext.getReaderContext().registerWithGeneratedName(advisorDef);
        }

        //3.若是是pointcut,則生成class爲AspectJExpressionPointcut
        //若是是pointcut-ref,則爲RuntimeBeanReference
        Object pointcut = parsePointcutProperty(advisorElement, parserContext);
        if (pointcut instanceof BeanDefinition) {
            advisorDef.getPropertyValues().add(POINTCUT, pointcut);
            parserContext.registerComponent(
                new AdvisorComponentDefinition(advisorBeanName, advisorDef, (BeanDefinition) pointcut));
        }
        else if (pointcut instanceof String) {
            advisorDef.getPropertyValues().add(POINTCUT, new RuntimeBeanReference((String) pointcut));
            parserContext.registerComponent(
                new AdvisorComponentDefinition(advisorBeanName, advisorDef));
        }
    }
    finally {
        this.parseState.pop();
    }
}
複製代碼
private AbstractBeanDefinition createAdvisorBeanDefinition(Element advisorElement, ParserContext parserContext) {
    RootBeanDefinition advisorDefinition = new RootBeanDefinition(DefaultBeanFactoryPointcutAdvisor.class);
    advisorDefinition.setSource(parserContext.extractSource(advisorElement));

    //1.通知
    String adviceRef = advisorElement.getAttribute(ADVICE_REF);
    if (!StringUtils.hasText(adviceRef)) {
        parserContext.getReaderContext().error(
            "'advice-ref' attribute contains empty value.", advisorElement, this.parseState.snapshot());
    }
    else {
        advisorDefinition.getPropertyValues().add(
            ADVICE_BEAN_NAME, new RuntimeBeanNameReference(adviceRef));
    }

    //2.排序
    if (advisorElement.hasAttribute(ORDER_PROPERTY)) {
        advisorDefinition.getPropertyValues().add(
            ORDER_PROPERTY, advisorElement.getAttribute(ORDER_PROPERTY));
    }

    return advisorDefinition;
}
複製代碼

從上述源碼能夠看到,對於<aop:advisor/>標籤使用的的DefaultBeanFactoryPointcutAdvisor,而且設置了通知和排序。對於名稱的設置也是同樣的,有就用,沒有就自動生成。

private Object parsePointcutProperty(Element element, ParserContext parserContext) {
    //1.針對pointcut屬性,使用AspectJExpressionPointcut直接建立,這裏的建立方式和以前的同樣
   if (element.hasAttribute(POINTCUT)) {
        // Create a pointcut for the anonymous pc and register it.
        String expression = element.getAttribute(POINTCUT);
        AbstractBeanDefinition pointcutDefinition = createPointcutDefinition(expression);
        pointcutDefinition.setSource(parserContext.extractSource(element));
        return pointcutDefinition;
    }
    //2.對於pointcut-ref屬性,直接返回表達式,而後使用RuntimeBeanReference進行引用
    else if (element.hasAttribute(POINTCUT_REF)) {
        String pointcutRef = element.getAttribute(POINTCUT_REF);
        if (!StringUtils.hasText(pointcutRef)) {
            parserContext.getReaderContext().error(
                "'pointcut-ref' attribute contains empty value.", element, this.parseState.snapshot());
            return null;
        }
        return pointcutRef;
    }
}
複製代碼

上述源碼只展現了關鍵的源碼,能夠看到,主要是根據pointcut的形式來決定生成不一樣的類,若是是pointcut屬性,則直接建立AspectJExpressionPointcut爲class的BeanDefinition,若是是使用pointcut-ref進行引用,則根據切點表達式建立RuntimeBeanReference,最終都是做爲DeafultBeanFactoryPointcutAdvisor爲class的BeanDefinition的屬性值,以便後續使用。

通常這種方式不多用,在事務管理的是有用過。

4.解析<aop:aspect/>標籤

private void parseAspect(Element aspectElement, ParserContext parserContext) {
    //1.<aop:declare-parents/>會被轉爲DeclareParentsAdvisor
    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));
    }

    //2.處理<aop:aspect/>的子標籤,before、after、around、after-returning、after-throwing
    NodeList nodeList = aspectElement.getChildNodes();
    boolean adviceFoundAlready = false;
    for (int i = 0; i < nodeList.getLength(); i++) {
        Node node = nodeList.item(i);
        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));
            }
            AbstractBeanDefinition advisorDefinition = parseAdvice(
                aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences);
            beanDefinitions.add(advisorDefinition);
        }
    }

    //3.<aop:aspect/>自己轉爲AspectComponentDefinition
    AspectComponentDefinition aspectComponentDefinition = createAspectComponentDefinition(
        aspectElement, aspectId, beanDefinitions, beanReferences, parserContext);
    parserContext.pushContainingComponent(aspectComponentDefinition);

    //4.繼續處理<aop:pointcut/>
    // 若是上面的Advice使用的是pointcut,則上面引用了,這裏建立便可。
    // 若是上面的Advice使用的pointcut,則自己已經建立了
    List<Element> pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT);
    for (Element pointcutElement : pointcuts) {
        parsePointcut(pointcutElement, parserContext);
    }
}
複製代碼

上述源碼只展現了關鍵的部分。這個方法總體流程也很好理解,就是解析<aop:aspect/>標籤內的子標籤。

1.<aop:declare-parents/>

<aop:declare-parents/>標籤表示代理子類應該實現哪些接口,最終會被轉爲classDeclareParentsAdivsorBeanDefinition。基本不怎麼使用。

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

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

        //1.工廠Bean,用於根據aspectName和Advice的method獲取Method對象
        RootBeanDefinition methodDefinition = new RootBeanDefinition(MethodLocatingFactoryBean.class);
        methodDefinition.getPropertyValues().add("targetBeanName", aspectName);
        methodDefinition.getPropertyValues().add("methodName", adviceElement.getAttribute("method"));
        methodDefinition.setSynthetic(true);

        //2.Bean工廠,用於根據aspectName從beanFactory中獲取Aspect Bean
        RootBeanDefinition aspectFactoryDef =
            new RootBeanDefinition(SimpleBeanFactoryAwareAspectInstanceFactory.class);
        aspectFactoryDef.getPropertyValues().add("aspectBeanName", aspectName);
        aspectFactoryDef.setSynthetic(true);

        //3.獲取Advice而且註冊AspectJExpressionPointcut的BeanDefinition
        AbstractBeanDefinition adviceDef = createAdviceDefinition(
            adviceElement, parserContext, aspectName, order, methodDefinition, aspectFactoryDef,
            beanDefinitions, beanReferences);

        //4.最終生成的仍是AspectJPointcutAdvisor
        RootBeanDefinition advisorDefinition = new RootBeanDefinition(AspectJPointcutAdvisor.class);
        advisorDefinition.setSource(parserContext.extractSource(adviceElement));
        advisorDefinition.getConstructorArgumentValues().addGenericArgumentValue(adviceDef);
        if (aspectElement.hasAttribute(ORDER_PROPERTY)) {
            advisorDefinition.getPropertyValues().add(
                ORDER_PROPERTY, aspectElement.getAttribute(ORDER_PROPERTY));
        }
    
        parserContext.getReaderContext().registerWithGeneratedName(advisorDefinition);

        return advisorDefinition;
}
複製代碼

要看這個方法,首先先看下類圖:

能夠看到這五種通知都是AbstractAspectJAdvice子類,並且它們的構造方法都調用了父類的構造方法。

這個構造方法包含了三個重要的參數:通知方法(Method)、切點(AspectJExpressionPointcut)、切面實例工廠(AspectJInstanceFactory),看到這三個參數就能夠很明白的知道後續通知方法的執行、切面實例工廠的獲取、切點的類匹配和方法匹配必定都是經過該類實現的。

上述parseAdvice方法其實就是這個參數的建立的過程。

1.Advice Method

首先建立classMethodLocationFactoryBeanBeanDefinition,這是一個工廠Bean,就是用來生產通知Method實例的。

public class MethodLocatingFactoryBean implements FactoryBean<Method>, BeanFactoryAware {

	private String targetBeanName;

	private String methodName;

	private Method method;

	@Override
	public void setBeanFactory(BeanFactory beanFactory) {
		Class<?> beanClass = beanFactory.getType(this.targetBeanName);
		this.method = BeanUtils.resolveSignature(this.methodName, beanClass);
	}

	@Override
	public Method getObject() throws Exception {
		return this.method;
	}
}
複製代碼

上述源碼只展現了關鍵部分,能夠看到是經過IOC容器根據targetBeanName獲取目標BeanClass,而後根據methodName經過Class獲取Method實例。

注意:這裏的targetBeanName其實就是<aop:aspect ref="aspectName"/>中的ref的值,而methodName就是<aop:before method="before"/>中的method的值。

2.Aspect Instance

其次建立classSimpleBeanFactoryAwareAspectJInstanceFactory,這是一個實例工廠,就是用來生產Aspect實例的。

public interface AspectInstanceFactory extends Ordered {
	Object getAspectInstance();
	ClassLoader getAspectClassLoader();
}
複製代碼

這個接口是專門用來獲取AspectInstance的工廠。

public class SimpleBeanFactoryAwareAspectInstanceFactory implements AspectInstanceFactory, BeanFactoryAware {
    private String aspectBeanName;
    private BeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
        this.beanFactory = beanFactory;
        Assert.notNull(this.aspectBeanName, "'aspectBeanName' is required");
    }

    @Override
    public Object getAspectInstance() {
        return this.beanFactory.getBean(this.aspectBeanName);
    }
    
    @Override
	public ClassLoader getAspectClassLoader() {
		if (this.beanFactory instanceof ConfigurableBeanFactory) {
			return ((ConfigurableBeanFactory) this.beanFactory).getBeanClassLoader();
		}
		else {
			return ClassUtils.getDefaultClassLoader();
		}
	}
}
複製代碼

上述源碼只展現了關鍵部分。能夠看到是根據aspectBeanName從IOC容器中獲取Bean實例的。

注意:asepctBeanName其實就是<aop:aspect id="aspectBeanName"/>中的id的值。

3.AspectJPointcutAdvisor

而後就是根據通知類型獲取通知類以及切點的設置。

private AbstractBeanDefinition createAdviceDefinition( Element adviceElement, ParserContext parserContext, String aspectName, int order, RootBeanDefinition methodDef, RootBeanDefinition aspectFactoryDef, List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) {

    //1.class爲AspectJXXX的BeanDefinition
    RootBeanDefinition adviceDefinition = new RootBeanDefinition(getAdviceClass(adviceElement, parserContext));
    adviceDefinition.setSource(parserContext.extractSource(adviceElement));

    //2.相關屬性設置
    adviceDefinition.getPropertyValues().add(ASPECT_NAME_PROPERTY, aspectName);
    adviceDefinition.getPropertyValues().add(DECLARATION_ORDER_PROPERTY, order);

    if (adviceElement.hasAttribute(RETURNING)) {
        adviceDefinition.getPropertyValues().add(
            RETURNING_PROPERTY, adviceElement.getAttribute(RETURNING));
    }
    if (adviceElement.hasAttribute(THROWING)) {
        adviceDefinition.getPropertyValues().add(
            THROWING_PROPERTY, adviceElement.getAttribute(THROWING));
    }
    if (adviceElement.hasAttribute(ARG_NAMES)) {
        adviceDefinition.getPropertyValues().add(
            ARG_NAMES_PROPERTY, adviceElement.getAttribute(ARG_NAMES));
    }

    //3.構造參數值設置
    ConstructorArgumentValues cav = adviceDefinition.getConstructorArgumentValues();
    //4.生成Method對象的Bean工廠
    cav.addIndexedArgumentValue(METHOD_INDEX, methodDef);

    //5.註冊AspectJExpressionPointcut或RuntimeBeanReference爲class的BeanDefinition
    Object pointcut = parsePointcutProperty(adviceElement, parserContext);
    if (pointcut instanceof BeanDefinition) {
        cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcut);
        beanDefinitions.add((BeanDefinition) pointcut);
    }
    else if (pointcut instanceof String) {
        RuntimeBeanReference pointcutRef = new RuntimeBeanReference((String) pointcut);
        cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcutRef);
        beanReferences.add(pointcutRef);
    }

    //6.生成Aspect Instance的BeanFactory
    cav.addIndexedArgumentValue(ASPECT_INSTANCE_FACTORY_INDEX, aspectFactoryDef);

    return adviceDefinition;
}
複製代碼

這個方法代碼看着多,其實就是三步核心:1.獲取Advice;2.設置Advice的屬性;3.設置以前的三個構造參數。

  1. 獲取Advice
private Class<?> getAdviceClass(Element adviceElement, ParserContext parserContext) {
    String elementName = parserContext.getDelegate().getLocalName(adviceElement);
    if (BEFORE.equals(elementName)) {
        return AspectJMethodBeforeAdvice.class;
    }
    else if (AFTER.equals(elementName)) {
        return AspectJAfterAdvice.class;
    }
    else if (AFTER_RETURNING_ELEMENT.equals(elementName)) {
        return AspectJAfterReturningAdvice.class;
    }
    else if (AFTER_THROWING_ELEMENT.equals(elementName)) {
        return AspectJAfterThrowingAdvice.class;
    }
    else if (AROUND.equals(elementName)) {
        return AspectJAroundAdvice.class;
    }
    else {
        throw new IllegalArgumentException("Unknown advice kind [" + elementName + "].");
    }
}
複製代碼

獲取Advice很簡單,就是根據標籤判斷是哪一種通知,返回相應通知的Class

  1. 設置Advice屬性

設置Advice的屬性就更加簡單了,主要是AspecJAfterReturningAdivceAspectJAfterThrowingAdvice以及通知方法參數。

  1. 設置構造參數

設置以前的三個構造參數MethodAspect Instance已經拿到了,如今主要的是建立Pointcut。對於Pointcut會有兩種狀況:1.pointcut;2.pointcut-ref。

從上面的代碼中能夠看到,若是<aop:before pointcut="xxx"/>,則會直接生成ClassAspectJExpressionPointcutBeanDefinition;而對於<aop:before ref="xxx"/>,則會生成RuntimeBeanReferencePointcut進行引用,在運行的時候,會自動拿到。這一點和以前的<aop:advisor/>是同樣的。

三個參數齊全,就能夠直接設置構造參數了,最後返回對應AspectJXXXAdviceClassBeanDefinition

最後回到parseAdvice方法,將拿到的BeanDefinition做爲ClassAspectJPointcutAdvisorBeanDefintion的構造參數值。

那麼至此Advisor也就拿到了。

3.<aop:aspect/>
private AspectComponentDefinition createAspectComponentDefinition( Element aspectElement, String aspectId, List<BeanDefinition> beanDefs, List<BeanReference> beanRefs, ParserContext parserContext) {

    //1.全部的AspectJPointcutAdvisor,每一個Advice都會生成一個
    BeanDefinition[] beanDefArray = beanDefs.toArray(new BeanDefinition[beanDefs.size()]);
    //2.全部的ref
    BeanReference[] beanRefArray = beanRefs.toArray(new BeanReference[beanRefs.size()]);
    Object source = parserContext.extractSource(aspectElement);
    return new AspectComponentDefinition(aspectId, beanDefArray, beanRefArray, source);
}
複製代碼

被轉爲ClassAspectConpenentDefintion,保存了全部的AspectJPointcutAdvisorref

4.<aop:pointcut/>

<aop:aspect/>中的<aop:pointcut/><aop:config/>中的<aop:pointcut/>解析方式是同樣的,生成ClassAspectJExpressionPointcutBeanDefintion

注意:

AspectJExpressionPointcutPrototype,若是有Advice引用這個Pointcut,那麼有幾個Advice,就會產生幾個Pointcut,也就會有幾個AspectJPointcutAdvisor存儲AdvicePointcut

2.aop:scoped-proxy

class ScopedProxyBeanDefinitionDecorator implements BeanDefinitionDecorator {

    private static final String PROXY_TARGET_CLASS = "proxy-target-class";

    //裝飾者
    @Override
    public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext) {
        //1.代理方式
        boolean proxyTargetClass = true;
        if (node instanceof Element) {
            Element ele = (Element) node;
            if (ele.hasAttribute(PROXY_TARGET_CLASS)) {
                proxyTargetClass = Boolean.valueOf(ele.getAttribute(PROXY_TARGET_CLASS));
            }
        }

        //2.建立代理對象,代理對象的名稱和原對象一致,原對象的名稱發生改變
        // Register the original bean definition as it will be referenced by the scoped proxy
        // and is relevant for tooling (validation, navigation).
        BeanDefinitionHolder holder =
            ScopedProxyUtils.createScopedProxy(definition, parserContext.getRegistry(), proxyTargetClass);
        String targetBeanName = ScopedProxyUtils.getTargetBeanName(definition.getBeanName());
        parserContext.getReaderContext().fireComponentRegistered(
            new BeanComponentDefinition(definition.getBeanDefinition(), targetBeanName));
        return holder;
    }

}
複製代碼

這是一個裝飾者模式,目的是根據不一樣的代理方式爲原有的BeanDefinition生成代理的BeanDefinition。那麼當使用beanName獲取獲取bean的時候,本質上拿到的是代理bean

public static BeanDefinitionHolder createScopedProxy(BeanDefinitionHolder definition, BeanDefinitionRegistry registry, boolean proxyTargetClass) {

    //1.原始BeanDefinition信息
    String originalBeanName = definition.getBeanName();
    BeanDefinition targetDefinition = definition.getBeanDefinition();
    //2.重寫beanName,變爲scopedTarget.元名稱
    String targetBeanName = getTargetBeanName(originalBeanName);

    //3.建立代理BeanDefinition,本質上是一個class爲工廠Bean,能夠生成代理對象,並設置相關屬性
    RootBeanDefinition proxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class);
    proxyDefinition.setDecoratedDefinition(new BeanDefinitionHolder(targetDefinition, targetBeanName));
    proxyDefinition.setOriginatingBeanDefinition(targetDefinition);
    proxyDefinition.setSource(definition.getSource());
    proxyDefinition.setRole(targetDefinition.getRole());

    proxyDefinition.getPropertyValues().add("targetBeanName", targetBeanName);
    if (proxyTargetClass) {
        targetDefinition.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
        // ScopedProxyFactoryBean's "proxyTargetClass" default is TRUE, so we don't need to set it explicitly here.
    }
    else {
        proxyDefinition.getPropertyValues().add("proxyTargetClass", Boolean.FALSE);
    }

    proxyDefinition.setAutowireCandidate(targetDefinition.isAutowireCandidate());
    proxyDefinition.setPrimary(targetDefinition.isPrimary());
    if (targetDefinition instanceof AbstractBeanDefinition) {
        proxyDefinition.copyQualifiersFrom((AbstractBeanDefinition) targetDefinition);
    }

    targetDefinition.setAutowireCandidate(false);
    targetDefinition.setPrimary(false);

    //4.注入原始BeanDefinition,可是名稱是重寫以後的beanName,而代理BeanDefinition使用原始的beanName
    // 這樣咱們拿到的Bean雖然名稱是咱們注入的那個,可是本質上已經成爲了代理Bean
    registry.registerBeanDefinition(targetBeanName, targetDefinition);

    //5.返回代理BeanDefinition並注入IOC容器
    return new BeanDefinitionHolder(proxyDefinition, originalBeanName, definition.getAliases());
}
複製代碼

這個方法很好理解,就是爲原有的BeanDefinition生成了一個代理的BeanDefinition,原有的BeanDefintion和代理BeanDefinition都會注入IOC容器,當從容器中獲取相應的Bean時候,獲取到的是代理的Bean。固然代理的方式根據proxy-target-class屬性進行設置的,默認爲false,是``JDK代理,爲true,是cglib`代理。

這個標籤的使用場景,好比一個Scope做用的Bean A 引用一個Prototype做用的Bean B ,那麼會致使A引用的B永遠是同一個對象,而不是每次請求都是新的B實例。

<bean class="com.ly.scope.A">
    <property name="b" ref="b"/>
</bean>
<bean id="b" class="com.ly.scope.B" scope="prototype">
    <aop:scoped-proxy/>
</bean>
複製代碼

3.aop:aspect-autoproxy

@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
    //1.生成class爲AnnotationAwareAspectJAutoProxyCreator的BeanDefinition
    AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
    //2.<aop:include name="expression"/>是用來處理指示被@Aspect註解的Bean,也就是切面,並做爲BeanDefinition的屬性
    extendBeanDefinition(element, parserContext);
    return null;
}
複製代碼

<aop:aspect-autoproxy/>標籤的做用是掃描被@Aspect註解的Bean

public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary( ParserContext parserContext, Element sourceElement) {

    //1.注入BeanDefinition,class爲AnnotationAwareAspectJAutoProxyCreator
    BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
        parserContext.getRegistry(), parserContext.extractSource(sourceElement));
    //2.proxy-target-class、expose-proxy
    useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
    registerComponentIfNecessary(beanDefinition, parserContext);
}

public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
    return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}

private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) {
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");

    //1.beanDefinition已經存在,則根據權重設置beanClassName
    if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
        BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
        if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
            int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
            int requiredPriority = findPriorityForClass(cls);
            if (currentPriority < requiredPriority) {
                apcDefinition.setBeanClassName(cls.getName());
            }
        }
        return null;
    }

    //2.新建,並設置相關屬性
    RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
    beanDefinition.setSource(source);
    beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
    beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
    return beanDefinition;
}
複製代碼

能夠看到是註冊了一個classAnnotationAwareAspectJAutoProxyCreatorBeanDefnition,這個BeanDefintion在調用AOP,獲取全部候選AOP的時候會用到,用於掃描全部@AspectJ註解的Bean

<aop:aspectj-autoproxy/>也支持兩個屬性:proxy-target-classexpose-proxyproxy-target-class表示代理方式,expose-proxy表示暴露代理對象,可使用AopContext#currentProxy獲取代理對象。

能夠看到AnnotationAwareAspectJAutoProxyCreatorAspectJAwareAdvisorAutoProxyCreator的子類,AspectJAwareAdvisorAutoProxyCreator是解析<aop:config/>的時候使用的。

相關文章
相關標籤/搜索