Spring为什么不支持static字段注入

环境: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方法中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值