如何在组件 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;
}
}
我正在学习 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 initiateSomeService
inSomeController
anymore as it requires a parameterless constructor
伟大的计划,以及要走的路。 @Autowired
不依赖于默认构造函数。它仅表示您希望为该字段注入该类型的对象。该对象如何生效(默认构造函数、带参数的构造函数)对于 @Autowired
无关紧要。所以你的那部分理解是错误的。
using
@Autowired
on constructor level, I cannot use this becauseSomeContext
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;
}
}