SpringBoot 启动配置原理
启动配置原理
| 12
 3
 4
 5
 
 | public class SpringBootApplication {public static void main(String[] args) {
 SpringApplication.run(SpringBootApplication.class, args);
 }
 }
 
 | 
点进启动类的run方法可以看到,在源码中先创建SpringApplication对象,然后在执行它的run方法。
| 12
 3
 4
 5
 6
 7
 
 | public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {return run(new Class<?>[] { primarySource }, args);
 }
 
 public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
 return new SpringApplication(primarySources).run(args);
 }
 
 | 
创建SpringApplication对象
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 
 | public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {this.resourceLoader = resourceLoader;
 Assert.notNull(primarySources, "PrimarySources must not be null");
 
 this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
 
 this.webApplicationType = WebApplicationType.deduceFromClasspath();
 
 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
 
 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
 
 this.mainApplicationClass = deduceMainApplicationClass();
 }
 
 | 


运行run方法
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 
 | public ConfigurableApplicationContext run(String... args) {StopWatch stopWatch = new StopWatch();
 stopWatch.start();
 ConfigurableApplicationContext context = null;
 Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
 configureHeadlessProperty();
 
 SpringApplicationRunListeners listeners = getRunListeners(args);
 
 listeners.starting();
 try {
 
 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
 
 
 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
 
 configureIgnoreBeanInfo(environment);
 
 Banner printedBanner = printBanner(environment);
 
 context = createApplicationContext();
 
 exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
 new Class[] { ConfigurableApplicationContext.class }, context);
 
 
 
 
 prepareContext(context, environment, listeners, applicationArguments, printedBanner);
 
 
 refreshContext(context);
 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, ex, exceptionReporters, listeners);
 throw new IllegalStateException(ex);
 }
 
 try {
 
 listeners.running(context);
 }
 catch (Throwable ex) {
 handleRunFailure(context, ex, exceptionReporters, null);
 throw new IllegalStateException(ex);
 }
 
 return context;
 }
 
 | 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 
 | private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
 
 context.setEnvironment(environment);
 postProcessApplicationContext(context);
 
 applyInitializers(context);
 
 listeners.contextPrepared(context);
 if (this.logStartupInfo) {
 logStartupInfo(context.getParent() == null);
 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 = getAllSources();
 Assert.notEmpty(sources, "Sources must not be empty");
 load(context, sources.toArray(new Object[0]));
 
 listeners.contextLoaded(context);
 }
 
 | 
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | 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);
 }
 }
 
 | 
| 12
 3
 4
 
 | private SpringApplicationRunListeners getRunListeners(String[] args) {Class<?>[] types = new Class<?>[] {SpringApplication.class, String[].class};
 return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
 }
 
 | 
配置在META-INF/spring.factories: ApplicationContextInitializer 、SpringApplicationRunListener 
只需要放在ioc容器中 :ApplicationRunner 、CommandLineRunner

事件监听机制
ApplicationContextInitializer 
| 12
 3
 4
 5
 6
 
 | public class HelloApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {@Override
 public void initialize(ConfigurableApplicationContext applicationContext) {
 System.out.println("ApplicationContextInitializer...initialize..."+applicationContext);
 }
 }
 
 | 
SpringApplicationRunListener
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 
 | public class HelloSpringApplicationRunListener implements SpringApplicationRunListener {
 
 public HelloSpringApplicationRunListener(SpringApplication application, String[] args){
 
 }
 
 @Override
 public void starting() {
 System.out.println("SpringApplicationRunListener...starting...");
 }
 
 @Override
 public void environmentPrepared(ConfigurableEnvironment environment) {
 Object o = environment.getSystemProperties().get("os.name");
 System.out.println("SpringApplicationRunListener...environmentPrepared.."+o);
 }
 
 @Override
 public void contextPrepared(ConfigurableApplicationContext context) {
 System.out.println("SpringApplicationRunListener...contextPrepared...");
 }
 
 @Override
 public void contextLoaded(ConfigurableApplicationContext context) {
 System.out.println("SpringApplicationRunListener...contextLoaded...");
 }
 
 @Override
 public void started(ConfigurableApplicationContext context) {
 System.out.println("SpringApplicationRunListener...started...");
 }
 }
 
 | 
配置(META-INF/spring.factories)\表示换行,,表示分隔
| 12
 3
 
 | org.springframework.context.ApplicationContextInitializer=\ com.springboot.listener.HelloApplicationContextInitializer
 org.springframework.boot.SpringApplicationRunListener=\ com.springboot.listener.HelloSpringApplicationRunListener
 
 | 
