三Spring-ioc容器--1容器加载源码分析

三Spring-ioc容器--1容器加载源码分析

三Spring-ioc容器--1容器加载源码分析

3.1 web与非web项目ioc容器创建

3.1.1 spring容器的概念梳理

容器:是spring框架实现功能的核心,负责对象的创建和管理,负责对象的整个生命周期的管理——创建、装配、销毁。

IOC:控制反转的思想

IOC容器:负责对应用程序内对象的创建,以及管理对象之间的依赖关系。(spring容器负责对象的创建和管理控制权,就是控制权利的反转,所以spring容器成为IOC容器。

注意:IOC容器有很多实现,spring容器只是其中一种。

spring应用上下文:spring容器的抽象化表述,就是实现需要spring帮忙管理的对象放入容器的功能,的一种容器对象。(ApplicationContext本质上说就是一个维护Bean定义以及对象之间协作关系的高级接口)

注意:spring核心是容器,而框架本身的容器有很多种实现。一种是不常用的beanfactory,提供基本的DI功能和第三方拓展点的功能;另外一种是继承beanfactory而派生的应用上下文,提供更多的服务供开发者使用。(有了应用上下文,就能够向容器注册需要spring管理的对象)

image-20220401190456505

上下文抽象接口ApplicationContext提供多种类型容器实现:

① AnnotationConfigApplicationContext:从一个或多个基于java的配置类中加载上下文定义,适用于java注解的方式;

② ClassPathXmlApplicationContext:从类路径下的一个或多个xml配置文件中加载上下文定义,适用于xml配置的方式;

③ FileSystemXmlApplicationContext:从文件系统下的一个或多个xml配置文件中加载上下文定义,也就是说系统盘符中加载xml配置文件;

④ AnnotationConfigWebApplicationContext:专门为web应用准备的,适用于注解方式;

⑤ XmlWebApplicationContext:从web应用下的一个或多个xml配置文件加载上下文定义,适用于xml配置方式。

只要将你需要IOC容器替你管理的对象基于xml也罢,java注解也好,总之你要将需要管理的对象(Spring中我们都称之问bean)、bean之间的协作关系配置好,然后利用应用上下文对象applicationContext加载进我们的Spring容器,容器就能为你的程序提供你想要的对象管理服务了。

(1)xml配置方式配置bean,并建立bean之间的依赖关系

<?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
           http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
    <bean id="man" class="spring.chapter1.domain.Man">
        <constructor-arg ref="qqCar" />
    </bean>
    <bean  id="qqCar" class="spring.chapter1.domain.QQCar"/>
</beans>

通过applicationcontext将配置加载到IOC容器,让spring管理对象,使用时候从容器中获取bean:

public class Test {
    public static void main(String[] args) {
        //加载项目中的spring配置文件到容器
//        ApplicationContext context = new ClassPathXmlApplicationContext("resouces/applicationContext.xml");
        //加载系统盘中的配置文件到容器
        ApplicationContext context = new FileSystemXmlApplicationContext("E:/Spring/applicationContext.xml");
        //从容器中获取对象实例
        Man man = context.getBean(Man.class);
        man.driveCar();
    }
}

(2)注解方式:

//同xml一样描述bean以及bean之间的依赖关系
@Configuration
public class ManConfig {
    @Bean
    public Man man() {
        return new Man(car());
    }
    @Bean
    public Car car() {
        return new QQCar();
    }
}
public class Test {
    public static void main(String[] args) {
        //从java注解的配置中加载配置到容器
        ApplicationContext context = new AnnotationConfigApplicationContext(ManConfig.class);
        //从容器中获取对象实例
        Man man = context.getBean(Man.class);
        man.driveCar();
    }
}

3.1.2 spring容器启动方式

(1)spring通过对Junit框架的整合功能来启动Spring非Web容器(推荐)

				<dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <scope>test</scope>
        </dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.1.3.RELEASE</version>
</dependency>
@RunWith(SpringJUnit4ClassRunner.class)  
@ContextConfiguration({"classpath:spring-config.xml"})  
public class TestBeanImplTest {  
    @Resource TestBean testBean;  
      
    @Test  
    public void getBeanTest(){  
        
          system.out.print(testBean);
    }  
}  

(2)在main方法中初始化spring容器(非web容器)

applicationContext.xml配置bean属性

<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
	<!-- 通过构造方法配置bean的属性 -->
	<bean id="car" class="com.atguigu.spring.beans.Car">
		<constructor-arg value="Baoma"></constructor-arg>
		<constructor-arg value="Beijing"></constructor-arg>
		<constructor-arg value="3000"></constructor-arg>
	</bean>
</beans>

main创建IOC容器:

package com.atguigu.spring beans;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
	public static void main(String[] args) {
		// 1.创建Spring的IOC容器对象-通过使用ClassPathXmlApplicationContext类加载配置文件创建容器
		// ApplicationContext代表的是IOC容器
		ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
		// 2.从IOC容器中获取Bean示例
		Car car = (Car)ctx.getBean("car");
		System.out.println(car);
	}
}

