目录
12.4.2 Application Events and Listeners
12.4.3 ApplicationRunner 与 CommandLineRunner
第十二章 原理解析
12.1 Profile功能
为了方便多环境适配,springboot简化了profile功能,简化了环境的切换导致需要修改配置文件
12.1.1 application-profile功能
- 默认配置文件application.yaml,任何时候都会加载
- 指定环境配置文件application-{env}.yaml
- 激活指定环境
- 配置文件激活
- 命令行激活:java -jar xxx.jar --spring.profiles.active=prod --person.name=haha
- 修改配置文件的任意值,命令行优先
- 默认配置与环境配置同时生效
- 同名配置项,profile配置优先
server.port=8080 #同名配置 #在配置文件中指定激活的环境,默认配置文件和指定环境的配置文件都会生效,但是同名属性会覆盖 spring.profiles.active=prod
12.1.2 @Profile条件装配功能
当加上了@Profile注解,可以使某个环境的条件下这个组件才生效:
public interface Person {
String getName();
Integer getAge();
}
@Profile(value = {"prod","default"}) //prod、default环境下生效
@Component
@ConfigurationProperties("person")//读取配置文件中person开头的值
@Data
public class Boss implements Person {
private String name;
private Integer age;
}
@Profile("test")
@Component
@ConfigurationProperties("person")
@Data
public class Worker implements Person {
private String name;
private Integer age;
}
除了标在类上,也可以标在方法上:
@Configuration
public class MyConfig {
@Profile("prod")
@Bean
public Color red(){
return new Color();
}
@Profile("test")
@Bean
public Color green(){
return new Color();
}
}
12.1.3 profile分组
spring.profiles.active=myprod #生产环境组 spring.profiles.group.myprod[0]=ppd spring.profiles.group.myprod[1]=prod #测试环境组 spring.profiles.group.mytest[0]=test
12.2 外部化配置
比如把数据库的登录信息等不是固定写死在配置文件中,而实抽取出现写在一个外部配置文件中
12.2.1 外部配置源
常用:Java属性文件、YAML文件、环境变量、命令行参数
比如获取环境变量的值:
@RestController
public class HelloController {
@Value("${MAVEN_HOME}")
private String msg;
@GetMapping("/msg")
public String getMsg(){
return msg+"==>"+osName;
}
}
获取系统的环境变量和属性:
@SpringBootApplication
public class Boot09FeaturesProfileApplication {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(Boot09FeaturesProfileApplication.class, args);
ConfigurableEnvironment environment = run.getEnvironment();
Map<String, Object> systemEnvironment = environment.getSystemEnvironment();
Map<String, Object> systemProperties = environment.getSystemProperties();
System.out.println(systemEnvironment);
System.out.println(systemProperties);
}
}
12.2.2 配置文件查找位置
application.properties和application.yaml文件读取位置:
-
classpath 根路径
-
classpath 根路径下config目录
-
jar包当前目录
-
jar包当前目录的config目录
-
/config子目录的直接子目录
12.2.3 配置文件加载顺序
-
当前jar包内部的application.properties和application.yml
-
当前jar包内部的application-{profile}.properties和application-{profile}.yml
-
引用的外部jar包的application.properties和application.yml
-
引用的外部jar包的application-{profile}.properties和application-{profile}.yml
12.2.4 关键
指定环境优先,外部优先,后面的可以覆盖前面的的同名配置项
12.3 自定义starter
12.3.1 starter启动原理
- starter-pom引入autoconfigurer包
12.3.2 自定义starter
-
atguigu-hello-spring-boot-starter(启动器)
-
atguigu-hello-spring-boot-starter-autoconfigure(自动配置包)
pom.xml文件:
<?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.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.atguigu</groupId>
<artifactId>atguigu-hello-spring-boot-starter-autoconfigure</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>atguigu-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>
在配置文件中自动注入需要的类:
/**
* 默认不要放在容器中
*/
public class HelloService {
@Autowired
HelloProperties helloProperties;
public String sayHello(String userName){
return helloProperties.getPrefix() + ":"+userName+"》"+helloProperties.getSuffix();
}
}
@ConfigurationProperties("atguigu.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;
}
}
@Configuration
@EnableConfigurationProperties(HelloProperties.class) //默认HelloProperties放在容器中
public class HelloServiceAutoConfiguration{
@ConditionalOnMissingBean(HelloService.class)
@Bean
public HelloService helloService(){
HelloService helloService = new HelloService();
return helloService;
}
}
在类路径下创建一个META-INF文件夹,这个文件夹下有一个文件spring.factories:
# Auto Configure # 指定项目一启动要加载哪个包哪个自动配置类 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.atguigu.hello.auto.HelloServiceAutoConfiguration
此时可以在其他项目中引入这个自定义的starter场景:
<dependency> <groupId>com.atguigu</groupId> <artifactId>atguigu-hello-spring-boot-starter</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
#在配置文件中定义属性atguigu.hello开头 atguigu.hello.prefix=ATGUIGU atguigu.hello.suffix=88888
12.4 SpringBoot原理
SpringBoot原理【Spring注解】、SpringMVC原理、自动配置原理、SpringBoot原理
12.4.1 SpringBoot启动过程
- 创建SpringApplication
- 保存一些信息
- 利用工具类ClassUtils判定当前应用的类型,一般就是Servlet类型
-
bootstrappers初始启动引导器(List<Bootstrapper>):去spring.factories文件中找 org.springframework.boot.Bootstrapper
- 找初始化器ApplicationContextInitializer,去spring.factories找ApplicationContextInitializer
- List<ApplicationContextInitializer<?>> initializers
- 找应用监听器ApplicationListener,去spring.factories找ApplicationListener
- List<ApplicationListener<?>> listeners
- 运行SpringApplication
- StopWatch:监控整个项目的启停
- 记录应用的启动时间
- createBootstrapContext():创建引导上下文(Context环境)
- 获取到所有之前的bootstrappers挨个执行intitialize()来完成对引导启动器上下文环境设置
- 让当前应用进入headless模式,java.awt.headless
- 获取所有RunListener(运行监听器)【为了方便所有Listener进行事件感知】
- getSpringFactoriesInstances去spring.factories找SpringApplicationRunListener
- 遍历SpringApplicationRunListener调用starting方法
- 相当于事件通知机制,项目正在starting
- 保存命令行参数,ApplicationArguments
-
准备环境prepareEnvironment()
- 返回或者创建基础环境信息对象,StandardServletEnvironment
-
配置环境信息对象
-
读取所有的配置源的配置属性值
-
- 绑定环境信息
- 监听器调用listener.environmentPrepared(),通知所有的监听器当前环境准备完成
- 创建IOC容器,createApplicationContext()
-
根据项目类型创建容器,一般为Servlet
- 当前Servlet类型会创建AnnotationConfigServletWebServerApplicationContext
-
- 准备ApplicationContext IOC容器的基本信息,prepareContext()
- 保存环境信息
-
IOC容器的后置处理流程
-
应用初始化器applyInitializers
- 遍历所有的ApplicationContextInitializer,调用initialize来对ioc容器进行初始化扩展功能(可以自定义initialize)
- 遍历所有的listener调用contextPrepared,EventPublishRunListenr通知所有的监听器contextPrepared
- 所有的监听器调用contextLoaded,通知所有的监听器contextLoaded
- 刷新IOC容器,refreshContext
- 创建容器中的所有组件(Spring注解)
-
容器刷新完成后工作,afterRefresh
- 所有监听器调用listeners.started(context),通知所有的监听器started
-
调用所有runners,callRunners()
- 获取容器中的ApplicationRunner
- 获取容器中的CommandLineRunner
- 合并所有runner并且按照@Order进行排序
- 遍历所有的runner,调用 run 方法
-
如果以上有异常
- 调用Listener的failed
- 如果没有异常,调用所有监听器的running方法,listeners.running(context),通知所有的监听器running
- running如果有问题,继续通知failed,调用所有Listener的failed,通知所有的监听器 failed
12.4.2 Application Events and Listeners
-
ApplicationContextInitializer
-
ApplicationListener
-
SpringApplicationRunListener
可以自动义组件:在启动过程中某些时机增添自己需要的功能
public class MyApplicationContextInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("MyApplicationContextInitializer ....initialize.... ");
}
}
public class MyApplicationListener implements ApplicationListener {
@Override
public void onApplicationEvent(ApplicationEvent event) {
System.out.println("MyApplicationListener.....onApplicationEvent...");
}
}
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....");
}
}
12.4.3 ApplicationRunner 与 CommandLineRunner
runner接口:
@FunctionalInterface
public interface ApplicationRunner {
/**
* Callback used to run the bean.
* @param args incoming application arguments
* @throws Exception on error
*/
void run(ApplicationArguments args) throws Exception;
}
@FunctionalInterface
public interface CommandLineRunner {
/**
* Callback used to run the bean.
* @param args incoming main method arguments
* @throws Exception on error
*/
void run(String... args) throws Exception;
}
可以自定义组件:
@Order(1)
@Component
public class MyApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("MyApplicationRunner...run...");
}
}
/**
* 应用启动做一个一次性事情
*/
@Order(2)
@Component
public class MyCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("MyCommandLineRunner....run....");
}
}
PS:根据尚硅谷视频整理,如有侵权,联系删除