一、系统初始化器介绍
- 类名:ApplicationContextInitializer
- 介绍:Spring容器刷新之前执行的一个回调函数
- 作用:向SpringBoot容器中注入属性
- 使用:继承接口自定义实现
实现方式一:自定义初始化器向SpringBoot容器中注入了属性
1.实现ApplicationContextInitializer接口
package com.gf.springboot.initializer;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import java.util.HashMap;
import java.util.Map;
@Order(1)
//新建一个类继承系统初始化器的接口
public class FirstInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
//继承方法并实现
@Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
ConfigurableEnvironment environment = configurableApplicationContext.getEnvironment();
//自定义属性
Map<String,Object> map =new HashMap<>();
//放入属性
map.put("key1","value1");
//打包成一个属性
MapPropertySource mapPropertySource = new MapPropertySource("firstInitializer", map);
//放入环境当中
environment.getPropertySources().addLast(mapPropertySource);
//输出
System.out.println("run firstInitializer");
//完成第一个系统初始化器的编写,接下来需要注册到容器当中
}
}
2.spring.factories内填写接口实现
org.springframework.context.ApplicationContextInitializer=com.gf.springboot.initializer.FirstInitializer
3.key值为org.springframework.context.ApplicationContextInitializer
在controller文件中新增接口用于验证,并添加用于验证的service
SpringAccountController
package com.gf.springboot.controller;
import com.gf.springboot.bean.SpringAccount;
import com.gf.springboot.service.SpringAccountService;
import com.gf.springboot.service.TestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.Optional;
@Controller
@RequestMapping("/spring")
public class SpringAccountController {
@Autowired
private SpringAccountService springAccountService;
@Autowired
private TestService testService;
@RequestMapping("/account/{id}")
@ResponseBody
public String accountNum(@PathVariable(value = "id") Integer id) {
return Optional.ofNullable(springAccountService.getById(id)).map(SpringAccount::toString).orElse("empty String");
}
//验证是否将自定义属性正确的注册进去
@RequestMapping("test")
@ResponseBody
public String test(){
//需要新建一个工具类,定义一个方法,使得这个接口可以调用
return testService.test();
}
}
TestService
package com.gf.springboot.service;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class TestService implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext=applicationContext;
}
public String test(){
return applicationContext.getEnvironment().getProperty("key3");
}
}
运行
实现方式二:
1.实现ApplicationContextInitializer接口
package com.gf.springboot.initializer;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import java.util.HashMap;
import java.util.Map;
@Order(2)
//新建一个类继承系统初始化器的接口
public class SecondInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
//继承方法并实现
@Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
ConfigurableEnvironment environment = configurableApplicationContext.getEnvironment();
//自定义属性
Map<String,Object> map =new HashMap<>();
//放入属性
map.put("key2","value2");
//打包成一个属性
MapPropertySource mapPropertySource = new MapPropertySource("secondInitializer", map);
//放入环境当中
environment.getPropertySources().addLast(mapPropertySource);
//输出
System.out.println("run secondInitializer");
//完成第一个系统初始化器的编写,接下来需要注册到容器当中
}
}
2.SpringApplication类初始后设置进去
package com.gf.springboot;
import com.gf.springboot.initializer.SecondInitializer;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.gf.springboot.mapper")
public class SpringbootApplication {
public static void main(String[] args) {
// SpringApplication.run(SpringbootApplication.class, args);
//换一种启动方式
//先new一个SpringApplication
SpringApplication springApplication = new SpringApplication(SpringbootApplication.class);
//添加一个系统初始化器
springApplication.addInitializers(new SecondInitializer());
//启动
springApplication.run(args);
}
}
实现方式三:
1.实现ApplicationContextInitializer接口
package com.gf.springboot.initializer;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import java.util.HashMap;
import java.util.Map;
@Order(3)
//新建一个类继承系统初始化器的接口
public class ThirdInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
//继承方法并实现
@Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
ConfigurableEnvironment environment = configurableApplicationContext.getEnvironment();
//自定义属性
Map<String,Object> map =new HashMap<>();
//放入属性
map.put("key3","value3");
//打包成一个属性
MapPropertySource mapPropertySource = new MapPropertySource("thirdInitializer", map);
//放入环境当中
environment.getPropertySources().addLast(mapPropertySource);
//输出
System.out.println("run thirdInitializer");
//完成第一个系统初始化器的编写,接下来需要注册到容器当中
}
}
2.application.properties内填写接口实现
server.port=8080
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/spring?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=123456
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=com.gf.springboot.bean
mybatis.configuration.map-underscore-to-camel-case=true
context.initializer.classes=com.gf.springboot.initializer.ThirdInitializer
3.key值为context.initializer.classes
注意:
- 都需要实现ApplicationContextInitializer接口
- order值越小越先执行
- application.properties中定义的优先于其它方式
二、SpringFactoriesLoader介绍
自定义系统初始化器是如何被SpringBoot容器识别并加载到容器类中
- 框架内部使用的通用工厂加载机制
- 从classpath下多个jar包特定位置读取文件并初始化类
- 文件类型必须是kv形式,即properties类型
- key是全限定名(抽象类|接口),value是实现,多个实现用逗号分隔
作用:Spring Boot框架中从类路径jar包中读取特定文件实现扩展类的载入
流程:
三、系统初始化器原理解析
ApplicationContextInitializer
系统初始化器的实现是如何被调用的以及背后的实现原理
作用:
- 上下文刷新即refresh方法前调用
- 用来编码设置一些属性变量通常用在web环境中
- 可以通过order接口进行排序
四、总结
- 介绍下SpringFactoriesLoader? 这个类是Springboot工厂中的加载类,springboot用它来完成扩展点的实现的载入
- SpringFactoriesLoader如何加载工厂类?读取指定路径下的指定文件,读成一个properties对象,然后依次遍历文件内容,组装成类名和对应的实现,然后依次遍历,通过order进行排序
- 系统初始化器的作用?是SpringBoot容器的一个回调接口,通过它向容器定义我们的属性
- 系统初始化器调用的时机?run方法中的prepareContext方法中
- 如何自定义实现系统初始化器?
- 自定义实现系统初始化器有哪些注意事项?order值的大小排序问题,不同实现方式使得order值失效