如何在组件 class 中注入私有字段,同时在 Spring 的父 class 中继续使用 @Autowired 启动?

How to inject a private field in a Component class while keep initiating with @Autowired in parent class in Spring?

我正在学习 Spring,同时我喜欢使用 @Component 和 @Autowired 让 Spring 管理依赖 bean 的想法。例如,我有一个 Service class 和 Builder Class 我可以用

// SomeService.java
@Service
public class SomeService {
    // SomeBuilder is a @Component class
    @Autowired
    SomeBuilder someBuilder;
}

// SomeController.java
@Component
public class SomeController {
    @Autowired
    SomeService someSerivce;
}

Spring 将使用@Autowired 处理从 SomeController 到 SomeService 再到 SomeBuilder 的创建。但是,现在我的 SomeService class 需要一个私有字段,它不是组件 class,只是一个普通的上下文对象,例如

// SomeService.java
@Service
public class SomeService {
    @Autowired
    SomeBuilder someBuilder;
    
    private SomeContext someContext;

    // Plan A: Using constructor to initiate the private field. However, I cannot use @Autowired to initiate SomeService in SomeController anymore as it requires a parameterless constructor
    // Plan B: using @Autowired on constructor level, I cannot use this because SomeContext is not a @Component class
    //public SomeService(SomeContext someContext) {
        //this.someContext = someContext;
    //}
    
    // Plan C: This seems work but I kinda feel it's not the right way, as usually private field are initiated through constructor
    //public void init(SomeContext someContext) {
        // this.someContext = someContext;
    //}
    
    // demo usage of someContext
    public someAnswer realMethod() {
        System.out.println(someContext.getName());
    }
}

现在我不知道如何注入 someContext,我用了

方案 A:使用 class 构造函数

分配私有字段

计划 B:在构造函数级别使用 @Autowired

方案C:使用有线方式分配私有字段。

但我不满意,也没有明确的方法来做正确的事情。

首先让我们看看您的计划并破坏一些 myths/misunderstandings。

Plan A: Using constructor to initiate the private field. However, I cannot use @Autowired to initiate SomeService in SomeController anymore as it requires a parameterless constructor

伟大的计划,以及要走的路。 @Autowired 不依赖于默认构造函数。它仅表示您希望为该字段注入该类型的对象。该对象如何生效(默认构造函数、带参数的构造函数)对于 @Autowired 无关紧要。所以你的那部分理解是错误的。

using @Autowired on constructor level, I cannot use this because SomeContext is not a @Component class

如果 bean 中只有一个构造函数 Spring 将自动使用它来满足依赖关系。所以在这种情况下你不需要 @Autowired。 bean 不必是 @Component,bean 只是应用程序上下文可用的 class 的一个实例。实现这一点的一种方法是将其标记为 @Component,但还有其他方法。就像在 @Configuration class 中定义一个 @Bean 方法来构造 bean。

@Configuration
@ComponentScan("your.base.package")
public class YourConfiguration {

  @Bean
  public SomeContext someContext() {
    return new SomeContext();
  }
}

类似的东西。它将通过 @ComponentScan 检测 @Component 注释的 classes 并将创建一个 SomeContext 类型的 bean 用作 bean。

Plan C: This seems work but I kinda feel it's not the right way, as usually private field are initiated through constructor

您的所有字段都应该是 private,而不仅仅是在构造函数中初始化的字段,@Autowired 字段也是如此。您不希望这些字段可以很容易地从外部访问以便对其进行修改。所以让他们 private.

综上所述,构造函数注入优于字段注入或 setters/methods 注入。它比字段注入和强制依赖的方式更清晰,隐藏更少(对于可选依赖,您可以使用 setter/method)。

因此使用上面的配置和下面的 classes,它应该“正常工作 (tm)”。

// SomeService.java
@Service
public class SomeService {
  // SomeBuilder is a @Component class  
  private final SomeBuilder someBuilder;
  private final SomeContext someContext;
  public SomeService(SomeBuilder someBuilder, SomeContext someContext) {
    this.someBuilder=someBuilder;
    this.someContext=someContext;
  }
}

// SomeController.java
@Component
public class SomeController {

  private final SomeService someSerivce;

  public SomeController(SomeService someService) {
    this.someService=someService;
  }
}