背景
springboot项目启动需要根据所处项目组需求加载dll动态库,并启用相应的功能,于是在配置文件application.yml添加字段区分是否需要加载以及功能启用。
thermal-sdk:
enable: true
问题
springboot提供@Value注解可以向变量注入配置值,于是有以下代码
@Configuration
public class ThermalDisposeServerJNI {
@Value("${thermal-sdk.enable:false}")
private static boolean thermalSdkEnable;
static {
if (thermalSdkEnable){
// load dll
}
}
}
很遗憾,thermalSdkEnable并不能拿到true值,因为@Value并不能给静态变量注入属性值。
解决方案
- 创建Config类来管理静态变量,在Config类中通过@Value注入属性值
@Component
@Data
public class Config {
@Value("${thermal-sdk.enable:false}")
private boolean enableThermalSdk;
public static Config getInstance(){
return ConfigContextUtil.getBean("config", Config.class);
}
}
- 创建ConfigContext类,实现ApplicationContextAware接口
@Component
public class ConfigContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(@NotNull ApplicationContext applicationContext) throws BeansException {
ConfigContextUtil.applicationContext = applicationContext;
}
public static <T> T getBean(String beanName, Class<? extends T> beanType) {
return applicationContext.getBean(beanName, beanType);
}
}
- 在static代码块中调用Config类中enableThermalSdk变量的get方法
public class ThermalDisposeServerJNI {
public static final boolean ENABLE_THERMAL_SDK;
static {
ENABLE_THERMAL_SDK = Config.getInstance().isEnableThermalSdk();
if(ENABLE_THERMAL_SDK) {
// load dll
}
}
}
总结
- @Value不能直接给static变量注入属性值,@Value注入属性值基于spring的实例,参与spring的生命周期,而static变量是静态变量,且不参与spring的生命周期。
- 关于调用链路:
- Config构造器
- ConfigContextUtil构造器
- ConfigContextUtil注入applicationContext属性
- static块方法执行
- Config类getInstance方法被调用
- ConfigContextUtil类getBean方法被调用
- Config类enableThermalSdk属性的get方法被调用
- 大致猜想一下:jvm加载ThermalDisposeServerJNI类时,分析static代码块,需要调用Config的实例方法get方法,调用ConfigContextUtil变量applicationContext的getBean方法,所以预先实例化了,等这两个类的实例化完成,spring的生命周期也完成了,也就能成功注入属性。