如何从属性文件转换数据并设置为 List 属性(使用 PropertyPlaceholderConfigurer)?
How to convert data from properties file and set to List property (with PropertyPlaceholderConfigurer)?
我有一个实体:
public class Auditorium {
private Integer id;
private String name;
private Integer numberOfSeats;
private List<String> vipSeats;
public Auditorium(String name, Integer numberOfSeats, List<String> vipSeats) {
this.name = name;
this.numberOfSeats = numberOfSeats;
this.vipSeats = vipSeats;
}
我想从 属性 文件创建 Auditorium
实例,例如:
auditorium1.name=yellow hall
auditorium1.number-of-seats=150
auditorium1.vip-seats=1,2,3,4,5,6,7,8,9
我有几个不同的文件,我想创建不同的 Auditorium
bean。
这是来自 spring.xml
的片段:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:auditorium1.properties</value>
<value>classpath:auditorium2.properties</value>
<value>classpath:auditorium3.properties</value>
</list>
</property>
<property name="ignoreResourceNotFound" value="true"/>
<property name="systemPropertiesMode">
<util:constant
static-field="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer.SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
</property>
</bean>
<util:list id="auditorium1" value-type="java.lang.String">
<value></value>
</util:list>
<util:list>
<bean class="net.lelyak.edu.entity.Auditorium"
p:name="${auditorium1.name}" p:numberOfSeats="${auditorium1.number-of-seats}" p:vipSeats="${auditorium1.vip-seats}"/>
</util:list>
但是我收到一个错误:
因为No matching constructor found
.
更好的是 List<Integer> vipSeats
。这种情况可以吗?
如何解决这个麻烦?
这里有 2 个方面要介绍,所以让我们一次一个地讲:
1)IJ(IntelliJ Idea 的缩写)建议根据您的 bean 声明,Spring 将尝试调用无参数构造函数,该构造函数显然不存在,因为您定义了 public Auditorium(String name, Integer numberOfSeats, List<String> vipSeats)
.
因此您可以将其配置为调用上述构造函数并传递正确的参数,按照 spring docs:
<bean class="com.example.Auditorium">
<constructor-arg name="name" value="${auditorium1.name}"/>
<constructor-arg name="numberOfSeats" value="${auditorium1.number-of-seats}"/>
<constructor-arg name="vipSeats" value="${auditorium1.vip-seats}"/>
</bean>
除此之外,Spring 无需特殊处理即可注入您的列表,如您在下面的日志片段中所见,通过简单的 toString()
IJ 自动生成方法输出:
16:13:56.569 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating instance of bean 'com.example.Auditorium#0'
16:13:56.601 [main] DEBUG com.example.Auditorium - Auditorium{id=null, name='yellow hall', numberOfSeats=150, vipSeats=[1,2,3,4,5,6,7,8,9]}
2) 我看到您正在尝试使用 p-namespace to pass arguments to your constructor, however that's used to set properties/attribute after bean instantiation. You can use the c-namespace 来达到与使用 <constructor-arg>
标签相同的效果并且不那么冗长(请注意根据 spring docs、p & c 命名空间未在 XSD 文件中定义,仅存在于 Spring 的核心):
<beans ...
xmlns:c="http://www.springframework.org/schema/c"
...>
<bean id="myAuditorium" class="com.example.Auditorium" c:name="${auditorium1.name}" c:numberOfSeats="${auditorium1.number-of-seats}" c:vipSeats="${auditorium1.vip-seats}"/>
再次为这两个定义生成的日志片段:
16:26:52.258 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating instance of bean 'com.example.Auditorium#0'
16:26:52.287 [main] DEBUG com.example.Auditorium - Auditorium{id=null, name='yellow hall', numberOfSeats=150, vipSeats=[1,2,3,4,5,6,7,8,9]}
...
16:26:52.287 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating instance of bean 'com.example.Auditorium#1'
16:26:52.288 [main] DEBUG com.example.Auditorium - Auditorium{id=null, name='yellow hall', numberOfSeats=150, vipSeats=[1,2,3,4,5,6,7,8,9]}
注意: 根据您使用的 Spring 版本和您的需要,可能还需要检查 annotation-based configs。这可能是个人喜好,但我发现它们更干净且更易于维护,并且 IJ 对这两种配置类型都提供了出色的 Spring 支持。
特殊要求更新:注释简介
首先要看你项目的设置,你是用basicSpringCore还是Springboot等等。对于这个介绍: 我将从你现在所在的位置开始:
启用组件扫描,以便 Spring 可以发现和创建您的组件、服务等
<?xml version="1.0" encoding="UTF-8"?>
<beans ...
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="...
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.example" />
然后改变我们的class。如果您不需要在构造函数中做任何其他事情,那么我们可以简单地删除它并注释字段。 注释字段时 请考虑到 Spring 只有在实例化 bean 后才能填充它们,因此在构造函数中使用 then 很可能会导致 NPE。在这种情况下,您可以使用 @PostConstruct
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@Component
public class Auditorium {
private static final Logger log = LoggerFactory.getLogger(Auditorium.class);
private Integer id;
@Value("${auditorium1.name}")
private String name;
@Value("${auditorium1.number-of-seats}")
private Integer numberOfSeats;
@Value("${auditorium1.vip-seats}")
private List<String> vipSeats;
@PostConstruct
private void doAfterConstruction() {
log.debug(this.toString());
}
@Override
public String toString() {
return "Auditorium{" +
"id=" + id +
", name='" + name + '\'' +
", numberOfSeats=" + numberOfSeats +
", vipSeats=" + vipSeats +
'}';
}
}
正如您将在日志中看到的那样,Spring 将实例化 bean,然后为其字段注入值,最终 post-处理器将调用 @PostConstruct
注释方法:
12:20:34.037 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'auditorium'
12:20:34.038 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating instance of bean 'auditorium'
12:20:34.045 [main] DEBUG o.s.c.a.CommonAnnotationBeanPostProcessor - Found init method on class [com.example.Auditorium]: private void com.example.Auditorium.doAfterConstruction()
12:20:34.045 [main] DEBUG o.s.c.a.CommonAnnotationBeanPostProcessor - Registered init method on class [com.example.Auditorium]: org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement@1ec4fbf0
12:20:34.060 [main] DEBUG o.s.b.f.annotation.InjectionMetadata - Registered injected element on class [com.example.Auditorium]: AutowiredFieldElement for private java.lang.String com.example.Auditorium.name
12:20:34.060 [main] DEBUG o.s.b.f.annotation.InjectionMetadata - Registered injected element on class [com.example.Auditorium]: AutowiredFieldElement for private java.lang.Integer com.example.Auditorium.numberOfSeats
12:20:34.060 [main] DEBUG o.s.b.f.annotation.InjectionMetadata - Registered injected element on class [com.example.Auditorium]: AutowiredFieldElement for private java.util.List com.example.Auditorium.vipSeats
12:20:34.060 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Eagerly caching bean 'auditorium' to allow for resolving potential circular references
12:20:34.062 [main] DEBUG o.s.b.f.annotation.InjectionMetadata - Processing injected element of bean 'auditorium': AutowiredFieldElement for private java.lang.String com.example.Auditorium.name
12:20:34.067 [main] DEBUG o.s.b.f.annotation.InjectionMetadata - Processing injected element of bean 'auditorium': AutowiredFieldElement for private java.lang.Integer com.example.Auditorium.numberOfSeats
12:20:34.072 [main] DEBUG o.s.b.f.annotation.InjectionMetadata - Processing injected element of bean 'auditorium': AutowiredFieldElement for private java.util.List com.example.Auditorium.vipSeats
12:20:34.084 [main] DEBUG o.s.c.a.CommonAnnotationBeanPostProcessor - Invoking init method on bean 'auditorium': private void com.example.Auditorium.doAfterConstruction()
12:20:34.085 [main] DEBUG com.example.Auditorium - Auditorium{id=null, name='yellow hall', numberOfSeats=150, vipSeats=[1,2,3,4,5,6,7,8,9]}
12:20:34.094 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Finished creating instance of bean 'auditorium'
或者如果你想保留你的构造函数,我们将把它标记为 @Autowired
并注释参数:
@Component
public class Auditorium {
private static final Logger log = LoggerFactory.getLogger(Auditorium.class);
private Integer id;
private String name;
private Integer numberOfSeats;
private List<String> vipSeats;
@Autowired
public Auditorium(@Value("${auditorium1.name}") String name,
@Value("${auditorium1.number-of-seats}") Integer numberOfSeats,
@Value("${auditorium1.vip-seats}") List<String> vipSeats) {
this.name = name;
this.numberOfSeats = numberOfSeats;
this.vipSeats = vipSeats;
log.debug(this.toString());
}
}
还有日志,这次比较简单
12:29:09.492 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'auditorium'
12:29:09.492 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating instance of bean 'auditorium'
12:29:09.525 [main] DEBUG com.example.Auditorium - Auditorium{id=null, name='yellow hall', numberOfSeats=150, vipSeats=[1,2,3,4,5,6,7,8,9]}
12:29:09.526 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Eagerly caching bean 'auditorium' to allow for resolving potential circular references
12:29:09.548 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Finished creating instance of bean 'auditorium'
我有一个实体:
public class Auditorium {
private Integer id;
private String name;
private Integer numberOfSeats;
private List<String> vipSeats;
public Auditorium(String name, Integer numberOfSeats, List<String> vipSeats) {
this.name = name;
this.numberOfSeats = numberOfSeats;
this.vipSeats = vipSeats;
}
我想从 属性 文件创建 Auditorium
实例,例如:
auditorium1.name=yellow hall
auditorium1.number-of-seats=150
auditorium1.vip-seats=1,2,3,4,5,6,7,8,9
我有几个不同的文件,我想创建不同的 Auditorium
bean。
这是来自 spring.xml
的片段:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:auditorium1.properties</value>
<value>classpath:auditorium2.properties</value>
<value>classpath:auditorium3.properties</value>
</list>
</property>
<property name="ignoreResourceNotFound" value="true"/>
<property name="systemPropertiesMode">
<util:constant
static-field="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer.SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
</property>
</bean>
<util:list id="auditorium1" value-type="java.lang.String">
<value></value>
</util:list>
<util:list>
<bean class="net.lelyak.edu.entity.Auditorium"
p:name="${auditorium1.name}" p:numberOfSeats="${auditorium1.number-of-seats}" p:vipSeats="${auditorium1.vip-seats}"/>
</util:list>
但是我收到一个错误:
因为No matching constructor found
.
更好的是 List<Integer> vipSeats
。这种情况可以吗?
如何解决这个麻烦?
这里有 2 个方面要介绍,所以让我们一次一个地讲:
1)IJ(IntelliJ Idea 的缩写)建议根据您的 bean 声明,Spring 将尝试调用无参数构造函数,该构造函数显然不存在,因为您定义了 public Auditorium(String name, Integer numberOfSeats, List<String> vipSeats)
.
因此您可以将其配置为调用上述构造函数并传递正确的参数,按照 spring docs:
<bean class="com.example.Auditorium">
<constructor-arg name="name" value="${auditorium1.name}"/>
<constructor-arg name="numberOfSeats" value="${auditorium1.number-of-seats}"/>
<constructor-arg name="vipSeats" value="${auditorium1.vip-seats}"/>
</bean>
除此之外,Spring 无需特殊处理即可注入您的列表,如您在下面的日志片段中所见,通过简单的 toString()
IJ 自动生成方法输出:
16:13:56.569 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating instance of bean 'com.example.Auditorium#0'
16:13:56.601 [main] DEBUG com.example.Auditorium - Auditorium{id=null, name='yellow hall', numberOfSeats=150, vipSeats=[1,2,3,4,5,6,7,8,9]}
2) 我看到您正在尝试使用 p-namespace to pass arguments to your constructor, however that's used to set properties/attribute after bean instantiation. You can use the c-namespace 来达到与使用 <constructor-arg>
标签相同的效果并且不那么冗长(请注意根据 spring docs、p & c 命名空间未在 XSD 文件中定义,仅存在于 Spring 的核心):
<beans ...
xmlns:c="http://www.springframework.org/schema/c"
...>
<bean id="myAuditorium" class="com.example.Auditorium" c:name="${auditorium1.name}" c:numberOfSeats="${auditorium1.number-of-seats}" c:vipSeats="${auditorium1.vip-seats}"/>
再次为这两个定义生成的日志片段:
16:26:52.258 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating instance of bean 'com.example.Auditorium#0'
16:26:52.287 [main] DEBUG com.example.Auditorium - Auditorium{id=null, name='yellow hall', numberOfSeats=150, vipSeats=[1,2,3,4,5,6,7,8,9]}
...
16:26:52.287 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating instance of bean 'com.example.Auditorium#1'
16:26:52.288 [main] DEBUG com.example.Auditorium - Auditorium{id=null, name='yellow hall', numberOfSeats=150, vipSeats=[1,2,3,4,5,6,7,8,9]}
注意: 根据您使用的 Spring 版本和您的需要,可能还需要检查 annotation-based configs。这可能是个人喜好,但我发现它们更干净且更易于维护,并且 IJ 对这两种配置类型都提供了出色的 Spring 支持。
特殊要求更新:注释简介
首先要看你项目的设置,你是用basicSpringCore还是Springboot等等。对于这个介绍: 我将从你现在所在的位置开始:
启用组件扫描,以便 Spring 可以发现和创建您的组件、服务等
<?xml version="1.0" encoding="UTF-8"?>
<beans ...
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="...
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.example" />
然后改变我们的class。如果您不需要在构造函数中做任何其他事情,那么我们可以简单地删除它并注释字段。 注释字段时 请考虑到 Spring 只有在实例化 bean 后才能填充它们,因此在构造函数中使用 then 很可能会导致 NPE。在这种情况下,您可以使用 @PostConstruct
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@Component
public class Auditorium {
private static final Logger log = LoggerFactory.getLogger(Auditorium.class);
private Integer id;
@Value("${auditorium1.name}")
private String name;
@Value("${auditorium1.number-of-seats}")
private Integer numberOfSeats;
@Value("${auditorium1.vip-seats}")
private List<String> vipSeats;
@PostConstruct
private void doAfterConstruction() {
log.debug(this.toString());
}
@Override
public String toString() {
return "Auditorium{" +
"id=" + id +
", name='" + name + '\'' +
", numberOfSeats=" + numberOfSeats +
", vipSeats=" + vipSeats +
'}';
}
}
正如您将在日志中看到的那样,Spring 将实例化 bean,然后为其字段注入值,最终 post-处理器将调用 @PostConstruct
注释方法:
12:20:34.037 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'auditorium'
12:20:34.038 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating instance of bean 'auditorium'
12:20:34.045 [main] DEBUG o.s.c.a.CommonAnnotationBeanPostProcessor - Found init method on class [com.example.Auditorium]: private void com.example.Auditorium.doAfterConstruction()
12:20:34.045 [main] DEBUG o.s.c.a.CommonAnnotationBeanPostProcessor - Registered init method on class [com.example.Auditorium]: org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement@1ec4fbf0
12:20:34.060 [main] DEBUG o.s.b.f.annotation.InjectionMetadata - Registered injected element on class [com.example.Auditorium]: AutowiredFieldElement for private java.lang.String com.example.Auditorium.name
12:20:34.060 [main] DEBUG o.s.b.f.annotation.InjectionMetadata - Registered injected element on class [com.example.Auditorium]: AutowiredFieldElement for private java.lang.Integer com.example.Auditorium.numberOfSeats
12:20:34.060 [main] DEBUG o.s.b.f.annotation.InjectionMetadata - Registered injected element on class [com.example.Auditorium]: AutowiredFieldElement for private java.util.List com.example.Auditorium.vipSeats
12:20:34.060 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Eagerly caching bean 'auditorium' to allow for resolving potential circular references
12:20:34.062 [main] DEBUG o.s.b.f.annotation.InjectionMetadata - Processing injected element of bean 'auditorium': AutowiredFieldElement for private java.lang.String com.example.Auditorium.name
12:20:34.067 [main] DEBUG o.s.b.f.annotation.InjectionMetadata - Processing injected element of bean 'auditorium': AutowiredFieldElement for private java.lang.Integer com.example.Auditorium.numberOfSeats
12:20:34.072 [main] DEBUG o.s.b.f.annotation.InjectionMetadata - Processing injected element of bean 'auditorium': AutowiredFieldElement for private java.util.List com.example.Auditorium.vipSeats
12:20:34.084 [main] DEBUG o.s.c.a.CommonAnnotationBeanPostProcessor - Invoking init method on bean 'auditorium': private void com.example.Auditorium.doAfterConstruction()
12:20:34.085 [main] DEBUG com.example.Auditorium - Auditorium{id=null, name='yellow hall', numberOfSeats=150, vipSeats=[1,2,3,4,5,6,7,8,9]}
12:20:34.094 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Finished creating instance of bean 'auditorium'
或者如果你想保留你的构造函数,我们将把它标记为 @Autowired
并注释参数:
@Component
public class Auditorium {
private static final Logger log = LoggerFactory.getLogger(Auditorium.class);
private Integer id;
private String name;
private Integer numberOfSeats;
private List<String> vipSeats;
@Autowired
public Auditorium(@Value("${auditorium1.name}") String name,
@Value("${auditorium1.number-of-seats}") Integer numberOfSeats,
@Value("${auditorium1.vip-seats}") List<String> vipSeats) {
this.name = name;
this.numberOfSeats = numberOfSeats;
this.vipSeats = vipSeats;
log.debug(this.toString());
}
}
还有日志,这次比较简单
12:29:09.492 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'auditorium'
12:29:09.492 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating instance of bean 'auditorium'
12:29:09.525 [main] DEBUG com.example.Auditorium - Auditorium{id=null, name='yellow hall', numberOfSeats=150, vipSeats=[1,2,3,4,5,6,7,8,9]}
12:29:09.526 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Eagerly caching bean 'auditorium' to allow for resolving potential circular references
12:29:09.548 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Finished creating instance of bean 'auditorium'