目录
4.1 示例代码讲解:XmlBeanDefinitionReader
4.1.1 配置文件封装 ClassPathResource
4.1.2 加载 Bean (XmlBeanDefinitionReader.loadBeanDefinitions(ClassPathResource))
1.在Spring中的作用
DefaultListableBeanFactory 是整个bean加载的核心部分,是 Spring 注册及加载bean的默认实现。
2.类图
如上图,红框内为 DefaultListableBeanFactory 的方法分类,按照继承的类进行分类,例如下图,分别继承自对应类的方法
3.类图分析
DefaultListableBeanFactory 继承了这些接口的特性,实现了对bean的注册后处理。
4、XmlBeanFactory
XmlBeanFactory 对DefaultListableBeanFactory进行了扩展,主要用于从 XML 配置文件中读取 BeanDefinition,对于注册和获取 Bean 都是使用父类 DefaultListableBeanFactory 继承的方法去实现。
XMLBeanFactory 中增加了 XmlBeanDefinitionReader 类型的reader 对资源文件进行读取和注册。
Resource 定位配置文件 --》 XmlBeanDefinitionReader 解析 Resource 读取注册BeanDefinition
XmlBeanFactory示例代码
application-context.xml 配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.suning.spring.boot.train.domain.User">
<property name="name" value="zhangsan"/>
<property name="id" value="1"/>
</bean>
</beans>
User
public class User {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
XmlBeanFactory 加载配置文件,初始化bean,并获取容器内bean的过程代码:
DefaultListableBeanFactory defaultListableBeanFactory = new XmlBeanFactory(new ClassPathResource("application-context.xml"));
User user = defaultListableBeanFactory.getBean(User.class);
System.out.println(user);
运行结果
4.1 示例代码讲解:XmlBeanDefinitionReader
Reader是解析 Resource配置文件的,需要先了解 applicatio-context.xml 文件怎么变成 Resource的
4.1.1 配置文件封装 ClassPathResource
Spring中用 Resource 接口封装底层资源,统一管理,例如application-context.xml。
扩展知识:ResourceLoader
Resource实现有:文件(FileSystemResource)、ClassPath资源(ClassPathResource)、URL资源(URLResource)、InputStream资源(InputStreamResource)、Byte数组(ByteArrayResource)等。
ClassPathResource 源码:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.core.io;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
public class ClassPathResource extends AbstractFileResolvingResource {
// 文件路径,例如 application-context.xml文件
private final String path;
// 类加载器
@Nullable
private ClassLoader classLoader;
@Nullable
private Class<?> clazz;
public ClassPathResource(String path) {
this(path, (ClassLoader)null);
}
// 构造函数
public ClassPathResource(String path, @Nullable ClassLoader classLoader) {
Assert.notNull(path, "Path must not be null");
String pathToUse = StringUtils.cleanPath(path);
if(pathToUse.startsWith("/")) {
pathToUse = pathToUse.substring(1);
}
// 初始化文件路径
this.path = pathToUse;
// 指定类加载器,如果没有传类加载器,则用默认的类加载器,翻看源码是:Thread.currentThread().getContextClassLoader()
this.classLoader = classLoader != null?classLoader:ClassUtils.getDefaultClassLoader();
}
public ClassPathResource(String path, @Nullable Class<?> clazz) {
Assert.notNull(path, "Path must not be null");
this.path = StringUtils.cleanPath(path);
this.clazz = clazz;
}
/** @deprecated */
@Deprecated
protected ClassPathResource(String path, @Nullable ClassLoader classLoader, @Nullable Class<?> clazz) {
this.path = StringUtils.cleanPath(path);
this.classLoader = classLoader;
this.clazz = clazz;
}
// 获取配置文件路径
public final String getPath() {
return this.path;
}
// 获取类加载器
@Nullable
public final ClassLoader getClassLoader() {
return this.clazz != null?this.clazz.getClassLoader():this.classLoader;
}
// 判断文件是否存在
public boolean exists() {
return this.resolveURL() != null;
}
@Nullable
protected URL resolveURL() {
return this.clazz != null?this.clazz.getResource(this.path):(this.classLoader != null?this.classLoader.getResource(this.path):ClassLoader.getSystemResource(this.path));
}
// 此方法继承自InputStreamSource,ClassPathResource实现了具体逻辑
// 主要就是通过类加载器加载配置文件到内存 InputStream中
public InputStream getInputStream() throws IOException {
InputStream is;
if(this.clazz != null) {
is = this.clazz.getResourceAsStream(this.path);
} else if(this.classLoader != null) {
is = this.classLoader.getResourceAsStream(this.path);
} else {
is = ClassLoader.getSystemResourceAsStream(this.path);
}
if(is == null) {
throw new FileNotFoundException(this.getDescription() + " cannot be opened because it does not exist");
} else {
return is;
}
}
public URL getURL() throws IOException {
URL url = this.resolveURL();
if(url == null) {
throw new FileNotFoundException(this.getDescription() + " cannot be resolved to URL because it does not exist");
} else {
return url;
}
}
public Resource createRelative(String relativePath) {
String pathToUse = StringUtils.applyRelativePath(this.path, relativePath);
return this.clazz != null?new ClassPathResource(pathToUse, this.clazz):new ClassPathResource(pathToUse, this.classLoader);
}
@Nullable
public String getFilename() {
return StringUtils.getFilename(this.path);
}
public String getDescription() {
StringBuilder builder = new StringBuilder("class path resource [");
String pathToUse = this.path;
if(this.clazz != null && !pathToUse.startsWith("/")) {
builder.append(ClassUtils.classPackageAsResourcePath(this.clazz));
builder.append('/');
}
if(pathToUse.startsWith("/")) {
pathToUse = pathToUse.substring(1);
}
builder.append(pathToUse);
builder.append(']');
return builder.toString();
}
public boolean equals(@Nullable Object other) {
if(this == other) {
return true;
} else if(!(other instanceof ClassPathResource)) {
return false;
} else {
ClassPathResource otherRes = (ClassPathResource)other;
return this.path.equals(otherRes.path) && ObjectUtils.nullSafeEquals(this.classLoader, otherRes.classLoader) && ObjectUtils.nullSafeEquals(this.clazz, otherRes.clazz);
}
}
public int hashCode() {
return this.path.hashCode();
}
}
如以上代码中注释的,ClassPathResource 初始化时主要保存 classpath路径下配置文件路径,选择类加载器,然后在继承自 InputStreamResource 的方法 getInputStream中 通过 类加载器加载配置文件到内存(InputStream)中,供Reader使用。
扩展:InputStreamResource 的 getInputStream方法在其他 Resource中的实现。
Spring配置文件封装成 Resource 后,读取工作就交给 XmlBeanDefinitionReader来处理。
XmlBeanFactory 源码
public class XmlBeanFactory extends DefaultListableBeanFactory {
private final XmlBeanDefinitionReader reader;
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, (BeanFactory)null);
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
// 初始化
this.reader = new XmlBeanDefinitionReader(this);
// 解析 Resource 并加载 BeanDefinition
this.reader.loadBeanDefinitions(resource);
}
}
如上所示:this.reader.loadBeanDefinitions(resource) 才是加载bean 的方法
4.1.2 加载 Bean (XmlBeanDefinitionReader.loadBeanDefinitions(ClassPathResource))
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
public static final int VALIDATION_NONE = 0;
public static final int VALIDATION_AUTO = 1;
public static final int VALIDATION_DTD = 2;
public static final int VALIDATION_XSD = 3;
private static final Constants constants = new Constants(XmlBeanDefinitionReader.class);
private int validationMode = 1;
private boolean namespaceAware = false;
private Class<? extends BeanDefinitionDocumentReader> documentReaderClass = DefaultBeanDefinitionDocumentReader.class;
private ProblemReporter problemReporter = new FailFastProblemReporter();
private ReaderEventListener eventListener = new EmptyReaderEventListener();
private SourceExtractor sourceExtractor = new NullSourceExtractor();
@Nullable
private NamespaceHandlerResolver namespaceHandlerResolver;
private DocumentLoader documentLoader = new DefaultDocumentLoader();
@Nullable
private EntityResolver entityResolver;
private ErrorHandler errorHandler;
private final XmlValidationModeDetector validationModeDetector;
private final ThreadLocal<Set<EncodedResource>> resourcesCurrentlyBeingLoaded;
public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
super(registry);
this.errorHandler = new SimpleSaxErrorHandler(this.logger);
this.validationModeDetector = new XmlValidationModeDetector();
this.resourcesCurrentlyBeingLoaded = new NamedThreadLocal("XML bean definition resources currently being loaded");
}
public void setValidating(boolean validating) {
this.validationMode = validating?1:0;
this.namespaceAware = !validating;
}
public void setValidationModeName(String validationModeName) {
this.setValidationMode(constants.asNumber(validationModeName).intValue());
}
public void setValidationMode(int validationMode) {
this.validationMode = validationMode;
}
public int getValidationMode() {
return this.validationMode;
}
public void setNamespaceAware(boolean namespaceAware) {
this.namespaceAware = namespaceAware;
}
public boolean isNamespaceAware() {
return this.namespaceAware;
}
public void setProblemReporter(@Nullable ProblemReporter problemReporter) {
this.problemReporter = (ProblemReporter)(problemReporter != null?problemReporter:new FailFastProblemReporter());
}
public void setEventListener(@Nullable ReaderEventListener eventListener) {
this.eventListener = (ReaderEventListener)(eventListener != null?eventListener:new EmptyReaderEventListener());
}
public void setSourceExtractor(@Nullable SourceExtractor sourceExtractor) {
this.sourceExtractor = (SourceExtractor)(sourceExtractor != null?sourceExtractor:new NullSourceExtractor());
}
public void setNamespaceHandlerResolver(@Nullable NamespaceHandlerResolver namespaceHandlerResolver) {
this.namespaceHandlerResolver = namespaceHandlerResolver;
}
public void setDocumentLoader(@Nullable DocumentLoader documentLoader) {
this.documentLoader = (DocumentLoader)(documentLoader != null?documentLoader:new DefaultDocumentLoader());
}
public void setEntityResolver(@Nullable EntityResolver entityResolver) {
this.entityResolver = entityResolver;
}
protected EntityResolver getEntityResolver() {
if(this.entityResolver == null) {
// 加载Resource的方法类,https://blog.csdn.net/ruanhao1203/article/details/103493017
ResourceLoader resourceLoader = this.getResourceLoader();
if(resourceLoader != null) {
// SAX解析xml用到的EntityResolver:https://blog.csdn.net/ruanhao1203/article/details/103489931
this.entityResolver = new ResourceEntityResolver(resourceLoader);
} else {
this.entityResolver = new DelegatingEntityResolver(this.getBeanClassLoader());
}
}
return this.entityResolver;
}
public void setErrorHandler(ErrorHandler errorHandler) {
this.errorHandler = errorHandler;
}
public void setDocumentReaderClass(Class<? extends BeanDefinitionDocumentReader> documentReaderClass) {
this.documentReaderClass = documentReaderClass;
}
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return this.loadBeanDefinitions(new EncodedResource(resource));
}
/**
* 1. ClassPathResource 被包装成 EncodedResource
* 2. EncodedResource中取出 InputStream 后包装成 org.xml.sax.InputSource(准备通过SAX方式读取XML文件)
* 3. 继续调用 doLoadBeanDefinitions
*/
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if(this.logger.isTraceEnabled()) {
this.logger.trace("Loading XML bean definitions from " + encodedResource);
}
// 通过属性记录已经加载的资源
Set<EncodedResource> currentResources = (Set)this.resourcesCurrentlyBeingLoaded.get();
if(currentResources == null) {
currentResources = new HashSet(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if(!((Set)currentResources).add(encodedResource)) {
throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
} else {
int var5;
try {
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if(encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
// 核心代码
var5 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());
} finally {
inputStream.close();
}
} catch (IOException var15) {
throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), var15);
} finally {
((Set)currentResources).remove(encodedResource);
if(((Set)currentResources).isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
return var5;
}
}
public int loadBeanDefinitions(InputSource inputSource) throws BeanDefinitionStoreException {
return this.loadBeanDefinitions(inputSource, "resource loaded through SAX InputSource");
}
public int loadBeanDefinitions(InputSource inputSource, @Nullable String resourceDescription) throws BeanDefinitionStoreException {
return this.doLoadBeanDefinitions(inputSource, new DescriptiveResource(resourceDescription));
}
/**
* 1.获取xml文件的验证模式
* 2.加载xml文件解析后获取 Document
* 3.根据返回的Document 注册Bean信息
*/
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
try {
Document doc = this.doLoadDocument(inputSource, resource);
int count = this.registerBeanDefinitions(doc, resource);
if(this.logger.isDebugEnabled()) {
this.logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
} catch (BeanDefinitionStoreException var5) {
throw var5;
} catch (SAXParseException var6) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + var6.getLineNumber() + " in XML document from " + resource + " is invalid", var6);
} catch (SAXException var7) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", var7);
} catch (ParserConfigurationException var8) {
throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, var8);
} catch (IOException var9) {
throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, var9);
} catch (Throwable var10) {
throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, var10);
}
}
// 获取xml解析后的 Document:https://blog.csdn.net/ruanhao1203/article/details/103487888
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
/**
* inputSource: SAX包装EncodedResource 后的
* this.getEntityResolver():SAX用,https://blog.csdn.net/ruanhao1203/article/details/103489931
* this.errorHandler:SAX用
* this.getValidationModeForResource(resource):验证 spring 的 是需要 DTD 模式还是 XSD模式
* this.isNamespaceAware()
*/
return this.documentLoader.loadDocument(inputSource, this.getEntityResolver(), this.errorHandler, this.getValidationModeForResource(resource), this.isNamespaceAware());
}
/**
* 如果 XMLBeanDefinitionReader.setValidationMode 设置了验证模式,则用设置的,
* 否则使用自动检测模式,this.detectValidationMode 委托给 XmlValidationModeDetector 验证
* spring 的 是需要 DTD 模式还是 XSD模式
*/
protected int getValidationModeForResource(Resource resource) {
int validationModeToUse = this.getValidationMode();
if(validationModeToUse != 1) {
return validationModeToUse;
} else {
int detectedMode = this.detectValidationMode(resource);
return detectedMode != 1?detectedMode:3;
}
}
// 委托给 XmlValidationModeDetector 验证 spring 的 是需要 DTD 模式还是 XSD模式
protected int detectValidationMode(Resource resource) {
if(resource.isOpen()) {
throw new BeanDefinitionStoreException("Passed-in Resource [" + resource + "] contains an open stream: cannot determine validation mode automatically. Either pass in a Resource that is able to create fresh streams, or explicitly specify the validationMode on your XmlBeanDefinitionReader instance.");
} else {
InputStream inputStream;
try {
inputStream = resource.getInputStream();
} catch (IOException var5) {
throw new BeanDefinitionStoreException("Unable to determine validation mode for [" + resource + "]: cannot open InputStream. Did you attempt to load directly from a SAX InputSource without specifying the validationMode on your XmlBeanDefinitionReader instance?", var5);
}
try {
// 委托给 XmlValidationModeDetector,验证 spring 的 是需要 DTD 模式还是 XSD模式,
// 对应源码:https://blog.csdn.net/ruanhao1203/article/details/103488757
return this.validationModeDetector.detectValidationMode(inputStream);
} catch (IOException var4) {
throw new BeanDefinitionStoreException("Unable to determine validation mode for [" + resource + "]: an error occurred whilst reading from the InputStream.", var4);
}
}
}
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader();
int countBefore = this.getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource));
return this.getRegistry().getBeanDefinitionCount() - countBefore;
}
protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
return (BeanDefinitionDocumentReader)BeanUtils.instantiateClass(this.documentReaderClass);
}
public XmlReaderContext createReaderContext(Resource resource) {
return new XmlReaderContext(resource, this.problemReporter, this.eventListener, this.sourceExtractor, this, this.getNamespaceHandlerResolver());
}
public NamespaceHandlerResolver getNamespaceHandlerResolver() {
if(this.namespaceHandlerResolver == null) {
this.namespaceHandlerResolver = this.createDefaultNamespaceHandlerResolver();
}
return this.namespaceHandlerResolver;
}
protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
ClassLoader cl = this.getResourceLoader() != null?this.getResourceLoader().getClassLoader():this.getBeanClassLoader();
return new DefaultNamespaceHandlerResolver(cl);
}
}
如上代码:
1.ClassPathResource 包装成 EncodedResource ,Encoded 编码,见名知意,就是用过指定编码读取配置文件的。
2.EncodedResource 继续包装成 InputResource
3.继续调用 doLoadBeanDefinitions
// 获取 XML 验证模式是 DTD还是XSD
// 解析xml 获取 Document
Document doc = this.doLoadDocument(inputSource, resource);
// 根据 Document 注册bean
int count = this.registerBeanDefinitions(doc, resource);
这里分为两部分
至此bean 放入spring容器就结束了
5、getBean