上篇分析了Spring对资源文件的加载过程,接下来我们就要开始分析Spring的IoC容器了(基于XmlBeanFactory)。
1.IoC容器启动过程简析
注意:以BeanFactory为基础的IoC容器在启动完成之后,并不会立刻实例化配置文件中的bean,首次实例化发生在我们第一次向容器索取的过程中。如果IoC容器这个概念生涩难懂、或者让人觉得有些深奥的话,那么就理解为一个类的实例化即可,只不过这个类的实例化过程,比较复杂而已!
在上一篇也介绍了IoC容器的启动过程为:加载资源文件、解析资源文件、注册BeanDefinition,我们再来看一个更为详细的流程图(该流程图只列举了比较重要的步骤)。
image.png
总而言之,就是将xml文件转换为SpringIoC容器的内部表示。
2. XmlBeanFactory初始化
打开我们之前复习Spring知识点的测试类,以xmlBeanFactory = new XmlBeanFactory(new ClassPathResource("v2/day01.xml"));
为切入点。打开XmlBeanFactory类。
public class XmlBeanFactory extends DefaultListableBeanFactory { // 实例化XmlBeanDefinitionReader对象 private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this); /** * 通过指定Resource对象创建XmlBeanFactory实例 */ public XmlBeanFactory(Resource resource) throws BeansException { this(resource, null); } /** * 通过指定Resource对象和父BeanFactory创建XmlBeanFactory实例 */ public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { // 依次向上实例化父类构造器 super(parentBeanFactory); // 解析xml配置文件,将其转换为IoC容器的内部表示 this.reader.loadBeanDefinitions(resource); } }
先来分析加载bean之前的准备工作,XmlBeanFactory父类初始化和XmlBeanDefinitionReader初始化。
2.1 父类初始化
通过XmlBeanFactory的继承关系依次调用各个父类的构造方法:
flowchat st=>start: 开始 e=>end: 结束 op1=>operation: 初始化AbstractBeanFactory op2=>operation: 初始化AbstractAutowireCapableBeanFactory op3=>operation: 初始化DefaultListableBeanFactory op4=>operation: 初始化XmlBeanFactory st->op1->op2->op3->op4->e
该过程初始化的信息很多,我们选择其中比较重要的几点做下介绍。
- 忽略指定接口的自动装配功能
public AbstractAutowireCapableBeanFactory() { super(); // 忽略指定接口的自动装配功能 ignoreDependencyInterface(BeanNameAware.class); ignoreDependencyInterface(BeanFactoryAware.class); ignoreDependencyInterface(BeanClassLoaderAware.class); }
忽略指定接口的自动装配功能:如ClassA引用了ClassB,那么当Spring在获取ClassA的实例时,如果发现ClassB还没有被初始化,那么Spring会自动初始化ClassB。但是如果ClassB实现了BeanNameAware接口的话,则Spring不会自动初始化ClassB,这就是忽略指定接口的自动装配。
2.2 初始化XmlBeanDefinitionReader
将xml文件中的配置转换为IoC内部的表示就是由XmlBeanDefinitionReader来完成的。XmlBeanDefinitionReader继承了AbstractBeanDefinitionReader类,我们来其初始化都完成了哪些操作。
protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); this.registry = registry; // Determine ResourceLoader to use. // 1、确定ResourceLoader使用。 if (this.registry instanceof ResourceLoader) { this.resourceLoader = (ResourceLoader) this.registry; } else { this.resourceLoader = new PathMatchingResourcePatternResolver(); } // Inherit Environment if possible // 2、如果环境可继承则继承registry的环境,否则重新创建环境 if (this.registry instanceof EnvironmentCapable) { this.environment = ((EnvironmentCapable) this.registry).getEnvironment(); } else { this.environment = new StandardEnvironment(); } }
- 1、确定ResourceLoader使用。该方法的核心就是确定当前使用的类加载器
public PathMatchingResourcePatternResolver() { this.resourceLoader = new DefaultResourceLoader(); }
public DefaultResourceLoader() { this.classLoader = ClassUtils.getDefaultClassLoader(); }
public static ClassLoader getDefaultClassLoader() { ClassLoader cl = null; try { //优先获取线程上下文类加载器 cl = Thread.currentThread().getContextClassLoader(); } catch (Throwable ex) { // Cannot access thread context ClassLoader - falling back... } if (cl == null) { // No thread context class loader -> use class loader of this class. // 获取当前类的类加载器 cl = ClassUtils.class.getClassLoader(); if (cl == null) { // getClassLoader() returning null indicates the bootstrap ClassLoader try { //获取SystemClassLoader cl = ClassLoader.getSystemClassLoader(); } catch (Throwable ex) { // Cannot access system ClassLoader - oh well, maybe the caller can live with null... } } } return cl; }
- 2、如果环境可继承则继承registry的环境,否则重新创建环境
系统环境包括了系统环境属性(主机变量信息)、JVM系统环境属性(JDK版本,JDK目录等)、默认激活节点、属性解析器等。
StandardEnvironment初始化
public class StandardEnvironment extends AbstractEnvironment { /** 系统环境属性 System environment property source name: {@value}. */ public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment"; /** JVM系统环境属性 JVM system properties property source name: {@value}. */ public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties"; /** * Customize the set of property sources with those appropriate for any standard * Java environment: * <ul> * <li>{@value #SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME} * <li>{@value #SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME} * </ul> * <p>Properties present in {@value #SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME} will * take precedence over those in {@value #SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME}. * @see AbstractEnvironment#customizePropertySources(MutablePropertySources) * @see #getSystemProperties() * @see #getSystemEnvironment() */ @Override protected void customizePropertySources(MutablePropertySources propertySources) { // 主要通过System类来获取信息 // 获取系统环境属性并加入到propertySources中 propertySources.addLast(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties())); // 获取JVM系统环境属性并加入到propertySources中 propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment())); } }
AbstractEnvironment初始化
private final MutablePropertySources propertySources = new MutablePropertySources(); private final ConfigurablePropertyResolver propertyResolver = new PropertySourcesPropertyResolver(this.propertySources); public AbstractEnvironment() { customizePropertySources(this.propertySources); }