Spring源码笔记-1.6 自定义标签的解析

# 自定义标签的使用

主要内容都在书上,p80

关键的部分为

  • 创建XSD文件
  • 创建类 继承AbstractSingleBeanDefinitionParser , 其继承关系最主要是实现了BeanDefinitionParser 接口,顾名思义,可以用来解析bean.
  • 创建Handler文件, 继承自 NamespaceHandlerSupport ,目的是将上面的组件注册到Spring容器内
  • 编写Spring.handlers 和 Spring.schemas文件.

这样,自定义的配置就结束了.

Spring加载自定义bean的流程主要就是遇到自定义标签后就去Spring.handlers和Spring.schemas中取找对应的handler 和 XSD .从而可以拿到parser

而代码里的主要步骤为

  1. 拿到标签对应的命名空间
  2. 根据命名空间找到对应的handler
  3. 调用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等属性的准备