(3)调用静态方法时初始化(非web)

package com.data.test;
 
import com.data.test.monitor.ServiceFacade;
import org.slf4j.Logger;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
 
/**
 * @description: single
 * @author: feng
 * @create: 2018-12-21 17:49
 **/
 
public class SingleTest {
 
    public static Logger logger = org.slf4j.LoggerFactory.getLogger(SingleTest.class);
 
    private static SingleTest sin = new SingleTest ();
 
    public static ServiceFacade serviceFacade;
 
  	//无参构造函数,创建spring容器,并getBean来启动
    private SingleTest (){
        logger.info("初始化Spring容器开始。");
        ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:spring-config.xml");
        serviceFacade  =(ServiceFacade) ac.getBean("ebMonitorInnerServiceFacade");
        logger.info("获取bean-ServiceFacade:{}",serviceFacade );
    }
 
  	//调用static方法get,然后触发SingleTest实例化,并执行无参构造函数
    public static SingleTest get(){
        return sin;
    }
}

(4)web容器启动

web项目中,由于配置文件的存在,可以不需要开发人员自己使用ClassPathXmlApplicationContext创建IOC容器,而是在配置文件中写好,在启动项目时,由项目自己创建IOC容器

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 	
	xmlns="http://xmlns.jcp.org/xml/ns/javaee" 
	xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" 
	id="WebApp_ID" version="3.1">
  	
  				<!--  作用一:创建IOC容器对应的对象
  		   				作用二:把那些对象放到了application对应的域里面 -->
  	<context-param>
  	<!-- 配置文件的位置, 类路径下的applicationContext.xml -->
  		<param-name>contextConfigLocation</param-name>
  		<param-value>classpath:applicationContext.xml</param-value>
  	</context-param>
  	<!-- 把 Spring 容器集成到 Web 应用里面 ,采用listener创建ApplicationContext实例
  	   1.spring提供ServletContextListener的一个实现类ContextLoaderListener,
  	               该类可以作为Listener使用,在只有一个xml配置文件时,可以将其放在WEB-INF文件夹下,
  	               这时不需要<context-param>配置,项目会在创建时自动查找web-inf/applicationContext.xml文件,
  	               因此,如果只有一个配置文件,并且名为applicationContext.xml,只需要在web.xml文件中加入listener配置即可
  	   2.当有多个xml配置文件需要载入(这种情况也适用于只有一个applicationContext.xml文件),
  	              则考虑用<context-param>元素来确定配置文件的文件名与路径。ContextLoadListenter加载时,会查找名为contextConfigLocation的参数。
  	              因此,配置context-param时,参数名应该是contextConfigLocation,param-value则指定文件所在的位置,比如在类路径下的某个文件
    	 3.如是没有通过contextConfigLocation指定配置文件,spring会自动查找applicationContext.xml文件;
    	  如果有contextConfigLocation,则利用该参数确定的配置文件,如果无法找到合适的配置文件,spring将无法正常初始化
    -->
  	<listener>
  		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  	</listener>
</web-app>

总结:

可以看出,Spring项目需要借助类ApplicationContext 来手动建IOC容器而整合Spring的Web项目,在配置文件中配置监听器listener,在项目启动时,在配置文件中写清楚applicationContext.xml文件所在位置即可,项目会在启动时,自己创建IOC容器。

3.2 spring bean 后置处理器

3.2.1 概述

BeanPostProcessor接口定义回调方法,可以实现该方法来提供自己的实例化逻辑、依赖解析逻辑等。也可以在spring容器中通过插入一个或多个BeanPostProcessor的实现来完成,在bean实例化、配置、初始化完成时候,进行自定义逻辑回调方法。

可以配置多个BeanPostProcessor接口,通过执行其实现的Ordered接口提供的order属性,来控制这些后置处理器的调用顺序。

BeanPostProcessor可以对bean实例进行操作,因此spring IOC容器实例化一个bean实例,然后可以交给BeanPostProcessor来处理它们的后续工作。

工作原理:ApplicationContext会自动检测由BeanPostProcessor接口实现的自定义bean,注册这些bean为后置处理器,然后通过在容器中创建bean,在适当的时机调用。

public interface BeanPostProcessor {

