前面的章节已经介绍了AOP的相关概念和一些知识点,接下来我们就要开始分析SpringAOP的源码了,接下来的分析都基于@AspectJ
注解。
1.aspectj-autoproxy标签简介
使用注解方式应用aop需要在配置文件中配置<aop:aspectj-autoproxy/>
, 而该标签有两个属性,proxy-target-class
和 expose-proxy
。
- proxy-target-class
如果被代理的目标对象至少实现了一个接口,则会使用JDK动态代理,所有实现该目标类实现的接口都将被代理;如果该目标对象没有实现任何接口,则创建CGLIB动态代理。但是可以通过proxy-target-class属性强制指定使用CGLIB代理。如果指定了proxy-target-class="true"
则将强制开启CGLIB动态代理。 - expose-proxy
解决目标对象内部的自我调用无法实施切面增强的问题。
2.aspectj-autoproxy标签解析
在第十七小节,讲解了Spring对默认标签的解析过程,但是没有对默认标签解析进行分析,今天就来分析一下Spring是如何解析自定义标签aspectj-autoproxy的。顺便通过对该标签的解析,补充十七节欠缺的分析。
- 自定义标签解析入口
打开DefaultBeanDefinitionDocumentReader类的parseBeanDefinitions方法
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { // 1、解析默认命名空间 if (delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; // 默认命名空间 if (delegate.isDefaultNamespace(ele)) { parseDefaultElement(ele, delegate); } // 自定义命名空间 else { delegate.parseCustomElement(ele); } } } } // 2、解析自定义命名空间 else { delegate.parseCustomElement(root); } }
通过parseCustomElement解析自定义标签
- 自定义标签解析过程
/** * 解析自定义标签 * @param ele * @param containingBd * @return */ @Nullable public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) { // 1、获取namespaceUri String namespaceUri = getNamespaceURI(ele); if (namespaceUri == null) { return null; } // 2、根据namespaceUri得到命名空间解析器 NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); if (handler == null) { error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele); return null; } // 2、使用命名空间解析器解析自定义标签 return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); }
对自定义标签的解析交给了NamespaceHandler接口,如果我们想在Spring中实现自己的自定义标签,那么就需要实现NamespaceHandler接口,并通过重写其中的方法,来完成对自定义标签的解析。
- 获取NamespaceHandler
public NamespaceHandler resolve(String namespaceUri) { // 1.获取所有的namespaceUri,NamespaceHandler键值对map集合并得到 // 当前namespaceUri对应的NamespaceHandler类 Map<String, Object> handlerMappings = getHandlerMappings(); Object handlerOrClassName = handlerMappings.get(namespaceUri); if (handlerOrClassName == null) { return null; } else if (handlerOrClassName instanceof NamespaceHandler) { return (NamespaceHandler) handlerOrClassName; } else { // 2、通过BeanUtils实例化NamespaceHandler并调用其init方法进行初始化操作 String className = (String) handlerOrClassName; try { Class<?> handlerClass = ClassUtils.forName(className, this.classLoader); if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) { throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri + "] does not implement the [" + NamespaceHandler.class.getName() + "] interface"); } NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass); namespaceHandler.init(); handlerMappings.put(namespaceUri, namespaceHandler); return namespaceHandler; } catch (ClassNotFoundException ex) { throw new FatalBeanException("Could not find NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "]", ex); } catch (LinkageError err) { throw new FatalBeanException("Unresolvable class definition for NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "]", err); } } }
该方法非常简单,首先根据namespaceUri从所有的Map<namespaceUri,NamespaceHandler>中获取对应的NamespaceHandler实现类,然后通过BeanUtils.instantiateClass实例化该类,最后调用NamespaceHandler实例的init方法。
- AopNamespaceHandler的init方法
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()); }
在该方法中我们已经看到了aspectj-autoproxy标签,该方法也很简单,不做过多的分析。接下来分析aspectj-autoproxy解析的过程。
3.通过NamespaceHandler的parse方法解析aspectj-autoproxy标签
public BeanDefinition parse(Element element, ParserContext parserContext) { BeanDefinitionParser parser = findParserForElement(element, parserContext); return (parser != null ? parser.parse(element, parserContext) : null); }
具体的转换工作委托给了BeanDefinitionParser实例:
public BeanDefinition parse(Element element, ParserContext parserContext) { // 1、注册AnnotationAwareAspectJAutoProxyCreator AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element); // 2、处理子标签<aop:include/>, 指定@Aspect类,支持正则表达式,符合该表达式的切面类才会被应用 extendBeanDefinition(element, parserContext); return null; }
该转换过程主要完成两个功能,注册AnnotationAwareAspectJAutoProxyCreator和解析标签<aop:aspectj-autoproxy/>
,并在第二步解析了<aop:include/>
子标签,我们逐步分析一下:
3.1 注册AnnotationAwareAspectJAutoProxyCreator
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(ParserContext parserContext, Element sourceElement) { // 1、注册AnnotationAwareAspectJAutoProxyCreator BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary( parserContext.getRegistry(), parserContext.extractSource(sourceElement)); // 2、解析子标签 proxy-target-class 和 expose-proxy useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement); // 3、注册组件并发送组件注册事件 registerComponentIfNecessary(beanDefinition, parserContext); }
- 注册AnnotationAwareAspectJAutoProxyCreator
private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) { // cls --> org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator // AUTO_PROXY_CREATOR_BEAN_NAME --> org.springframework.aop.config.internalAutoProxyCreator if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) { // 如果registry已经包含了internalAutoProxyCreator, BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME); // 如果已经注册的internalAutoProxyCreator不是AnnotationAwareAspectJAutoProxyCreator,则需要判断优先级并决定使用哪个 if (!cls.getName().equals(apcDefinition.getBeanClassName())) { int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName()); int requiredPriority = findPriorityForClass(cls); if (currentPriority < requiredPriority) { apcDefinition.setBeanClassName(cls.getName()); } } // 如果已经注册的internalAutoProxyCreator是AnnotationAwareAspectJAutoProxyCreator,则无需特殊处理 return null; } RootBeanDefinition beanDefinition = new RootBeanDefinition(cls); beanDefinition.setSource(source); // HIGHEST_PRECEDENCE --> 指定最高优先级 beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE); // ROLE_INFRASTRUCTURE --> 表示Spring的内部bean beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); // 注册BeanDefinition registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition); return beanDefinition; }
- 解析子标签 proxy-target-class 和 expose-proxy
private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, @Nullable Element sourceElement) { if (sourceElement != null) { // PROXY_TARGET_CLASS_ATTRIBUTE --> proxy-target-class boolean proxyTargetClass = Boolean.parseBoolean(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE)); if (proxyTargetClass) { AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); } // EXPOSE_PROXY_ATTRIBUTE --> expose-proxy boolean exposeProxy = Boolean.parseBoolean(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE)); if (exposeProxy) { AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry); } } }
该过程最核心的作用就是注册AnnotationAwareAspectJAutoProxyCreator,关于该类的作用,我们会在下面的章节分析。
3.2 处理子标签
<aop:include/>
标签的作用:例如有下面的配置文件,并指定了<aop:include name="aspectJTest"></aop:include>
,则aspectJTest切面类会被应用,且name支持正则表达式,如果配置文件中的切面类不符合name的表达式,则切面类不会被应用。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 1、proxy-target-class 如果被代理的目标对象至少实现了一个接口,则会使用JDK动态代理,所有实现该目标类实现的接口都将被代理 如果该目标对象没有实现任何接口,则创建CGLIB动态代理。 但是可以通过proxy-target-class属性强制指定使用CGLIB代理, 2、expose-proxy 解决目标对象内部的自我调用无法实施切面增强的问题 --> <aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"> <!-- 指定@Aspect类,支持正则表达式,符合该表达式的切面类才会被应用--> <aop:include name="aspectJTest"></aop:include> </aop:aspectj-autoproxy> <bean id="aopTestBean" class="com.lyc.cn.v2.day04.AopTestBean"></bean> <bean name="aspectJTest" class="com.lyc.cn.v2.day04.AspectJTest"></bean> </beans>
了解了标签的作用之后,我们来看其解析过程,比较简单就不附加注释了:
private void extendBeanDefinition(Element element, ParserContext parserContext) { BeanDefinition beanDef = parserContext.getRegistry().getBeanDefinition(AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME); if (element.hasChildNodes()) { addIncludePatterns(element, parserContext, beanDef); } } private void addIncludePatterns(Element element, ParserContext parserContext, BeanDefinition beanDef) { ManagedList<TypedStringValue> includePatterns = new ManagedList<>(); NodeList childNodes = element.getChildNodes(); for (int i = 0; i < childNodes.getLength(); i++) { Node node = childNodes.item(i); if (node instanceof Element) { Element includeElement = (Element) node; TypedStringValue valueHolder = new TypedStringValue(includeElement.getAttribute("name")); valueHolder.setSource(parserContext.extractSource(includeElement)); includePatterns.add(valueHolder); } } if (!includePatterns.isEmpty()) { includePatterns.setSource(parserContext.extractSource(element)); beanDef.getPropertyValues().add("includePatterns", includePatterns); } }
那么到这里<aop:aspectj-autoproxy/>
标签的解析工作也就完成了。
4.总结
本节主要分析了<aop:aspectj-autoproxy/>
标签的解析,而该标签也是Spring的自定义标签,所以,如果大家有自己的自定义标签时,可以参考借鉴Spring的源码,写出自己的标签解析过程。通过继承NamespaceHandler实现自己的命名空间解析器即可。
在解析<aop:aspectj-autoproxy/>
的过程中,除了对标签的解析工作之外,还注册了AnnotationAwareAspectJAutoProxyCreator,该类的作用是什么呢,我们留在下节分析。