spring @Autowire 属性与设置器

Posted

技术标签:

【中文标题】spring @Autowire 属性与设置器【英文标题】:spring @Autowire property vs setter 【发布时间】:2016-02-07 08:38:56 【问题描述】:

将@Autowired 注释到属性或在setter 中执行有什么区别?

据我所知,它们都有相同的结果,但是有什么理由使用其中一个而不是另一个?

更新(更简洁)

这有区别吗

package com.tutorialspoint;

import org.springframework.beans.factory.annotation.Autowired;

public class TextEditor 
   private SpellChecker spellChecker;

   @Autowired
   public void setSpellChecker( SpellChecker spellChecker )
      this.spellChecker = spellChecker;
   

   public void spellCheck() 
      spellChecker.checkSpelling();
   

还有这个

package com.tutorialspoint;

import org.springframework.beans.factory.annotation.Autowired;

public class TextEditor 
   @Autowired
   private SpellChecker spellChecker;

   public TextEditor() 
      System.out.println("Inside TextEditor constructor." );
   

   public void spellCheck()
      spellChecker.checkSpelling();
   

【问题讨论】:

我不知道为什么这个问题的答案会是固执己见。我想知道在 setter 中或直接在属性中使用 @Autowired 是否有实际区别。不问哪个好,只要有区别就行了 【参考方案1】:

有时您需要一个类 A 的实例,但您不将 A 存储在类的字段中。 您只需要一个实例来执行一次性操作。或者,您使用 A 实例获取 B 的实例,并将 B 存储在字段中。

在这些情况下,设置器(或构造器)自动装配会更适合您。 您不会有未使用的类级字段。

具体例子: 您需要构造 RabbitTemplate(向 RabbitMQ 发送消息的对象) 要构建它,您需要 ConnectionFactoryhttp://docs.spring.io/spring-amqp/docs/latest_ga/api/org/springframework/amqp/rabbit/core/RabbitTemplate.html#RabbitTemplate-org.springframework.amqp.rabbit.connection.ConnectionFactory-

您不需要存储该 ConnectionFactory。在这种情况下,代码如下所示:

Class MyClass 
private RabbitTemplate template;

@Autowired 
void setConnectionFactory(ConnectionFactory c) 
    template=new RabbitTemplate(c);


...比直接自动装配 ConnectionFactory 字段更好。

在此示例中,在构造函数级别自动装配会更好,因为您的对象将始终被完全构造。很明显,ConnectionFactory 是强制依赖,而不是可选依赖。

【讨论】:

【参考方案2】:

有了@Autowired注解,你就不需要setter方法了。一旦您的 bean 的构造函数完成分配/创建对象,Spring 将扫描此注解并注入您注解的对象实例。

如果您有 setter 并且仍在使用 xml 配置,您将显式设置属性。

话虽如此,您可以使用自动装配注解来注解您的构造函数和 setter 方法,这是我更喜欢的,因为这会给我以后离开 Spring 的灵活性(尽管我不会这样做)。

【讨论】:

在我用来更新帖子的 Spring 示例中,DI 术语有什么不同吗? 没有区别,你仍然在注入依赖。这只是构造函数或设置器的选择,例如 也许这可以让我们更清楚地了解这个问题。 Setter DI vs. Constructor DI in Spring? @SMA - 如何通过 setter 注入远离 Spring?设置器上方是否还会有一个 Autowired 注释,即“spring”? 知道了,感谢@veritas 的澄清。在这种情况下,您可以使用JDK 提供的Inject 注释。关于相同的细节已经结束here【参考方案3】:

如果您在 property 上使用@Autowired 注释,spring 将使用 spring.xml 启动该属性。在这种情况下,您不需要 setter。

如果您在 setter 上使用@Autowired 注释,则您指定 spring 应该使用此 setter 方法启动此属性,您可以在其中添加自定义代码,比如用这个属性初始化一些其他的属性

示例用法: 在使用 JdbcTemplate 使用 DAO 操作的情况下,您需要 DataSource 作为 JdbcTemplate 的输入,但 DataSource 本身不需要作为属性。因此,您可以使用 DataSource Setter 通过自动连接 DataSource Setter 来初始化 JdbcTempate。请看下面的代码:

