【问题标题】:spring @Autowire property vs setterspring @Autowire 属性与设置器
【发布时间】: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 是否有实际区别。不问哪个好,只要有区别就行了

标签: java spring dependency-injection


【解决方案1】:

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

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

具体例子:
您需要构造 RabbitTemplate(向 RabbitMQ 发送消息的对象) 要构建它,您需要 ConnectionFactory
http://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 方法,这样您就可以在属性自动装配后执行初始化。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2016-11-02
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多