代码
在上一节中提到spring如何根据schemaLocation来查找对应的XSD、DTD文件。如果我们增加一个XSD、DTD文件可以根据上一节中的规则来处理。但是我们增加了XSD、DTD文件,我们是想把其中定义的内容添加到spring的 beanFactory。这个时候,应该怎么做呢。
spring 对于bean的加载
我们以xmlBeanFactory作为例子来分析一下spring如何将xml文件中的配置转换为对应的BeanDefinition接口的实现。
1.xmlBeanFactory初始化代码
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
// 使用XmlBeanDefinitionReader类来加载资源文件
this.reader.loadBeanDefinitions(resource);
}
2.对于资源文件的处理
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
// 将Resource转换为带编码的Resource
return loadBeanDefinitions(new EncodedResource(resource));
}
@Override
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isInfoEnabled()) {
logger.info("Loading XML bean definitions from " + encodedResource.getResource());
}
// 防止一个资源文件被多次引用解析
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
// 加载spring的bean
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
3.将xml文件解析为document
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
//将resource文件流转换为Document资源(上一节中有提到如何查找对于的XSD、DTD文件,就是在这里进行)
Document doc = doLoadDocument(inputSource, resource);
// 注册bean
return registerBeanDefinitions(doc, resource);
}
//....
}
/**
* 注册bean
**/
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
// 此处将对于bean的注册委托给了BeanDefinitionDocumentReader来进行处理
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
4 处理spring的profile
protected void doRegisterBeanDefinitions(Element root) {
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
// 判断是否是spring-bean的文件
if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isInfoEnabled()) {
logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
preProcessXml(root);
// 解析文件并生成对应的BeanDefinnition
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);
this.delegate = parent;
}
/**
* 解析文件并生成对应的BeanDefinnition
*/
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
// 是否是spring自带的beans空间
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
// 循环处理子节点
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
//如果是自定义的元素,需要自行处理
//在这里spring会根据不同的namespace去进行处理
delegate.parseCustomElement(root);
}
}
5 根据namespace获取对应的解析器
@Nullable
public BeanDefinition parseCustomElement(Element ele) {
return parseCustomElement(ele, null);
}
/**
* spring对于如何解析自定义标签都是在这个方法中进行处理
*
*/
@Nullable
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
// 根据元素信息获取namespace
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
// 根据namespace来获取到对应的NamespaceHandler,如果没有自定义namespacehandlerResolve,那么会使用默认的DefaultNamespaceHandlerResolver
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));
}
6.DefaultNamespaceHandlerResolver
public NamespaceHandler resolve(String namespaceUri) {
// 获取所有的handler信息
Map<String, Object> handlerMappings = getHandlerMappings();
// 根据namespace来获取指定的handler
Object handlerOrClassName = handlerMappings.get(namespaceUri);
if (handlerOrClassName == null) {
return null;
}
else if (handlerOrClassName instanceof NamespaceHandler) {
return (NamespaceHandler) handlerOrClassName;
}
else {
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的初始化接口
namespaceHandler.init();
// 将新加载的namespaceHandler存储在内存中方便下次调用
handlerMappings.put(namespaceUri, namespaceHandler);
return namespaceHandler;
}
catch (ClassNotFoundException ex) {
throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" +
namespaceUri + "] not found", ex);
}
catch (LinkageError err) {
throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" +
namespaceUri + "]: problem with handler class file or dependent class", err);
}
}
}
/*
* 获取配置文件中的硬编码
*/
private Map<String, Object> getHandlerMappings() {
Map<String, Object> handlerMappings = this.handlerMappings;
if (handlerMappings == null) {
synchronized (this) {
handlerMappings = this.handlerMappings;
if (handlerMappings == null) {
try {
// 加载所有的(META-INF/spring.handlers)文件为Properties
Properties mappings =
PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
if (logger.isDebugEnabled()) {
logger.debug("Loaded NamespaceHandler mappings: " + mappings);
}
Map<String, Object> mappingsToUse = new ConcurrentHashMap<>(mappings.size());
CollectionUtils.mergePropertiesIntoMap(mappings, mappingsToUse);
handlerMappings = mappingsToUse;
this.handlerMappings = handlerMappings;
}
catch (IOException ex) {
throw new IllegalStateException(
"Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
}
}
}
}
return handlerMappings;
}
我们可以查看一下spring自带的一些spring.handlers文件
http\://www.springframework.org/schema/c=org.springframework.beans.factory.xml.SimpleConstructorNamespaceHandler
http\://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler
http\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler
此时我们可以根据对应的namespace查找到对应的Handler处理类了,就可以对文件中的元素进行处理了
7.NamespaceHandlerSupport
spring并没有把对于xml的硬编码解析放在这里,只是会通过元素的第一级属性,去找到对应的BeanDefinitionParserr,BeanDefinitionParser才是真正对文件进行硬编码解析的地方。
/**
* 为一级元素注册对应的解析类
*
**/
@Override
public void init() {
registerBeanDefinitionParser("constant", new ConstantBeanDefinitionParser());
registerBeanDefinitionParser("property-path", new PropertyPathBeanDefinitionParser());
registerBeanDefinitionParser("list", new ListBeanDefinitionParser());
registerBeanDefinitionParser("set", new SetBeanDefinitionParser());
registerBeanDefinitionParser("map", new MapBeanDefinitionParser());
registerBeanDefinitionParser("properties", new PropertiesBeanDefinitionParser());
}
如何实现自定义的类型
- 自定义对应的XSD、DTD文件放置在JAR包中
- 添加META-INF/spring.schemas,其中根据自定的schemaLoction来映射到自定义的XSD、DTD文件位置
- 继承NamespaceHandlerSupport,实现自己的namespaceHandler,需要重写其中的init()方法,并在其中为第一层元素指定对应的BeanDefinitionParser
- 添加META-INF/spring.handlers,根据自定的namespace来映射到自己的namespaceHandler上
- 实现BeanDefinitionParser,硬编码解析XML文件,并将解析生成的BeanDefinition实现注册到BeanFactory