springAOP源碼分析

來源:互聯網
上載者:User

標籤:rac   對象   pos   document   資訊   定義   contain   print   一個   

SpringAop實現為動態代理進行實現的,實現方式有2種,JDK動態代理和CGlib動態代理
先寫一個AOP的案列加以說明
設定檔代碼為:

    <bean id="userDao" class="com.spring.aop.service.UserDaoImpl"/>        <bean id="logger" class="com.spring.aop.log.Logger" />        <!-- 切面:切入點和通知 -->    <aop:config>        <aop:aspect id="aspect"  ref="logger">            <aop:pointcut expression="execution(* com.spring.aop.service..*.*(..))" id="udpateUserMethod" />            <aop:before method="recordBefore" pointcut-ref="udpateUserMethod" />            <aop:after method="recordAfter"   pointcut-ref="udpateUserMethod" />        </aop:aspect>    </aop:config>

  其中增強類Logger的實現為:

package com.spring.aop.log;public class Logger {     public void recordBefore(){            System.out.println("recordBefore");        }                public void recordAfter(){            System.out.println("recordAfter");        }    } 

被曾強類UserDaoImpl和被曾強類介面的實現為:

package com.spring.aop.service;public interface UserDao {    void addUser();    void deleteUser();}package com.spring.aop.service;public class UserDaoImpl implements UserDao {    @Override    public void addUser() {        System.out.println("add user ");    }    @Override    public void deleteUser() {         System.out.println("delete user ");    }}

測試方法代碼:

package com.spring.aop.main;import org.springframework.context.support.ClassPathXmlApplicationContext;import com.spring.aop.service.UserDao;public class testAop {    public static void main(String[] args) {            ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("springAop.xml");//BeanDefination的解析註冊,代理對象的產生                    UserDao userDao = (UserDao) applicationContext.getBean("userDao");//可以看到userDao類型是以$Proxy開頭的,說明是通過JDK動態代理的方式擷取的            userDao.addUser();//增強行為發生的時刻    }}

運行結果:

可以看出對目標方法進行了增強。

下面開始從Spring XML解析進行源碼分析

從DefaultBeanDefinitionDocumentReader類的parseBeanDefinitions方法開始進行分析,在parseBeanDefinitions方法分為spring預設標籤解析和自訂標籤解析,在這裡解析標籤<aop:config>的時候,使用到了自訂標籤解析,代碼如下:

    public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {        String namespaceUri = getNamespaceURI(ele);        NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);        if (handler == null) {            error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);            return null;        }
//此時的handler指的是ConfigBeanDefinitionParser對象 return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); }

下面進入ConfigBeanDefinitionParser對象的parse方法進行分析:

    @Override    public BeanDefinition parse(Element element, ParserContext parserContext) {        CompositeComponentDefinition compositeDef =                new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));        parserContext.pushContainingComponent(compositeDef);        configureAutoProxyCreator(parserContext, element);        List<Element> childElts = DomUtils.getChildElements(element);        for (Element elt: childElts) {            String localName = parserContext.getDelegate().getLocalName(elt);            if (POINTCUT.equals(localName)) {                parsePointcut(elt, parserContext);            }            else if (ADVISOR.equals(localName)) {                parseAdvisor(elt, parserContext);            }
// 在這裡解析aspect標籤 else if (ASPECT.equals(localName)) { parseAspect(elt, parserContext); } } parserContext.popAndRegisterContainingComponent(); return null; }

 

    private void parseAspect(Element aspectElement, ParserContext parserContext) {        // 擷取aspect標籤上面定義的ID        String aspectId = aspectElement.getAttribute(ID);        // 擷取aspect標籤上面引用的增強類 logger        String aspectName = aspectElement.getAttribute(REF);        try {            // 將aspectId和aspectName封裝成 AspectEntry對象,並放入棧parseState中            this.parseState.push(new AspectEntry(aspectId, aspectName));            //把<aop:before>等通知相關的資訊封裝到AspectJPointcutAdvisor中,然後放到該集合裡            List<BeanDefinition> beanDefinitions = new ArrayList<BeanDefinition>();             //把ref相關的資訊如aop.xml中的logger,updateUserMethod等封裝到RunTimeBeanReference中,然後放到這個集合中            List<BeanReference> beanReferences = new ArrayList<BeanReference>();            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.            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 保證只是放入一次引用                        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));                    }                    // 把通知相關資訊封裝到AspectJPointcutAdvisor這個類中,同時封裝ref資訊然後放到BeanReferences中                    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();        }    }

 


最終是將<aop:config>配置的相關資訊封裝成類,然後放入到containingComponents棧中,方便以後進行操作

springAOP源碼分析

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.