	/**
	 * Apply this BeanPostProcessor to the given new bean instance <i>before</i> any bean
	 * initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
	 * or a custom init-method). The bean will already be populated with property values.
	 * The returned bean instance may be a wrapper around the original.
	 * @param bean the new bean instance
	 * @param beanName the name of the bean
	 * @return the bean instance to use, either the original or a wrapped one;
	 * if {@code null}, no subsequent BeanPostProcessors will be invoked
	 * @throws org.springframework.beans.BeansException in case of errors
	 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
	 */
  	//在传入的参数bean的初始化之前执行
	Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;

	Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;

}

示例:

在初始化bean的之前和之后实现复杂的逻辑,提供了两个访问内置bean对象的后置处理方法(*后置处理器单独注册bean*,可以获取ioc容器内的bean,完成前置和后置的处理)

img

Bean.xml文件内配置:

img

Before和after两个方法,会在bean.xml中的init配置方法执行前后分别执行。

[3.2.2 后置处理器埋点设计](#3.3.3 后置处理器的注册与实例化时机)

详见后续内容。

3.3 spring容器启动流程

3.3.1容器加载流程(容器加载,封装和注册beandefinition)

3.3.1.1 xml与注解容器加载方式对比

容器加载方式:

img

两种容器加载流程如下:

image-20230309181402838 image-20230309181427725

比较两种容器加载流程:

(1)xml配置解析

容器上下文刷新refresh入口-----AbstractApplicationContext

@Override
// 1 容器初始化过程,构造函数会调用refresh入口方法
public void refresh() throws BeansException, IllegalStateException {
     synchronized (this.startupShutdownMonitor) {
          // Prepare this context for refreshing.
          prepareRefresh();

          /** 2(1)创建BeanFactory对象ConfigurableListableBeanFactory,将BF功能代理到AC上
          		 (2)xml解析
                    	*传统标签解析:bean、import等
                    	*自定义标签解析:<context:component-scan base-package="xxx.xx.oo">
                              *自定义标签解析流程
                               a 根据标签头找到对应的namespaceUri
                               b 加载spring所有jar中的spring.handlers文件,并建立映射关系
                               c 根据namespaceUri从映射关系中找到对应的实现namespaceHandler接口的类
                               d 调用类的init方法,init方法是注册各种自定义标签的解析类
                               e 根据namespaceUri找到对应的解析类,调用parse方法完成解析
                 (3)把解析出来的xml标签信息封装入beandefinition对象
          // Tell the subclass to refresh the internal bean factory. 
          */
          ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();	

          //3 设置beanFactory一些属性,略
          // Prepare the bean factory for use in this context.
          prepareBeanFactory(beanFactory);

          try {
               //3   Allows post-processing of the bean factory in context subclasses.
               postProcessBeanFactory(beanFactory);

               //4 循环调用所有实现两大组件的后置接口????
               // Invoke factory processors registered as beans in the context.
               invokeBeanFactoryPostProcessors(beanFactory);

               /** 5 提前实例化两大组件bean---都是BeanPostProcessor
                    (AutwireAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor)
                    注册BeanPostProcessor,并提前实例化上面两个得到bean,然后缓存到CopyOnWrite容器内
                    */
               // Register bean processors that intercept bean creation.
               registerBeanPostProcessors(beanFactory);

               //6 消息国际化处理
               // Initialize message source for this context.
               initMessageSource();

               //7 初始化事件管理器
               // Initialize event multicaster for this context.
               initApplicationEventMulticaster();

               //8 执行扩展方法
               //模板方法模式,扩展其他bean的实例化(空的方法,需要由实现类完成该方法的定义,并调用)
               (spring-boot中Tomcat的启动在此扩展方法中完成)
               // Initialize other special beans in specific context subclasses.
               onRefresh();

               //9 注册事件监听器
               // Check for listener beans and register them.
               registerListeners();

               /** 10 绝大部分bean实例化(bean实例化核心方法)
               实例化完成以下过程:
                    a bean 的实例化过程
                    b ioc依赖注入
                    c 注解支持
                    d BeanPostProcessor的执行
                    e aop的入口
               */
               // Instantiate all remaining (non-lazy-init) singletons.
               finishBeanFactoryInitialization(beanFactory);

               //11 完成容器加载后续处理
               // Last step: publish corresponding event.
               finishRefresh();
          }

(2)注解扫描

AnnotationConfigApplicationContext构造函数注解扫描入口

public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {

     //step2
     public AnnotationConfigApplicationContext() {
          // 完成三大PostProcessor组件的beandefinition注册
          this.reader = new AnnotatedBeanDefinitionReader(this);
          //完成默认scanner的注册:ComponentScanner、NamespaceHandler的注册
          this.scanner = new ClassPathBeanDefinitionScanner(this);
     }

     //step1 传入basepackages包名初始化applicationcontext
     public AnnotationConfigApplicationContext(String... basePackages) {
          //组件注册、过滤器设置——见前无参数构造函数
          //step2 注册三大组件到beanfactory内
          this();
          //step3 扫描basepackage下所有@bean、@component注解的类,封装成beandefinition对象,由扫描器scanner完成
          //包扫描注册beandefinition对象
          scan(basePackages);
          //step4 执行容器的覆盖refresh方法
          refresh();
     }

完成PostProcessor三大件注册的AnnotatedBeanDefinitionReader类:

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
     Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
     Assert.notNull(environment, "Environment must not be null");
     this.registry = registry;
     this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
     
// 注册三大组件,过程同xml解析中组件注册相同
     AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}

ClassPathBeanDefinitionScanner扫描器,扫描basepackage下所有@bean、@component注解的类注册为beandefinition

public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
                                      Environment environment, @Nullable ResourceLoader resourceLoader) {

     Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
     this.registry = registry;

     //注册默认过滤器,扫描默认注解@service、@component等
     //同xml即系中扫描器的创建过程
     if (useDefaultFilters) {
          registerDefaultFilters();
     }
     setEnvironment(environment);
     setResourceLoader(resourceLoader);
}

//step3 包扫描
public int scan(String... basePackages) {
     int beanCountAtScanStart = this.registry.getBeanDefinitionCount();

     //执行包扫描
     doScan(basePackages);

     // Register annotation config processors, if necessary.
     if (this.includeAnnotationConfig) {
          AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
     }

     return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}

refresh的实现类:AbstractApplicationContext

@Override
public void refresh() throws BeansException, IllegalStateException {
     synchronized (this.startupShutdownMonitor) {
          // Prepare this context for refreshing.
          prepareRefresh();

          // Tell the subclass to refresh the internal bean factory.
          // step5 执行被重写的obtainFreshBeanFactory
          //对于注解容器,具体实现由GenericApplicationContext重写
          ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

          // Prepare the bean factory for use in this context.
          prepareBeanFactory(beanFactory);

          try {
               // Allows post-processing of the bean factory in context subclasses.
               postProcessBeanFactory(beanFactory);

               // Invoke factory processors registered as beans in the context.
               //step6 
               invokeBeanFactoryPostProcessors(beanFactory);

               // Register bean processors that intercept bean creation.
               //step7 提前实例化两大组件bean
               (autowired、common)
               registerBeanPostProcessors(beanFactory);

               // Initialize message source for this context.
               //step8消息国际化处理
               initMessageSource();

               // Initialize event multicaster for this context.
               //step9 初始化事件管理器
               initApplicationEventMulticaster();

               // Initialize other special beans in specific context subclasses.
               //step10 执行扩展方法
               onRefresh();

               // Check for listener beans and register them.
               //step11 注册事件监听器
               registerListeners();

               // Instantiate all remaining (non-lazy-init) singletons.
               //step12 绝大部分bean实例化
               finishBeanFactoryInitialization(beanFactory);

               // Last step: publish corresponding event.
               //step13 完成容器加载后续处理
               finishRefresh();
          }
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
     //模板方法设计模式
     //针对annotationconfigapplicationcontext类,由GenericApplicationContext重写此方法
refreshBeanFactory();
return getBeanFactory();
}
protected final void refreshBeanFactory() throws IllegalStateException {
     
     //注解扫描重写GenericApplicationContext.refreshBeanFactory()方法(没有重要实现);xml配置解析重写的是:AbstractRefreshableApplicationContext.refreshBeanFactory()方法(核心实现就在此)
if (!this.refreshed.compareAndSet(false, true)) {
throw new IllegalStateException(
"GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once");
}
this.beanFactory.setSerializationId(getId());
}

refresh()容器刷新的后续步骤,两种容器加载方式相同。

3.3.1.2 xml容器加载过程(源码分析)

代码分析,以DubboDemo为例。

image-20221015234658641

image-20221015234721414

image-20221015234759888

image-20230309181521638

在上面ClassPathXmlApplicationContext实例化过程中,构造函数,调用refresh()方法,该方法由AbstractApplicationContext实现。

TAG0 refresh()入口

容器上下文刷新refresh入口-----AbstractApplicationContext.refresh()

@Override
//TAG0 refresh()入口
// 1 容器applicationContext初始化过程,构造函数会调用refresh入口方法
public void refresh() throws BeansException, IllegalStateException {
     synchronized (this.startupShutdownMonitor) {
          // Prepare this context for refreshing.
          prepareRefresh();

//TAG1 obtainFreshBeanFactory()
          /** 2(1) 创建BeanFactory对象
          			(2)xml解析
                    	*传统标签解析:bean、import等
                    	*自定义标签解析:<context:component-scan base-package="xxx.xx.oo">
                              *自定义标签解析流程
                               a 根据标签头找到对应的namespaceUri
                               b 加载spring所有jar中的spring.handlers文件,并建立映射关系
                               c 根据namespaceUri从映射关系中找到对应的实现namespaceHandler接口的类
                               d 调用类的init方法,init方法是注册各种自定义标签的解析类
                               e 根据namespaceUri找到对应的解析类,调用parse方法完成解析
                 (3)把解析出来的xml标签信息封装从beandefinition对象
                 
          // 作用:让applicationContext的子类去刷新内部的beanFactory
          */
          ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();	

          //3 设置beanFactory一些属性,略
          // Prepare the bean factory for use in this context.
          prepareBeanFactory(beanFactory);

          try {
               //3   Allows post-processing of the bean factory in context subclasses.
               postProcessBeanFactory(beanFactory);

               //4 循环调用所有实现两大组件的后置接口????
               // Invoke factory processors registered as beans in the context.
               invokeBeanFactoryPostProcessors(beanFactory);

               /** 5 提前实例化两大组件bean---都是BeanPostProcessor
                    (AutwireAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor)
                    注册BeanPostProcessor,并提前实例化上面两个得到bean,然后缓存到CopyOnWrite容器内
                    */
               // Register bean processors that intercept bean creation.
               registerBeanPostProcessors(beanFactory);

               //6 消息国际化处理
               // Initialize message source for this context.
               initMessageSource();

               //7 初始化事件管理器
               // Initialize event multicaster for this context.
               initApplicationEventMulticaster();

               //8 执行扩展方法
               //模板方法模式,扩展其他bean的实例化
               (spring-boot中Tomcat的启动在此扩展方法中完成)
               // Initialize other special beans in specific context subclasses.
               onRefresh();

               //9 注册事件监听器
               // Check for listener beans and register them.
               registerListeners();

               /** 10 绝大部分bean实例化(bean实例化核心方法)
               实例化完成以下过程:
                    a bean 的实例化过程
                    b ioc依赖注入
                    c 注解支持
                    d BeanPostProcessor的执行
                    e aop的入口
               */
               // Instantiate all remaining (non-lazy-init) singletons.
               finishBeanFactoryInitialization(beanFactory);

               //11 完成容器加载后续处理
               // Last step: publish corresponding event.
               finishRefresh();
          }
TAG1 obtainFreshBeanFactory()

AbstractApplicationContext.obtainFreshBeanFactory(该方法是模板方法,具体实现由实现类实现)

/**让subclass子类,去刷新internal(内部)beanfactory
*/
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//TAG1.1
  //模板方法设计模式
		refreshBeanFactory();
     //获取beanfactory对象
		return getBeanFactory();
	}

image-20230309181558200

TAG1.1 refreshBeanFactory()

该方法的实现,由AbstractApplicationContext的子类实现(模板方法)

AbstractRefreshableApplicationContext.refreshBeanFactory()

/** 该方法的实现,对context底层的beanfactory进行真正刷新
需要先关闭之前创建的beanfactory(如果有),然后为context的下一个生命周期,初始化一个新的beanfactory
*/
	@Override
	protected final void refreshBeanFactory() throws BeansException {
		if (hasBeanFactory()) {
			destroyBeans();
			closeBeanFactory();
		}
		try {
//TAG1.1.1 创建DefaultListableBeanFactory
      /** -------------------------创建并设置创建DefaultListableBeanFactory属性------------------------*/
               //创建DefaultListableBeanFactory
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			beanFactory.setSerializationId(getId());
               /**设置beanfactory属性:
               是否允许循环依赖;是否允许相同名称注册不同的bean实现;
                修改属性后,需要调用refresh()方法刷新容器才能有效 */
			customizeBeanFactory(beanFactory);
      
//TAG1.1.2 loadBeanDefinitions
               //加载beandefinition对象
			loadBeanDefinitions(beanFactory);
			synchronized (this.beanFactoryMonitor) {
				this.beanFactory = beanFactory;
			}
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
	}

上面方法主要完成两个任务:

1 创建applicationContext的内部类beanFactory的实例-----创建DefaultListableBeanFactory,并设置其属性;

2 loadBeanDefinitions(beanFactory),执行加载beandefinition的过程

TAG1.1.2 loadBeanDefinitions

该方法由子类实现(模板方法)

AbstractXmlApplicationContext.loadBeanDefinitions()解析配置核心方法

	//通过XmlBeanDefinitionReader加载bean的定义definitions
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {

//LBD1 XmlBeanDefinitionReader创建
  /**----------------------XmlBeanDefinitionReader创建及配置属性--------------------------*/
  //XmlBeanDefinitionReader构造函数,要求传入的是BeanDefinitionRegistry(这里创建的是DefaultListableBeanFactory,其是registry的实现类)
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
		// Configure the bean definition reader with this context's
		// resource loading environment.
		beanDefinitionReader.setEnvironment(this.getEnvironment());
     //因为applicationContext实现了ResourceLoader,且实现ResourcePatternReslover
		beanDefinitionReader.setResourceLoader(this);
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

		// Allow a subclass to provide custom initialization of the reader,
		// then proceed with actually loading the bean definitions.
		initBeanDefinitionReader(beanDefinitionReader);
  
//LBD2 loadBeanDefinitions(reader)
     //核心入口方法
		loadBeanDefinitions(beanDefinitionReader);
	}

上面过程主要做如下工作:

1 创建XmlBeanDefinitionReader,并设置内部属性registry、resourceloader;然后init初始化该beandefinitionreader;

2 执行真正的loadbeandefinitions的核心方法

LBD1 XmlBeanDefinitionReader创建
image-20230309181706964

reader中与BeanDefinition的加载、注册相关类之间的引用关系。

LBD2 loadBeanDefinitions(reader)
	protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
    //获取配置资源,根据config资源是resource还是configLocations,分别reader.loadBeanDefinition加载bd
    
//LBD2.1 
		Resource[] configResources = getConfigResources();
		if (configResources != null) {
			reader.loadBeanDefinitions(configResources);
		}
    
//LBD2.2 reader.loadBeanDefinitions(locat)
          //获取xml配置文件
		String[] configLocations = getConfigLocations();
		if (configLocations != null) {
               //reader解析入口
               //解析xml:在xmlBeanDefinitionReader的父类中完成
			reader.loadBeanDefinitions(configLocations);
		}
	}

image-20220317112735305

LBD2.2 reader.loadBeanDefinitions(locat)

AbstractBeanDefinitionReader.loadBeanDefinitions

	public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
		Assert.notNull(locations, "Location array must not be null");
		int count = 0;
		for (String location : locations) {
			count += loadBeanDefinitions(location);
		}
		return count;
	}
	public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
		return loadBeanDefinitions(location, null);
	}
	public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
		ResourceLoader resourceLoader = getResourceLoader();
		if (resourceLoader == null) {
			throw new BeanDefinitionStoreException(
					"Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
		}

          //这里的resourceLoader,是ClassPathXmlApplicationContext,其也是Resolver子类(详细看ResourceLoader)
		if (resourceLoader instanceof ResourcePatternResolver) {
			// Resource pattern matching available.
			try {
        
//LBD2.2.1 resolver.getResources(loca)
                    //把spring.xml封装为resource(resource继承自InputStreamSource)
				Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
                    //入口
//LBD2.2.2 loadBeanDefinitions(resources)
				int count = loadBeanDefinitions(resources);
				if (actualResources != null) {
					Collections.addAll(actualResources, resources);
				}
				if (logger.isTraceEnabled()) {
					logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
				}
				return count;
			}
			catch (IOException ex) {
				throw new BeanDefinitionStoreException(
						"Could not resolve bean definition resource pattern [" + location + "]", ex);
			}
		}
		else {
			......
		}
	}

上面完成两个操作:

1 将spring.xml封装为resource(resource继承自InputStreamSource)

2 执行loadbeandefinition

LBD2.2.1 resolver.getResources(loca)

将配置文件加载为Resource资源

image-20221016204700028

image-20221016204724990

调用ResourcePatternResolver接口的getResources(location)方法,此处resourceLoader是classPathXmlApplicationContext,继续向下跟进代码。

image-20221016204353446

image-20221016203450515

然后进入resolver.getResource内,然后调用PathMatchingResourcePatternResolver.getResources(locationPattern)方法

PathMatchingResourcePatternResolver
  //这里传入的location位“provider.xml"
public Resource[] getResources(String locationPattern) throws IOException {
    Assert.notNull(locationPattern, "Location pattern must not be null");
  			//如果以classpath*:"开头
    if (locationPattern.startsWith("classpath*:")) {
      //截取classpath*:之后的内容。然后此处执行AntPathMatcher.isPattern
        return this.getPathMatcher().isPattern(locationPattern.substring("classpath*:".length())) ? this.findPathMatchingResources(locationPattern) : this.findAllClassPathResources(locationPattern.substring("classpath*:".length()));
    } 
  			//如果location不是以classpath*:"开头,以classpath:开头,或者其它
 		 else {
       	//截取classpath:之后的location值-----------------------------“provider.xml”执行该处
        int prefixEnd = locationPattern.indexOf(":") + 1;
        return this.getPathMatcher().isPattern(locationPattern.substring(prefixEnd)) ? this.findPathMatchingResources(locationPattern) : new Resource[]{this.getResourceLoader().getResource(locationPattern)};
    }
}
LBD2.2.1.1 spring配置文件classpath:与classpath*:加载

1 Maven创建项目,配置文件放置在resources文件夹内。classpath为java文件编译后的目录target/classes/。src源码下的xml在编译后也会复制到classpath下。

image-20221017162850609

2 配置文件的配置,在web.xml中,或者在启动xmlApplicatonContext中传入参数。

web.xml中配置contextConfigLocation的具体位置信息

image-20221017164631080

image-20221017164453235

3 Java自身提供获取resource资源的方法

class.getClassLoader().getResource(string);

class.getResource(string);

//获取配置文件,上面传入的string参数,不含classpath:,而是如"provider.xml"

4 classpath:、classpath:、classpath:xxx.xml、classpath:xxx.xml区别

classpath::该值匹配的是,当前项目编译后的文件classes文件路径——target/classes;

classpath*: : 该值匹配的地址,是当前项目classpath、jdk和项目所依赖的jar包中的classpath所有的文件中的配置文件信息;

xxx.xml:匹配某个确定的xml配置文件;

xxx*.xml:匹配相同前缀xxx的多个xml配置文件

LBD2.2.1.2 获取classpath:配置文件源码

继续向下执行,跟踪源码

 		 else {
       	//截取classpath:之后的location值-----------------------------“provider.xml”执行该处
        int prefixEnd = locationPattern.indexOf(":") + 1;
        return this.getPathMatcher().isPattern(locationPattern.substring(prefixEnd)) ? this.findPathMatchingResources(locationPattern) : new Resource[]{this.getResourceLoader().getResource(locationPattern)};
    }
}

this.getPathMatcher()获得AntPathMatcher,执行其isPattern

AntPathMatcher
public boolean isPattern(String path) {
    return path.indexOf(42) != -1 || path.indexOf(63) != -1;
}

此处,42、63分别是字符中的和?,这里是检测path("provider.xml")是否有或者?符号。如果有,表示需要对多个资源进行加载,获取Resource资源。这里不含,返回false,

new Resource[]{this.getResourceLoader().getResource(locationPattern)}   //通过resourceLoader获取resource

image-20221017170804408

然后,调用ClassPathXmlApplicationContext.getResource(location),获取配置文件对应资源

public class DefaultResourceLoader implements ResourceLoader {

 public Resource getResource(String location) {
        Assert.notNull(location, "Location must not be null");
        if (location.startsWith("/")) {
            return this.getResourceByPath(location);
        } else if (location.startsWith("classpath:")) {
            return new ClassPathResource(location.substring("classpath:".length()), this.getClassLoader());
        } else {
            try {
                URL url = new URL(location); //创建URL的location,如果没有protocol指定,会抛异常MalformedURLException
                return new UrlResource(url);  
            } catch (MalformedURLException var3) {
                return this.getResourceByPath(location); //执行resource获取
            }
        }
    }
  
  	//创建xml文件对应的resource对象
    protected Resource getResourceByPath(String path) {
        return new DefaultResourceLoader.ClassPathContextResource(path, this.getClassLoader());
    }

image-20221017172044985

LBD2.2.2 loadBeanDefinitions(resources)

继续跟进加载beandefinition核心代码:

image-20220317123245893

public abstract class AbstractBeanDefinitionReader implements EnvironmentCapable, BeanDefinitionReader {

public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
		Assert.notNull(resources, "Resource array must not be null");
		int count = 0;
		for (Resource resource : resources) {
      //子类实现
			count += loadBeanDefinitions(resource);
		}
		return count;
	}

XmlBeanDefinitionReader.loadBeanDefinitions(Resource resource)

public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
  	//将resource封装为EncodedResource,其内指定了编码类型
		return loadBeanDefinitions(new EncodedResource(resource));
	}
	public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
          
		Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
		if (currentResources == null) {
			currentResources = new HashSet<>(4);
			this.resourcesCurrentlyBeingLoaded.set(currentResources);
		}
	
		try {
               //从resource中获取文件流
			InputStream inputStream = encodedResource.getResource().getInputStream();
			try {
              //包装成inputSource类(jdk类),并设置编码类型
				InputSource inputSource = new InputSource(inputStream);
				if (encodedResource.getEncoding() != null) {
					inputSource.setEncoding(encodedResource.getEncoding());
				}
                    //实际做文档解析的入口方法
				return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
			}
			finally {
				inputStream.close();
			}
		}
	
	}
	protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {

		try {
               //解析xml配置文件的resource对象,封装成document对象
			Document doc = doLoadDocument(inputSource, resource);
               //传入doc,将解析到的标签元素封装成beandefinition对象
               //注册beandefinition入口方法
			int count = registerBeanDefinitions(doc, resource);
			if (logger.isDebugEnabled()) {
				logger.debug("Loaded " + count + " bean definitions from " + resource);
			}
			return count;
		}

完成两个任务:

1 将xml对应的resource配置文件,解析并封装成document对象;

2 调用registerBeanDefinitions(doc, resource),将解析到的xml上标签元素封装为beandefinition对象,并注册bd到beandefinitionRegistry上。

/** 注册包含在给定 DOM 文档中的 bean 定义。由 {@code loadBeanDefinitions} 调用。
*/
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
     //创建BeanDefinitionDocumentReader对象
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		int countBefore = getRegistry().getBeanDefinitionCount();
     //通过documentreader注册
     /**createReaderContext(resource)创建了XmlReaderContext(为xml上下文对象)
     XmlReaderContext持有XmlBeanDefinitionReader引用,可以获得注册器beanDefinitionRegistry,
     通过registry把解析的beandefinition对象缓存
     */
//LBD2.2.2.1 documentReader.registerBeanDefinitions
  documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
  
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}
LBD2.2.2.1 documentReader.registerBeanDefinitions

