《spring源码深度解析》spring BeanDefinition解析(自定义标签)

———— 5.1.3.RELEASE
words: 1.4k    views:    time: 7min
IOC


上一篇梳理了XmlBeanDefinitionReader解析注册BeanDefinition的过程,但主要是针对默认标签的解析,这里主要介绍下其对于自定义标签的解析策略。

示例

扩展spring自定义配置标签大致需要以下几个步骤:

  1. 创建一个需要扩展的组件
  2. 定义一个XSD文件描述组件内容
  3. 创建一个文件,实现BeanDefinitionParser接口,用来解析XSD文件中的定义
  4. 创建一个Handler,扩展自NamespaceHandlerSupport,将组件注册到spring容器
  5. 编写spring.handlers和spring.schemas文件
User.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class User {

private String name;

private String email;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}
}
META-INF/user.xsd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8"?>
<!-- xmlns:xs 表示数据类型等定义来自w3 -->
<!-- targetNamespace 表示文档中要定义的元素来自什么命名空间 -->
<!-- xmlns 表示此文档的默认命名空间是什么 -->
<!-- elementFormDefault 表示要求xml文档的每一个元素都要有命名空间指定 -->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.shanhm1991.com/schema/user" xmlns="http://www.shanhm1991.com/schema/user"
elementFormDefault="qualified">

<xs:element name="user">
<xs:complexType>
<xs:attribute name="id" type="xs:string" />
<xs:attribute name="name" type="xs:string" />
<xs:attribute name="email" type="xs:string" />
</xs:complexType>
</xs:element>
</xs:schema>
UserBeanDefinitionParser.java
1
2
3
4
5
6
7
8
9
10
11
12
13
public class UserBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {

@Override
protected Class<?> getBeanClass(Element element) {
return User.class;
}

@Override
protected void doParse(Element element, BeanDefinitionBuilder builder) {
builder.addPropertyValue("name", element.getAttribute("name"));
builder.addPropertyValue("email", element.getAttribute("email"));
}
}
UserNamespaceHandler.java
1
2
3
4
5
6
7
public class UserNamespaceHandler extends NamespaceHandlerSupport{

@Override
public void init() {
registerBeanDefinitionParser("user", new UserBeanDefinitionParser());
}
}
META-INF/spring.schemas
1
http\://www.shanhm1991.com/schema/user.xsd=META-INF/user.xsd
META-INF/spring.handlers
1
http\://www.shanhm1991.com/schema/user=test.test.UserNamespaceHandler
bean.xml
1
2
3
4
5
6
7
8
9
10
11
<?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:self="http://www.shanhm1991.com/schema/user"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.shanhm1991.com/schema/user
http://www.shanhm1991.com/schema/user.xsd">

<self:user id="test" name="shanhm1991" email="shanhm1991@163.com" />
</beans>
App.java
1
2
3
4
5
6
7
8
public class App {

public static void main(String[] args) {
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("bean.xml"));
User user = (User)beanFactory.getBean("test");
System.out.println(user.getName() + ":" + user.getEmail());
}
}

BeanDefinitionParserDelegate

parseCustomElement

下面接着前文中的BeanDefinitionParserDelegate.parseCustomElement来梳理一下其解析过程

getNamespaceHandlerResolver

首先是获取自定义标签处理器

org.springframework.beans.factory.xml.BeanDefinitionParserDelegate
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public BeanDefinition parseCustomElement(Element ele) {
return parseCustomElement(ele, null);
}

public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}

这里的NamespaceHandler是由NamespaceHandlerResolver根据namespaceUri解析出来的,而NamespaceHandlerResolver本身是在XmlBeanDefinitionReader创建XmlReaderContext时初始化和传入的

org.springframework.beans.factory.xml.XmlBeanDefinitionReader
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}

public XmlReaderContext createReaderContext(Resource resource) {
return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
this.sourceExtractor, this, getNamespaceHandlerResolver());
}

public NamespaceHandlerResolver getNamespaceHandlerResolver() {
if (this.namespaceHandlerResolver == null) {
this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
}
return this.namespaceHandlerResolver;
}

protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
ClassLoader cl = (getResourceLoader() != null ? getResourceLoader().getClassLoader() : getBeanClassLoader());
return new DefaultNamespaceHandlerResolver(cl);
}
resolveNamespaceHandler

NamespaceHandlerResolver中也是依赖spring.handlers中的配置来尝试获取NamespaceHandler的,并在初始化之后立即调用其init()方法,对应上面的示例,其实就是注册了namespace对应的BeanDefinitionParser

org.springframework.beans.factory.xml.DefaultNamespaceHandlerResolver
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 NamespaceHandler resolve(String namespaceUri) {
Map<String, Object> handlerMappings = getHandlerMappings(); // 已经配置的handler映射
Object handlerOrClassName = handlerMappings.get(namespaceUri); // 根据命名空间获取
if (handlerOrClassName == null) {
return null;
}else if (handlerOrClassName instanceof NamespaceHandler) { // 已经解析成NamespaceHandler
return (NamespaceHandler) handlerOrClassName;
}else { // 还没有解析,是className
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); // 初始化实例并放入handlerMappings
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);
}
}
}
findParserForElement

然后便是委托对应的NamespaceHandler进行解析,不过它也是交给上面初始化时注册的BeanDefinitionParser来进行解析的

org.springframework.beans.factory.xml.NamespaceHandlerSupport
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private final Map<String, BeanDefinitionParser> parsers = new HashMap<>();

public BeanDefinition parse(Element element, ParserContext parserContext) {
BeanDefinitionParser parser = findParserForElement(element, parserContext);
return (parser != null ? parser.parse(element, parserContext) : null);
}

private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
String localName = parserContext.getDelegate().getLocalName(element);
BeanDefinitionParser parser = this.parsers.get(localName);
if (parser == null) {
parserContext.getReaderContext().fatal(
"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
}
return parser;
}
parse

自定义标签并不用从头开始解析,只需基于AbstractSingleBeanDefinitionParser提供的模板方法重写其中的doParse即可

org.springframework.beans.factory.xml.AbstractBeanDefinitionParser
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
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;
}
org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser
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
protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
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) {
// Inner bean definition must receive same scope as containing bean.
builder.setScope(containingBd.getScope());
}
if (parserContext.isDefaultLazyInit()) {
// Default-lazy-init applies to custom bean definitions as well.
builder.setLazyInit(true);
}
doParse(element, parserContext, builder);
return builder.getBeanDefinition();
}


参考:

  1. 《spring源码深度解析》 郝佳