標籤: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源碼分析