转载

Spring技术内幕-容器刷新:wac.refresh

Spring对于程序员说来说都不陌生;作为一个强大的开源技术,帮助我们能够更好的进行项目的开发与维护。

继上一篇 Spring技术内幕学习:Spring的启动过程 之后,关于Spring的源码学习落了一大截,因为菜鸟成长系列是目前最先想完成的,所以这块就被耽误了,不过之后将会持续更新Spring相关的技术知识。

上次在Spring的启动过程文章中对Spring的启动过程做了一个较为详细的说明和分析。那么在实际的过程中,Spring的启动实际上就是Spring容器的初始化过程。本文将从源码的角度结合自己断点执行过程中保留的现场来分析一下容器的刷新过程(主要分析前几个方法,后面几个会分开来说)。

Spring的启动是通过ContextLoaderListener来进行的,在ContextLoaderListener中通过委托父类ContextLoader的initWebApplicationContext来完成具体的初始化过程。具体的启动过程可以看下之前的那篇文章。

在initWebApplicationContext方法是用来创建容器的,核心代码如下:

Spring技术内幕-容器刷新:wac.refresh

今天主要来看configureAndRefreshWebApplicationContext方法中最后的wac.refresh()到底发生了哪些事;

Spring技术内幕-容器刷新:wac.refresh

1、obtainFreshBeanFactory:BeanFactory的刷新和创建

refresh()方法主要为IoC容器Bean的生命周期管理提供条件,Spring IoC容器载入Bean定义资源文件从其子类容器的refreshBeanFactory()方法启动,所以整个refresh()中“ConfigurableListableBeanFactory beanFactory =obtainFreshBeanFactory();”这句以后代码的都是注册容器的信息源和生命周期事件,载入过程就是从这句代码启动。 AbstractApplicationContext的obtainFreshBeanFactory()方法调用子类容器的refreshBeanFactory()方法,启动容器载入Bean定义资源文件的过程。 refresh()方法的作用是:在创建IoC容器前,如果已经有容器存在,则需要把已有的容器销毁和关闭,以保证在refresh之后使用的是新建立起来的IoC容器。refresh的作用类似于对IoC容器的重启,在新建立好的容器中对容器进行初始化,对Bean定义资源进行载入。 和refreshBeanFactory方法类似,载入Bean定义的方法loadBeanDefinitions也使用了委派模式,在AbstractRefreshableApplicationContext类中只定义了抽象方法,具体的实现调用子类容器中的方法实现。

//通知子类去刷新内部bean 工厂

Spring技术内幕-容器刷新:wac.refresh

再来看refreshBeanFactory

此实现执行该上下文的底层bean工厂的实际刷新,关闭以前的bean工厂(如果有的话),并为上下文生命周期的下一阶段初始化一个新的bean工厂。

Spring技术内幕-容器刷新:wac.refresh

customizeBeanFactory(DefaultListableBeanFactory beanFactory)

/**
        //通过当前上下文来自定义内部bean工厂<br>
	 * Customize the internal bean factory used by this context.
	 * Called for each {@link #refresh()} attempt.
	 * <p>The default implementation applies this context's
	 * {@linkplain #setAllowBeanDefinitionOverriding "allowBeanDefinitionOverriding"}
	 * and {@linkplain #setAllowCircularReferences "allowCircularReferences"} settings,
	 * if specified. Can be overridden in subclasses to customize any of
	 * {@link DefaultListableBeanFactory}'s settings.
	 * @param beanFactory the newly created bean factory for this context
	 * @see DefaultListableBeanFactory#setAllowBeanDefinitionOverriding
	 * @see DefaultListableBeanFactory#setAllowCircularReferences
	 * @see DefaultListableBeanFactory#setAllowRawInjectionDespiteWrapping
	 * @see DefaultListableBeanFactory#setAllowEagerClassLoading
	 */
	protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
		if (this.allowBeanDefinitionOverriding != null) {
			beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
		}
		if (this.allowCircularReferences != null) {
			beanFactory.setAllowCircularReferences(this.allowCircularReferences);
		}
	}

XmlWebApplicationContext类中loadBeanDefinitions(beanFactory)

@Override
	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		// Create a new XmlBeanDefinitionReader for the given BeanFactory.
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

		// Configure the bean definition reader with this context's
		// resource loading environment.
		beanDefinitionReader.setEnvironment(getEnvironment());
		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);
		loadBeanDefinitions(beanDefinitionReader);
	}

AbstractApplicationContext调用loadBeanDefinitions(DefaultListableBeanFactory beanFactory) ,此方法根据首先创建XmlBeanDefinitionReader对象,然后配置该对象的上下文和资源加载环境,同时调用子类实现的initBeanDefinitionReader对XmlBeanDefinitionReader进行个性化配置,最近后入到initBeanDefinitionReader(beanDefinitionReader)的调用:

  1. 据给定的BeanFactory创建XmlBeanDefinitionReader 对象
  2. 配置beanDefinitionReader的上下文和资源加载环境
  3. 用子类实现的initBeanDefinitionReader对XmlBeanDefinitionReader进行个性化配置initBeanDefinitionReader(beanDefinitionReader);
  4. 调用载入Bean定义的方法,在当前类中只定义了抽象的loadBeanDefinitions方法,具体的实现调用子类容器

装载bean定义通过XmlBeanDefinitionReader。

