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