每一个Spring Boot项目都会用到启动类
@SpringBootApplication
public class SpringbootActuatorApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootActuatorApplication.class, args);
}
}
这个启动类省去了在写代码时的许多繁琐的配置类,让程序员不用再配置xml文件,不用再配置tomcat,这样就不用花费大量时间在搭建环境上。那么Spring Boot具体是怎样启动的呢?
1.Spring Boot底层实现
@SpringBootApplication注解是Spring Boot的核心注解,它其实是一个组合注解。
@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 {
...
}
而在众多注解中,实际只有三个注解最为重要
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
所以Spring Boot启动类也可以写作
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
因此这里主要介绍这三个注解。
(1)@SpringBootConfiguration
进入@SpringBootConfiguration源码可以看到,其中主要还是运用了@Configuration注解。
@Configuration标注在类上,相当于把该类作为spring的xml配置文件中的,作用为:配置spring容器(应用上下文)
package com.dxz.demo.configuration;
import org.springframework.context.annotation.Configuration;
@Configuration
public class TestConfiguration {
public TestConfiguration() {
...
}
}
相当于
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:util="http://www.springframework.org/schema/util" xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.0.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.0.xsd" default-lazy-init="false">
</beans>
而@Configuration与另一个注解@Bean配合使用就可以创建一个简单的spring配置类,可以用来替代相应的xml配置文件。
@Configuration
public class Conf {
@Bean
public Student student() {
Student student= new Student ();
student.setClass(class());
return class;
}
@Bean
public Class class() {
return new Class();
}
}
相当于
<beans>
<bean id = "student" class="com.test.Student">
<property name="class" ref = "class"></property>
</bean>
<bean id = "class" class="com.test.Class"></bean>
</beans>
@Configuration的注解类标识这个类可以使用Spring IoC容器作为bean定义的来源。
@Bean注解告诉Spring,一个带有@Bean的注解方法将返回一个对象,该对象应该被注册为在Spring应用程序上下文中的bean。
(2)@ComponentScan
@ComponentScan这个注解在Spring中很重要,@ComponentScan的功能其实就是自动扫描并加载符合条件的组件(比如@Component和@Repository等)或者bean定义,最终将这些bean定义加载到IOC容器中。
我们可以通过basePackages等属性来定制@ComponentScan自动扫描的范围,如果不指定,则默认Spring框架会从声明@ComponentScan所在类的package进行扫描。
(3)@EnableAutoConfiguration
@EnableAutoConfiguration的作用是帮助SpringBoot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot,并创建对应配置类的Bean,并把该Bean实体交给IoC容器进行管理。
其具体实现为
@SuppressWarnings("deprecation")
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
...
}
其中,最关键的要属@Import(EnableAutoConfigurationImportSelector.class),借助EnableAutoConfigurationImportSelector,@EnableAutoConfiguration可以帮助SpringBoot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器。
其中的EnableAutoConfigurationImportSelector是自动配置关键,SpringFactoriesLoader属于Spring框架私有的一种扩展方案,其主要功能就是从指定的配置文件META-INF/spring.factories加载配置。配合@EnableAutoConfiguration使用的话,它更多是提供一种配置查找的功能支持,即根据@EnableAutoConfiguration的完整类名org.springframework.boot.autoconfigure.EnableAutoConfiguration作为查找的Key,获取对应的一组@Configuration类。
所以,@EnableAutoConfiguration自动配置其实就是:从classpath中搜寻所有的META-INF/spring.factories配置文件,并将其中org.springframework.boot.autoconfigure.EnableutoConfiguration对应的配置项通过反射实例化,使其成为标注了@Configuration的JavaConfig形式的IoC容器配置类,然后汇总并加载到IOC容器中。
2.SpringBoot启动原理
SpringBoot整个启动流程分为两个步骤:初始化一个SpringApplication对象、执行该对象的run方法。
(1)SpringApplication初始化
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//判断是否是web项目
this.webApplicationType = deduceWebApplicationType();
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
可以知道初始化流程中最重要的就是通过SpringFactoriesLoader找到spring.factories文件中配置的ApplicationContextInitializer和ApplicationListener两个接口的实现类名称,以便后期构造相应的实例
ApplicationContextInitializer的主要目的是在ConfigurableApplicationContext做refresh之前,对ConfigurableApplicationContext实例做进一步的设置或处理。
ApplicationListener的目的就没什么好说的了,它是Spring框架对Java事件监听机制的一种框架实现。Spring Boot提供两种方式来添加自定义监听器通过SpringApplication.addListeners(ApplicationListener<?>... listeners)或者SpringApplication.setListeners(Collection<? extends ApplicationListener<?>> listeners)两个方法来添加一个或者多个自定义监听器。然后还需要在我们直接在自己的jar包的META-INF/spring.factories文件中新增配置即可:
org.springframework.context.ApplicationListener=\
cn.moondev.listeners.xxxxListener\(自定义监听器)
(2)Spring Boot执行run()方法
Spring Boot应用的整个启动流程都封装在SpringApplication.run方法中,本质上其实就是在spring的基础之上做了大量的封装。
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
//1.通过SpringFactoriesLoader查找并加载所有的SpringApplicationRunListeners,通过调 用starting()方法通知所有的SpringApplicationRunListeners:应用开始启动
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
//2.创建并配置当前应用将要使用的Environment
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
//3.打印banner
Banner printedBanner = printBanner(environment);
//4.根据是否是web项目,来创建不同的ApplicationContext容器
context = createApplicationContext();
//5.创建一系列FailureAnalyzer
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//6.初始化ApplicationContext
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//7.调用ApplicationContext的refresh()方法,刷新容器
refreshContext(context);
//8.查找当前context中是否注册有CommandLineRunner和ApplicationRunner,如果有则遍历执行它们。
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, listeners, exceptionReporters, ex);
throw new IllegalStateException(ex);
}
listeners.running(context);
return context;
}
1.通过SpringFactoriesLoader查找并加载所有的SpringApplicationRunListeners,通过调用starting()方法通知所有的SpringApplicationRunListeners:应用开始启动。
2.创建并配置当前应用将要使用的Environment,Environment用于描述应用程序当前的运行环境,其抽象了两个方面的内容:配置文件(profile)和属性(properties),不同的环境可以使用不同的配置文件,而属性则可以从配置文件、环境变量、命令行参数等来源获取。因此,当Environment准备好后,在整个应用的任何时候,都可以从Environment中获取资源。
3.打印banner(可以自定义)
4.根据是否是web项目,来创建不同的ApplicationContext容器
5.创建一系列FailureAnalyzer,创建流程依然是通过SpringFactoriesLoader获取到所有实现FailureAnalyzer接口的class,然后在创建对应的实例。FailureAnalyzer用于分析故障并提供相关诊断信息。
6.初始化ApplicationContext
7.调用ApplicationContext的refresh()方法,刷新容器
8.查找当前context中是否注册有CommandLineRunner和ApplicationRunner,如果有则遍历执行它们。