// Create a new XmlBeanDefinitionReader for the given BeanFactory. 通过给定的bean工厂创建一个新的XmlBeanDefinitionReader
1.XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

2.使用上下文的资源加载环境配置bean定义读取器。
   beanDefinitionReader.setEnvironment(getEnvironment());
   beanDefinitionReader.setResourceLoader(this);
   beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
3.允许子类提供reader的自定义初始化,然后继续实际加载bean定义。
    //通过制定的XmlBeanDefinitionReader来载入beandefinitionReader 
   initBeanDefinitionReader(beanDefinitionReader)
   // 通过制定的XmlBeanDefinitionReader来载入bean definitions
   loadBeanDefinitions(beanDefinitionReader)

AbstractApplicationContext调用loadBeanDefinitions(beanDefinitionReader),这个方法是取得资源或资源路径然后通过传入的reader去加载BeanDefinitions。

Spring技术内幕-容器刷新:wac.refresh

2、loadBeanDefinitions

目前使用Spring的配置都是基于XML的,因此使用XmlBeanDefinitionReader 中的loadBeanDefinitions方法。

Spring技术内幕-容器刷新:wac.refresh
Spring技术内幕-容器刷新:wac.refresh

看doLoadBeanDefinitions,这个就是具体的读取文件配置,然后注册成Bean

Spring技术内幕-容器刷新:wac.refresh
Spring技术内幕-容器刷新:wac.refresh

3、prepareBeanFactory

配置工厂的标准上下文特性,如上下文的类装载器和后处理器。

Spring技术内幕-容器刷新:wac.refresh
  • 告诉内部bean工厂使用上下文的类装入器等。
  • 上下文回调配置bean工厂。
  • BeanFactory接口未登记为普通工厂的解析式。MessageSource登记(为自动装配创建)作为一个Bean
  • 如果创建;就去寻找LoadTimeWeaver,然后准备组织
  • 注册默认环境bean。

通过断点来看下当前的beanFactory

Spring技术内幕-容器刷新:wac.refresh
Spring技术内幕-容器刷新:wac.refresh

继续执行...

Spring技术内幕-容器刷新:wac.refresh
Spring技术内幕-容器刷新:wac.refresh

beanDefinitionMap

Spring技术内幕-容器刷新:wac.refresh

manualSingletonNames

Spring技术内幕-容器刷新:wac.refresh

4、postProcessBeanFactory

注册web特性的全局域

1).registerWebApplicationScopes

Spring技术内幕-容器刷新:wac.refresh

注册具有web特性的域;包括:"request", "session", "globalSession", "application"

Spring技术内幕-容器刷新:wac.refresh

看下存储结构:

Spring技术内幕-容器刷新:wac.refresh

registerScope方法

Spring技术内幕-容器刷新:wac.refresh
2).registerEnvironmentBeans

注册web特性 环境bean(“contextparameters”、“ContextAttribute”)与给定的WebApplicationContext使用BeanFactory。

1.servletContext

Spring技术内幕-容器刷新:wac.refresh

2.servletConfig

Spring技术内幕-容器刷新:wac.refresh

3.registerSingleton

Spring技术内幕-容器刷新:wac.refresh
Spring技术内幕-容器刷新:wac.refresh
Spring技术内幕-容器刷新:wac.refresh

这里是找到了我们默认的配置文件参数:

Spring技术内幕-容器刷新:wac.refresh

beanName=contextParameters

Spring技术内幕-容器刷新:wac.refresh
Spring技术内幕-容器刷新:wac.refresh
Spring技术内幕-容器刷新:wac.refresh

最后是将contextAttributes放入;contextAttributes中包含的属性值比较多,具体如下面所示:

Spring技术内幕-容器刷新:wac.refresh

主要包括: javax.servlet.context.tempdir, org.apache.catalina.resources, org.springframework.web.context.support.ServletContextScope, org.apache.tomcat.util.scan.MergedWebXml, org.apache.tomcat.InstanceManager, org.apache.catalina.jsp_classpath, javax.websocket.server.ServerContainer, org.apache.tomcat.JarScanner

Spring技术内幕-容器刷新:wac.refresh

这里是把需要的东西全部载入进来了,有很多。就不贴了(mime-mapping)....

5、invokeBeanFactoryPostProcessors

BeanDefinitionRegistryPostProcessor实例化:标准BeanFactoryPostProcessor的扩展,BeanFactoryPostProcessor的作用是用来进一步定义注册的BeanDefinition,IoC容器本质就是Bean管理,所以BeanFactoryPostProcessor本身也是Bean,要对BeanFactoryPostProcessor的BeanDefinition进一步定义就通过BeanDefinitionRegistryPostProcessor进行注册,BeanDefinitionRegistryPostProcessor及其子类是Ioc容器最实例化的一类Bean。它们在ConfigurableApplicationContext(ApplicationContext子接口)实现类调用refresh()方法调用invokeBeanFactoryPostProcessors(beanFactory);方法时就被实例化。

OK,今天关于这部分的分析就到此结束了,后面的过程会在下一篇Spring系列文章中继续来讲refresh中的过程。

祝大家平安夜快乐....

如果您对系列文章有任何意见,可以给我留言,感谢大家。

下面是一个接苹果的姿势,呼呼呼.....

Spring技术内幕-容器刷新:wac.refresh
原文  https://juejin.im/post/5a3f5b43f265da432e5c37ea
正文到此结束
Loading...