【Spring 源码】Spring 加载资源并装配对象的过程(XmlBeanDefinitionReader)
Spring 加载资源并装配对象过程
在Spring中对XML配置文件的解析从3.1版本开始不再推荐使用XmlBeanFactory而是使用XmlBeanDefinitionReader。
ClassPathResource resource = new ClassPathResource("bean.xml");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(resource);
// 上面步骤只是将spring bean的配置文件解析并注册到spring容器中,并未实例化
// 经过下面的步骤,才会执行实例化
Person person = (Person) factory.getBean("person");
person.say();
解析XML主要流程:
1、定义好Spring 的配置文件
2、通过Resource对象将Spring配置文件进行抽象,抽象成一个Resource对象
3、定义好Bean工厂(各种BeanFactory)
4、定义好XmlBeanDefinitionReader对象,并将工厂作为参数传递进去供后续回调使用
5、通过XmlBeanDefinitionReader对象读取之前抽象出的Resource对象(包含了XML文件的解析过程)
6、本质上,XML文件的解析是由XmlBeanDefinitionReader对象交由BeanDefinitionParserDelegate委托来完成的,实质上这里使用了委托设计模式。--注册bean定义到容器
7、IoC容器创建完闭,用户可以通过容器获取所需对象信息。--实例化
重要:在DefaultBeanDefinitionDocumentReader类中的doRegisterBeanDefinitions方法使用了经典的模板方法设计模式,子类可以重写preProcessXml与poseProcessXml方法,实现对XML配置文件的自定义扩展。
singleton bean的实例化过程:(org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String))
1、Spring 的bean实际上是缓存在 ConcurrentHashMap(singletonObjects)中(org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean))
2、在创建bean之前,首先需要将该bean的创建标识设定好,表示该bean已经或即将被创建,为的是增强缓存效率(org.springframework.beans.factory.support.AbstractBeanFactory#markBeanAsCreated)
3、根据bean的scope属性确定是singleton还是prototype等范围,然后创建相应的bean对象。
4、通过Java反射来创建bean实例,在创建之前首先检查访问修饰符,如果不是public的,则调用setAccessible(true)来突破Java的语法限制,使得可以通过如私有构造方法来创建对象实例。(org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean)
5、接下来,寻找bean的属性值,完成属性注入(org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean)
6、将所有创建出的singleton实例添加到缓存当中,供下次使用调用(org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingleton)
涉及的源代码UML
ClassPathResource
DefaultListableBeanFactory
XmlBeanDefinitionReader
验证代码
代码结构
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>spring</groupId>
<artifactId>zqq</artifactId>
<version>1.0-SNAPSHOT</version>
<name>zqq</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--1)Spring核心依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.4.RELEASE</version>
</dependency>
<!--Spring DAO层依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.3.4.RELEASE</version>
</dependency>
<!--3)Spring Web相关依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.3.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.4.RELEASE</version>
</dependency>
<!--4)Spring test相关依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.4.RELEASE</version>
</dependency>
</dependencies>
<build>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.7.1</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
App.java
package spring;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.core.io.ClassPathResource;
public class App
{
public static void main( String[] args )
{
ClassPathResource resource = new ClassPathResource("beans.xml");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(resource);
Person person = (Person) factory.getBean("person");
person.say();
}
}
Person.java
package spring;
public interface Person {
void say();
}
PersonImpl.java
package spring;
public class PersonImpl implements Person {
String name="";
@Override
public void say() {
System.out.println(name);
}
public void setName(String name) {
this.name=name;
}
}
注意:由于开始创建的是quickstart工程,没有resources目录,而beans.xml需要放到该目录下才会被找到,需要在project structure的modules管理中新建资源文件夹。