SpringBoot启动过程

指定SpringBoot的入口

通常我们使用Spring Boot的时候,会有一个作为程序主入口的类,并在其上标注SpringBootApplication。

首先我们来看一下这个SpringBootApplication标注,如下所示:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
)}
)
public @interface SpringBootApplication

重点关注注解SpringBootConfiguration的注解中的Configuration,如下所示。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration	public SpringApplication(Object... sources) 

也就是说,被SpringBootApplication标注的是一个Component。

 

容器的启动过程

举例说明,以SpringApplication.run(XXXX.class, args)作为程序的入口。

该方法调用过程中,先创建SpringApplication的实例,

	public SpringApplication(Object... sources) {
		initialize(sources);
	}

在其初始化方法中,确定当前环境是否为web环境,设置Initializers和Listeners。

private void initialize(Object[] sources) {
		if (sources != null && sources.length > 0) {
			this.sources.addAll(Arrays.asList(sources));
		}
		// 判断是否存在Servlet和ConfigurableWebApplicationContext,存在则表示属于web环境
		// 这两个类是靠引入SpringBoot内置的Web模块,如tomcat,而引入的。
		this.webEnvironment = deduceWebEnvironment();
		// 设置Initializers(类型为ApplicationContextInitializer),定义在SpringBoot内核中,spring-boot、spring-boot-autoconfigure
		setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
		// 设置Listeners(类型为ApplicationListener)
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}

关注此处:

 

// 判断是否存在Servlet和ConfigurableWebApplicationContext,存在则表示属于web环境
		// 这两个类是靠引入SpringBoot内置的Web模块,如tomcat,而引入的。
		this.webEnvironment = deduceWebEnvironment();

后续在创建ApplicationContext的时候,会根据this.webEnvironment的取值来创建AnnotationConfigEmbeddedWebApplicationContext(web环境)或AnnotationConfigApplicationContext(非web环境)。

因此,如果使用者想创建一个web应用,但发现日志中提示创建的是AnnotationConfigApplicationContext时,请检查pom中是否依赖了spring-boot-starter-web,该依赖会引入tomcat-embed-core。

初始化SpringApplication之后,调用其run方法,在方法中创建并刷新ApplicationContext(参见Spring容器启动过程),广播容器生命周期事件。

public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		configureHeadlessProperty();
		SpringApplicationRunListeners listeners = getRunListeners(args);
		// 广播ApplicationStartedEvent
		listeners.started();
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
			// 创建环境,广播ApplicationEnvironmentPreparedEvent消息
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
			Banner printedBanner = printBanner(environment);
			// 创建上下文,对于web环境创建AnnotationConfigEmbeddedWebApplicationContext
			// 对于非web环境创建AnnotationConfigApplicationContext
			context = createApplicationContext();
			// 稍后分析本方法
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
			// 刷新上下文,即Spring容器的上下文刷新过程,参见Spring容器的启动过程
			refreshContext(context);
			afterRefresh(context, applicationArguments);
			// 广播ApplicationReadyEvent或ApplicationFailedEvent
			listeners.finished(context, null);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}
			return context;
		}
		catch (Throwable ex) {
			handleRunFailure(context, listeners, ex);
			throw new IllegalStateException(ex);
		}
	}

 

关注prepareContext方法,如下所示:

 

private void prepareContext(ConfigurableApplicationContext context,
			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments, Banner printedBanner) {
		context.setEnvironment(environment);
		postProcessApplicationContext(context);
		// 调用之前设置到容器中的Initializer的initialize方法
		applyInitializers(context);
		listeners.contextPrepared(context);
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}

		// Add boot specific singleton beans
		context.getBeanFactory().registerSingleton("springApplicationArguments",
				applicationArguments);
		if (printedBanner != null) {
			context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
		}

		// Load the sources
		Set<Object> sources = getSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		// 关注此方法,加载Bean
		load(context, sources.toArray(new Object[sources.size()]));
		listeners.contextLoaded(context);
	}

