3.0、Spring源码学习:认识 AbstractApplicationContext.refresh()

前言

体能状态先于精神状态,习惯先于决心,聚焦先于喜好

所有初始化 Spring 容器的操作都会调用这个 refresh() 方法

上一篇文章提到类这一点 2、Spring源码学习:认识加载 xml 文件的 ClassPathXmlApplicationContext
从其定义(org.springframework.context.ConfigurableApplicationContext.refresh() )来看,用于加载或者刷新来自 XML 文件、properties 文件 或者关系数据库的配置
是一个在开始阶段调用的方法,简单说,其负责加载的(non-lazy-init)单例 bean 要么全部成功,要么全部失败
refresh()的实现方法位于 org.springframework.context.support.AbstractApplicationContext

//进去 debug 跟踪 refresh()方法即可
ApplicationContext application=new ClassPathXmlApplicationContext("classpath:applicationContext2.xml");

refresh() 方法 修改 active flag 为true

refresh() 内部调用方法 org.springframework.context.support.AbstractApplicationContext.prepareRefresh(
准备刷新上下文,设置开始时间和 active flag 未true
这个 active flag 是作为 是否需要刷新 applicatonContext 对象的标志

提供一个新入口

GenericApplicationContext

因为 在 Spring 容器加载的初期, refresh()方法都会被调用,本文开启了另一个入口
org.springframework.context.support.GenericApplicationContext
这个类可以为我们提供更灵活的配置,即手动去设置配置文件,以及手动调用 refresh() 方法

@Test
public void testRefresh() {
	//org.springframework.context.support.GenericApplicationContext
	GenericApplicationContext ctx = new GenericApplicationContext();
	XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(ctx);
	//文件位于 src/main/resources 目录下
	xmlReader.loadBeanDefinitions(new ClassPathResource("applicationContext.xml"));
	PropertiesBeanDefinitionReader propReader = new PropertiesBeanDefinitionReader(ctx);
	propReader.loadBeanDefinitions(new ClassPathResource("otherBeans.properties"));
	//从这里进去源码
	ctx.refresh();
	//MyBean myBean = (MyBean) ctx.getBean("myBean");
}
refresh() 的 UML 关系图

用于表示 refresh() 方法与其他接口或类关系的UML 关系图

在这里插入图片描述

进入源码

refresh()

简单来说,Spring 创建一个webApplicationContext 之后,需要进行 refresh()动作,其中最主要的就是加载指定的 Spring 的配置文件。
并且将在这个方法中修改if (!cwac.isActive()) {判断是是否激活标志 传送门
加载或者刷新持久化对配置信息,该配置可能是一个XML 文件,properties 文件,或者关系型数据库
由于这是一个启动阶段对方法,如果启动失败对化,所有已经被创建对单例bean都需要被销毁,以避免不稳定对资源出现.换句话说,这个方法被调用完毕后,要么所有的单例bean被加载到(上下文),要么一个都没有被加载.

@Override
public void refresh() throws BeansException, IllegalStateException {
	synchronized (this.startupShutdownMonitor) {
		//刷新之前的准备工作,包括设置启动时间,是否激活标识位,初始化属性源(property source)配置
		//这里将beanFactory 是否激活修改为 true
		prepareRefresh();
		
		// 通知子类刷新自己的 BeanFactory (将 bean定义转化为 beandefination 对象,添加到 BeanFactory,但是未实例化)
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

		// Prepare the bean factory for use in this context.
		// 准备 beanFactory 供上下文使用(context,看具体实现的子类,设置ClassLoader,Spel解析器,忽略部分接口的注入,实例化部分类,添加后置处理器-postprocess)
		prepareBeanFactory(beanFactory);
		try {
			// Allows post-processing of the bean factory in context subclasses.
			//模版方法:允许子类对 beanFactory 进行 后续处理(post-processing),webXmlApplicationContext 设置了scope等参数
			postProcessBeanFactory(beanFactory);
			// 实例化、注册 beanFactory 中 扩展了 BeanFactoryPostProcessor 接口的beanFactory 处理器,比如ConfigurationClassPostProcessor 用于  @Configuration、@Bean、@PropertySource 标签的处理,在普通普通 bean 实例化前调用
			invokeBeanFactoryPostProcessors(beanFactory);
			
			// 实例化、注册、调用 beanFactory中扩展了 BeanPostProcessor 的后置处理器bean,如 AutowiredAnnotationBeanPostProcessor(处理被@Autowired注解修饰的bean并注入)
			registerBeanPostProcessors(beanFactory);

			// Initialize message source for this context.
			// 初始化 消息国际化工具类 MessageSource
			initMessageSource();

			// Initialize event multicaster for this context.
			// 初始化 事件 组播处理器
			initApplicationEventMulticaster();

			// 模版方法:初始化子类上下文中被特殊定义的bean;Spring 提供的是一个空方法,供子类扩展
			onRefresh();

			// 检查并注册事件监听器bean,广播early application events
			registerListeners();

			// 实例化所有non-lazy-init(非懒加载的) 的单例bean,实例化的过程各种 BeanPostProcessor 开始起作用
			finishBeanFactoryInitialization(beanFactory);

			// 最后一步:发布相应的事件-上下文刷新工作完成了
			//清除上下文资源缓存(如扫描中的ASM元数据);初始化上下文的生命周期处理器,并刷新(找出Spring容器中实现了Lifecycle接口的bean并执行start()方法)。发布ContextRefreshedEvent事件告知对应的ApplicationListener进行响应的操作
			finishRefresh();
		}

		catch (BeansException ex) {
			if (logger.isWarnEnabled()) {
				logger.warn("Exception encountered during context initialization - "+"cancelling refresh attempt: " + ex);
			}
			//catch逻辑:销毁已经被创建的单例 bean,避免不稳定资源的出现
			destroyBeans();

			//catch逻辑:重置 ‘active’标志
			cancelRefresh(ex);

			// 继续抛出异常给调用者
			throw ex;
		}

		finally {
			// finally逻辑:重置 Spring's core 的缓存信息,因为我们可能不会再用到这些单例bean的元数据信息了
			resetCommonCaches();
		}
	}
}

对 refresh()中关键代码的解读

prepareRefresh();

org.springframework.context.support.AbstractApplicationContext.prepareRefresh(
准备刷新上下文,设置开始时间和 active flag 以便于开始任何配置文件的初始化动作。
这里的 active flag 默认为 false,一般作为 新创建 webApplicationContext 对象是否刷新的标志 如 传送门

	protected void prepareRefresh() {
		this.startupDate = System.currentTimeMillis();
		this.closed.set(false);
		this.active.set(true);

		if (logger.isInfoEnabled()) {
			logger.info("Refreshing " + this);
		}
		// 初始化任何通配符匹配的文件,你可以任务该方法之后,操作系统相关、JVM相关信息都被加载了,准备的参数可以通过 getEnvironment() 来获取
		initPropertySources();

		//检测必须参数的非空性,通过 ConfigurablePropertyResolver#setRequiredProperties 设置
		//debug发现,默认情况该方法没啥用-没有需要检测的从参数
		getEnvironment().validateRequiredProperties();

		//实例化LinkedHashSet,允许早期的 ApplicationEvents 集合在 multicaster 可用时被发布
		this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
	}
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

本方法用于将配置文件中的bean加载到 beanDefination 中,然后将beanDefination 放入到 beanFactory,但是需要注意到是,只是加载了 bean到定义信息,还没有进行注入、实例化等工作.
限于篇幅,单独起一篇文章
3.2ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

在这里插入图片描述

prepareBeanFactory(beanFactory);

本方法用于设置 beanFactory 的类加器、SpEL解析器、bean的属性编辑器、特殊接口的免自动注入和特殊类型的指定注入,环境对象设置为单例
限于篇幅,单独写一篇文章
3.3 prepareBeanFactory(beanFactory);

postProcessBeanFactory(beanFactory);

代码运行到这里,Spring 上下文已经将基本的 beanFactory 和 beanDefination 准备好了。
该方法允许子类对 beanFactory 进行进一步的后置处理;
该方法默认为空方法,允许子类根据需要选择是否覆盖重写,体现了模板方法模式;
具体来说,如果子类为 ClassPathApplicationContext ,那么这个方法依旧是一个空方法

后置处理器的概念

Spring 中的事件允许不同的bean之间的信息交换,体现了观察者模式。
事件可以是既定的事件,也可以是自定义的事件,事件发布后由多播处理器统一处理,对所有的监听器进行事件转发——该过程可以是多线程异步的。更详细的介绍可以看下文
Spring中的事件发布和处理

invokeBeanFactoryPostProcessors(beanFactory);:修饰 beanDefination

该方法与 beanFactory 相关的后置处理器的初始化、注册和调用
该方法主要用于处理 beanDefination,此外进一步对 configuration 修饰的类中标注的 beandefination 进行发掘

for (BeanDefinitionRegistryPostProcessor postProcessor : registryPostProcessorBeans) {
	postProcessor.postProcessBeanDefinitionRegistry(registry);
}

在 postProcessBeanDefinitionRegistry 方法内部是对配置类对具体处理——发掘使用注解注册的beanDefinition

processConfigBeanDefinitions(registry);

重点关注 ConfigurationClassPostProcessor 这个后置处理器,用于处理代码中的
@Component、@Configuration、@ComponentScan 、@Bean 注解的处理
可以理解为,Spring Bean 可以来自于 XML定义,也可以来自 Java 代码中的定义.

 @Configuration
 @PropertySource("classpath:/com/myco/app.properties")

可以自己实现 BeanFactoryPostProcessor 接口,该接口注解中有描述:允许修改 beanFactory中的 beanDefination 但是千万不要进行任何可能导致bean实例化的操作,一来破坏了Spring的组织结构,而来会导致注解注入无法生效——对于注解逻辑到处理还没到呢——除非你是用xml注入——所以千万不要在自定义到 BeanFactoryPostProcessor 到逻辑中进行任何实例化的操作.

自定义处理器和默认处理器可以有一个优先级设置——实现 PriorityOrdered 接口,在调用处理器时会按照排序进行

registerBeanPostProcessors(beanFactory);:修饰bean

该方法会实例化、注册、调用 beanFactory中扩展了 BeanPostProcessor 的后置处理器bean,如 AutowiredAnnotationBeanPostProcessor(处理被@Autowired注解修饰的bean并注入)
Must be called before any instantiation of application beans(必须在应用上下文实例化前调用这些处理器)
在这里插入图片描述
该方法仅仅用于与 普通 Spring bean 相关的后置处理器的处理
允许自己实现 BeanPostProcessor ,参与 bean 实例化过程的处理,允许实现 PriorityOrdered 可口以实现优先级处理

延伸阅读:Spring 中关于 多个BeanPostProcessor代理同个Bean的问题

initMessageSource();

配置 i18n 即国际化资源
i18n项目源码

initApplicationEventMulticaster();

初始化应用的事件多播处理器,事件发布后由该处理器统一进行处理

onRefresh();

默认是空实现,给子类扩展的空间,又是模版方法模式呦

registerListeners();

注册监听器,比如上下文完成刷新事件
也可以自定义监听器,由于多播处理器并不会区分监听器,所以需要监听器自己区分不同的事件进行针对性处理。

可以自定义监听器
Spring中的事件发布和处理

finishBeanFactoryInitialization(beanFactory);

实例化所有未实例化且非懒加载的单例对象

/**
* ConfigurableListableBeanFactory beanFactory
* Instantiate all remaining (non-lazy-init) singletons.
/* 
beanFactory.preInstantiateSingletons();

创建 bean时会先判断该bean是否已经创建或者正在创建

  • 已经创建 DefaultSingletonBeanRegistry.java
/** Cache of singleton objects: bean name --> bean instance */
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);
  • 正在创建 DefaultSingletonBeanRegistry.java
/** Names of beans that are currently in creation (using a ConcurrentHashMap as a Set) */
	private final Map<String, Boolean> singletonsCurrentlyInCreation = new ConcurrentHashMap<String, Boolean>(16);

如果开始初始化一个 bean,会将这个name放入下面的map中,value=true

protected void beforeSingletonCreation(String beanName) {
		if (!this.inCreationCheckExclusions.containsKey(beanName) &&
				this.singletonsCurrentlyInCreation.put(beanName, Boolean.TRUE) != null) {
			throw new BeanCurrentlyInCreationException(beanName);
		}
	}

创建完毕后要移除

protected void afterSingletonCreation(String beanName) {
		if (!this.inCreationCheckExclusions.containsKey(beanName) &&
				!this.singletonsCurrentlyInCreation.remove(beanName)) {
			throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
		}
	}
  • 创建一个bean之后的处理
	protected void addSingleton(String beanName, Object singletonObject) {
		synchronized (this.singletonObjects) {
			this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
			this.singletonFactories.remove(beanName);
			this.earlySingletonObjects.remove(beanName);
			this.registeredSingletons.add(beanName);
		}
	}

  • 判断循环依赖的逻辑

已在创建的bean和当前创建的名字一样 AbstractBeanFactory.java

/** Names of beans that are currently in creation */
	private final ThreadLocal<Object> prototypesCurrentlyInCreation =
			new NamedThreadLocal<Object>("Prototype beans currently in creation");

  • 已经至少被创建了一次 AbstractBeanFactory.java
/**
	 * Names of beans that have already been created at least once
	 * (using a ConcurrentHashMap as a Set)
	 */
	private final Map<String, Boolean> alreadyCreated = new ConcurrentHashMap<String, Boolean>(64);

finishRefresh();
protected void finishRefresh() {
		// 初始化上下文的生命周期
		initLifecycleProcessor();

		// Propagate refresh to lifecycle processor first.
		getLifecycleProcessor().onRefresh();

		// 发布事件 ContextRefreshedEvent ,监听器可以收到这个事件
		publishEvent(new ContextRefreshedEvent(this));

		// Participate in LiveBeansView MBean, if active.
		LiveBeansView.registerApplicationContext(this);
	}
初始化期间出现异常
destroyBeans();

catch逻辑:销毁所有已经创建的单利 bean

cancelRefresh(ex);

catch逻辑:重置 ‘active’标志

参考资料

[1]、https://www.jianshu.com/p/bede81fad45c

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'requestMappingHandlerMapping' defined in class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]: Ambiguous mapping. Cannot map 'sendEmail' method com.example.spring.controller.Others.SendEmail#sendEmail(SendEmailFilter) to { [/send]}: There is already 'rabbitMqController' bean method com.example.spring.controller.Others.RabbitMqController#send() mapped. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1751) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:599) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:521) at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:961) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:915) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:584) at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:730) at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:432) at org.springframework.boot.SpringApplication.run(SpringApplication.java:308) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1302) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1291) at com.example.spring.Application.main(Application.java:11) Caused by: java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'sendEmail' method
04-20

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值