class DaoDemo
   //@Autowired
   //private DataSource dataSource;
   private JdbcTemplate jdbcTemplate;

   @Autowired
   public void setDataSource(DataSource dataSource)
     //this.dataSource = dataSource;  
     this.jdbcTemplate = new JdbcTemplate(dataSource);
   

   public int getTableRowCount()
      String sql = "SELECT COUNT(*) FROM DEMOTABLE";
      //jdbcTemplate.setDataSource(dataSource);    //No need to do this as its done in DataSource Setter now.
      return jdbcTemplate.queryForObject(sql,Integer.class);


在上面的代码中,dataSource 的唯一用途是在 JdbcTemplate 中传递。因此,在这里创建 dataSource 的属性没有意义。因此,只需使用 DataSource bean 的 setter 方法上的 @Autowired 从 spring.xml 获取其条目并在该特定时间本身使用它。

【讨论】:

【参考方案4】:

自动装配有 3 种类型:

基于属性
@Autowired
private MyService service;
基于构造函数。请注意,在这种情况下,在 Spring Boot 中您甚至不需要 @Autowired 注释:
class MyController 
 private final MyService service;

 public MyController(MyService service) 
  this.service = service;
 

基于二传手:
private MyService service;


@Autowired
public void setService(MyService service) 
 this.service = service;

建议使用基于构造器的,如果不可能的话,使用基于Setter,最后基于Property。

为什么?

首先,因为在基于构造函数的情况下,您甚至不使用任何 Spring 注释。这有助于您过渡到不同的框架。

其次,基于构造函数或设置器,使单元测试更容易。您不需要使用任何 Spring 特定的测试工具,您只能使用 Junit 和 Mockito。

第三,基于构造函数很好,因为您可以将属性声明为final,并且不公开有助于类的不变性和线程安全的设置器。

【讨论】:

【参考方案5】:

自动装配在项目中一致使用时效果最佳。如果一般不使用自动装配,开发人员可能会混淆使用它来只装配一个或两个 bean 定义。在字段上使用 @Autowired 时,您不需要 setter 方法,它一方面使类更小且更易于阅读,但另一方面使模拟类有点难看。

属性和构造函数参数设置中的显式依赖项始终覆盖自动装配。您不能自动装配所谓的简单属性,例如基元、字符串和类(以及此类简单属性的数组)。此限制是设计使然。

自动连线不如显式连线精确。 Spring 会小心避免猜测可能会产生意外结果的歧义,Spring 管理的对象之间的关系不再明确记录。

可能无法从 Spring 容器生成文档的工具使用接线信息。

容器中的多个 bean 定义可能与要自动装配的 setter 方法或构造函数参数指定的类型匹配。对于数组、集合或 Map,这不一定是问题。然而,对于期望单个值的依赖项,这种歧义不会被任意解决。如果没有唯一的 bean 定义可用,则抛出异常。

【讨论】:

【参考方案6】:

如果可以的话,你应该避免使用 setter。如果你不需要它,最好它不存在,对吧?

我个人更喜欢 Guice 允许我写作

public class TextEditor 
   private final SpellChecker spellChecker;

   @Inject public TextEditor(SpellChecker spellChecker) 
      this.spellChecker = spellChecker;
   

   public void spellCheck()
      spellChecker.checkSpelling();
   

这更进一步:有了final 字段,我知道它永远不会改变,我得到了multithreading visibility guarantee。

【讨论】:

【参考方案7】:

在一种情况下,对 OPTIONAL 属性使用 @Autowired 不起作用。

如果您想使用该属性进行一些初始化,它可能不会在调用构造函数之前设置,并且由于它是可选的,因此您不能将其作为参数放入构造函数中。

在这种情况下,最好使用@Autowired setter 方法,这样您就可以在属性自动装配后执行初始化。

【讨论】:

以上是关于spring @Autowire 属性与设置器的主要内容,如果未能解决你的问题,请参考以下文章

从头认识Spring-2.5 @Autowire @Inject @Qualifier @Named的相同与不同

spring03autowire属性

Spring4学习笔记 - 配置Bean - 自动装配 关系 作用域 引用外部属性文件

Spring Autowire自动装配

Spring笔记04(DI(给属性赋值),自动装配(autowire))

spring自动装配有几种方式