1、概述
一般来说,springboot工程环境配置放在properties文件中,启动的时候将工程中的properties/yaml文件的配置项加载到内存中。但这种方式改配置项的时候,需要重新编译部署,考虑到这种因素,今天介绍将配置项存到数据库表中,在工程启动时把配置项加载到内存中。
springboot提供了两个接口: CommandLineRunner 和 ApplicationRunner 。实现其中接口,就可以在工程启动时将数据库中的数据加载到内存。使用的场景有:加载配置项到内存中;启动时将字典或白名单数据加载到内存(或缓存到Redis中)。
ApplicationRunner 与CommandLineRunner工作方式相同,唯一的区别在于两种方法入参方式不同,实现这两个接口就可以让应用程序代码在启动完成后,接收流量前被调用。如果实现了多次,则必须实现org.springframework.core.Ordered或者使用org.springframework.core.annotation.Order注解来定义他们之间的顺序,@Order(1)数字越小优先级越高
不使用注解@Order执行顺序:
静态代码块 > 构造代码块 > 构造方法(Constructor) > @Autowired > @PostConstruct > ApplicationRunner > CommandLineRunner
推荐使用: ApplicationRunner 或者 CommandLineRunner方式
springbean初始化流程
st=>start: 开始
op1=>operation: Spring容器加载
op2=>operation: 调用构造函数
op3=>operation: @PostConstruct方法调用
op4=>operation: init()调用
op5=>operation: 其他代码
op6=>operation: destroy()调用
op7=>operation: @PreDestroy方法调用
e=>end: 结束
st->op1->op2->op3->op4->op5->op6->op7->e
静态代码块:仅在类加载时执行一次。
构造代码块:创建一次对象,执行一次
静态方法里不能调用类的实例方法,但可以调用构造方法(因为不需要实例)
2、加载方式
2.1、ApplicationRunner接口
package com.example.demo.config;
import com.example.demo.service.ICodeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Component
@Order(1) // 初始化加载优先级,数字越小优先级越高
public class InitData implements ApplicationRunner {
private static Map<String,Dictionary> dataMap = new HashMap<>();
@Autowired
private DictionaryDao dictionaryDao;
@Override
public void run(ApplicationArguments args) throws Exception {
// 字典信息放到map中
List<Dictionary> dicList = dictionaryDao.findAllDictionary();
for(Dictionary dic : dicList){
dataMap.put(dic.getKey,dic);
}
}
// 获取字典信息的具体方法
public static String getDicDataByKey(String key){
Dictionary dictionary = dataMap.get(dataMap);
return dictionary ;
}
}
2.2、CommandLineRunner接口
package com.example.demo.config;
import com.example.demo.service.ICodeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Component
@Order(1) // 初始化加载优先级,数字越小优先级越高
public class InitDicData implements CommandLineRunner {
private static Map<String,Dictionary> dataMap = new HashMap<>();
@Autowired
private DictionaryDao dictionaryDao;
@Override
public void run(String... args) throws Exception {
// 字典信息放到map中
List<Dictionary> dicList = dictionaryDao.findAllDictionary();
for(Dictionary dic : dicList){
dataMap.put(dic.getKey,dic);
}
}
// 获取字典信息的具体方法
public static String getDicDataByKey(String key){
Dictionary dictionary = dataMap.get(dataMap);
return dictionary ;
}
}
2.3、@PostConstruct注解
很多人滥用该注解,并且认为该注解是基于Spring的。其实不然,@PostConstruct和@PreDestroy是在Java EE 5引入并且基于Servlet规范, 位于javax.annotation包下,Java最初的设计者认为,这些功能并不是Java核心API,因此就放到了扩展包中。该注解执行的优先级很高,在ApplicationRunner接口之前执行。
@PostConstruct注解修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器调用一次,类似于Servlet的init()方法,被@PostConstruct注解修饰的方法会在构造函数之后,init()方法执行之前执行
被@PreDestroy注解修饰的方法会在服务器卸载Servlet的时候运行,并且只会被服务器调用一次,类似于Servlet的destroy()方法,被@PreDestroy注解修饰的方法会在destroy()方法之后、在Servlet被彻底卸载之前执行
@Component
@Slf4j
public class CacheDicUtils {
private static Map<String, Dictionary> dataMap = new HashMap<>();
@Autowired
private DictionaryDao dictionaryDao;
@PostConstruct
public void init() {
List<Dictionary> list = dictionaryDao.findAllDictionary();
for (Dictionary dictionary : list) {
dataMap.put(dictionary.getName(), dictionary);
}
}
@PreDestroy
public void destroy() {
log.info("系统启动成功,dataMap加载完成!");
}
public static Dictionary getDictDataByName(String name) {
Dictionary dictionary = dataMap.get(name);
return sysDictionary ;
}
}
2.4、static静态代码块
public class DicMap {
@Autowired
private static DictionaryDao dictionaryDao;
private static HashMap<String,Dictionary> dataMap = new HashMap<>();
static {
// 在这里我们不能使用注入进来的dictionaryDao,因为它此时还没有被创建出来,所以我们要在系统
// 运行的时候获取项目的上下文手动去获取这个bean
dictionaryDao = SpringUtil.getBean(DictionaryDao.class);
queryDic();
}
// 把字典信息放到map中
public static void queryDic(){
List<Dictionary> dicList = dictionaryDao.findAllDictionary();
for(Dictionary dic : dicList){
dataMap.put(dic.getKey,dic);
}
}
// 获取字典信息的具体方法
public static String getDicDataByKey(String key){
Dictionary dictionary = dataMap.get(dataMap);
return dictionary ;
}
}