spring 字段注入的内部工作原理以及为什么不推荐使用

Internal working of field injection in spring and why is it not recommended to use

正如标题所暗示的,我想知道字段注入在 spring 中是如何工作的,我阅读了很多这方面的文章,并了解了一些类似下面的内容,但不明白背后的确切原因它:

-> 不应该使用它,因为当您进行单元测试时,您依赖于 spring 在字段注入的情况下实例化 class 的容器。

-> 在字段注入的情况下不能使用“final”关键字,这意味着不能使字段不可变。

-> 内部使用反射

我想知道@Autowired 在内部究竟是如何工作的,它是如何使用反射的,我想了解上述所有要点背后的确切原因,当我们编写以下代码时幕后发生了什么:

@Component
public class B {

    @Autowired
    private A a1;

}

我已经阅读过有关此主题的堆栈溢出的类似问题,但找不到我正在寻找的确切解释。

Spring 有 Bean Post 处理器的概念。

当 spring 构建一个 bean 时,它会应用已注册的 bean post 处理器来帮助“初始化”该 bean。

所以,有 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor 处理自动装配。

基本上它适用于新创建的对象。 Spring 内省 bean 的字段(通过使用反射)。具有 @Autowired 的字段是使用此 bean post 处理器处理的主题。它在应用程序上下文中找到注入的候选者并实际注入值。

现在有了这些信息,就可以理解为什么最终字段不能自动装配了。别管 spring,在纯 Java 中,final 字段必须在声明 (final int i = 123) 期间或在 class 的构造函数中直接实例化。但是自动装配发生在构造函数之后,所以不可能自动装配最终字段。

至于单元测试,私有属性必须以某种方式从测试中配置。但是由于它们是封装的(是的,spring 在这种情况下使用时会破坏封装),不可能为包含字段注入的 class 编写好的测试。这就是切换到构造函数注入的原因。

public class FieldInjection {
   @Autowired
   private A a;
}

VS.

public class ConstructorInjection {

   private final A a;

   // this can be generated by lombok, you don't have to put @Autowired on constructor in the case of single constructor, spring will use it to create a bean
   public ConstructorInjection(A a) {
      this.a = a; 
   }
}

现在无法对 FieldInjection class 进行测试:


public class FieldInjectionTest {

   @Test
   void test() {
      FieldInjection underTest = new FieldInjection();
      how do you know that you should instantiate A a.  ???? 
   }
}

然而,在构造函数注入的情况下,这是一项微不足道的任务:

public class ConstructorInjectionTest {

   @Test
   void test() {
     A a = mock(A.class);
     ConstructorInjection underTest = new ConstructorInjection(a); 
     // the dependencies must be supplied in the constructor 
     // otherwise its impossible to create an object under test
   }
}