抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

SpringIOC-命名空间解析

什么是命名空间

​ 什么是spring命名空间?这个就要从XML说了,Spring的配置管理可以利用XML方式进行配置,而XML里面就有命名空间这个概念。。实际上就和标签的意思有点像 你给一个命名空间以后,这个XML文件里面就可以用那个命名空间上下文里面的标签了。简化配置用,你可以去看看Spring AOP用命名空间和不用命名空间的配置有什么区别。

​ 首先xmlns=”http://www.springframework.org/schema/beans" xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance 是必须有的。
​ xsi:schemaLocation:为指定了用于解析和校验xml的定义文件(xsd)的位置。

xmlns

​ 是为引用Spring的模块功能指定命名空间。其中的content是”http://www.springframework.org/schema/content"这个命名空间的简称,"http://www.springframework.org/schema/content"是命名空间的全称。必须在xsi中为命名空间指定对应的schema文件。

xsi:schemaLocation

​ 是为每个命名空间指定了对应的Schema文档,其定义的语法为:xsi:schemaLocation =”全称命名空间1 全称命名空间1对应的Schema文件空格”。

命名空间分析

我们拿如下的spring配置文件举例

1
2
3
4
5
6
7
8
9
10
11
12
<?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:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

<context:component-scan base-package="com.test"/>
<bean id="userDao" class="com.test.spring.dao.UserDao" p:dialect="MYSQL"/>
</beans>

解析步骤

定位命名

根据相应的标签找到对应的xmlns对应的地址

首先找到context或者p 命令空间的xmlns

context的命令空间是 http://www.springframework.org/schema/context

p的命令空间是 http://www.springframework.org/schema/p

找到解析类

根据相应的jar包,从jar包的META-INF下的spring.handlers找到xmlns地址对应的解析类

spring.handlers对应的内容

spring-bean对应标签的映射关系

spring-context对应标签的映射关系

解析类分析

我们发现映射的类都实现了NamespaceHandler接口

SimplePropertyNamespaceHandler
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
public class SimplePropertyNamespaceHandler implements NamespaceHandler {

private static final String REF_SUFFIX = "-ref";


@Override
public void init() {
}

@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
parserContext.getReaderContext().error(
"Class [" + getClass().getName() + "] does not support custom elements.", element);
return null;
}

/**
* 装饰着模式解析 p命名标签
*
* @param node
* @param definition
* @param parserContext
* @return
*/
@Override
public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext) {
if (node instanceof Attr) {
Attr attr = (Attr) node;
//命名标签的名称 例如 p:username="123" 获取到的是 username
String propertyName = parserContext.getDelegate().getLocalName(attr);
//获取到属性的值
String propertyValue = attr.getValue();
//获取definition的MutablePropertyValues列表
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);
}
//如果是以 -ref 就是对象引用注入
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;
}

}

ContextNamespaceHandler
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
public class ContextNamespaceHandler extends NamespaceHandlerSupport {

@Override
public void init() {
//注册property-placeholder 解析器
//<context:property-placeholder location="classpath*:jdbc.properties"/>
registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());

//注册property-override注册器
registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());

//注册annotation-config解析器
//<contex:annotation-config/>
registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());

//注册component-scan解析器
//<context:component-scan base-package="com.xiangxue.jack"/>
registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());

registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
}

}
进行初始化注册

NamespaceHandler接口是有一个init方法,主要来完成对应标签内容的解析类注册。

1
2
3
4
5
// 注册标签的解析类
private final Map<String, BeanDefinitionParser> parsers = new HashMap<>();
protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
this.parsers.put(elementName, parser);
}

命令空间相关解析类

Spring在解析xml文件时,主要用到NamespaceHandlerResolver接口、NamespaceHandler接口、BeanDefinitionParser接口。

NamespaceHandlerResolver

NamespaceHandlerResolver接口,是为了获取BeanDefinitionParser接口NamespaceHandler接口实例

1
2
3
4
5
6
7
8
9
10
11
12
@FunctionalInterface
public interface NamespaceHandlerResolver {

/**
* 根据对应的URI来获取对应的解析类
* 例如 根据http://www.springframework.org/schema/context 获取解析类
* ContextNamespaceHandler
*/
@Nullable
NamespaceHandler resolve(String namespaceUri);

}
DefaultNamespaceHandlerResolver 类

