java - 如何从属性文件转换数据并设置为列表属性(使用 PropertyPlaceholderConfigurer)?

标签 java spring properties-file

我有一个实体:

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>

但是我得到一个错误:

enter image description here

因为 No matching constructor found .

更好的是List<Integer> vipSeats .在这种情况下可能吗?

如何解决这个麻烦?

最佳答案

这里有两个方面要讨论,所以让我们一次一个地讨论:

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 不需要特殊处理来注入(inject)您的列表,正如您在下面的日志片段中看到的那样,由一个简单的 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将参数传递给构造函数,但是它用于在 bean 实例化之后设置属性/属性。您可以使用 c-namespace达到与使用 <constructor-arg> 相同的效果标记并减少冗长(请注意,根据 spring 文档,p 和 c namespace 未在 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 支持。



特殊要求更新:注释简介

首先,这取决于您的项目设置,您是否使用 basic Spring Core 或 Spring Boot 等。对于这个介绍:我将从您现在所在的位置开始:

启用组件扫描,以便 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,然后为其字段注入(inject)值,最终后处理器将调用 @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'

关于java - 如何从属性文件转换数据并设置为列表属性(使用 PropertyPlaceholderConfigurer)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35316140/

相关文章:

Java HTTP GET 请求给出 403 Forbidden,但在浏览器中有效

java - 深度优先搜索/广度优先搜索简单实现

spring - Spring-Security 中默认的 AuthenticationManager 是什么?它是如何进行身份验证的?

hibernate - Spring + Hibernate 懒加载

java - "get' 与 "getProperty"之间的区别

java - 安全的 SimpleDateFormat 解析

java - Netbeans:如何知道我当前正在查看什么 JAR

spring - 在 Spring 中进行集成测试时如何模拟 Eureka?

spring - 加载不同的属性以进行开发和部署

java - 如何实现模块化 Ant 构建?