问题描述
这是我的代码:
@Component
@Configuration
@PropertySource("application.properties")
public class Program {
@Value("${app.title}")
private String appTitle;
public Program() {
System.out.println(appTitle);
}
}
application.properties具有
app.title=The Program
输出为null
的{{1}}实例。
那么,我想念什么? 我尝试了几个例子,没有一个奏效。
解决方法
没什么问题,只是不要在构造函数中这样做...
,由于appTitle
是一个自动装配的字段,因此在最初构造对象之后才设置它。这就是在您的示例中该值仍为null的原因。在这种情况下,bean的构建过程如下:
- 调用
Program
构造函数,创建一个新的Program
实例 - 在新建的bean上将
appTitle
字段设置为${app.title}
此的理想解决方案取决于您的目标。如果确实需要构造函数中的值,则可以将其作为自动装配的构造函数参数传递。该值将在构造函数中可用:
@Component
@Configuration
@PropertySource("application.properties")
public class Program {
public Program(@Value("${app.title}") appTitle) {
System.out.println(appTitle);
}
}
如果在构造函数本身中不需要它,但需要它来进行Bean的正确初始化,则可以选择使用@javax.annotation.PostConstruct
注释在对象构造之后但在使用之前使用它。可供其他地方使用:
@Component
@Configuration
@PropertySource("application.properties")
public class Program {
@Value("${app.title}")
private String appTitle;
@PostConstruct
public void printAppTitle() {
System.out.println(appTitle);
}
}
最后,如果您在构造时不需要该值,但是在bean的生命周期中需要它,那么您所拥有的将起作用。它只是在构造函数本身的主体中不可用:
@Component
@Configuration
@PropertySource("application.properties")
public class Program {
@Value("${app.title}")
private String appTitle;
}
,
在编写此问题的其他答案时,假设目标是创建一个在其创建过程中使用给定属性的Spring托管Bean。但是,根据您在另一个答案中的评论,您似乎想要回答的问题是如何在无参数构造函数中访问外部化属性(由@Value
提供的属性)。这是基于您的期望,例如Spring之类的Java控制反转(IoC)容器应允许在无参数构造函数中访问外部化的属性(可能还包括其他依赖项)。在这种情况下,此答案将解决在无参数构造函数中访问属性的特定问题。
虽然有一定方法可以实现此目标,但它们都不是Spring框架的惯用用法。您已经发现,自动构造字段(即使用setter注入初始化的字段)无法在构造函数中访问。
有两个部分来解释为什么会这样。首先,为什么它以编程方式发挥作用?其次,为什么要按原样设计?
Spring文档的setter-based dependency injection部分解决了第一个问题:
基于Setter的DI是通过在调用无参数构造函数或无参数
static
工厂方法以实例化bean之后,在bean上调用setter方法来实现的。
在这种情况下,这意味着首先使用无参数构造函数创建对象。其次,一旦构造了对象,就在构造的bean上初始化appTitle
。由于该字段直到构造对象后才初始化,因此在构造函数中它将具有其默认值null
。
第二个问题是为什么以这种方式设计Spring,而不是以某种方式访问构造函数中的属性。 Spring文档中的constructor-based or setter-based DI?侧栏清楚地表明,通常在处理强制性依赖项时,构造函数参数实际上是惯用的方法。
由于可以混合使用基于构造函数的DI和基于setter的DI,因此,将构造函数用于强制性依赖项,将setter方法或配置方法用于可选性依赖项是一个很好的经验法则。 [...]
Spring团队通常提倡构造函数注入,因为它可以让您将应用程序组件实现为不可变对象,并确保所需的依赖项不为null。此外,注入构造函数的组件始终以完全初始化的状态返回到客户端(调用)代码。 [...]
Setter注入主要应仅用于可以在类中分配合理的默认值的可选依赖项。 [...]
构造该对象所需的属性肯定会归类为强制性依赖项。因此,惯用的Spring用法是在构造函数中传递此必需值。
因此,总而言之,Spring框架不支持尝试在无参数构造函数中访问应用程序属性,并且实际上与建议的框架使用背道而驰。