我是陈皮,一个在互联网 Coding 的 ITer,个人微信公众号「陈皮的JavaLib」关注第一时间阅读最新文章。
问题描述
在某个 Spring 项目中,看到有人在 Spring Bean 中对static
静态属性使用@Value
注解进行属性值注入,结果没将配置文件中配置的值赋值到属性中。下面演示进行问题复现。
Springboot 项目的配置文件 application.yml 有如下配置变量。
person.name: 陈皮
person.age: 18
Spring bean 类,定义2个静态熟悉,分别使用属性注入,和 set 方法注入属性值。
package com.chenpi;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* @author 陈皮
* @version 1.0
* @description
* @date 2022/4/3
*/
@Component
public class Person {
@Value("${person.name}")
private static String name;
private static String age;
@Value("${person.age}")
public void setAge(String age) {
Person.age = age;
}
@Override
public String toString() {
return "Person{" + "name='" + name + '\''
+ ", age='" + age + '\''
+ '}';
}
}
编写测试单元进行验证。
package com.chenpi;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class ApplicationTests {
@Autowired
private Person person;
@Test
public void testValue() {
System.out.println(person);
}
}
测试结果显示,@Value 注解放在静态属性上注入值失败,而 @Value 放在 setter 方法上(注意,该方法也不能是静态方法)注入成功。
Person{name='null', age='18'}
分析
首先,从启动日志看出,Autowired 注解不能作用在静态属性上。
2022-04-03 13:47:38.156 INFO 10576 --- [ Test worker] f.a.AutowiredAnnotationBeanPostProcessor : Autowired annotation is not supported on static fields: private static java.lang.String com.chenpi.Person.name
我们通过这打印的日志,找到 Spring 源码中打印这句日志的地方,发现对于需要注入的属性或者注入方法,对 static 属性或者 static 方法进行了排除。
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
Class<?> targetClass = clazz;
do {
final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
ReflectionUtils.doWithLocalFields(targetClass, field -> {
AnnotationAttributes ann = findAutowiredAnnotation(field);
if (ann != null) {
// 这里对静态属性注入做了过滤
if (Modifier.isStatic(field.getModifiers())) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation is not supported on static fields: " + field);
}
return;
}
boolean required = determineRequiredStatus(ann);
currElements.add(new AutowiredFieldElement(field, required));
}
});
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
}
AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod);
if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
// 这里对静态方法做了过滤
if (Modifier.isStatic(method.getModifiers())) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation is not supported on static methods: " + method);
}
return;
}
if (method.getParameterCount() == 0) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation should only be used on methods with parameters: " +
method);
}
}
boolean required = determineRequiredStatus(ann);
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new AutowiredMethodElement(method, required, pd));
}
});
elements.addAll(0, currElements);
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
return new InjectionMetadata(clazz, elements);
}
结论
从源码中发现,理论上 Spring 是可以对静态域进行注入的,但是 Spring 没有这样做。其实应该也可以理解,毕竟 Spring 一般是对 Spring Bean 进行管理,而 Spring bean 是实例,那么对于 bean 的依赖注入发生的时机应该是在实例的生命周期中,而不是在类的生命周期中。
本次分享到此结束啦~~
如果觉得文章对你有帮助,点赞、收藏、关注、评论,您的支持就是我创作最大的动力!