#
自定义标签的使用
主要内容都在书上,p80
关键的部分为
- 创建XSD文件
- 创建类 继承AbstractSingleBeanDefinitionParser , 其继承关系最主要是实现了
BeanDefinitionParser
接口,顾名思义,可以用来解析bean. - 创建Handler文件, 继承自 NamespaceHandlerSupport ,目的是将上面的组件注册到Spring容器内
- 编写Spring.handlers 和 Spring.schemas文件.
这样,自定义的配置就结束了.
Spring加载自定义bean的流程主要就是遇到自定义标签后就去Spring.handlers和Spring.schemas中取找对应的handler 和 XSD .从而可以拿到parser
而代码里的主要步骤为
- 拿到标签对应的命名空间
- 根据命名空间找到对应的handler
- 调用handler的parse方法
#
获取标签的命名空间
调用org.w3c.dom.Node中的getNamespaceURI()
#
提取自定义标签处理器
1
| NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
|
这个resolve方法是接口,由DefaultNamespaceHandlerResolver 默认命名空间解析器来实现,其实现里面的第一步便是
1
| Map<String, Object> handlerMappings = this.getHandlerMappings();
|
原来handlerMappings
是个map,想必是在注册时肯定把解析器添加到这个map里面来,用namespaceUri做key,这样找的时候就很好找了.
取出结果如果是NamespaceHandler 便可以返回,往下读我们发现,之所以这么爽取出来就是handler,是因为已经做过这个解析,把找出的handler塞入了map,相当于是缓存了.
否则就是类名 className, 我们需要把它转为handler
1
2
3
4
5
6
7
8
9
10
11
12
13
| //使用反射,将类路径转化为类
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");
} else {
//初始化类
NamespaceHandler namespaceHandler = (NamespaceHandler)BeanUtils.instantiateClass(handlerClass);
//调用类自定义的init()方法,这是所有handler都必须实现的方法
namespaceHandler.init();
//塞入缓存
handlerMappings.put(namespaceUri, namespaceHandler);
return namespaceHandler;
}
|
#
设计模式–单例模式
this.getHandlerMappings(); 获取handlerMapping处使用了单例模式
声明为volatile
1
| private volatile Map<String, Object> handlerMappings;
|
获取单例时使用双重检查锁定
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| private Map<String, Object> getHandlerMappings() {
if (this.handlerMappings == null) {
synchronized(this) {
if (this.handlerMappings == null) {
try {
//读取"META-INF/spring.handlers" 转为map
Properties mappings = PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Loaded NamespaceHandler mappings: " + mappings);
}
Map<String, Object> handlerMappings = new ConcurrentHashMap(mappings.size());
CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
this.handlerMappings = handlerMappings;
} catch (IOException var5) {
throw new IllegalStateException("Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", var5);
}
}
}
}
return this.handlerMappings;
}
|
上面的volatile非常关键,如果没有的话,在初始化对象 和 设置handlerMapping指向内存空间 中间可能发生重排序,导致另外的线程拿到了handlerMapping的空间地址,但是其实还没有初始化完成.
#
标签解析
拿到解析器之后,就调用解析的parse方法,返回BeanDefinition
我们的自定义handler里面无需实现parse方法,在父类NamespaceHandlerSupport
中自有实现,
1
2
3
| public BeanDefinition parse(Element element, ParserContext parserContext) {
return this.findParserForElement(element, parserContext).parse(element, parserContext);
}
|
而parse方法主要步骤就是调用自己实现类的parseInternal
方法,parseInternal
方法除了调用我们实现的doParse方法之外,首先会进行一系列的数据准备,包括对beanClass.scope.lazyInit等属性的准备