有关详细信息,请参阅以下代码。这会在NullPointerException
方法中引发name()
(在下面标注注释)。我的理解是Spring按顺序读取@Configuration
,因此首先自动namePrinter
,然后nameProvider
。由于nameProvider
用于name()
,而NamePrinter
需要为@Bean
public Name name(NameProvider nameProvider) {
return nameProvider.getName();
}
构造函数自动装配,因此可以解释NPE。我也知道两个解决方案:
另一种是通过参数自动装配,即:
@Autowired
我也觉得这应该是Spring的工作,并且希望在有许多参数的情况下避免这种情况 - 在这些情况下,在配置中使用import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
interface Name {
String getName();
}
@Component
class NameProvider {
public Name getName() {
return new Name() {
@Override
public String getName() {
return "Foo";
}
};
}
}
@Component
class NamePrinter {
private final Name name;
@Autowired
public NamePrinter(Name name) {
this.name = name;
}
public void print() {
System.out.println(name.getName());
}
}
@Configuration
@ComponentScan(basePackageClasses = { SpringAutowiringPrbConfig.class })
class SpringAutowiringPrbConfig {
@Autowired
private NamePrinter namePrinter;
@Autowired
private NameProvider nameProvider;
@Bean
public Name name() {
return nameProvider.getName(); // NullPointerException here
}
}
public class SpringAutowiringPrb {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
SpringAutowiringPrbConfig.class);
NamePrinter namePrinter = context.getBean(NamePrinter.class);
namePrinter.print();
context.close();
}
}
对我来说更具可读性。
有些问题:
版本:
代码:
{{1}}
答案 0 :(得分:1)
行为:
这是一个循环的例子。在调用@Configuration
方法之前,Spring的@Autowired
类确实会解析@Bean
个目标。 但是,如果@Autowired
解析过程的一部分需要调用@Bean
方法,那么您必须能够处理它。
在您的示例中,您有
@Autowired
private NamePrinter namePrinter;
@Autowired
private NameProvider nameProvider;
看起来,在这种情况下(这与反射以及如何从Field
对象中检索Class
对象),Spring会首先尝试解析namePrinter
字段。要执行该解决方案,Spring必须实例化并初始化NamePrinter
bean。因此它将调用
@Autowired
public NamePrinter(Name name) {
this.name = name;
}
要做到这一点,它需要一个Name
bean注入构造函数。要获取Name
bean,需要调用
@Bean
public Name name() {
return nameProvider.getName(); // NullPointerException here
}
回到这里。 nameProvider
尚未处理,null
也是如此。
你可以查看堆栈跟踪,看看当NPE发生时,Spring正处于自动装配的过程中。
关于上述解决方案2 - 为什么是Spring处理方法 参数和自动装配的类变量有何不同?
它并不是真的。当Spring需要自动装载时
@Bean
public Name name(NameProvider nameProvider) {
return nameProvider.getName();
}
它检查NameProvider
bean定义并初始化bean(如果存在)。然后它可以将它注入构造函数中。以前,它没有任何关于在方法中可以使用哪些对象的提示。 Spring使用的反射无法查看方法的主体。它只能看它的定义。
有没有合理的理由Spring没有订购bean 由依赖顺序自动装配,而是选择源文件 定义顺序?
Spring使用反射来获取Field
的{{1}}个对象。特别是,它使用Class
表示
返回数组中的元素没有排序,也没有排序 特别的顺序。
所以Class#getDeclaredFields()
首先出现就是(非)幸运。
你遇到过这样的问题吗?如果是这样,你是如何解决它们的? 你的特殊情况?希望我在这里忽略了一些可能的东西 有用
分析您的代码并尝试充实任何循环依赖项。考虑使用namePrinter
,虽然这在这里没有帮助。
还可以选择放弃@DependsOn
(及其专业化)并使用@Component
方法执行所有操作。
你可以
@Bean
这是有效的,因为Spring创建了一个@Bean
public NamePrinter namePrinter() {
return new NamePrinter(name());
}
@Bean
public NameProvider nameProvider() {
return new NameProvider();
}
@Bean
public Name name() {
return nameProvider().getName();
}
类的自定义子类(和实例),它可以拦截对@Configuration
方法的调用,缓存调用的结果,并提供相同的结果在以后的所有调用中。