「这是我参与2022首次更文挑战的第4天,活动详情查看:2022首次更文挑战」。
@Value注解与局部变量加载顺序问题
前文
昨天同事讲到遇到一个奇怪的问题,@Value注解数据并没有注入因此产生空指针问题。经过排查发现是同事在使用注解的同时,使用了类的属性,并将注解注入的值直接初始化到属性中,在调用时发现该属性对应的值并没有被加载进来,出现了空指针问题。因此对该注解与变量的初始化顺序进行一下讨论。
问题分析及解决
@Service
public class TestValueService {
@Value("${test.testvalue.value}")
private Integer data;
private HashMap<String,Integer> map = new HashMap<String,Integer>(){
{
put("data",data);
}
};
}
首先来看一下问题代码,在初始化对象阶段,同时存在变量的初始化,以及从配置文件中读取数据。再将读取到的数据传递到数据的初始化中。由于变量初始化在注入配置值之前执行,因此导致了这个问题。
遇到了这个问题,我们已经确认是加载顺序导致的。因此首先要了解一下加载顺序问题。对于@Value注解来说,它的注入时机是当对象的默认构造函数执行完成后进行注入。而对于变量的初始化,会在所有方法执行前进行执行,包含构造函数。也就是该问题深层次的原因,是变量map会首先进行初始化,然后进行构造函数的执行,最后才会进入到@Value注解注入的步骤。
了解到这部分,我们就需要进行改造,让@Value的注入优先于变量初始化执行,或者优先于构造函数执行。因此代码变成了如下的样子:
@Service
public class TestValueService {
private HashMap<String,Integer> map = new HashMap<>();
public TestValueService(@Value("${test.testvalue.value}") String data) {
map.put("data",Integer.valueOf(data));
}
}
在执行构造函数时,直接指定从配置文件中进行数据的读取,在构造函数中进行变量的初始化,这样就可以保证变量初始化不会遇到空指针问题,能从配置文件中取到所需的数据。
后记
- 千古兴亡多少事?悠悠。不尽长江滚滚流。