这个类实现了NamespaceHandlerResolver接口,是NamespaceHandlerResolver的唯一实现类,核心方法是resolve,迎来根据URI来获取对应的NamespaceHandler实现类

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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver {
....
/**
* Locate the {@link NamespaceHandler} for the supplied namespace URI
* from the configured mappings.
* 根据URI获取对那个的NamespaceHandler
*
* @param namespaceUri the relevant namespace URI
* @return the located {@link NamespaceHandler}, or {@code null} if none found
*/
@Override
@Nullable
public NamespaceHandler resolve(String namespaceUri) {
// 1.拿到配置文件的所有命名空间和对应的handler
// 例如:"http://www.springframework.org/schema/aop" -> "org.springframework.aop.config.AopNamespaceHandler"
//获取spring中所有jar包里面的 "META-INF/spring.handlers"文件,并且建立映射关系
Map<String, Object> handlerMappings = getHandlerMappings();
// 2.拿到当前命名空间对应的handler (可能是handler的className,也可能是已经实例化的handler)
//根据namespaceUri:http://www.springframework.org/schema/p,获取到这个命名空间的处理类
Object handlerOrClassName = handlerMappings.get(namespaceUri);
if (handlerOrClassName == null) {
// 2.1 如果不存在namespaceUri对应的handler,则返回null
return null;
} else if (handlerOrClassName instanceof NamespaceHandler) {
// 2.2 如果是已经实例化的handler,则直接强转返回
return (NamespaceHandler) handlerOrClassName;
} else {
// 2.3 如果是handler的className
String className = (String) handlerOrClassName;
try {
// 2.3.1 根据className,使用类加载器拿到该类
Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
// 2.3.2 校验是否是继承自NamespaceHandler(所有的handler都继承自NamespaceHandler)
if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
}
// 2.3.3 使用无参构造函数实例化handlerClass类
//实例化NamespaceHandler解析类
NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
// 2.3.4 调用handler类的初始化方法(将命名空间下的节点名和对应的解析器注册到parsers缓存中)
//调用处理类的init方法,在init方法中完成标签元素解析类的注册
namespaceHandler.init();
// 2.3.5 将实例化的handler放到缓存,替换原来的className
// 原来为: namespaceUri -> handler的className,会被覆盖成: namespaceUri -> 实例化的handler
handlerMappings.put(namespaceUri, namespaceHandler);
// 返回实例化后的handler对象
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);
}
}
}

/**
* Load the specified NamespaceHandler mappings lazily.
* 获取所有的 URI和NamespaceHandler的映射关系
*/
private Map<String, Object> getHandlerMappings() {
// 1.如果handlerMappings已经加载过,则直接返回
Map<String, Object> handlerMappings = this.handlerMappings;
if (handlerMappings == null) {
synchronized (this) {
// 2.如果handlerMappings还没加载过,则进行加载
handlerMappings = this.handlerMappings;
if (handlerMappings == null) {
if (logger.isTraceEnabled()) {
logger.trace("Loading NamespaceHandler mappings from [" + this.handlerMappingsLocation + "]");
}
try {
// 2.1 使用给定的类加载器从指定的类路径资源加载所有属性
//加载"META-INF/spring.handlers"文件过程
Properties mappings =
PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
if (logger.isTraceEnabled()) {
logger.trace("Loaded NamespaceHandler mappings: " + mappings);
}
//所有"META-INF/spring.handlers"文件里面的内容建立映射关系
handlerMappings = new ConcurrentHashMap<>(mappings.size());
// 2.2 将Properties转换成Map, mappings -> handlerMappings
//将属性文件合并到handlerMappings的MAP中
CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
// 2.3 将加载到的所有命名空间映射放到缓存
this.handlerMappings = handlerMappings;
} catch (IOException ex) {
throw new IllegalStateException(
"Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
}
}
}
}
return handlerMappings;
}


...

}

我们知道 handlerMappingsLocation 的默认值为 “META-INF/spring.handlers”,因此在这边会使用指定的 classLoader 从所有类路径资源(META-INF/spring.handlers)加载所有属性,并使用 Properties 来存放 spring.handlers 文件中的内容(命名空间和 handler 的键值对,例如下图)。

2.2 将 Properties 转成 Map。其中 key 为命名空间,例如:http://www.springframework.org/schema/context;value 为命名空间对应的 handler,例如:org.springframework.context.config.ContextNamespaceHandler,所有的handler都需要自己实现。

2.3 最后将转换后的 handlerMappings 放到缓存。

NamespaceHandler接口

NamespaceHandler接口,是为了根据不同的命名空间和元素获取不同的BeanDefinitionParser接口实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public interface NamespaceHandler {

//这个方法也重要 用来注册xml对象的配置的解析类
//例如 <tx:annotation-driven/> 对应的配置解析类
void init();

// 用来解析自定义标签的例如 context 标签
@Nullable
BeanDefinition parse(Element element, ParserContext parserContext);

//对装饰器标签解析 例如p 标签等
@Nullable
BeanDefinitionHolder decorate(Node source, BeanDefinitionHolder definition, ParserContext parserContext);

}

NamespaceHandlerSupport抽象类