ApplicationRunner 
 | 12
 3
 4
 5
 6
 7
 
 | @Componentpublic class HelloApplicationRunner implements ApplicationRunner {
 @Override
 public void run(ApplicationArguments args) throws Exception {
 System.out.println("ApplicationRunner...run....");
 }
 }
 
 | 
CommandLineRunner 
| 12
 3
 4
 5
 6
 7
 
 | @Componentpublic class HelloCommandLineRunner implements CommandLineRunner {
 @Override
 public void run(String... args) throws Exception {
 System.out.println("CommandLineRunner...run..."+ Arrays.asList(args));
 }
 }
 
 | 
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | 输出顺序:SpringApplicationRunListener...starting...
 SpringApplicationRunListener...environmentPrepared...
 ApplicationContextInitializer...initialize...
 SpringApplicationRunListener...contextPrepared...
 SpringApplicationRunListener...contextLoaded...
 ApplicationRunner...run....
 CommandLineRunner...run...
 SpringApplicationRunListener...started...
 
 | 
自定义starter
SpringBoot starter机制
SpringBoot中的starter是一种非常重要的机制,能够抛弃以前繁杂的配置,将其统一集成进starter,应用者只需要在maven中引入starter依赖,SpringBoot就能自动扫描到要加载的信息并启动相应的默认配置。
starter让我们摆脱了各种依赖库的处理,需要配置各种信息的困扰。SpringBoot会自动通过classpath路径下的类发现需要的Bean,并注册进IOC容器。
SpringBoot提供了针对日常企业应用研发各种场景的spring-boot-starter依赖模块。所有这些依赖模块都遵循着约定成俗的默认配置,并允许我们调整这些配置,即遵循“约定大于配置”的理念。
为什么要自定义starter
在我们的日常开发工作中,经常会有一些独立于业务之外的配置模块,我们经常将其放到一个特定的包下,然后如果另一个工程需要复用这块功能的时候,需要将代码硬拷贝到另一个工程,重新集成一遍,麻烦至极。
如果我们将这些可独立于业务代码之外的功配置模块封装成一个个starter,复用的时候只需要将其在pom中引用依赖即可,SpringBoot为我们完成自动装配。
| 12
 3
 4
 
 | <dependency><groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter</artifactId>
 </dependency>
 
 | 
spring-boot-starter中集成了spring全家桶的许多依赖支持,如下:

命名规范
启动器(starter),启动器模块是一个空 JAR 文件,仅提供辅助性依赖管理,这些依赖可能用于自动装配或者其他类库。
一个完整的Spring Boot Starter可能包含以下组件:
- autoconfigure模块:包含自动配置的代码
- starter模块:提供对autoconfigure模块的依赖,以及一些其它的依赖

如果你不需要区分这两个概念的话,也可以将自动配置代码模块与依赖管理模块合并成一个模块。
官方命名空间:
– 前缀:“spring-boot-starter-” 
– 模式:spring-boot-starter-模块名
– 举例:spring-boot-starter-web、spring-boot-starter-actuator、spring-boot-starter-jdbc
推荐自定义命名空间:
– 后缀:“-spring-boot-starter” 
– 模式:模块-spring-boot-starter
– 举例:mybatis-spring-boot-starter
如何编写
| 12
 3
 4
 5
 6
 7
 
 | @Configuration  @ConditionalOnXXX
 @AutoConfigureAfter
 @Bean
 
 @ConfigurationPropertie
 @EnableConfigurationProperties
 
 | 
| 12
 3
 4
 5
 6
 
 | 自动配置类要能加载
 将需要启动就加载的自动配置类,配置在META-INF/spring.factories
 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
 org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
 org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
 
 | 
新建项目
新建一个工程名midkuro-spring-boot-starter的maven空项目,只做pom.xml的依赖引入。

新建一个工程名midkuro-spring-boot-starter-autoconfigurer的Spring-boot项目

并修改其midkuro-spring-boot-starter-autoconfigurer项目的pom.xml文件,引入spring-boot-starter依赖
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 
 | <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>
 
 <groupId>cn.midkuro.com</groupId>
 <artifactId>midkuro-spring-boot-starter-autoconfigurer</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <packaging>jar</packaging>
 
 <parent>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-parent</artifactId>
 <version>2.1.8.RELEASE</version>
 </parent>
 
 <properties>
 <maven-jar-plugin.version>2.6</maven-jar-plugin.version>
 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
 <java.version>1.8</java.version>
 </properties>
 
 <dependencies>
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter</artifactId>
 </dependency>
 </dependencies>
 
 </project>
 
 | 
