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的生命週期

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章