Spring Boot当前版本为2.2.0,之前一直对Spring Boot的各个模块比较模糊,很难理解自动装配等注解与容器启动的关系等。但是后面梳理完了之后发现,Spring Boot只是在Spring的基础上做了比较多的封装和扩展,个人理解。 还是先看一下,
一、Spring Boot的特性
1、独立运行Spring项目
之前的时候Spring只是作为一个服务的一部分,或者说是容器。特别是之前的web项目是通过监听Servlet服务器规范的ContextLoadeoaderListener(或者ContextLoaderPlugin)等方式,监听服务器启动后启动Ioc容器。现在不论是jar还是war都可以使用java -jar xxx.jar方式启动项目。
2、嵌入式Servlet服务器(如Tomcat)
独立运行Spring项目,特别是web项目的情况下,使用内嵌的Servlet服务器(如Tomcat提供的内嵌版本)。首先是启动Spring项目,然后在AbstractApplicationContext的refresh方法,在执行模板方法的onRefresh的扩展部分,调用createWebServer方法以创建Servlet服务器,是Spring与Tomcat角色的转变。
3、提供starter简化maven配置
Spring Boot提供了一系列的starter的maven依赖,特别是spring-boot-starter-parent。只需要一个基础的依赖就能引入大部分的自动装配的模块(功能)。
4、自动装配
自动装配使用 @Import和ImportBeanDefinitionRegistrar回调接口的方式实现,那么发生的时机是 SpringApplication.run执行时,会执行AbstractApplicationContext的refresh方法时执行。具体自动装配的执行时机是在Spring源码-ImportSelector实现分析或参见SpringIoc源码(十)- ApplicationContext(六)- refresh(ConfigurationClassPostProcessor上)。
5、准生产的应用监控
Spring Boot在原有Spring的Environment的基础上进行扩展出Actuator模块,可以完成内存等的常规运营监控,特别适合小公司能快速搭建监控。
6、无代码生成和xml配置
使用条件注解(@Conditional以及其扩展注解)和Java配置组合,不需要任何的xml配置就能实现所有配置。
在梳理完Spring的源码后发现,其强大之处在于Ioc容器、ApplicationContext的生命周期、回调接口(BeanPostProcessor等),以及每个Bean的生命周期回调。生命周期也就意味着模板方法模式定义的回调,同样Spring Boot也定义了自己的回调组件。
SpringApplicationRunListener、(贯穿了Spring Boot的整个启动过程)
ApplicationContextInitializer(只是AbstractApplicationContext的准备阶段)
ApplicationRunner、CommandLineRunner(环境参数相关回调,只是前者参数是被封装过的,后者是main方法的原参数)
二、SpringApplication结构
1、主要字段
// @SpringBootApplication注解标注的类
private Set<Class<?>> primarySources;
// 注册成Bean的全限定名,默认为空后续会将primarySources添加进去
private Set<String> sources = new LinkedHashSet<>();
// main方法类,一般与primarySources相同
private Class<?> mainApplicationClass;
// 启动Banner图,默认会启动SpringBootBanner
private Banner.Mode bannerMode = Banner.Mode.CONSOLE;
// 是否大于启动的StopWatch的信息
private boolean logStartupInfo = true;
// 是否大于main方法启动的args参数
private boolean addCommandLineProperties = true;
// 是否为ApplicationContext添加ConversionService
private boolean addConversionService = true;
// Banner图
private Banner banner;
// 类加载器,用于在ApplicationContext还没有启动时,加载类
// ApplicationContextInitializer等类型的类
private ResourceLoader resourceLoader;
// Bean的名称生成器,否则走默认的名称生成方式(第一个字母小写的驼峰,多个大写连着的除外)
private BeanNameGenerator beanNameGenerator;
// Spring的Environment
private ConfigurableEnvironment environment;
// ApplicationContext的子类类型
private Class<? extends ConfigurableApplicationContext> applicationContextClass;
// Application类型,用于确认上面生成的applicationContextClass类型
private WebApplicationType webApplicationType;
// 配置headless的值
private boolean headless = true;
// 是否注册钩子
private boolean registerShutdownHook = true;
// 初始化器(后面会详细分析)
private List<ApplicationContextInitializer<?>> initializers;
// 监听(后面会详细分析)
private List<ApplicationListener<?>> listeners;
// 默认配置,会叠加@ConfigurationProperties
private Map<String, Object> defaultProperties;
// Spring Profiles
private Set<String> additionalProfiles = new HashSet<>();
// 是否允许BeanDefinition覆盖
private boolean allowBeanDefinitionOverriding;
// 是否自定义Environment,因为Spring的ApplicationContext在refresh时会初始化
private boolean isCustomEnvironment = false;
// 是否加载LazyInitializationBeanFactoryPostProcessor类型的BeanPostProcessor
private boolean lazyInitialization = false;
2、构造器和run方法
// 1、类加载器、main方法类的Class
SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources);
// 2、main方法类的Class
SpringApplication(Class<?>... primarySources);
首先构造器可以传入类加载器,一般都不传入,则在启动时候使用当前线程进行获取类加载器,后续主要用于加载ApplicationContextInitializer和ApplicationListener等类型的类。
primarySources为@SpringBootApplication注解标记的类,主要用于后续需要使用该类的包(作为基准包)进行@Conponent的扫描。以及判断当前类是否为@Conponent,是的话则注册为Bean。
// main方法类的Class、main方法启动参数
run(Class<?> primarySource, String... args);
run(Class<?>[] primarySources, String[] args);
run(String... args);
如果构造器没有传入primarySource参数,则在run方法时还可以传入。但是run方法执行时,一定需要传入args,很多地方会进行使用,并且会进行封装成ApplicationArguments。若不需要使用自动扫描基础包下的@Component的功能,则可以调用方法(不建议):
public static void main(String[] args) throws Exception {
SpringApplication.run(new Class<?>[0], args);
}