Field @Autowired 背后的魔力是什么

问题描述 投票:0回答:2

我目前正在提高我的 Spring 知识。我想知道当我在字段上使用 Spring 注释 @Autowire 时到底会发生什么。

这是一段代码:

输出帮助文件

@Component
public class OutputHelper {
    @Autowired
    @Qualifier("csvOutputGenerator")
    private IOutputGenerator outputGenerator;

    public void setOutputGenerator(IOutputGenerator outputGenerator) {
        this.outputGenerator = outputGenerator;
    }

    // I can focus only on what my code do because my objects are injected
    public void generateOutput(){
        outputGenerator.generateOutput();
    }
}

CsvOutputGenerator 文件

@Component 
public class CsvOutputGenerator implements IOutputGenerator {
    public void generateOutput(){
        System.out.println("Csv Output Generator");
    } 
}

申请文件

public static void main(String[] args) {
    // Create the spring context
    ApplicationContext context = new ClassPathXmlApplicationContext("META-INF/spring/spring-module.xml");

    // Get the configured OutpuHelper from the spring-module.xml
    OutputHelper output = (OutputHelper) context.getBean("outputHelper");

    // Display output from the output configured
    output.generateOutput(); 
}

我的配置文件只包含

<context:component-scan base-package="com.xxx.xxx.output"/>

当我执行此代码时,一切正常。但让我惊讶的是,当我删除 OutPutHelper 文件中的 setOutputGenerator 时,我的代码仍然有效。我认为通过此配置,OutputHelper 首先使用默认构造函数创建并使用 setter 初始化。

我预计会出现错误,因为变量outputGenerator无法初始化。

有人可以帮助我理解吗?

spring
2个回答
4
投票

拥有字段

@Autowired
的想法是有问题的。它有效,但会给您实施的其他方面(即测试)带来困难。

有3种注射方式:

  • 字段 - 基本配置为直接将反射 (Field.set(Object, Object)) 应用于字段:

      @Autowired
      private MyInterface field;
    
  • setters - 通过这种方法,每个依赖项的配置都通过一个属性(spring 遍历所有方法并使用

    Method.invoke(Object, Object...)
    执行每个用 @Autowired 注释的方法,因此其值为使用其设置器配置如下:

      @Autowired
      public void setField(MyInterface value) { 
          this.field = value;
      }
    
  • 构造函数 - 最后也是我更喜欢的方法,构造函数注入。该方法基本上用

    @Autowired
    注释构造函数,您可以直接在构造函数上配置 bean,而不是使用方法或字段。为此,spring 将选择一个构造函数来实例化您的
    @Component
    ,并且它将使用
    @Autowired
    (如果存在)或空的 params 构造函数,并使用 Constructor.newInstance(Object...) 调用它。示例:

      @Component
      public class Implementation {
          private MyInterface field;
          @Autowired
          public Implementation(MyInterface value) {
              Assert.notNull(value, "value should not be null");
              this.field = value;
          }
      }
    

控制反转(或依赖注入)背后的想法之一是能够隔离一段代码,以提供良好的测试实现支持。

为了更深入,有必要评论一下,在单元测试期间,您希望该类处于隔离形式,您将与该类一起使用的所有内容基本上都是其依赖项(注入)的模拟。

那么,结果是什么:

  • 如果你进行字段注入,那么每次在测试期间使用一些反射来设置bean来配置bean将是相当昂贵的(需要引入另一个逻辑来配置要测试的bean)。
  • 使用 setter 注入方法,您将能够使用自己的 bean 来配置它,并使用隔离您的实现和测试其功能所需的模拟。
  • 最后,通过构造函数注入方法,您不仅可以支持配置 bean,还可以需要它的依赖项。这意味着对于每个新的依赖项,都会在构造函数上添加一个新参数,这会给您带来开发时间的优势,例如,您将能够在开发时间看到由于引入该新依赖项而影响的单元测试(一旦你的 IDE 会为你指出)。

3
投票

简单回答

实际上,setter 是没有用的,因为

CDI
使用 java Reflection 来访问字段。

这意味着字段不再通过方法调用来访问。 反射允许迭代类的所有字段并检查是否有特定注释的注释。

在这种情况下,如果类中的字段使用

@Autowired
(或
@Inject
,更符合 J2E)进行注释,则容器将迭代搜索是否存在适合当前属性的注册 bean。


深入了解

当上下文启动时,容器会迭代类并搜索所有用

@Inject
@Autowired
注释的字段。

对于这些字段,它会搜索可用的 bean。

这是一个必须简单的例子:

public class SpringClassInChargeOfDependencyInjection {
    public void handdleInjections(T objectWithInjectableField) {
        Class<T> clazz = objectWithInjectableField.class;
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            if (field.isAnnotationPresent(Autowired.class) || field.isAnnotationPresent(Inject.class)) {
                //find a bean for the type;
                Object injectableBean = getAvailablebean(field.getType());
                field.setAccessible(true);
                //inject the value into the class, this line explain why the setter is not necessary
                field.set(objectWithInjectableField, injectableBean);
            }
        }
    }
}

这是一个非工作示例,只是为了解释它是如何工作的。

小贴士

您可以考虑使用

@Inject
代替
@Autowired
,后者是由Spring创建的,
@Inject
是JSR-330的一部分。 Spring 也理解
@Inject
,您只需将
javax.inject
jar 依赖项添加到您的项目中即可。如果稍后你想从 spring 切换到其他东西(例如 guice),你不必更改所有
@Autowired
注释

<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>
© www.soinside.com 2019 - 2024. All rights reserved.