环境:Spring6.1.8
1. 静态字段&方法注入
首先,我们来确认一下,在Spring框架中,是否不允许对静态字段和方法进行注入的各种使用方式。
1.1 @Resource注入
@Component
public class PersonController {
@Resource
private static PersonService ps;
}
容器启动后,输出如下错误
错误直接就提示了@Resource不支持静态字段的注入。
1.2 @Autowired注入
@Component
public class PersonController {
@Autowired
private static PersonService ps ;
public String toString() {
return "PersonController [ps=" + ps + "]";
}
}
输出结果
PersonController [ps=null]
@Autowired注解虽然不能注入静态字段,但是没有抛出异常。反而这种没有错误提示的更加危险只有用到的时候才抛出NPE异常。
1.3 @Resource方法注入
private static PersonService ps ;
@Resource
public static void setPs(PersonService personService) {
ps = personService ;
}
容器启动后,输出如下错误
与静态字段注入一样,抛出了非常明确的错误不支持静态方法的注入。
1.4 @Autowired方法注入
// 代码省略,将上面@Resource方法注入的注解替换为@Autowired即可
输出结果
06:00:25.344 [main] INFO xxx- Autowired annotation is not supported on static fields
PersonController [ps=null]
同样与静态字段注入情况一样,不会抛出异常。但是输出了不支持static字段注入的日志信息。
接下来,看看JSR-330标准注入是否生效。
1.5 @Inject注入
注:你需要先引入以下依赖
<dependency>
<groupId>jakarta.inject</groupId>
<artifactId>jakarta.inject-api</artifactId>
<version>${version}</version>
</dependency>
该注解的使用方式与上面的 @Resource/ @Autowired一样。
@Inject
private static PersonService ps ;
输出结果
06:06:11.894 [main] INFO xxx- Autowired annotation is not supported on static fields
PersonController [ps=null]
与 @Autowired一样,不会抛出异常。方法注入与上面的情况是一样的。
通过上面几种Spring注入方式验证结果得出不管你使用什么方式进行注入static字段和方法都是不支持的。
2. 正确注入静态字段
2.1 实例方法注入
public class PersonController {
private static PersonService ps;
@Resource
// @Autowired
// @Inject
public void setPs(PersonService personService) {
ps = personService ;
}
}
以上3中注解方式都可以。
2.2 构造函数注入
private static PersonService ps;
public PersonController(PersonService personService) {
ps = personService ;
}
构造函数注入,不需要任何的注解。
3. 源码分析
说明:关于 @Resource注解的处理是由CommonAnnotationBeanPostProcessor处理器进行处理。而 @Autowired和 @Inject注解则是由AutowiredAnnotationBeanPostProcessor处理器进行处理。
3.1 @Resource注解处理
在进行注入前会获取所有使用@Resource注解标注的字段及方法。
查找对应注解方法入口
public class CommonAnnotationBeanPostProcessor {
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
// 查找字段/方法上的注解
InjectionMetadata metadata = findResourceMetadata(beanName, beanType, null);
}
}
进入上面的findResourceMetadata方法
private InjectionMetadata findResourceMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
// ...
InjectionMetadata metadata = buildResourceMetadata(clazz);
return metadata;
}
通过当前的类构建使用 @Resource注解的相关元数据信息。
private InjectionMetadata buildResourceMetadata(Class<?> clazz) {
do {
// 字段检查
ReflectionUtils.doWithLocalFields(targetClass, field -> {
// 判断是否是jakarta.ejb.EJB注解
if (ejbAnnotationType != null && field.isAnnotationPresent(ejbAnnotationType)) {
// 如果是静态字段则直接抛出异常
if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException("@EJB annotation is not supported on static fields");
}
}
// 判断是否是jakarta.annotation.Resource注解
else if (jakartaResourceType != null && field.isAnnotationPresent(jakartaResourceType)) {
// 如果是静态字段则直接抛出异常
if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException("@Resource annotation is not supported on static fields");
}
}
// 判断是否是javax.annotation.Resource注解
else if (javaxResourceType != null && field.isAnnotationPresent(javaxResourceType)) {
// 如果是静态字段则直接抛出异常
if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException("@Resource annotation is not supported on static fields");
}
}
});
// 方法检查
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
if (ejbAnnotationType != null && bridgedMethod.isAnnotationPresent(ejbAnnotationType)) {
if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
// 方法如果是静态方法则抛出异常
if (Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException("@EJB annotation is not supported on static methods");
}
}
}
else if (jakartaResourceType != null && bridgedMethod.isAnnotationPresent(jakartaResourceType)) {
if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
// 方法如果是静态方法则抛出异常
if (Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException("@Resource annotation is not supported on static methods");
}
}
}
else if (javaxResourceType != null && bridgedMethod.isAnnotationPresent(javaxResourceType)) {
if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
// 方法如果是静态方法则抛出异常
if (Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException("@Resource annotation is not supported on static methods");
}
}
}
});
}
while (targetClass != null && targetClass != Object.class);
}
通过上面的源码得知,在进行类字段/方法解析时就会判断相应的注解是否是静态的,如果是则直接抛出异常(此时是还没有进入到注入阶段)。
3.2 @Autowired注解
@Autowired与@Inject注解都是通过同一个处理器进行处理的。
查找对应注解方法入口
public class AutowiredAnnotationBeanPostProcessor {
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
findInjectionMetadata(beanName, beanType, beanDefinition) ;
}
private InjectionMetadata findInjectionMetadata(String beanName, Class<?> beanType, RootBeanDefinition beanDefinition) {
InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
return metadata;
}
private InjectionMetadata findAutowiringMetadata(...) {
// ...
InjectionMetadata metadata = buildAutowiringMetadata(clazz) ;
}
}
遍历当前类中的字段及方法
private InjectionMetadata buildAutowiringMetadata(Class<?> clazz) {
do {
// 处理字段
ReflectionUtils.doWithLocalFields(targetClass, field -> {
MergedAnnotation<?> 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;
}
}
});
// 处理方法
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
// 方法是静态方法输出日志后直接返回
if (Modifier.isStatic(method.getModifiers())) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation is not supported on static methods: " + method);
}
return;
}
});
}
while (targetClass != null && targetClass != Object.class);
}
同样该注解的处理器与@Resource注解的处理器直接阶段是一样的,都是没有真正的进入注入阶段。而真正进入注入阶段是在直接AbstractAutowireCapableBeanFactory#populateBean方法中。