我目前正在提高我的Spring知识。我想知道当我在一个字段上使用Spring注释@Autowire时会发生什么。
这是一段代码:
OutputHelper文件
@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无法初始化。
有人能帮我理解吗?
答案 0 :(得分:4)
让字段@Autowired
的想法值得怀疑。它有效,但实施的其他方面(即测试)将很困难。
有三种类型的注射:
字段 - 基本上将反射(Field.set(Object, Object))直接应用于字段:
@Autowired
private MyInterface field;
setter - 使用这种方法,每个依赖项的配置都通过一个属性(spring遍历所有方法并使用Method.invoke(Object, Object...)执行每个注释@Autowired
的方法,因此使用{它的二传手如下:
@Autowired
public void setField(MyInterface value) {
this.field = value;
}
构造函数 - 最后一个,也是我更好的方法,构造函数注入。那个基本上用@Autowired
注释一个构造函数,而不是使用方法或字段,你可以直接在你的构造函数上配置你的bean。对于那个春天,将选择一个用于实例化@Component
的构造函数,如果存在,则使用@Autowired
或使用Constructor.newInstance(Object...)调用空params构造函数。例如:
@Component
public class Implementation {
private MyInterface field;
@Autowired
public Implementation(MyInterface value) {
Assert.notNull(value, "value should not be null");
this.field = value;
}
}
控制反转(或依赖注入)背后的一个想法是能够隔离一段代码,以提供合适的测试实现支持。
为了更深入,有必要评论一下,在单元测试期间,您希望该类处于其隔离状态,您将使用该类的所有内容基本上都是针对其依赖性(注入)的模拟。
那么,结果是什么:
答案 1 :(得分:3)
实际上,setter没用,因为CDI
使用java Reflection来访问字段。
这意味着方法调用不再访问字段。 反射允许迭代一个类的所有字段,并检查是否有带有spécific注释的注释。
在这种情况下,如果类中的某个字段带有@Autowired
(或@Inject
更多J2E complient),那么如果有一个可用的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依赖项添加到你的项目中。
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>