1.SpringBoot入口類
通過Spring Initializr建立項目,SpringBoot會自動生成如下所示的入口啟動類。
@SpringBootApplication
public class AppApplication {
public static void main(String[] args) {
SpringApplication.run(AppApplication.class, args);
}
}
2.@SpringBootApplication原理
@SpringBootApplication開啟了Spring的元件掃描和SpringBoot自動配置功能。實際上,它是一個複合注解,包含3個重要的注解@SpringBootConfiguration、@EnableAutoConfiguration和@ComponentScan。在SpringBoot早期版本中,需要在入口類中同時添加這3個注解,但是從1.2.0版本之後,隻需要在入口類添加@SpringBootApplication注解即可,其源代碼如下。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {
@Filter(type = FilterType.CUSTOM,classes = {TypeExcludeFilter.class}),
@Filter(type = FilterType.CUSTOM,classes = {AutoConfigurationExcludeFilter.class})
}
)
public interface SpringBootApplication {
@AliasFor(
annotation = EnableAutoConfiguration.class
)
Class<?>[] exclude() default {};
@AliasFor(
annotation = EnableAutoConfiguration.class
)
String[] excludeName() default {};
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackages"
)
String[] scanBasePackages() default {};
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackageClasses"
)
Class<?>[] scanBasePackageClasses() default {};
@AliasFor(
annotation = ComponentScan.class,
attribute = "nameGenerator"
)
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
- @SpringBootConfiguration:表明該類使用基于Java的注解,SpringBoot推薦使用基于Java的注解而不是XML配置。檢視@SpringBootConfiguration的源代碼可知,其是對@Configuration進行的簡單包裝,然後取名為SpringBootConfiguration。
- @EnableAutoConfiguration:開啟自動配置功能。檢視@EnableAutoConfiguration的源代碼可知,其包含@Import注解。@Import注解的主要作用是借助EnableAutoConfigurationImportSelector将SpringBoot應用所有符合條件的@Configuration配置加載到目前SpringBoot建立并使用的IOC容器中(Spring應用程式上下文ApplicationContext)。Spring架構提供了很多@Enable開頭的注解,這些注解都是借助@Import的支援,來收集和注冊特定場景相關的bean。
- @ComponentScan:啟動元件掃描注解,開發的元件或bean定義能自動發現并注入到Spring應用程式上下文。如控制層注解@Controller、服務層注解@Service和@Component等,這些注解都可以被@ComponentScan注解掃描到。
3.SpringApplication的run方法
除了@SpringBootConfiguration注解,入口類中還有一個重要的内容是SpringApplication.run方法。在run方法中,首先建立一個SpringApplication的對象執行個體,然後調用SpringApplication的run方法,其源碼如下所示。
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
this.configureHeadlessProperty();
//開啟監聽器
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
Collection exceptionReporters;
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
Banner printedBanner = this.printBanner(environment);
//建立應用上下文
context = this.createApplicationContext();
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
//準備應用上下文
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//重新整理應用上下文
this.refreshContext(context);
//重新整理後操作
this.afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
listeners.started(context);
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
從源代碼中可以看出,SpringBoot首先開啟了一個SpringApplicationRunListeners監聽器,然後通過createApplicationContext、prepareContext和refreshContext方法建立、準備和重新整理應用上下文ConfigurableApplicationContext,通過應用上下文加載應用所需要的類和各種配置環境等,最後啟動一個應用執行個體。
4.SpringApplicationRunListeners監聽器
SpringApplicationRunListeners接口規定了SpringBoot的生命周期,在各個生命周期廣播相應的ApplicationEvent事件,實際調用的是ApplicationListener,其源代碼如下所示。
public interface SpringApplicationRunListener {
//執行run方法時觸發
default void starting() {
}
//環境建立完成時觸發
default void environmentPrepared(ConfigurableEnvironment environment) {
}
//上下文建立完成時觸發
default void contextPrepared(ConfigurableApplicationContext context) {
}
//上下文載入配置時觸發
default void contextLoaded(ConfigurableApplicationContext context) {
}
default void started(ConfigurableApplicationContext context) {
}
default void running(ConfigurableApplicationContext context) {
}
default void failed(ConfigurableApplicationContext context, Throwable exception) {
}
}
ApplicationListener是Spring架構對Java監聽器模式的一種架構實作,其源代碼如下所示。
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
void onApplicationEvent(E var1);
}
ApplicationListener接口隻有一個方法onApplicationEvent。如果在上下文中部署實作了一個ApplicationListener接口的監聽器,每當ApplicationEvent事件釋出到ApplicationContext時,該監聽器就會得到通知。如果要為SpringBoot應用添加自定義的ApplicationListener,可通過SpringApplication.addListeners()或者SpringApplication.setListeners()方法添加一個或者多個自定義的ApplicationListener。
5.ApplicationContextInitializer接口
在SpringBoot準備上下文prepareContext的時候,會對ConfigurableApplicationContext執行個體做進一步的設定或處理,prepareContext源碼如下所示。
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
this.postProcessApplicationContext(context);
//對上下文進行設定和處理
this.applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
this.logStartupInfo(context.getParent() == null);
this.logStartupProfileInfo(context);
}
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
Set<Object> sources = this.getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
this.load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
}
在準備上下文prepareContext的方法中,通過applyInitializers方法對context上下文進行設定和處理,applyInitializers的源碼如下所示。
protected void applyInitializers(ConfigurableApplicationContext context) {
for (ApplicationContextInitializer initializer : getInitializers()) {
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
initializer.initialize(context);
}
}
在applyInitializers方法中,主要是調用ApplicationContextInitializer的initialize方法對應用上下文進行設定和處理。ApplicationContextInitializer本質上是一個回調接口,用于在ConfigurableApplicationContext執行refresh操作之前對它進行一些初始化操作。一般情況下,開發者無需自定義一個ApplicationContextInitializer,如果需要自定義一個ApplicationContextInitializer,則可以通過SpringApplication.addInitializers()來實作。
@FunctionalInterface
public interface ApplicationRunner {
/**
* Callback used to run the bean.
* @param args incoming application arguments
* @throws Exception on error
*/
void run(ApplicationArguments args) throws Exception;
}
@FunctionalInterface
public interface CommandLineRunner {
/**
* Callback used to run the bean.
* @param args incoming main method arguments
* @throws Exception on error
*/
void run(String... args) throws Exception;
}
public class MyCommandRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception{
//todo
}
}