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