然后修改midkuro-spring-boot-starter的pom.xml,引入相关依赖:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 
 | <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>
 
 <groupId>cn.midkuro.com</groupId>
 <artifactId>midkuro-spring-boot-starter</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <packaging>jar</packaging>
 
 <name>midkuro-spring-boot-starter</name>
 
 <properties>
 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 </properties>
 
 <dependencies>
 <dependency>
 <groupId>cn.midkuro.com</groupId>
 <artifactId>midkuro-spring-boot-starter-autoconfigurer</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 </dependency>
 
 </dependencies>
 </project>
 
 | 
定义配置类
在midkuro-spring-boot-starter-autoconfigurer工程中增加配置类
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 
 | package cn.midkuro.com.autoconfigurer;
 import org.springframework.boot.context.properties.ConfigurationProperties;
 
 @ConfigurationProperties(prefix = "mtex.hello")
 public class HelloProperties {
 private String prefix;
 private String suffix;
 
 public String getPrefix() {
 return prefix;
 }
 
 public void setPrefix(String prefix) {
 this.prefix = prefix;
 }
 
 public String getSuffix() {
 return suffix;
 }
 
 public void setSuffix(String suffix) {
 this.suffix = suffix;
 }
 }
 
 | 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 
 | package cn.midkuro.com.autoconfigurer;
 public class HelloService {
 
 private HelloProperties properties;
 
 public HelloProperties getProperties() {
 return properties;
 }
 
 public void setProperties(HelloProperties properties) {
 this.properties = properties;
 }
 
 public String sayHello(String name) {
 return properties.getPrefix() + "-" + name + "-" + properties.getSuffix();
 }
 }
 
 | 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 
 | package cn.midkuro.com.autoconfigurer;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 
 @Configuration
 @ConditionalOnWebApplication
 @EnableConfigurationProperties(HelloProperties.class)
 
 public class HelloServiceAutoConfiguration {
 
 @Autowired
 private HelloProperties properties;
 
 @Bean
 public HelloService helloService() {
 HelloService helloService = new HelloService();
 helloService.setProperties(properties);
 return helloService;
 }
 }
 
 | 

配置spring.factories
想要xxxAutoConfiguration配置默认生效,需要在META-INF/spring.factories增加启动类相关配置
| 12
 3
 
 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
 cn.midkuro.com.autoconfigurer.HelloServiceAutoConfiguration
 
 | 
\斜杠符号表示换行,多个类以,逗号分开,具体可以参考spring-boot-starter的配置文件:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 
 | org.springframework.context.ApplicationContextInitializer=\
 org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
 org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
 
 
 org.springframework.context.ApplicationListener=\
 org.springframework.boot.autoconfigure.BackgroundPreinitializer
 
 
 org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
 org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener
 
 
 org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
 org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
 org.springframework.boot.autoconfigure.condition.OnClassCondition,\
 org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
 
 
 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
 org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
 org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
 org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
 org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
 org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
 ...
 
 | 
测试
新建工程名midkuro-spring-boot-starter-test的spring-boot项目,引入打包好的自定义starter依赖:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 
 | <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>
 
 <groupId>cn.mastercom</groupId>
 <artifactId>mtex-spring-boot-starter-test</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <packaging>jar</packaging>
 
 <parent>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-parent</artifactId>
 <version>2.1.8.RELEASE</version>
 <relativePath/>
 </parent>
 
 <properties>
 <maven-jar-plugin.version>2.6</maven-jar-plugin.version>
 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
 <java.version>1.8</java.version>
 </properties>
 
 
 <dependencies>
 
 <dependency>
 <groupId>cn.midkuro.com</groupId>
 <artifactId>midkuro-spring-boot-starter</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 </dependency>
 
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-web</artifactId>
 </dependency>
 </dependencies>
 
 </project>
 
 | 
新建测试Controller:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | @RestControllerpublic class HelloController {
 @Autowired
 private HelloService helloService;
 
 @GetMapping("/sayHello")
 public void sayHello(String name) {
 helloService.sayHello(name);
 }
 }
 
 | 
添加配置到application.properties中:
| 12
 
 | mtex.hello.prefix=hellomtex.hello.suffix=world
 
 | 
运行启动类SpringBootStarterTestApplication:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 
 | package cn.midkuro.com;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 
 @SpringBootApplication
 public class SpringBootStarterTestApplication {
 public static void main(String[] args) {
 SpringApplication.run(SpringBootStarterTestApplication.class, args);
 }
 }
 
 | 
