springAOP源码分析
Posted 烟尘
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了springAOP源码分析相关的知识,希望对你有一定的参考价值。
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源码分析的主要内容,如果未能解决你的问题,请参考以下文章
Android 插件化VirtualApp 源码分析 ( 目前的 API 现状 | 安装应用源码分析 | 安装按钮执行的操作 | 返回到 HomeActivity 执行的操作 )(代码片段