Как преобразовать данные из файла свойств и установить свойство List (с помощью 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. Возможно ли это в данной ситуации?

Как решить эту проблему?


person catch23    schedule 10.02.2016    source источник


Ответы (1)


Здесь нужно рассмотреть 2 аспекта, поэтому давайте рассмотрим их по одному:

1) IJ (сокращение от IntelliJ Idea) предполагает, что в соответствии с вашим объявлением bean-компонента Spring будет пытаться вызвать конструктор без аргументов, которого явно не существует, поскольку вы определили public Auditorium(String name, Integer numberOfSeats, List<String> vipSeats).

Таким образом, вы можете настроить его для вызова упомянутого выше конструктора с передачей правильных аргументов в соответствии с документы Spring:

<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 для передачи аргументов вашему конструктору, однако он используется для установки свойств/атрибутов после создания экземпляра компонента. Вы можете использовать c -namespace для достижения того же эффекта, что и при использовании тега <constructor-arg>, и быть менее подробным (обратите внимание, что согласно документам Spring, пространства имен 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, которую вы используете, и ваших потребностей, возможно, стоит также проверить конфигурации на основе аннотаций. Это может быть личным предпочтением, но я нахожу их более чистыми и простыми в обслуживании, а у IJ отличная поддержка Spring для обоих типов конфигурации.



Обновление по специальному запросу: краткое введение в аннотацию

Прежде всего, это зависит от настройки вашего проекта, используете ли вы базовый 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" />

Затем измените наш класс. Если вам больше ничего не нужно делать в конструкторе, мы можем просто удалить его и аннотировать поля. При аннотировании полей имейте в виду, что 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-компонента, затем введет значения для его полей, и в конечном итоге постпроцессоры вызовут аннотированный метод @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'
person Morfic    schedule 10.02.2016
comment
Что означает IJ? - person catch23; 10.02.2016
comment
@nazar_art Хороший вопрос. Это сокращение от IntelliJ Idea, я узнал тему по снимку экрана с определением XML. Я просто обновлю ответ, чтобы сделать его более понятным. Спасибо - person Morfic; 10.02.2016
comment
Можете ли вы показать пример использования Annotation approach здесь. Это будет очень полезно для меня, потому что я использую Spring 3.2.13. - person catch23; 11.02.2016
comment
Вы знаете, как сохранить vipSeats точно так же, как Set. Потому что теперь он сохраняется как одна строка 1,2,3,4,5? - person catch23; 13.02.2016
comment
Как я могу настроить 3 разные аудитории, если мне нужно установить только одну для моей сущности? Нравится @Value("${auditorium1.name}") private String name; - person catch23; 13.02.2016
comment
@nazar_art Самое простое решение, которое приходит на ум, — определить свой собственный @Configuration с помощью 3 различных методов создания @Bean. Документы Spring имеют короткое введение и более подробный раздел по этому вопросу. - person Morfic; 15.02.2016
comment
Я попытался и, наконец, задал еще один вопрос: property">Как настроить конфигурацию Spring для List of Bean из разных файлов свойств? - person catch23; 17.02.2016