自定义命名空间解析 当我们想要扩展Spring 的配置时,可以定义我们自己的标签,然后解析成 bean定义,注册到Spring 容器中。这时需要用到 NamespaceHandler
。Spring 框架中除了beans顶级标签外,其他的顶级标签都是自定义命名空间中的标签。如,p
、 c
、util
命名空间。
NamespaceHandler
命名空间解析器Spring 框架中mvc
、context
、tx
等功能都是通过扩展这个接口来实现的。这个接口负责将标签解析成bean 定义对象。该接口提供了parse
和decorate
方法。parse
方法用来将顶级标签解析成BeanDefinition
对象。decorate
方法负责对parse
出来的BeanDefinition
进行进一步处理,需要解析的可以是元素属性和标签。 可以返回原来的BeanDefinition
,或者返回一个新的BeanDefinition
。
为了解析的方便,Spring 提供了一个抽象类NamespaceHandlerSupport
,封装了一些基础功能,并提供了两个新的接口(BeanDefinitionParser
和BeanDefinitionDecorator
)方便扩展。并提供了registerBeanDefinitionDecorator
、registerBeanDefinitionParser
、registerBeanDefinitionDecoratorForAttribute
三个方法分别注册BeanDefinitionParser
和BeanDefinitionDecorator
实现类,内部用map来维护标签名或者属性名到BeanDefinitionParser
和BeanDefinitionDecorator
的映射关系,解析时用名称(无命名空间前缀)来查找。
Spring提供的NamespaceHandler
Spring 提供了一些 NamespaceHandler
来实现自身功能。下面详细了解一下。
SimplePropertyNamespaceHandler
简单属性解析器解析的命名空间为 http://www.springframework.org/schema/p
。它将特定属性直接映射到bean属性。需要注意的重要一点是,NamespaceHandler
无法预知所有可能的属性名。该实现类直接实现了NamespaceHandler
。
下面是使用NamespaceHandler
的一个例子:
1 <bean id = "rob" class = "..TestBean" p:name ="Rob" p:spouse-ref ="sally" />
这里的p:name
直接对应于类TestBean
上的name
属性。p:spouse-ref
属性对应于spouse
属性,将value
所对应的bean注入到该属性中。
parse
方法直接记录日志信息,并返回 null
。因为它只支持对属性进行解析。
1 2 3 4 5 public BeanDefinition parse (Element element, ParserContext parserContext) { parserContext.getReaderContext().error( "Class [" + getClass().getName() + "] does not support custom elements." , element); return null ; }
decorate
将元素属性表示的属性设置到对应的对象属性中。每个属性都会调用一次这个方法。拿上文提到的例子来解释一下下面代码的逻辑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public BeanDefinitionHolder decorate (Node node, BeanDefinitionHolder definition, ParserContext parserContext) { if (node instanceof Attr) { Attr attr = (Attr) node; String propertyName = parserContext.getDelegate().getLocalName(attr); String propertyValue = attr.getValue(); MutablePropertyValues pvs = definition.getBeanDefinition().getPropertyValues(); if (pvs.contains(propertyName)) { parserContext.getReaderContext().error("Property '" + propertyName + "' is already defined using " + "both <property> and inline syntax. Only one approach may be used per property." , attr); } if (propertyName.endsWith(REF_SUFFIX)) { propertyName = propertyName.substring(0 , propertyName.length() - REF_SUFFIX.length()); pvs.add(Conventions.attributeNameToPropertyName(propertyName), new RuntimeBeanReference(propertyValue)); } else { pvs.add(Conventions.attributeNameToPropertyName(propertyName), propertyValue); } } return definition; }
小结:用这个自定义属性来替换<property>
使用起来更方便。
SimpleConstructorNamespaceHandler
简单构造函数解析器将自定义属性映射到构造函数参数。解析的命名空间地址为http://www.springframework.org/schema/c
。需要注意该解析器无法预知所有的参数。
下面看一个简单例子:
1 <bean id ="author" class ="..TestBean" c:name ="Enescu" c:work-ref ="compositions" />
将name
映射到TestBean
的构造函数name
参数,值为Enescu
,而work
参数引用了compositions
bean。
下面看一下源代码:
parse
方法不支持解析标签
1 2 3 4 5 public BeanDefinition parse (Element element, ParserContext parserContext) { parserContext.getReaderContext().error( "Class [" + getClass().getName() + "] does not support custom elements." , element); return null ; }
decorate
方法, 每个参数都会调用一次这个方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 public BeanDefinitionHolder decorate (Node node, BeanDefinitionHolder definition, ParserContext parserContext) { if (node instanceof Attr) { Attr attr = (Attr) node; String argName = StringUtils.trimWhitespace(parserContext.getDelegate().getLocalName(attr)); String argValue = StringUtils.trimWhitespace(attr.getValue()); ConstructorArgumentValues cvs = definition.getBeanDefinition().getConstructorArgumentValues(); boolean ref = false ; if (argName.endsWith(REF_SUFFIX)) { ref = true ; argName = argName.substring(0 , argName.length() - REF_SUFFIX.length()); } ValueHolder valueHolder = new ValueHolder(ref ? new RuntimeBeanReference(argValue) : argValue); valueHolder.setSource(parserContext.getReaderContext().extractSource(attr)); if (argName.startsWith(DELIMITER_PREFIX)) { String arg = argName.substring(1 ).trim(); if (!StringUtils.hasText(arg)) { cvs.addGenericArgumentValue(valueHolder); } else { int index = -1 ; try { index = Integer.parseInt(arg); } catch (NumberFormatException ex) { parserContext.getReaderContext().error( "Constructor argument '" + argName + "' specifies an invalid integer" , attr); } if (index < 0 ) { parserContext.getReaderContext().error( "Constructor argument '" + argName + "' specifies a negative index" , attr); } if (cvs.hasIndexedArgumentValue(index)) { parserContext.getReaderContext().error( "Constructor argument '" + argName + "' with index " + index+" already defined using <constructor-arg>." + " Only one approach may be used per argument." , attr); } cvs.addIndexedArgumentValue(index, valueHolder); } } else { String name = Conventions.attributeNameToPropertyName(argName); if (containsArgWithName(name, cvs)) { parserContext.getReaderContext().error( "Constructor argument '" + argName + "' already defined using <constructor-arg>." + " Only one approach may be used per argument." , attr); } valueHolder.setName(Conventions.attributeNameToPropertyName(argName)); cvs.addGenericArgumentValue(valueHolder); } } return definition; }
小结:配置构造函数的方式
使用<constructor-arg>
标签,可以使用名称来定义,也可以使用下标。
在<bean>
标签中使用c:
自定义属性, 属性名有两种方式:
下标形式_0
构造函数参数名形式c:name="value"
如果参数值需要引用bean,那么需要在属性名后加上_ref
表示引用。
两种方式对比:
方式
名称
下标
<constructor-arg>
标签
name=“name” value=“value” (ref=“beanid”)
index=“0” value=“value”(ref=“beanid”)
c:
属性
c: name=“value”(c:name_ref=“beanid”)
c:_0=“value” (c:_0_ref=“beanid”)
NamespaceHandlerSupport
命名空间解析器支持类实现自定义NamespaceHandler
的支持类。各个节点的解析和装饰分别通过BeanDefinitionParser
和BeanDefinitionDecorator
策略接口完成。提供registerBeanDefinitionParser
和registerBeanDefinitionDecorator
方法,用于注册BeanDefinitionParser
或BeanDefinitionDecorator
来处理特定元素。
BeanDefinitionParser
DefaultBeanDefinitionDocumentReader
用于处理自定义顶级标签的接口。实现类可以根据需要自由地将自定义标签中的元数据转换成任意多的bean定义。
AbstractBeanDefinitionParser
类BeanDefinitionParser
实现类,该类提供了许多方便的方法和模板方法,子类必须重写这些方法才能提供实际的解析逻辑。
当你想将任意复杂的XML解析为一个或多个bean定义时,请使用这个BeanDefinitionParser
实现。如果只是想将一些XML解析为一个bean定义,那么可以考虑该类更简单的扩展类,即 AbstractSingleBeanDefinitionParser
和 AbstractSimpleBeanDefinitionParser
。
parse
方法parse
方法是该解析器的入口,定义了整个解析的流程以及子类可以扩展的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public final BeanDefinition parse (Element element, ParserContext parserContext) { AbstractBeanDefinition definition = parseInternal(element, parserContext); if (definition != null && !parserContext.isNested()) { try { String id = resolveId(element, definition, parserContext); if (!StringUtils.hasText(id)) { parserContext.getReaderContext().error( "Id is required for element '" + parserContext.getDelegate().getLocalName(element) + "' when used as a top-level tag" , element); } String[] aliases = null ; if (shouldParseNameAsAliases()) { String name = element.getAttribute(NAME_ATTRIBUTE); if (StringUtils.hasLength(name)) { aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name)); } } BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases); registerBeanDefinition(holder, parserContext.getRegistry()); if (shouldFireEvents()) { BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder); postProcessComponentDefinition(componentDefinition); parserContext.registerComponent(componentDefinition); } } catch (BeanDefinitionStoreException ex) { String msg = ex.getMessage(); parserContext.getReaderContext().error((msg != null ? msg : ex.toString()), element); return null ; } } return definition; }
小结 解析流程:
调用parseInternal
方法将 xml 解析成BeanDefinition
,该方法由子类去实现
调用resolveId
方法得到bean 定义id,可以由子类重写
别名解析
注册bean 定义
发布事件,实现ReaderEventListener
进行监听
AbstractSingleBeanDefinitionParser
类只需要解析和定义一个bean定义的BeanDefinitionParser
实现基类,继承自AbstractBeanDefinitionParser
类,实现parseInternal
方法,近一步细化解析逻辑。
只需要从任意复杂的XML元素创建单个bean定义时,可以扩展这个解析器类。当你想从一个相对简单的自定义XML元素创建一个bean定义时,可以考虑扩展AbstractSimpleBeanDefinitionParser
类。
生成的bean定义将自动注册到org.springframework.beans.factory.support.BeanDefinitionRegistry
。只需要将自定义XML元素解析为一个bean定义就可以了。
parseInternal
方法1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(); String parentName = getParentName(element); if (parentName != null ) { builder.getRawBeanDefinition().setParentName(parentName); } Class<?> beanClass = getBeanClass(element); if (beanClass != null ) { builder.getRawBeanDefinition().setBeanClass(beanClass); } else { String beanClassName = getBeanClassName(element); if (beanClassName != null ) { builder.getRawBeanDefinition().setBeanClassName(beanClassName); } } builder.getRawBeanDefinition().setSource(parserContext.extractSource(element)); BeanDefinition containingBd = parserContext.getContainingBeanDefinition(); if (containingBd != null ) { builder.setScope(containingBd.getScope()); } if (parserContext.isDefaultLazyInit()) { builder.setLazyInit(true ); } doParse(element, parserContext, builder); return builder.getBeanDefinition();
小结 解析流程:
创建builder
设置父级定义名称
设置要转换成bean类,通过getBeanClass
方法或者getBeanClassName
根据父级bean定义设置scope
设置lazyInit属性
调用doParse
方法将xml对应的相关属性设置到builder中
获取BeanDefinition
对于只需要将xml映射为bean这样简单的解析情形,可以继承这个类实现doParse
方法,将xml的属性设置到BeanDefinitionBuilder
中即可。
ContextNamespaceHandler
上下文命名空间解析器继承自NamespaceHandlerSupport
类,在init
方法中注册了context
命名空间下的标签解析类。用来解析命名空间地址http://www.springframework.org/schema/context
。
context
命名空间下的自定义标签init
方法为每个标签都注册了一个解析类
标签
解析类
property-placeholder
org.springframework.context.config.PropertyPlaceholderBeanDefinitionParser
property-override
org.springframework.context.config.PropertyOverrideBeanDefinitionParser
annotation-config
org.springframework.context.annotation.AnnotationConfigBeanDefinitionParser
component-scan
org.springframework.context.annotation.ComponentScanBeanDefinitionParser
load-time-weaver
org.springframework.context.config.LoadTimeWeaverBeanDefinitionParser
spring-configured
org.springframework.context.config.SpringConfiguredBeanDefinitionParser
mbean-export
org.springframework.context.config.MBeanExportBeanDefinitionParser
mbean-server
org.springframework.context.config.MBeanServerBeanDefinitionParser
property-placeholder
标签解析org.springframework.context.config.PropertyPlaceholderBeanDefinitionParser
类用来解析该标签。继承自AbstractPropertyLoadingBeanDefinitionParser
, 该类又继承自AbstractSingleBeanDefinitionParser
,因此只返回单个BeanDefinition
。
AbstractPropertyLoadingBeanDefinitionParser
的doParse
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 protected void doParse (Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { String location = element.getAttribute("location" ); if (StringUtils.hasLength(location)) { location = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(location); String[] locations = StringUtils.commaDelimitedListToStringArray(location); builder.addPropertyValue("locations" , locations); } String propertiesRef = element.getAttribute("properties-ref" ); if (StringUtils.hasLength(propertiesRef)) { builder.addPropertyReference("properties" , propertiesRef); } String fileEncoding = element.getAttribute("file-encoding" ); if (StringUtils.hasLength(fileEncoding)) { builder.addPropertyValue("fileEncoding" , fileEncoding); } String order = element.getAttribute("order" ); if (StringUtils.hasLength(order)) { builder.addPropertyValue("order" , Integer.valueOf(order)); } builder.addPropertyValue("ignoreResourceNotFound" , Boolean.valueOf(element.getAttribute("ignore-resource-not-found" ))); builder.addPropertyValue("localOverride" , Boolean.valueOf(element.getAttribute("local-override" ))); builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); }
可以看出只是简单的将xml标签的属性取出并设置到builder中。
再看看PropertyPlaceholderBeanDefinitionParser
的doParse
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 protected void doParse (Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { super .doParse(element, parserContext, builder); builder.addPropertyValue("ignoreUnresolvablePlaceholders" , Boolean.valueOf(element.getAttribute("ignore-unresolvable" ))); String systemPropertiesModeName = element.getAttribute(SYSTEM_PROPERTIES_MODE_ATTRIBUTE); if (StringUtils.hasLength(systemPropertiesModeName) && !systemPropertiesModeName.equals(SYSTEM_PROPERTIES_MODE_DEFAULT)) { builder.addPropertyValue("systemPropertiesModeName" , "SYSTEM_PROPERTIES_MODE_" + systemPropertiesModeName); } if (element.hasAttribute("value-separator" )) { builder.addPropertyValue("valueSeparator" , element.getAttribute("value-separator" )); } if (element.hasAttribute("trim-values" )) { builder.addPropertyValue("trimValues" , element.getAttribute("trim-values" )); } if (element.hasAttribute("null-value" )) { builder.addPropertyValue("nullValue" , element.getAttribute("null-value" )); } }
返回的bean :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 protected Class<?> getBeanClass(Element element) { if (SYSTEM_PROPERTIES_MODE_DEFAULT.equals(element.getAttribute(SYSTEM_PROPERTIES_MODE_ATTRIBUTE))) { return PropertySourcesPlaceholderConfigurer.class; } return PropertyPlaceholderConfigurer.class; }
小结 从property-placeholder
标签中解析出的属性:
xml属性
bean 属性
location
locations
properties-ref
properties
file-encoding
fileEncoding
order
order
ignore-resource-not-found
ignoreResourceNotFound
local-override
localOverride
ignore-unresolvable
ignoreUnresolvablePlaceholders
system-properties-mode
systemPropertiesModeName
value-separator
valueSeparator
trim-values
trimValues
null-value
nullValue
property-override
标签解析org.springframework.context.config.PropertyOverrideBeanDefinitionParser
类用来解析该标签,与property-placeholder
标签类似,该类也继承自AbstractPropertyLoadingBeanDefinitionParser
,与property-placeholder
标签有一些共有的属性。
doParse
方法:
1 2 3 4 5 6 7 8 @Override protected void doParse (Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { super .doParse(element, parserContext, builder); builder.addPropertyValue("ignoreInvalidKeys" , Boolean.valueOf(element.getAttribute("ignore-unresolvable" ))); }
getBeanClass
方法:
1 2 3 protected Class<?> getBeanClass(Element element) { return PropertyOverrideConfigurer.class; }
小结 从property-override
标签中解析出的属性:
xml属性
bean 属性
location
locations
properties-ref
properties
file-encoding
fileEncoding
order
order
ignore-resource-not-found
ignoreResourceNotFound
local-override
localOverride
ignore-unresolvable
ignoreInvalidKeys
annotation-config
标签解析org.springframework.context.annotation.AnnotationConfigBeanDefinitionParser
用来解析该标签。该类直接实现了BeanDefinitionParser
接口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public BeanDefinition parse (Element element, ParserContext parserContext) { Object source = parserContext.extractSource(element); Set<BeanDefinitionHolder> processorDefinitions = AnnotationConfigUtils.registerAnnotationConfigProcessors(parserContext.getRegistry(), source); CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source); parserContext.pushContainingComponent(compDefinition); for (BeanDefinitionHolder processorDefinition : processorDefinitions) { parserContext.registerComponent(new BeanComponentDefinition(processorDefinition)); } parserContext.popAndRegisterContainingComponent(); return null ; }
从上面的代码中可以看到,该解析器注册了几个BeanPostProcessor
, 用来处理注解配置,这里先不展开,后面再具体看一下这几个类的作用。其实整个解析器关键的代码只有这一句:
AnnotationConfigUtils.registerAnnotationConfigProcessors(parserContext.getRegistry(), source);
后面的代码用来保证整个解析链的完整性,需要发布解析完成的事件。
component-scan
标签解析org.springframework.context.annotation.ComponentScanBeanDefinitionParser
类用来解析该标签。该类直接实现了BeanDefinitionParser
接口。
parse
方法1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public BeanDefinition parse (Element element, ParserContext parserContext) { String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE); basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage); String[] basePackages = StringUtils.tokenizeToStringArray(basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element); Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages); registerComponents(parserContext.getReaderContext(), beanDefinitions, element); return null ; }
小结 该解析器主要是配置一个ClassPathBeanDefinitionScanner
,然后调用其doScan
方法,默认扫描@Component
注解的类,并且可以配置过滤器调整扫描范围。解析出来的扫描路径以classpath*:
开头,因此可以扫描到jar里面的注解。
load-time-weaver
标签解析org.springframework.context.config.LoadTimeWeaverBeanDefinitionParser
标签用来解析该标签。继承自org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser
,返回一个bean定义。
getBeanClassName
方法:
1 2 3 4 5 6 7 protected String getBeanClassName (Element element) { if (element.hasAttribute(WEAVER_CLASS_ATTRIBUTE)) { return element.getAttribute(WEAVER_CLASS_ATTRIBUTE); } return DEFAULT_LOAD_TIME_WEAVER_CLASS_NAME; }
doParse
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 protected void doParse (Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); if (isAspectJWeavingEnabled(element.getAttribute(ASPECTJ_WEAVING_ATTRIBUTE), parserContext)) { if (!parserContext.getRegistry().containsBeanDefinition(ASPECTJ_WEAVING_ENABLER_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(ASPECTJ_WEAVING_ENABLER_CLASS_NAME); parserContext.registerBeanComponent( new BeanComponentDefinition(def, ASPECTJ_WEAVING_ENABLER_BEAN_NAME)); } if (isBeanConfigurerAspectEnabled(parserContext.getReaderContext().getBeanClassLoader())) { new SpringConfiguredBeanDefinitionParser().parse(element, parserContext); } }
该标签由org.springframework.context.config.SpringConfiguredBeanDefinitionParser
来解析。
parse
方法:
1 2 3 4 5 6 7 8 9 10 11 12 public BeanDefinition parse (Element element, ParserContext parserContext) { if (!parserContext.getRegistry().containsBeanDefinition(BEAN_CONFIGURER_ASPECT_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(); def.setBeanClassName(BEAN_CONFIGURER_ASPECT_CLASS_NAME); def.setFactoryMethodName("aspectOf" ); def.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); def.setSource(parserContext.extractSource(element)); parserContext.registerBeanComponent(new BeanComponentDefinition(def, BEAN_CONFIGURER_ASPECT_BEAN_NAME)); } return null ; }
AopNamespaceHandler
Aop 命名空间解析器用于aop
命名空间的NamespaceHandler
。继承自NamespaceHandlerSupport
,注册以下BeanDefinitionParser
:
标签
解析类
config
ConfigBeanDefinitionParser
aspectj-autoproxy
AspectJAutoProxyBeanDefinitionParser
注册BeanDefinitionDecorator
:
属性
解析类
scoped-proxy
ScopedProxyBeanDefinitionDecorator
为<aop:config>
标签提供一个BeanDefinitionParser
。config
标签可以包含嵌套的pointcut
、advisor
和aspect
标签。
pointcut
标签允许使用简单的语法创建命名的AspectJExpressionPointcut
bean:
1 <aop:pointcut id ="getNameCalls" expression ="execution(* *..ITestBean.getName(..))" />
使用advisor
标签,可以配置org.springframework.aop.Advisor
并自动将其应用于org.springframework.beans.factory.BeanFactory
中所有相关bean。advisor
标签支持内联和引用pointcut
:
1 2 3 4 5 6 7 <aop:advisor id ="getAgeAdvisor" pointcut ="execution(* *..ITestBean.getAge(..))" advice-ref ="getAgeCounter" /> <aop:advisor id ="getNameAdvisor" pointcut-ref ="getNameCalls" advice-ref ="getNameCounter" />
<aop:config>
标签解析org.springframework.aop.config.ConfigBeanDefinitionParser
类用来解析该标签。实现了BeanDefinitionParser
接口
parse
方法:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 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); } else if (ASPECT.equals(localName)) { parseAspect(elt, parserContext); } } parserContext.popAndRegisterContainingComponent(); return null ; }
parsePointcut
方法解析切入点1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 private AbstractBeanDefinition parsePointcut (Element pointcutElement, ParserContext parserContext) { String id = pointcutElement.getAttribute(ID); String expression = pointcutElement.getAttribute(EXPRESSION); AbstractBeanDefinition pointcutDefinition = null ; try { this .parseState.push(new PointcutEntry(id)); pointcutDefinition = createPointcutDefinition(expression); pointcutDefinition.setSource(parserContext.extractSource(pointcutElement)); String pointcutBeanName = id; if (StringUtils.hasText(pointcutBeanName)) { parserContext.getRegistry().registerBeanDefinition(pointcutBeanName, pointcutDefinition); } else { pointcutBeanName = parserContext.getReaderContext().registerWithGeneratedName(pointcutDefinition); } parserContext.registerComponent( new PointcutComponentDefinition(pointcutBeanName, pointcutDefinition, expression)); } finally { this .parseState.pop(); } return pointcutDefinition; }
parseAdvisor
解析advisor
标签1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 private void parseAdvisor (Element advisorElement, ParserContext parserContext) { AbstractBeanDefinition advisorDef = createAdvisorBeanDefinition(advisorElement, parserContext); String id = advisorElement.getAttribute(ID); try { this .parseState.push(new AdvisorEntry(id)); String advisorBeanName = id; if (StringUtils.hasText(advisorBeanName)) { parserContext.getRegistry().registerBeanDefinition(advisorBeanName, advisorDef); } else { advisorBeanName = parserContext.getReaderContext().registerWithGeneratedName(advisorDef); } 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(); } }
parseAspect
解析切面1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 private void parseAspect (Element aspectElement, ParserContext parserContext) { String aspectId = aspectElement.getAttribute(ID); String aspectName = aspectElement.getAttribute(REF); try { this .parseState.push(new AspectEntry(aspectId, aspectName)); List<BeanDefinition> beanDefinitions = new ArrayList<>(); List<BeanReference> beanReferences = new ArrayList<>(); 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)); } 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); } } 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(); } }
小结 注册的BeanDefinition
如下:
标签
BeanDefinition
config
AspectJAwareAdvisorAutoProxyCreator
pointcut
AspectJExpressionPointcut
advisor
DefaultBeanFactoryPointcutAdvisor
before,after,after-returning,after-throwing,around
AspectJPointcutAdvisor
declare-parents
DeclareParentsAdvisor
<aop:aspectj-autoproxy>
标签解析AspectJAutoProxyBeanDefinitionParser
类用来解析该标签,实现了BeanDefinitionParser
接口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public BeanDefinition parse (Element element, ParserContext parserContext) { AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element); extendBeanDefinition(element, parserContext); return null ; } 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); } }
小结 注册的BeanDefinition
如下:
标签
BeanDefinition
aspectj-autoproxy
AnnotationAwareAspectJAutoProxyCreator
MvcNamespaceHandler
解析MVC命名空间该类init
方法中注册了下面的解析器:
annotation-driven
AnnotationDrivenBeanDefinitionParser
default-servlet-handler
DefaultServletHandlerBeanDefinitionParser
interceptors
InterceptorsBeanDefinitionParser
resources
ResourcesBeanDefinitionParser
view-controller
ViewControllerBeanDefinitionParser
redirect-view-controller
ViewControllerBeanDefinitionParser
status-controller
ViewControllerBeanDefinitionParser
view-resolvers
ViewResolversBeanDefinitionParser
tiles-configurer
TilesConfigurerBeanDefinitionParser
freemarker-configurer
FreeMarkerConfigurerBeanDefinitionParser
groovy-configurer
GroovyMarkupConfigurerBeanDefinitionParser
script-template-configurer
ScriptTemplateConfigurerBeanDefinitionParser
cors
CorsBeanDefinitionParser
后面再专门写一篇讲MVC的。
自定义命名空间示例 自定义命名空间可以分为步:
定义NamespaceHandler
定义命名空间地址,创建META-INF/spring.handlers
文件,注册NamespaceHandler
如果NamespaceHandler
继承自NamespaceHandlerSupport
,则定义BeanDefinitionParser
。用来解析具体的标签
自定义NamespaceHandler
为了方便,通常是继承NamespaceHandlerSupport
。在init
方法中注册标签解析器。
1 2 3 4 5 6 public class MyNamespaceHandler extends NamespaceHandlerSupport { @Override public void init () { registerBeanDefinitionParser("start-up" ,new MyNamespaceStartUpBeanDefinitionParser()); } }
注册NamespaceHandler
在META-INF
目录下创建spring.handlers
文件,文件内容为:
1 http\://www.springframework.org/schema/my =cn.sexycode.spring.study.chapter3.MyNamespaceHandler
Spring 在会读到自定义命名空间时,从这个文件中解析对应的handler。=
前面为命名空间地址
实现BeanDefinitionParser
由于handler继承自NamespaceHandlerSupport
,那么我们可以实现这个接口用来解析标签。Spring 为我们提供了一个抽象类,用来只返回一个bean 定义的时候使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class MyNamespaceStartUpBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { @Override protected Class<?> getBeanClass(Element element) { return MyBeanPostProcessor.class; } @Override protected boolean shouldGenerateId () { return true ; } }
使用示例 上述几步之后,我们就可以在例子中使用了。xml文件:
1 2 3 4 5 6 7 8 <?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:my ="http://www.springframework.org/schema/my" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" > <my:start-up /> </beans >
因为我们没有定义dtd,导致无法通过校验。我们使用时可以设置为不校验。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class MyNamespaceHandlerDemo { public static void main (String[] args) { DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); reader.setValidating(false ); reader.loadBeanDefinitions("MyNamespaceHandlerDemo.xml" ); System.out.println(factory.getBean(MyBeanPostProcessor.class)); } }
cn.sexycode.spring.study.chapter3.MyBeanPostProcessor@78b1cc93
可以看到已经正确注册到容器中。