上面过程,主要是靠documentReader.regsitryBeanDefinitions,来注册。

方法中传入ReaderContext对象,用以获取registry。

image-20230309181816402

DefaultBeanDefinitionDocumentReader.registerBeanDefinitions(Document doc, XmlReaderContext readerContext)

该类作用: 接口的默认实现,该接口根据“spring-beans”XSD 格式(Spring 的默认 XML bean 定义格式)读取 bean 定义。

@Override
	public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
		this.readerContext = readerContext;
          //传入xml对应的doc下的root节点,来注册bd
		doRegisterBeanDefinitions(doc.getDocumentElement());
	}
	//用root,注册每个beandefinition
	protected void doRegisterBeanDefinitions(Element root) {
		BeanDefinitionParserDelegate parent = this.delegate;
    //用ReaderContext、element节点、parent的BeanDefinitionParserDelegate创建新的delegate委托对象
    //作用:处理嵌套的beans元素。每个delegate中保存嵌套的bean的属性信息,通过创建child delegate,并持有parent引用,在解析注册完毕后,最后重新设置为原delegate
		this.delegate = createDelegate(getReaderContext(), root, parent);

    //判断当前root节点对应的namespaceUri是否是BEANS_NAMESPACE_URI="http://www.springframework.org/schema/beans"
    //也就是判断root节点是否是beans默认命名空间上的element
		if (this.delegate.isDefaultNamespace(root)) {
      //对root上“profile”属性处理
			String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
			if (StringUtils.hasText(profileSpec)) {
				String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
						profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
				// We cannot use Profiles.of(...) since profile expressions are not supported
				// in XML config. See SPR-12458 for details.
				if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
					if (logger.isDebugEnabled()) {
						logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
								"] not matching: " + getReaderContext().getResource());
					}
					return;
				}
			}
		}
		//前置处理XML
		preProcessXml(root);
          //解析beandefinition的核心方法
          //解析 root节点和下面的所有子节点
		parseBeanDefinitions(root, this.delegate);
		postProcessXml(root);

    //处理完嵌套方法,重新设置delegate为parent
		this.delegate = parent;
	}
	protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    	//判断root节点是否是bean命名空间的元素
		if (delegate.isDefaultNamespace(root)) {
               //拿到root节点下所有子节点
			NodeList nl = root.getChildNodes();
			for (int i = 0; i < nl.getLength(); i++) {
				Node node = nl.item(i);
        //处理root下的子节点ele,用和root同样的处理逻辑
				if (node instanceof Element) {
					Element ele = (Element) node;
                         //判断方式:判断ele节点node的namespace,是否不为空或者为
                         // http://www.springframework.org/schema/beans"
					if (delegate.isDefaultNamespace(ele)) {
            
//3.3.1.2.1 默认bean解析
                              //解析普通默认标签:import,bean,alias等
						parseDefaultElement(ele, delegate);
					}
					else {
//3.3.1.2.2 自定义标签解析
                              //解析自定义标签,如<context:component-scan basepaackage="xxx.oo"等带前缀的标签
                              //带前缀的自定义标签,有一个xmlns:yyy与之对应
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
    //自定义命名空间的标签元素解析
		else {
			delegate.parseCustomElement(root);
		}
	}

BeanDefinitionParserDelegate

	// 判断是否是默认namespace
	public boolean isDefaultNamespace(@Nullable String namespaceUri) {
		return (!StringUtils.hasLength(namespaceUri) || BEANS_NAMESPACE_URI.equals(namespaceUri));
	}

	public boolean isDefaultNamespace(Node node) {
		return isDefaultNamespace(getNamespaceURI(node));
	}

parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)方法的逻辑,是通过delegate判断root节点是否是bean默认命名空间下的标签,来进行默认bean标签和自定义标签的解析逻辑;然后获取root下的所有child element,遍历处理child节点,同root同样的逻辑,解析节点。

posted @ 2023-03-10 17:07  LeasonXue  阅读(90)  评论(0编辑  收藏  举报