81、高级特性-Profile环境切换 为了方便多环境适配,Spring Boot简化了profile功能。
默认配置文件application.yaml
任何时候都会加载。 指定环境配置文件application-{env}.yaml
,env
通常替代为test
, 激活指定环境配置文件激活:spring.profiles.active=prod
命令行激活:java -jar xxx.jar --spring.profiles.active=prod --person.name=haha
(修改配置文件的任意值,命令行优先 ) 默认配置与环境配置同时生效 同名配置项,profile配置优先 @Profile条件装配功能 1 2 3 4 5 6 7 @Data @Component @ConfigurationProperties("person") public class Person { private String name; private Integer age; }
application.properties
1 2 3 person: name: lun age: 8
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public interface Person { String getName () ; Integer getAge () ; }@Profile("test") @Component @ConfigurationProperties("person") @Data public class Worker implements Person { private String name; private Integer age; }@Profile(value = {"prod","default"}) @Component @ConfigurationProperties("person") @Data public class Boss implements Person { private String name; private Integer age; }
application-test.yaml
1 2 3 4 5 person: name: test-张三 server: port: 7000
application-prod.yaml
1 2 3 4 5 person: name: prod-张三 server: port: 8000
application.properties
1 2 spring.profiles.active =prod
1 2 3 4 5 6 7 8 @Autowired private Person person;@GetMapping("/") public String hello () { return person.getClass().toString(); }
@Profile还可以修饰在方法上 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Color { }@Configuration public class MyConfig { @Profile("prod") @Bean public Color red () { return new Color (); } @Profile("test") @Bean public Color green () { return new Color (); } }
可以激活一组:
1 2 3 4 spring.profiles.active =production spring.profiles.group.production[0] =proddb spring.profiles.group.production[1] =prodmq
82、高级特性-配置加载优先级 外部化配置 官方文档 - Externalized Configuration
Spring Boot uses a very particular PropertySource
order that is designed to allow sensible overriding of values. Properties are considered in the following order (with values from lower items overriding earlier ones)(1优先级最低,14优先级最高):
Default properties (specified by setting SpringApplication.setDefaultProperties
). @PropertySource
annotations on your @Configuration
classes. Please note that such property sources are not added to the Environment
until the application context is being refreshed. This is too late to configure certain properties such as logging.*
and spring.main.*
which are read before refresh begins.Config data (such as application.properties
files) A RandomValuePropertySource
that has properties only in random.*
. OS environment variables. Java System properties (System.getProperties()
). JNDI attributes from java:comp/env
. ServletContext
init parameters.ServletConfig
init parameters.Properties from SPRING_APPLICATION_JSON
(inline JSON embedded in an environment variable or system property). Command line arguments. properties
attribute on your tests. Available on @SpringBootTest
and the test annotations for testing a particular slice of your application .@TestPropertySource
annotations on your tests.Devtools global settings properties in the $HOME/.config/spring-boot
directory when devtools is active.1 2 3 4 5 6 7 8 9 10 11 12 import org.springframework.stereotype.*;import org.springframework.beans.factory.annotation.*;@Component public class MyBean { @Value("${name}") private String name; }
外部配置源Java属性文件。 YAML文件。 环境变量。 命令行参数。 配置文件查找位置classpath 根路径。 classpath 根路径下config目录。 jar包当前目录。 jar包当前目录的config目录。 /config子目录的直接子目录。 配置文件加载顺序:当前jar包内部的application.properties
和application.yml
。 当前jar包内部的application-{profile}.properties
和 application-{profile}.yml
。 引用的外部jar包的application.properties
和application.yml
。 引用的外部jar包的application-{profile}.properties
和application-{profile}.yml
。 指定环境优先,外部优先,后面的可以覆盖前面的同名配置项。 83、高级特性-自定义starter细节 starter启动原理 starter的pom.xml引入autoconfigure依赖 1 2 3 4 graph LR A[starter] -->B[autoconfigure] B --> C[spring-boot-starter]
autoconfigure包中配置使用META-INF/spring.factories
中EnableAutoConfiguration
的值,使得项目启动加载指定的自动配置类
编写自动配置类 xxxAutoConfiguration
-> xxxxProperties
@Configuration
@Conditional
@EnableConfigurationProperties
@Bean
...... 引入starter --- xxxAutoConfiguration
--- 容器中放入组件 ---- 绑定xxxProperties
---- 配置项
自定义starter 目标:创建HelloService
的自定义starter。
创建两个工程,分别命名为hello-spring-boot-starter
(普通Maven工程),hello-spring-boot-starter-autoconfigure
(需用用到Spring Initializr创建的Maven工程)。
hello-spring-boot-starter
无需编写什么代码,只需让该工程引入hello-spring-boot-starter-autoconfigure
依赖:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <?xml version="1.0" encoding="UTF-8" ?> <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 > com.lun</groupId > <artifactId > hello-spring-boot-starter</artifactId > <version > 1.0.0-SNAPSHOT</version > <dependencies > <dependency > <groupId > com.lun</groupId > <artifactId > hello-spring-boot-starter-autoconfigure</artifactId > <version > 1.0.0-SNAPSHOT</version > </dependency > </dependencies > </project >
hello-spring-boot-starter-autoconfigure
的pom.xml如下:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <?xml version="1.0" encoding="UTF-8" ?> <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 https://maven.apache.org/xsd/maven-4.0.0.xsd" > <modelVersion > 4.0.0</modelVersion > <parent > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-parent</artifactId > <version > 2.4.2</version > <relativePath /> </parent > <groupId > com.lun</groupId > <artifactId > hello-spring-boot-starter-autoconfigure</artifactId > <version > 1.0.0-SNAPSHOT</version > <name > hello-spring-boot-starter-autoconfigure</name > <description > Demo project for Spring Boot</description > <properties > <java.version > 1.8</java.version > </properties > <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter</artifactId > </dependency > </dependencies > </project >
创建4个文件:com/lun/hello/auto/HelloServiceAutoConfiguration
com/lun/hello/bean/HelloProperties
com/lun/hello/service/HelloService
src/main/resources/META-INF/spring.factories
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import com.lun.hello.bean.HelloProperties;import com.lun.hello.service.HelloService;import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;import org.springframework.boot.context.properties.EnableConfigurationProperties;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configuration @ConditionalOnMissingBean(HelloService.class) @EnableConfigurationProperties(HelloProperties.class) public class HelloServiceAutoConfiguration { @Bean public HelloService helloService () { return new HelloService (); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import org.springframework.boot.context.properties.ConfigurationProperties;@ConfigurationProperties("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; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import com.lun.hello.bean.HelloProperties;import org.springframework.beans.factory.annotation.Autowired;public class HelloService { @Autowired private HelloProperties helloProperties; public String sayHello (String userName) { return helloProperties.getPrefix() + ": " + userName + " > " + helloProperties.getSuffix(); } }
1 2 3 org.springframework.boot.autoconfigure.EnableAutoConfiguration =\ com.lun.hello.auto.HelloServiceAutoConfiguration
1 2 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 <?xml version="1.0" encoding="UTF-8" ?> <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 https://maven.apache.org/xsd/maven-4.0.0.xsd" > <modelVersion > 4.0.0</modelVersion > <parent > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-parent</artifactId > <version > 2.4.2</version > <relativePath /> </parent > <groupId > com.lun</groupId > <artifactId > hello-spring-boot-starter-test</artifactId > <version > 1.0.0-SNAPSHOT</version > <name > hello-spring-boot-starter-test</name > <description > Demo project for Spring Boot</description > <properties > <java.version > 1.8</java.version > </properties > <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-test</artifactId > <scope > test</scope > </dependency > <dependency > <groupId > com.lun</groupId > <artifactId > hello-spring-boot-starter</artifactId > <version > 1.0.0-SNAPSHOT</version > </dependency > </dependencies > <build > <plugins > <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > </plugin > </plugins > </build > </project >
添加配置文件application.properties
: 1 2 hello.prefix =hello hello.suffix =666
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import com.lun.hello.service.HelloService;import org.junit.jupiter.api.Assertions;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest class HelloSpringBootStarterTestApplicationTests { @Autowired private HelloService helloService; @Test void contextLoads () { Assertions.assertEquals("hello: lun > 666" , helloService.sayHello("lun" )); } }
84、原理解析-SpringApplication创建初始化流程 SpringBoot启动过程 Spring Boot应用的启动类:
1 2 3 4 5 6 7 8 9 10 11 import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication public class HelloSpringBootStarterTestApplication { public static void main (String[] args) { SpringApplication.run(HelloSpringBootStarterTestApplication.class, args); } }
1 2 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 public class SpringApplication { ... 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); } public SpringApplication (Class<?>... primarySources) { this (null , primarySources); } 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(); this .bootstrappers = new ArrayList <>(getSpringFactoriesInstances(Bootstrapper.class)); setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this .mainApplicationClass = deduceMainApplicationClass(); } private Class<?> deduceMainApplicationClass() { try { StackTraceElement[] stackTrace = new RuntimeException ().getStackTrace(); for (StackTraceElement stackTraceElement : stackTrace) { if ("main" .equals(stackTraceElement.getMethodName())) { return Class.forName(stackTraceElement.getClassName()); } } } catch (ClassNotFoundException ex) { } return null ; } ... }
spring.factories:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 ... org.springframework.context.ApplicationContextInitializer =\ org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\ org.springframework.boot.context.ContextIdApplicationContextInitializer,\ org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\ org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\ org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer org.springframework.context.ApplicationListener =\ org.springframework.boot.ClearCachesApplicationListener,\ org.springframework.boot.builder.ParentContextCloserApplicationListener,\ org.springframework.boot.context.FileEncodingApplicationListener,\ org.springframework.boot.context.config.AnsiOutputApplicationListener,\ org.springframework.boot.context.config.DelegatingApplicationListener,\ org.springframework.boot.context.logging.LoggingApplicationListener,\ org.springframework.boot.env.EnvironmentPostProcessorApplicationListener,\ org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener ...
85、原理解析-SpringBoot完整启动过程 继续上一节,接着讨论return new SpringApplication(primarySources).run(args)
的run
方法
1 2 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 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 public class SpringApplication { ... public ConfigurableApplicationContext run (String... args) { StopWatch stopWatch = new StopWatch (); stopWatch.start(); DefaultBootstrapContext bootstrapContext = createBootstrapContext(); ConfigurableApplicationContext context = null ; configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(bootstrapContext, this .mainApplicationClass); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments (args); ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments); configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); context = createApplicationContext(); context.setApplicationStartup(this .applicationStartup); prepareContext(bootstrapContext, 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, listeners); throw new IllegalStateException (ex); } try { listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, null ); throw new IllegalStateException (ex); } return context; } private DefaultBootstrapContext createBootstrapContext () { DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext (); this .bootstrappers.forEach((initializer) -> initializer.intitialize(bootstrapContext)); return bootstrapContext; } private void configureHeadlessProperty () { System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this .headless))); } private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless" ; private SpringApplicationRunListeners getRunListeners (String[] args) { Class<?>[] types = new Class <?>[] { SpringApplication.class, String[].class }; return new SpringApplicationRunListeners (logger, getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this , args), this .applicationStartup); } private ConfigurableEnvironment prepareEnvironment (SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) { ConfigurableEnvironment environment = getOrCreateEnvironment(); configureEnvironment(environment, applicationArguments.getSourceArgs()); ConfigurationPropertySources.attach(environment); listeners.environmentPrepared(bootstrapContext, environment); DefaultPropertiesPropertySource.moveToEnd(environment); configureAdditionalProfiles(environment); bindToSpringApplication(environment); if (!this .isCustomEnvironment) { environment = new EnvironmentConverter (getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } ConfigurationPropertySources.attach(environment); return environment; } private void prepareContext (DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { context.setEnvironment(environment); postProcessApplicationContext(context); applyInitializers(context); listeners.contextPrepared(context); bootstrapContext.close(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); } private void callRunners (ApplicationContext context, ApplicationArguments args) { List<Object> runners = new ArrayList <>(); runners.addAll(context.getBeansOfType(ApplicationRunner.class).values()); runners.addAll(context.getBeansOfType(CommandLineRunner.class).values()); AnnotationAwareOrderComparator.sort(runners); for (Object runner : new LinkedHashSet <>(runners)) { if (runner instanceof ApplicationRunner) { callRunner((ApplicationRunner) runner, args); } if (runner instanceof CommandLineRunner) { callRunner((CommandLineRunner) runner, args); } } } private void handleRunFailure (ConfigurableApplicationContext context, Throwable exception, SpringApplicationRunListeners listeners) { try { try { handleExitCode(context, exception); if (listeners != null ) { listeners.failed(context, exception); } } finally { reportFailure(getExceptionReporters(context), exception); if (context != null ) { context.close(); } } } catch (Exception ex) { logger.warn("Unable to close ApplicationContext" , ex); } ReflectionUtils.rethrowRuntimeException(exception); } ... }
1 2 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 public interface ConfigurableApplicationContext extends ApplicationContext , Lifecycle, Closeable { String CONFIG_LOCATION_DELIMITERS = ",; \t\n" ; String CONVERSION_SERVICE_BEAN_NAME = "conversionService" ; String LOAD_TIME_WEAVER_BEAN_NAME = "loadTimeWeaver" ; String ENVIRONMENT_BEAN_NAME = "environment" ; String SYSTEM_PROPERTIES_BEAN_NAME = "systemProperties" ; String SYSTEM_ENVIRONMENT_BEAN_NAME = "systemEnvironment" ; String APPLICATION_STARTUP_BEAN_NAME = "applicationStartup" ; String SHUTDOWN_HOOK_THREAD_NAME = "SpringContextShutdownHook" ; void setId (String var1) ; void setParent (@Nullable ApplicationContext var1) ; void setEnvironment (ConfigurableEnvironment var1) ; ConfigurableEnvironment getEnvironment () ; void setApplicationStartup (ApplicationStartup var1) ; ApplicationStartup getApplicationStartup () ; void addBeanFactoryPostProcessor (BeanFactoryPostProcessor var1) ; void addApplicationListener (ApplicationListener<?> var1) ; void setClassLoader (ClassLoader var1) ; void addProtocolResolver (ProtocolResolver var1) ; void refresh () throws BeansException, IllegalStateException; void registerShutdownHook () ; void close () ; boolean isActive () ; ConfigurableListableBeanFactory getBeanFactory () throws IllegalStateException; }
1 2 3 4 5 org.springframework.boot.SpringApplicationRunListener =\ org.springframework.boot.context.event.EventPublishingRunListener
1 2 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 61 62 63 64 65 66 67 68 69 70 71 72 73 74 class SpringApplicationRunListeners { private final Log log; private final List<SpringApplicationRunListener> listeners; private final ApplicationStartup applicationStartup; SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener > listeners, ApplicationStartup applicationStartup) { this .log = log; this .listeners = new ArrayList <>(listeners); this .applicationStartup = applicationStartup; } void starting (ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) { doWithListeners("spring.boot.application.starting" , (listener) -> listener.starting(bootstrapContext), (step) -> { if (mainApplicationClass != null ) { step.tag("mainApplicationClass" , mainApplicationClass.getName()); } }); } void environmentPrepared (ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) { doWithListeners("spring.boot.application.environment-prepared" , (listener) -> listener.environmentPrepared(bootstrapContext, environment)); } void contextPrepared (ConfigurableApplicationContext context) { doWithListeners("spring.boot.application.context-prepared" , (listener) -> listener.contextPrepared(context)); } void contextLoaded (ConfigurableApplicationContext context) { doWithListeners("spring.boot.application.context-loaded" , (listener) -> listener.contextLoaded(context)); } void started (ConfigurableApplicationContext context) { doWithListeners("spring.boot.application.started" , (listener) -> listener.started(context)); } void running (ConfigurableApplicationContext context) { doWithListeners("spring.boot.application.running" , (listener) -> listener.running(context)); } void failed (ConfigurableApplicationContext context, Throwable exception) { doWithListeners("spring.boot.application.failed" , (listener) -> callFailedListener(listener, context, exception), (step) -> { step.tag("exception" , exception.getClass().toString()); step.tag("message" , exception.getMessage()); }); } private void doWithListeners (String stepName, Consumer<SpringApplicationRunListener> listenerAction, Consumer<StartupStep> stepAction) { StartupStep step = this .applicationStartup.start(stepName); this .listeners.forEach(listenerAction); if (stepAction != null ) { stepAction.accept(step); } step.end(); } ... }
86、原理解析-自定义事件监听组件 MyApplicationContextInitializer.java
1 2 3 4 5 6 7 8 9 import org.springframework.context.ApplicationContextInitializer;import org.springframework.context.ConfigurableApplicationContext;public class MyApplicationContextInitializer implements ApplicationContextInitializer { @Override public void initialize (ConfigurableApplicationContext applicationContext) { System.out.println("MyApplicationContextInitializer ....initialize.... " ); } }
MyApplicationListener.java
1 2 3 4 5 6 7 8 9 import org.springframework.context.ApplicationEvent;import org.springframework.context.ApplicationListener;public class MyApplicationListener implements ApplicationListener { @Override public void onApplicationEvent (ApplicationEvent event) { System.out.println("MyApplicationListener.....onApplicationEvent..." ); } }
MyApplicationRunner.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import org.springframework.boot.ApplicationArguments;import org.springframework.boot.ApplicationRunner;import org.springframework.core.annotation.Order;import org.springframework.stereotype.Component;@Order(1) @Component public class MyApplicationRunner implements ApplicationRunner { @Override public void run (ApplicationArguments args) throws Exception { System.out.println("MyApplicationRunner...run..." ); } }
MyCommandLineRunner.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import org.springframework.boot.CommandLineRunner;import org.springframework.core.annotation.Order;import org.springframework.stereotype.Component;@Order(2) @Component public class MyCommandLineRunner implements CommandLineRunner { @Override public void run (String... args) throws Exception { System.out.println("MyCommandLineRunner....run...." ); } }
MySpringApplicationRunListener.java
1 2 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 import org.springframework.boot.ConfigurableBootstrapContext;import org.springframework.boot.SpringApplication;import org.springframework.boot.SpringApplicationRunListener;import org.springframework.context.ConfigurableApplicationContext;import org.springframework.core.env.ConfigurableEnvironment;public class MySpringApplicationRunListener implements SpringApplicationRunListener { private SpringApplication application; public MySpringApplicationRunListener (SpringApplication application, String[] args) { this .application = application; } @Override public void starting (ConfigurableBootstrapContext bootstrapContext) { System.out.println("MySpringApplicationRunListener....starting...." ); } @Override public void environmentPrepared (ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) { System.out.println("MySpringApplicationRunListener....environmentPrepared...." ); } @Override public void contextPrepared (ConfigurableApplicationContext context) { System.out.println("MySpringApplicationRunListener....contextPrepared...." ); } @Override public void contextLoaded (ConfigurableApplicationContext context) { System.out.println("MySpringApplicationRunListener....contextLoaded...." ); } @Override public void started (ConfigurableApplicationContext context) { System.out.println("MySpringApplicationRunListener....started...." ); } @Override public void running (ConfigurableApplicationContext context) { System.out.println("MySpringApplicationRunListener....running...." ); } @Override public void failed (ConfigurableApplicationContext context, Throwable exception) { System.out.println("MySpringApplicationRunListener....failed...." ); } }
注册MyApplicationContextInitializer
,MyApplicationListener
,MySpringApplicationRunListener
:
resources / META-INF / spring.factories
:
1 2 3 4 5 6 7 8 org.springframework.context.ApplicationContextInitializer =\ com.lun.boot.listener.MyApplicationContextInitializer org.springframework.context.ApplicationListener =\ com.lun.boot.listener.MyApplicationListener org.springframework.boot.SpringApplicationRunListener =\ com.lun.boot.listener.MySpringApplicationRunListener
87、后会有期 路漫漫其修远兮,吾将上下而求索。
纸上得来终觉浅,绝知此事要躬行。
Spring Boot 2 场景整合篇虚拟化技术 安全控制 缓存技术 消息中间件 对象存储 定时调度 异步任务 分布式系统 Spring Boot 2 响应式编程响应式编程基础 Webflux开发Web应用 响应式访问持久化层 响应式安全开发 响应式原理 13、自动配置【源码分析】-自动包规则原理 ①、引导加载自动配置类 Spring Boot应用的启动类:
1 2 3 4 5 6 7 8 @SpringBootApplication public class MainApplication { public static void main (String[] args) { SpringApplication.run(MainApplication.class, args); } }
分析下@SpringBootApplication
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @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
。
@SpringBootConfiguration 1 2 3 4 5 6 7 8 9 10 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Configuration public @interface SpringBootConfiguration { @AliasFor( annotation = Configuration.class ) boolean proxyBeanMethods () default true ; }
@Configuration
代表当前是一个配置类。
@ComponentScan 指定扫描哪些Spring注解。
@ComponentScan 在07、基础入门-SpringBoot-自动配置特性 有用例。
@EnableAutoConfiguration 1 2 3 4 5 6 7 8 9 10 11 12 13 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration" ; Class<?>[] exclude() default {}; String[] excludeName() default {}; }
重点分析@AutoConfigurationPackage
,@Import(AutoConfigurationImportSelector.class)
。
Ⅰ@AutoConfigurationPackage 标签名直译为:自动配置包,指定了默认的包规则。
1 2 3 4 5 6 7 8 9 10 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Import(AutoConfigurationPackages.Registrar.class) public @interface AutoConfigurationPackage { String[] basePackages() default {}; Class<?>[] basePackageClasses() default {}; }
利用Registrar给容器中导入一系列组件 将指定的一个包下的所有组件导入进MainApplication所在包下。 14、自动配置【源码分析】-初始加载自动配置类 Ⅱ@Import(AutoConfigurationImportSelector.class) 利用getAutoConfigurationEntry(annotationMetadata);
给容器中批量导入一些组件
(进入到getAutoConfigurationEntry方法中)
调用List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes)
获取到所有需要导入到容器中的配置类
利用工厂加载 Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader);
得到所有的组件
从META-INF/spring.factories
位置来加载一个文件。默认扫描我们当前系统里面所有META-INF/spring.factories
位置的文件 spring-boot-autoconfigure-2.3.4.RELEASE.jar
包里面也有META-INF/spring.factories
1 2 3 4 5 6 7 org.springframework.boot.autoconfigure.EnableAutoConfiguration =\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ ...
虽然我们127个场景的所有自动配置启动的时候默认全部加载,但是xxxxAutoConfiguration
按照条件装配规则(@Conditional
),最终会按需配置。
如AopAutoConfiguration
类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Configuration( proxyBeanMethods = false ) @ConditionalOnProperty( prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true ) public class AopAutoConfiguration { public AopAutoConfiguration () { } ... }
15、自动配置【源码分析】-自动配置流程 ②、按需开启自动配置项 虽然我们127个场景的所有自动配置启动的时候默认全部加载。xxxxAutoConfiguration
按照条件装配规则(@Conditional
),最终会按需配置。
③、修改默认配置 以DispatcherServletAutoConfiguration
的内部类DispatcherServletConfiguration
为例子:
1 2 3 4 5 6 7 8 9 @Bean @ConditionalOnBean(MultipartResolver.class) @ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) public MultipartResolver multipartResolver (MultipartResolver resolver) { return resolver; }
SpringBoot默认会在底层配好所有的组件。但是如果用户自己配置了以用户的优先 ,例如characterEncodingFilter
1 2 3 4 @Bean @ConditionalOnMissingBean public CharacterEncodingFilter characterEncodingFilter () { }
④、SpringBoot自动配置总结 SpringBoot先加载所有的自动配置类 xxxxxAutoConfiguration 每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值。(xxxxProperties里面读取,xxxProperties和配置文件进行了绑定) 生效的配置类就会给容器中装配很多组件 只要容器中有这些组件,相当于这些功能就有了 定制化配置用户直接自己@Bean替换底层的组件 用户去看这个组件是获取的配置文件什么值就去修改。 xxxxxAutoConfiguration ---> 组件 ---> xxxxProperties里面拿值 ----> application.properties