spring的目的就是让我们的bean能成为一个纯粹的POJO,可以通过xml的配置形式解析并加载bean。可以通过XMLBeanFactory来加载
XmlBeanFactory xmlBeanFactory = new XmlBeanFactory(new ClassPathResource("spring-config.xml"));
MyTest myTest = (MyTest) xmlBeanFactory.getBean("myTest");
容器加载相关类
XmlBeanFactory继承自DefaultListableBeanFactory,而DefaultListableBeanFactory是整个bean加载的核心部分,是Spring注册及加载bean的默认实现,而对于XmlBcanFactory与DefaultListableBeanFactory不同的地方其实是在XmlBeanFactory中使用了自定义的XML读取器XmlBeanDefinitionReader,实现了个性化的BeanDefinitionReader读取,DefaultListableBeanFactory继承了AbstractAutowireCapableBeanFactory 并实现了ConfigurableListableBeanFactory 以及BeanDefinitionRegistry接口。
简易执行过程
上述代码中XmlBeanFactory xmlBeanFactory = new XmlBeanFactory(new ClassPathResource("spring-config.xml"))
执行的时序图如下:
- new ClassPathResource是读取将指定的xml文件resource,通过classPathResource的方法getInputStream获取InputStream流
- 实例化XmlBeanFactory,该类是解析xml的入口。并为属性reader赋值
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
- 通过XmlBeanDefinitionReader的方法loadBeanDefinitions解析xml和实例化指定xml配置中的bean。
ClassPathResource.java
在Java中,将不同来源的资源抽象成URL,通过注册不同的handler(URLStreamHandler)来处理不同来源的资源的读取逻辑。然而URL没有默认定义相对Classpath或ServletContext等资源的handler,虽然可以注册自己的URLStreamHandler来解析特定的URL前缀(协议),比如“classpath:”,然而这需要了解URL的实现机制,而且URL也没有提供基本的方法,如检查当前资源是否存在、检查当前资源是否可读等方法。因而Spring对其内部使用到的资源实现了自己的抽象结构:Resource接口封装底层资源。
public interface InputStreamSource {
//获取输入流
InputStream getInputStream() throws IOException;
}
Resource抽象很多方法:是否存在、是否可读、是否打开、是否file、获取URL、获取URI、获取File等额外方法。
public interface Resource extends InputStreamSource {
boolean exists();
URL getURL();
URI getURI()
...
}
对不同来源的资源文件都有相应的 Resource 实现 文件(FileSystemResource)、Classpath资源(ClassPathResource)、 URL 资源(UrlResource)、 InputStream资源(InputStreamResource)、Byte数组(ByteArrayResource)等。
ClassPathResource目的就是将在classPath下的spring-config.xml文件转化为对应的输入流InputStream。其ClassPathResource的构造函数实现比较简单。对自身的属性path和classLoader赋值。getInputStream方法获取InputStream就是通过classLoader来获取。
public ClassPathResource(String path, @Nullable ClassLoader classLoader) {
Assert.notNull(path, "Path must not be null");
String pathToUse = StringUtils.cleanPath(path);//对path的处理并赋值自身属性path
if (pathToUse.startsWith("/")) {
pathToUse = pathToUse.substring(1);
}
this.path = pathToUse;
//classLoader传进来是为null。通过ClassUtils.getDefaultClassLoader()获取对应的类加载器。
this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
}
ClassUtils.getDefaultClassLoader()方法实现:首先通过线程上下文获取classLoader,如果失败,则通过ClassUtils.class获取classLoader;如果ClassUtils.class获取失败,则获取系统的classLoader
public static ClassLoader getDefaultClassLoader() {
ClassLoader cl = null;
try {
//通过上下文能获取classLoader
cl = Thread.currentThread().getContextClassLoader();
}
catch (Throwable ex) {
// Cannot access thread context ClassLoader - falling back...
}
if (cl == null) {
//通过ClassUtils的class对象获取classLoader
cl = ClassUtils.class.getClassLoader();
if (cl == null) {
// getClassLoader() returning null indicates the bootstrap ClassLoader
try {
//获取系统classLoader
cl = ClassLoader.getSystemClassLoader();
}
catch (Throwable ex) {
// Cannot access system ClassLoader - oh well, maybe the caller can live with null...
}
}
}
return cl;
}
对getInputStream()的实现有几个方式获取InputStream,优先级自上而下。通过属性的clazz的类加载来获取InputStream;通过属性classLoader来获取InputStream;如果clazz和classLoader为null,则通过系统类加载器获取InputStream。
public InputStream getInputStream() throws IOException {
InputStream is;
if (this.clazz != null) {
//通过自身属性clazz处理
is = this.clazz.getResourceAsStream(this.path);
}
else if (this.classLoader != null) {
//通过自身属性classLoader处理
is = this.classLoader.getResourceAsStream(this.path);
}
else {
//clazz和classLoader为null,通过系统类加载器获取
is = ClassLoader.getSystemResourceAsStream(this.path);
}
if (is == null) {
throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist");
}
return is;
}
XmlBeanFactory.java
public class XmlBeanFactory extends DefaultListableBeanFactory {
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, null);
}
//构造两数内部再次调用 内部构造函数
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}
}
this.reader.loadBeanDefinitions(resource)
才是资源加载的真正实现,是我们分析的重点之一,这里将xml解析document和注册实例bean操作。
从XmlBeanFactory的构造方法开始执行到XmlBeanDefinitionReader读取到Document以及注册实例bean的执行过程的时序图;这里没有展示如何读取Document以及如何通过document来注册和实例bean(下一章详细描述),实际读取xml获取document方式跟普通读取xml代码一样,spring并没有做何改变。
- 1、2两步主要获取XmlBeanDefinitionReader实例,并通过XmlBeanDefinitionReader的loadBeanDefinitions方法来正真加载资源。
- 3、组要将classPathResource封装EncodedResource,主要处理编码问题。在第4、5之间会处理,在时序图中并没有展示处理
- 4、调用自身的方法loadBeanDefinitions(EncodedResource encodedResource)
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isTraceEnabled()) {
logger.trace("Loading XML bean definitions from " + encodedResource);
}
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!");
}
...
}
- 5、通过classPathResource的方法getInputStream获取InputStream流
- 6、通过InputSource封装InputSteam,InputSource并不是spring提供的类,是在org.xml.sax包下
- 7、上述几步都是做资源前置处理,也可以说是装备阶段。方法
doLoadBeanDefinitions(inputSource, encodedResource.getResource())
里面主要代码就是8、9展示的两步操作。获取xml的Document和通过Document注册实例化bean
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
...
try {
//获取InputStream
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
//java的org.xml.sax包下InputSource
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//读取xml为document和注册实例化bean
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
}
...
}
XmlBeanDefinitionReader.doLoadBeanDefinitions方法
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
//将inputStream解析document
Document doc = doLoadDocument(inputSource, resource);
//通过document注册实例化bean
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
}
...
}
讲到这里实际上都还是准备阶段。后续将讲述默认解析标签、自定义解析标签和bean的加载。