NamespaceHandlerSupport实现了NamespaceHandler接口,是一个模板设计模式,是对通用的注册方法进行了抽取

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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
public abstract class NamespaceHandlerSupport implements NamespaceHandler {

/**
* 注册标签的解析类
*/
private final Map<String, BeanDefinitionParser> parsers = new HashMap<>();

/**
* Stores the {@link BeanDefinitionDecorator} implementations keyed by the
* local name of the {@link Element Elements} they handle.
*/
private final Map<String, BeanDefinitionDecorator> decorators = new HashMap<>();

/**
* Stores the {@link BeanDefinitionDecorator} implementations keyed by the local
* name of the {@link Attr Attrs} they handle.
*/
private final Map<String, BeanDefinitionDecorator> attributeDecorators = new HashMap<>();


/**
* 将元素转换成BeanDefinition
*/
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
BeanDefinitionParser parser = findParserForElement(element, parserContext);
return (parser != null ? parser.parse(element, parserContext) : null);
}

/**
* 根据元素找到对应的BeanDefinitionParser来进行将元素转换为BeanDefinition
*/
@Nullable
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;
}

/**
* 进行装饰,调用对应的装饰器进行装饰并返回装饰后的BeanDefinition
*/
@Override
@Nullable
public BeanDefinitionHolder decorate(
Node node, BeanDefinitionHolder definition, ParserContext parserContext) {

BeanDefinitionDecorator decorator = findDecoratorForNode(node, parserContext);
return (decorator != null ? decorator.decorate(node, definition, parserContext) : null);
}

/**
* 查找对应节点的装饰器
*/
@Nullable
private BeanDefinitionDecorator findDecoratorForNode(Node node, ParserContext parserContext) {
BeanDefinitionDecorator decorator = null;
String localName = parserContext.getDelegate().getLocalName(node);
if (node instanceof Element) {
decorator = this.decorators.get(localName);
} else if (node instanceof Attr) {
decorator = this.attributeDecorators.get(localName);
} else {
parserContext.getReaderContext().fatal(
"Cannot decorate based on Nodes of type [" + node.getClass().getName() + "]", node);
}
if (decorator == null) {
parserContext.getReaderContext().fatal("Cannot locate BeanDefinitionDecorator for " +
(node instanceof Element ? "element" : "attribute") + " [" + localName + "]", node);
}
return decorator;
}


/**
* 注册标签的解析类
*/
protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
this.parsers.put(elementName, parser);
}

/**
* 注册装饰器
*/
protected final void registerBeanDefinitionDecorator(String elementName, BeanDefinitionDecorator dec) {
this.decorators.put(elementName, dec);
}

/**
* 注册装饰器的属性
*/
protected final void registerBeanDefinitionDecoratorForAttribute(String attrName, BeanDefinitionDecorator dec) {
this.attributeDecorators.put(attrName, dec);
}

}

BeanDefinitionParser接口

用来不同解析类的实现接口,调用parse来解析元素

1
2
3
4
5
6
public interface BeanDefinitionParser {

@Nullable
BeanDefinition parse(Element element, ParserContext parserContext);

}

解析流程

  1. Spring在解析xml文件时,主要调用的是DefaultBeanDefinitionDocumentReader类的parseBeanDefinitions方法。
  2. 在parseBeanDefinitions方法内,http://www.springframework.org/schema/beans视为默认命名空间,调用DefaultBeanDefinitionDocumentReader类的parseDefaultElement方法解析。其他命名空间,spring视为自定义命名空间,需要调用对应的NamespaceHandler实现类的parse方法进行解析。
  3. Spring对于xml元素的解析,最终落到BeanDefinitionParser接口的实现类上。具体顺序是:
    1. 先根据命名空间找到对应的NamespaceHandler接口的实现类。
    2. 然后在实现类的init方法中,初始化当前命名空间下,不同元素对应的BeanDefinitionParser解析类。根据这种元素与解析类的对应关系,调用对应BeanDefinitionParser接口实现类的parse方法解析即可。
  4. 命名空间与NamespaceHandler对应关系在对应工程的spring.handlers文件内。

代码流程

例如自定义标签解析的方法parseCustomElement

我们拿解析context标签举例

1
2
3
4
5
6
7
8
9
10
11
12
13
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
//获取Namespace的URI
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));
}
获取标签的URI

会根据Element获取对应元素schema的namespaceUri,然后根据namespaceUri获取到具体的NamespaceHandler接口的实现。针对context这个schema,对应的是http://www.springframework.org/schema/context

1
String namespaceUri = getNamespaceURI(ele);
获取解析类

根据对应的URI获取对应的NamespaceHandler,例如context标签就会根据http://www.springframework.org/schema/context从spring.handlers配置文件获取ContextNamespaceHandler的解析文件,并在resolve方法中调用init方法完成了NamespaceHandler的初始化,接下来就是具体的解析流程

1
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
具体解析

调用parse方法完成了对具体元素的解析,完成将元素转换成BeanDefinition的过程,具体过程可以在具体的解析类会在讲源码的过程中讲解,这里只进行命名标签流程的梳理

评论