【问题标题】:How to inject a private field in a Component class while keep initiating with @Autowired in parent class in Spring?如何在 Spring 的父类中继续使用 @Autowired 启动时在 Component 类中注入私有字段?
【发布时间】:2021-10-11 00:50:21
【问题描述】:

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

// 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 类需要一个私有字段,它不是 Component 类,只是一个普通的上下文对象,例如

// 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:使用类构造函数分配私有字段

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

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

但我不满意,也没有明确的方法来采取正确的方法。

【问题讨论】:

  • 这就是构造函数的用途。使用它们而不是现场注入。 (在“我需要一个不是 bean 的值”的情况下,您可以使用 @Bean 方法。)
  • 有什么可以参考的例子吗?我现在不是使用 Bean 注释的专家,你是建议将 @Bean 放在我的 init 方法上还是其他地方?
  • @Bean 方法进入@Configuration 类。您正在寻找的功能集在 3.0 中是新的时称为 JavaConfig,但现在只是配置 Spring 上下文的标准方法。
  • 字段为private这一事实并不意味着它必须通过构造函数进行初始化,这种思路是错误的。基本上你的所有字段都应该是private@Autowired (你可能最好使用构造函数注入,因为这比隐藏字段更清晰)。为什么SomeContext 不是一个bean?你对 bean 的定义是什么?在控制器中使用@Autowired 并且没有无参数构造函数的另一件事彼此无关。

标签: java spring dependency-injection autowired


【解决方案1】:

首先让我们看看你的计划,并消除一些神话/误解。

方案A:使用构造函数来初始化私有字段。但是,我不能再使用@AutowiredSomeController 中启动SomeService,因为它需要一个无参数的构造函数

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

在构造函数级别使用@Autowired,我不能使用它,因为SomeContext 不是@Component

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

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

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

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

Plan C:这似乎可行,但我觉得这不是正确的方式,因为通常private 字段是通过构造函数启动的

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

话虽如此,使用构造函数注入而不是字段注入或 setter/method 进行注入。它比字段注入和强制依赖的方式更清晰、更隐蔽(对于可选依赖,您可以使用 setter/method)。

所以使用上面的配置和下面的类,它应该“正常工作(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;
  }
}

【讨论】:

  • 非常感谢!从中学到了很多!
  • 就像你定义的@Bean of SomeContext 只是返回一个new SomeContext(),但我还需要设置SomeContext 的很多字段/属性...如果我这样做@Bean public SomeContext someContext(String name, Date createDate) { ... } 那么我怎么能打电话它返回给我一个完整的 someContext 然后将它注入到 SomeService 中?
  • 我发现我可以在运行时使用prototypebeanFactory 来启动我的@Bean SomeContext,但是,在我创建了我的SomeContext 之后,我怎么能不将它注入SomeService使用SomeService的构造函数?
  • 根据我的理解,我对 Spring DI 进行了一些额外的研究:要在私有字段上使用 Field DI,该字段需要是具有无参数构造函数的组件类,或者它可以具有构造函数,但其​​成员与组件类相同,Spring 可以在没有非依赖字段的情况下处理初始化。这可能不适用于需要在运行时创建的 bean 或类。所以我仍然使用作为字段 DI 的请求可能在这里不可用
  • 它非常有能力,要么你的来源有误,要么你的理解有误。对于@Autowired 字段/构造函数/方法,bean 如何存在并不重要。没有什么规定 @Autowired 字段类型需要有一个无参数(又名默认)构造函数。有一件事,如果您使用组件扫描,则必须有一个用于自动装配的构造函数,这可以只是类中的一个构造函数标记为@987654354的构造函数@.
猜你喜欢
  • 2019-09-16
  • 2016-11-14
  • 2020-09-22
  • 2013-05-01
  • 2016-05-30
  • 2016-02-01
  • 1970-01-01
  • 2023-03-19
  • 1970-01-01
相关资源
最近更新 更多