3. Springboot2启动过程源码分析-prepareContext方法

系列文章目录

1.Springboot2启动过程源码分析-SpringApplication初始化

2.Springboot2启动过程源码分析-SpringApplication#run

3.Springboot2启动过程源码分析-prepareContext方法

4.Springboot2启动过程源码分析-refresh方法

5.Springboot2启动过程源码分析-Bean的实例化

6.Springboot2启动过程源码分析-总结


 

文章目录


前言

本节主要对prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner)方法进行研究


prepareContext方法

prepareContext的源码及注释如下:

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
      SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
   //1.将ConfigurableEnvironment对象添加到context中
   context.setEnvironment(environment);
   postProcessApplicationContext(context);
   //2.对各种ApplicationContextInitializer进行初始化
   applyInitializers(context);
   //3.listeners中添加就绪的上下文对象
   listeners.contextPrepared(context);
   if (this.logStartupInfo) {
      logStartupInfo(context.getParent() == null);
      logStartupProfileInfo(context);
   }
   //4.这里的beanFactory是DefaultListableBeanFactory的实例
   //添加Springboot指定的bean
   ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
   beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
   if (printedBanner != null) {
      beanFactory.registerSingleton("springBootBanner", printedBanner);
   }
   if (beanFactory instanceof DefaultListableBeanFactory) {
      ((DefaultListableBeanFactory) beanFactory)
            .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
   }
   //5.Load the sources,sources集合里面存放的是我们的启动类,其实就是加载SpringApplication的primarySources
   Set<Object> sources = getAllSources();
   Assert.notEmpty(sources, "Sources must not be empty");
   
   //6.Load beans into the application context.加载bean到上下文中
   //值得研究一下的load方法
   load(context, sources.toArray(new Object[0]));
   listeners.contextLoaded(context);
}

这里我们主要看一下load(context, sources.toArray(new Object[0]));

在解读load源码前,先熟悉几个围绕Bean引出的概念

  • 1.BeanDefinition: 是一个内部的接口,定义了 Bean 描述的基本规范,是对象在容器中的抽象。

以下是BeanDefinition中的主要方法:

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {

	// set/get父definition的名称
	void setParentName(@Nullable String parentName);
	String getParentName();
	
	// set/get bean definition的类名
	void setBeanClassName(@Nullable String beanClassName);
	String getBeanClassName();

	// set/get bean的范围,如:SCOPE_SINGLETON(单例) SCOPE_PROTOTYPE(多实例)
	void setScope(@Nullable String scope);
	String getScope();

    // set/get 是否延迟初始化
	void setLazyInit(boolean lazyInit);
	boolean isLazyInit();

	// set/get 当前bean初始化所依赖的beans的名称
	void setDependsOn(@Nullable String... dependsOn);
	String[] getDependsOn();

	// set/get 是否对其他bean是自动装配的候选bean
	void setAutowireCandidate(boolean autowireCandidate);
	boolean isAutowireCandidate();

	// set/get 是否主要的候选bean
	void setPrimary(boolean primary);
	boolean isPrimary();

	// set/get factory bean的名称
	void setFactoryBeanName(@Nullable String factoryBeanName);
	String getFactoryBeanName();

    // set/get 工厂方法的名称
	void setFactoryMethodName(@Nullable String factoryMethodName);
	String getFactoryMethodName();

	// 返回一个用于新的bean实例上的属性值
	MutablePropertyValues getPropertyValues();

}

  • 2.BeanDefinitionRegistry:是一个内部的接口, 实现了AliasRegistry接口, 定义了一些对 bean的常用操作。
public interface BeanDefinitionRegistry extends AliasRegistry {

	// 常用: 往注册表中注册一个新的 BeanDefinition 实例 
	void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException;
	// 移除注册表中已注册的 BeanDefinition 实例
	void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
	// 从注册中心取得指定的 BeanDefinition 实例
	BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
	// 判断 BeanDefinition 实例是否在注册表中(是否注册)
	boolean containsBeanDefinition(String beanName);
	// 取得注册表中所有 BeanDefinition 实例的 beanName(标识)
	String[] getBeanDefinitionNames();
	// 返回注册表中 BeanDefinition 实例的数量
	int getBeanDefinitionCount();
	// beanName(标识)是否被占用
	boolean isBeanNameInUse(String beanName);
}
  • 3. BeanDefinitionLoader:用于加载bean的类

 实例化时会初始化以下对象

AnnotatedBeanDefinitionReader: 获取一个或多个带有注解的具体类,解析为BeanDefintion,注册到BeanDefinitionRegistry中

XmlBeanDefinitionReader:获取配置文件中的类,解析为BeanDefintion,注册到BeanDefinitionRegistry中

ClassPathBeanDefinitionScanner: 获取一个或多个包下的带有注解的类,解析为BeanDefintion,注册到BeanDefinitionRegistry中

BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
   Assert.notNull(registry, "Registry must not be null");
   Assert.notEmpty(sources, "Sources must not be empty");
   this.sources = sources;
   this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
   this.xmlReader = new XmlBeanDefinitionReader(registry);
   if (isGroovyPresent()) {
      this.groovyReader = new GroovyBeanDefinitionReader(registry);
   }
   this.scanner = new ClassPathBeanDefinitionScanner(registry);
   this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
}

现在,来看一下load方法

  • load方法作用: 1)创建BeanDefinitionLoader对象 2)调用BeanDefinitionLoader的load方法完成对sources中的bean进行注册
protected void load(ApplicationContext context, Object[] sources) {
   if (logger.isDebugEnabled()) {
      logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
   }
   //1.创建BeanDefinitionLoader对象
   BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
   if (this.beanNameGenerator != null) {
      loader.setBeanNameGenerator(this.beanNameGenerator);
   }
   if (this.resourceLoader != null) {
      loader.setResourceLoader(this.resourceLoader);
   }
   if (this.environment != null) {
      loader.setEnvironment(this.environment);
   }
   //2.BeanDefinitionLoader执行load
   loader.load();
}
其中1处,getBeanDefinitionRegistry(context)会获取BeanDefinitionRegistry的实例, 
createBeanDefinitionLoader是用来实例化BeanDefinitionLoader
protected BeanDefinitionLoader createBeanDefinitionLoader(
			BeanDefinitionRegistry registry, Object[] sources) {
		return new BeanDefinitionLoader(registry, sources);
	}

 BeanDefinitionLoader的构造方法需要两个参数: 

  1.registry 是一个ApplicationContent

  2.sources .有四种类型分别是class 、Resource 、PackPackage 、CharSequence ,在load(Object source)的时候判断资源的类型 。分别用  AnnotatedBeanDefinitionReader、 XmlBeanDefinitionReader、 BeanDefinitionReader 、ClassPathBeanDefinitionScanner去加载对应的资源放到ApplicationContent中,而每个Reader或Scanner在创建对象时已经通过构造方法中BeannDefinitionRegistry (ApplicationContent)注入了

BeanDefinitionLoader的load方法如下

其中2处,我们不是用Groovy实现的,因此会到第7行的if里面,调用AnnotateBeanDefinitionReader的register方法将我们的启动类注册到容器中。其实AnnotateBeanDefinitionReader的register做了很多事情也是一个重要的方法

private int load(Class<?> source) {
   if (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
      // Any GroovyLoaders added in beans{} DSL can contribute beans here
      GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class);
      load(loader);
   }
   if (isComponent(source)) {
      this.annotatedReader.register(source);
      return 1;
   }
   return 0;
}

 

总结

prepareContext围绕着上下文的准备展开,分析了如何将启动类注册到容器中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhao_rock_2016

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值