IoC / DI 特别是 Spring:没有 setter/constructor 的自动装配属性
IoC / DI especially Spring: Autowire properties without setter/constructor
我有一个我自己无法回答的问题 - 至少不是很好。
想象以下代码:
@Service
public class ServiceA {
public void doService() {
System.out.println("Doing ServiceA");
}
}
@Service
public class ServiceB {
@Autowired
ServiceA serviceA;
public void doService() {
serviceA.doService();
}
}
它有效,但它被认为是不好的做法吗?如果您想解耦这些 classes 或测试它们,您将无法手动设置依赖项。
另外,Spring 是如何处理的?是否创建了一个代理 class 并为 属性 添加了构造函数?
如果你不喜欢自动装配(我也是)。您可以使用构造函数依赖注入。
你不应该对 class byt 接口使用 dependy,spring 将服务器正确实现(解耦)
public interface ServiceA {
public void doService();
}
@Service
public class ServiceAImpl implement ServiceA {
public void doService() {
System.out.println("Doing ServiceA");
}
}
@Service
public class ServiceBImpl implements ServiceB {
private final ServiceA serviceA;
public ServiceBImpl(ServiceA serviceA) {
this.serviceA = serviceA;
}
public void doService() {
serviceA.doService();
}
}
这是否是一种不好的做法取决于您编写此代码的时代。在 EJB 时代,这是一个最佳实践,容器为您提供了生命周期的所有功能,即使在 Spring 中,它也很好,即使在 Spring 中,这是非常严格的模型 java config 或 xml 是更灵活的解决方案。
然而在 20xx 时代,尤其是在 TDD 和敏捷模型中,我可以说这是一个真正的 BAD PRACTICE。这些 bean 无法在 Spring 容器之外进行测试,即使在编译时,注释也会在 Spring 处耦合你。更好的解决方案可能是如下代码
class ServiceA {
public void doService() {
System.out.println("Doing ServiceA");
}
}
class ServiceB {
private final ServiceA serviceA;
ServiceB(ServiceA serviceA) {
this.serviceA = serviceA;
}
public void doService() {
serviceA.doService();
}
}
@Configuration
class ServiceConfig{
@Bean
public ServiceA serviceA(){
return new ServiceA();
}
@Bean
public ServiceB serviceB(ServiceA serviceA){
return new ServiceB(serviceA);
}
}
这样,ServiceA 和 ServiceB classes 是两个完全 Spring 免费的 bean,特别是对于业务逻辑,这是最佳实践,bean 是可测试的,因为我们的依赖关系是显式的。
想象一下提供 ServiceB 的测试 class 以这种方式编写代码,您可以存根或模拟 serviceA 依赖项,并且可以单独测试 ServiceB bean。
对于代理的故事不用担心,因为我们提供了一个配置 class ServiceA 和 ServiceB 是两个 bean,就像注释的 class,Spring 管理一个 java 配置 bean 就像一个带注释的 bean。不同之处在于,现在我们可以受益于显式组合,并且可以提供更灵活的配置方案。我们可以再次受益于 Spring 的神奇 aop,因为正如之前所说,在 Java 配置中配置的 Spring bean 完全等同于带注释的 bean。
我建议您像示例一样使用 java 配置。
希望对您有所帮助
更新:
回复:
另外,Spring 是如何处理的?是否创建了一个代理 class 并为 属性 添加了构造函数?
我可以说:使用组件扫描功能,让我们说 @ComponentScan
、spring 找到所有带有 @Component、@Service、@Repository 等原型注解的 bean这个注释中的一些是有用的,因为触发一些功能,例如@Repository,如果我们实现一个 JPA 存储库并注册一个 PersistanceExceptionTraslatorPostProcessor,它在 DataAccessException 层次结构的 Exception 中转换 SQL 本机异常,但其他注释只是一种方式例如注册一个带有注解@Component 的spring bean,@Service。
Spring 通过反射创建 bean 并注入用@Autowired 注释的字段,如果您通过反射在字段上使用@Autowired 直接设置该字段,否则使用 setter,在 xml 例如 setter 或构造函数是必需的。
如果你有两个构造函数,它是透明的,Spring 将使用空构造函数,然后通过反射提供@Autowired 属性,你甚至可以像下面这样:
@Service
class ServiceA {
public void doService() {
System.out.println("Doing ServiceA");
}
}
@Service
class ServiceB {
private ServiceA serviceA;
public ServiceB() {
}
@Autowired
public ServiceB(ServiceA serviceA) {
this.serviceA = serviceA;
}
public void doService() {
serviceA.doService();
}
}
在这种情况下,spring 认识到它使用带注释的构造函数和 @Autowired 来创建 bean 并提供依赖项。无论如何,最佳实践绝对是我回答中的第一段代码。它在依赖项中是明确的,在您的业务代码库中 Spring 免费
我有一个我自己无法回答的问题 - 至少不是很好。
想象以下代码:
@Service
public class ServiceA {
public void doService() {
System.out.println("Doing ServiceA");
}
}
@Service
public class ServiceB {
@Autowired
ServiceA serviceA;
public void doService() {
serviceA.doService();
}
}
它有效,但它被认为是不好的做法吗?如果您想解耦这些 classes 或测试它们,您将无法手动设置依赖项。
另外,Spring 是如何处理的?是否创建了一个代理 class 并为 属性 添加了构造函数?
如果你不喜欢自动装配(我也是)。您可以使用构造函数依赖注入。
你不应该对 class byt 接口使用 dependy,spring 将服务器正确实现(解耦)
public interface ServiceA {
public void doService();
}
@Service
public class ServiceAImpl implement ServiceA {
public void doService() {
System.out.println("Doing ServiceA");
}
}
@Service
public class ServiceBImpl implements ServiceB {
private final ServiceA serviceA;
public ServiceBImpl(ServiceA serviceA) {
this.serviceA = serviceA;
}
public void doService() {
serviceA.doService();
}
}
这是否是一种不好的做法取决于您编写此代码的时代。在 EJB 时代,这是一个最佳实践,容器为您提供了生命周期的所有功能,即使在 Spring 中,它也很好,即使在 Spring 中,这是非常严格的模型 java config 或 xml 是更灵活的解决方案。
然而在 20xx 时代,尤其是在 TDD 和敏捷模型中,我可以说这是一个真正的 BAD PRACTICE。这些 bean 无法在 Spring 容器之外进行测试,即使在编译时,注释也会在 Spring 处耦合你。更好的解决方案可能是如下代码
class ServiceA {
public void doService() {
System.out.println("Doing ServiceA");
}
}
class ServiceB {
private final ServiceA serviceA;
ServiceB(ServiceA serviceA) {
this.serviceA = serviceA;
}
public void doService() {
serviceA.doService();
}
}
@Configuration
class ServiceConfig{
@Bean
public ServiceA serviceA(){
return new ServiceA();
}
@Bean
public ServiceB serviceB(ServiceA serviceA){
return new ServiceB(serviceA);
}
}
这样,ServiceA 和 ServiceB classes 是两个完全 Spring 免费的 bean,特别是对于业务逻辑,这是最佳实践,bean 是可测试的,因为我们的依赖关系是显式的。
想象一下提供 ServiceB 的测试 class 以这种方式编写代码,您可以存根或模拟 serviceA 依赖项,并且可以单独测试 ServiceB bean。
对于代理的故事不用担心,因为我们提供了一个配置 class ServiceA 和 ServiceB 是两个 bean,就像注释的 class,Spring 管理一个 java 配置 bean 就像一个带注释的 bean。不同之处在于,现在我们可以受益于显式组合,并且可以提供更灵活的配置方案。我们可以再次受益于 Spring 的神奇 aop,因为正如之前所说,在 Java 配置中配置的 Spring bean 完全等同于带注释的 bean。
我建议您像示例一样使用 java 配置。
希望对您有所帮助
更新: 回复: 另外,Spring 是如何处理的?是否创建了一个代理 class 并为 属性 添加了构造函数?
我可以说:使用组件扫描功能,让我们说 @ComponentScan
、spring 找到所有带有 @Component、@Service、@Repository 等原型注解的 bean这个注释中的一些是有用的,因为触发一些功能,例如@Repository,如果我们实现一个 JPA 存储库并注册一个 PersistanceExceptionTraslatorPostProcessor,它在 DataAccessException 层次结构的 Exception 中转换 SQL 本机异常,但其他注释只是一种方式例如注册一个带有注解@Component 的spring bean,@Service。
Spring 通过反射创建 bean 并注入用@Autowired 注释的字段,如果您通过反射在字段上使用@Autowired 直接设置该字段,否则使用 setter,在 xml 例如 setter 或构造函数是必需的。
如果你有两个构造函数,它是透明的,Spring 将使用空构造函数,然后通过反射提供@Autowired 属性,你甚至可以像下面这样:
@Service
class ServiceA {
public void doService() {
System.out.println("Doing ServiceA");
}
}
@Service
class ServiceB {
private ServiceA serviceA;
public ServiceB() {
}
@Autowired
public ServiceB(ServiceA serviceA) {
this.serviceA = serviceA;
}
public void doService() {
serviceA.doService();
}
}
在这种情况下,spring 认识到它使用带注释的构造函数和 @Autowired 来创建 bean 并提供依赖项。无论如何,最佳实践绝对是我回答中的第一段代码。它在依赖项中是明确的,在您的业务代码库中 Spring 免费