进入load方法:

	protected void load(ApplicationContext context, Object[] sources) {
		if (logger.isDebugEnabled()) {
			logger.debug(
					"Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
		}
		// 创建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);
		}
		loader.load();
	}

关注createBeanDefinitionLoader方法:

BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
		Assert.notNull(registry, "Registry must not be null");
		Assert.notEmpty(sources, "Sources must not be empty");
		this.sources = sources;
		// 创建AnnotationBeanDefinitionReader,向上下文中注入Annotation相关的BeanPostProcessor
		this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
		// 创建XmlBeanDefinitionReader
		this.xmlReader = new XmlBeanDefinitionReader(registry);
		if (isGroovyPresent()) {
			this.groovyReader = new GroovyBeanDefinitionReader(registry);
		}
		// 设置ClassPathBeanDefinitionScanner
		this.scanner = new ClassPathBeanDefinitionScanner(registry);
		// 设置ClassExcludeFilter
		this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
	}

在分析该方法之前,大家先回忆一下在没有SpringBoot的情况下,是如何配置xml配置文件的。以下几步是比较常见的:

1. 配置<context:component-scan>及exclude-filter,目的是为了注入Annotation相关的BeanPostProcessor;这些BeanPostProcessor会在refresh容器上下文的时候,介入到容器中注入的Bean的生命周期中,对Bean进行加工处理;

2. 对于需要引入数据库事务处理的场景,需要配置<tx:annotation-driven>;

3. 对于需要引入aop的场景,需要配置<aop:aspectj-autoproxy>。

而对于SpringBoot而言,这些步骤都不需要体现在配置xml中了,而是在SpringBoot启动的过程中,自动进行处理,从而达到对开发者透明的效果。那么是在何处进行自动处理的呢?请看AnnotationBeanDefinitionReader的构造函数:

	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);
		AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
	}

其中的AnnotationConfigUtils.registerAnnotationConfigProcessors中,可以看到以下步骤:

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
			BeanDefinitionRegistry registry, Object source) {


		DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
		if (beanFactory != null) {
			if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
				beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
			}
			if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
				beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
			}
		}


		Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<BeanDefinitionHolder>(4);


		// 注入ConfigurationClassPostProcessor,处理@Configuration注解
		if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
		}


		// 注入AutowiredAnnotationBeanPostProcessor,处理@Value和@Autowired
		if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
		}


		// 注入RequiredAnnotationBeanPostProcessor,处理@Required
		if (!registry.containsBeanDefinition(REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(RequiredAnnotationBeanPostProcessor.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
		}


		// 注入CommonAnnotationBeanPostProcessor
		// Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.
		if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
		}


		// Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.
		if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition();
			try {
				def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,
						AnnotationConfigUtils.class.getClassLoader()));
			}
			catch (ClassNotFoundException ex) {
				throw new IllegalStateException(
						"Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
			}
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
		}


		if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
		}
		if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
		}


		return beanDefs;
	}

 

从中可以看到,SpringBoot帮我们注入了常用的BeanPostProcessor,如AutowiredAnnotationBeanPostProcessor,RequiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor,用于处理@Value,@Autowired,@Required等Annotation。而这些,在SpringBoot推出之前,需要在xml中通过引入Context命名空间,借助于ContextNameSpaceHandler来引入。

 

除此之外,关注ConfigurationClassPostProcessor,这是一个BeanFactoryPostProcessor,其作用为处理标注了@Configuration的Bean。每一个标注有@Configuration的Bean都类似于xml配置文件,其内部可包含标注有@Bean的方法,就如同在xml配置文件中配置有bean一样。此外,还可以标注@Import、@ImportResource、@ComponentScan等,这些标注的作用,可参考xml中的同名配置。

经过这一步之后,SpringBoot注入了所需的BeanPostProcessor,接下来refresh ApplicationContext,完成容器的启动过程。如:Spring框架浅析 -- IoC容器与Bean的生命周期

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值