Spring @Autowire 关于属性 vs 构造函数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/40620000/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 01:15:54  来源:igfitidea点击:

Spring @Autowire on Properties vs Constructor

springdependency-injectionconstructorautowired

提问by GSUgambit

So since I've been using Spring, if I were to write a service that had dependencies I would do the following:

因此,由于我一直在使用 Spring,如果我要编写一个具有依赖项的服务,我会执行以下操作:

@Component
public class SomeService {
     @Autowired private SomeOtherService someOtherService;
}

I have now run across code that uses another convention to achieve the same goal

我现在遇到了使用另一个约定来实现相同目标的代码

@Component
public class SomeService {
    private final SomeOtherService someOtherService;

    @Autowired
    public SomeService(SomeOtherService someOtherService){
        this.someOtherService = someOtherService;
    }
}

Both of these methods will work, I understand that. But is there some advantage to using option B? To me, it creates more code in the class and unit test. (Having to write constructor and not being able to use @InjectMocks)

这两种方法都行,我明白。但是使用选项 B 有什么好处吗?对我来说,它在类和单元测试中创建了更多代码。(必须编写构造函数并且无法使用@InjectMocks)

Is there something I'm missing? Is there anything else the autowired constructor does besides add code to the unit tests? Is this a more preferred way to do dependency injection?

有什么我想念的吗?除了向单元测试添加代码之外,自动装配的构造函数还有什么其他功能吗?这是进行依赖注入的更首选方法吗?

回答by JB Nizet

Yes, option B (which is called constructor injection) is actually recommended over field injection, and has several advantages:

是的,选项 B(称为构造函数注入)实际上比字段注入更推荐,并且有几个优点:

  • the dependencies are clearly identified. There is no way to forget one when testing, or instantiating the object in any other circumstance (like creating the bean instance explicitly in a config class)
  • the dependencies can be final, which helps with robustness and thread-safety
  • you don't need reflection to set the dependencies. InjectMocks is still usable, but not necessary. You can just create mocks by yourself and inject them by simply calling the constructor
  • 清楚地确定了依赖关系。在测试或在任何其他情况下实例化对象(例如在配置类中显式创建 bean 实例)时,无法忘记一个
  • 依赖关系可以是最终的,这有助于提高健壮性和线程安全性
  • 您不需要反射来设置依赖项。InjectMocks 仍然可用,但不是必需的。您可以自己创建模拟并通过简单地调用构造函数来注入它们

See this blog postfor a more detailed article, by one of the Spring contributors, Olivier Gierke.

有关 更详细的文章,请参阅Spring 贡献者之一Olivier Gierke 的这篇博文。

回答by developer

I will explain you in simple words:

我会用简单的话给你解释:

In Option(A),you are allowing anyone (in different class outside/inside the Spring container) to create an instance using default constructor (like new SomeService()), which is NOT good as you need SomeOtherServiceobject (as a dependency) for your SomeService.

在选项(A)中,您允许任何人(在 Spring 容器外部/内部的不同类中)使用默认构造函数(如new SomeService())创建实例,这并不好,因为您需要SomeOtherService对象(作为依赖项)作为SomeService.

Is there anything else the autowired constructor does besides add code to the unit tests? Is this a more preferred way to do dependency injection?

除了向单元测试添加代码之外,自动装配的构造函数还有什么其他功能吗?这是进行依赖注入的更首选方法吗?

Option(B) is preferred approachas it does NOT allow to create SomeServiceobject without actually resolving the SomeOtherServicedependency.

Option(B) 是首选方法,因为它不允许在SomeService没有实际解决SomeOtherService依赖关系的情况下创建对象。

回答by stinger

Please note, that since Spring 4.3you don't even need an @Autowired on your constructor, so you can write your code in Java style rather than tying to Spring's annotations. Your snippet would look like that:

请注意,从Spring 4.3 开始,您的构造函数甚至不需要 @Autowired,因此您可以用 Java 风格编写代码,而不是绑定到 Spring 的注释。您的代码段如下所示:

@Component
public class SomeService {
    private final SomeOtherService someOtherService;

    public SomeService(SomeOtherService someOtherService){
        this.someOtherService = someOtherService;
    }
}

回答by Daniel Perník

Good to know

很高兴知道

If there is only one constructor call, there is no need to include an @Autowired annotation. Then you can use something like this:

如果只有一个构造函数调用,则不需要包含@Autowired 注解。然后你可以使用这样的东西:

@RestController
public class NiceController {

    private final DataRepository repository;

    public NiceController(ChapterRepository repository) {
        this.repository = repository;
    }
}

... example of Spring Data Repository injection.

... Spring Data Repository 注入示例。

回答by Dougie T

Actually, In my experience, The second option is better. Without the need for @Autowired. In fact, it is wiser to create code that is not too tightly coupled with the framework(as good as Spring is). You want code that tries as much as possible to adopt a deferred decision-makingapproach. That is as much pojoas possible, so much such that the framework can be swapped out easily. So I would advise you create a separate Config file and define your bean there, like this:

实际上,根据我的经验,第二种选择更好。无需@Autowired. 事实上,创建与框架耦合不太紧密的代码(和 Spring 一样好)是更明智的做法。您需要尽可能多地尝试采用延迟决策方法的代码。这是尽可能多的pojo,以至于框架可以很容易地换掉。所以我建议你创建一个单独的配置文件并在那里定义你的 bean,如下所示:

In SomeService.javafile:

SomeService.java文件中:

public class SomeService {
    private final SomeOtherService someOtherService;

    public SomeService(SomeOtherService someOtherService){
        this.someOtherService = someOtherService;
    }
}

In ServiceConfig.javafile:

ServiceConfig.java文件中:

@Config
public class ServiceConfig {
    @Bean
    public SomeService someService(SomeOtherService someOtherService){
        return new SomeService(someOtherService);
    }
}

In fact, if you want to get deeply technical about it, there are thread safety questions (among other things) that arise with the use of Field Injection(@Autowired), depending on the size of the project obviously. Check this outto learn more on the advantages and disadvantages of Autowiring. Actually, the pivotal guys actually recommend that you use Constructor injectioninstead of Field Injection

事实上,如果你想深入了解它,使用Field Injection( @Autowired)会出现线程安全问题(除其他外),这显然取决于项目的大小。查看此内容以了解有关Autowiring优点和缺点的更多信息。实际上,关键人实际上建议您使用构造函数注入而不是字段注入

回答by Swapan Pramanick

Autowiredconstructors provides a hook to add custom code before registering it in the spring container. Suppose SomeServiceclass extends another class named SuperSomeServiceand it has some constructor which takes a name as its argument. In this case, Autowiredconstructor works fine. Also, if you have some other members to be initialized, you can do it in the constructor before returning the instance to spring container.

Autowired在spring容器中注册之前,constructors提供了一个钩子来添加自定义代码。假设SomeServiceclass 扩展了另一个名为的类,SuperSomeService并且它有一些构造函数,该构造函数将名称作为其参数。在这种情况下,Autowired构造函数工作正常。此外,如果您还有其他一些成员要初始化,您可以在将实例返回到 spring 容器之前在构造函数中进行初始化。

public class SuperSomeService {
     private String name;
     public SuperSomeService(String name) {
         this.name = name;
     }
}

@Component
public class SomeService extends SuperSomeService {
    private final SomeOtherService someOtherService;
    private Map<String, String> props = null;

    @Autowired
    public SomeService(SomeOtherService someOtherService){
        SuperSomeService("SomeService")
        this.someOtherService = someOtherService;
        props = loadMap();
    }
}