在spring中基于属性文件创建bean列表

在spring中基于属性文件创建bean列表,spring,properties,Spring,Properties,我想在spring中创建基于属性文件的bean列表。为了说明这个问题,让我们假设我有一个教室 public class ClassRoom { private List<Student> students; public void setStudents(List<Student> students) { this.students = students; } } public class Student { private Strign

我想在spring中创建基于属性文件的bean列表。为了说明这个问题,让我们假设我有一个
教室

public class ClassRoom {

  private List<Student> students;

  public void setStudents(List<Student> students) {
      this.students = students;
  }
}

public class Student {
  private Strign firstName;
  private String lastName;

  /* cosntructor, setters, getters ... */
}
还有一些很好的实用程序,可以将这样的文件转换成我的
学生
类的
列表

到目前为止,我已经为我的spring配置中包含的学生列表创建了单独的.xml配置文件,但我并不特别喜欢向客户机提供部分xml配置的想法。我认为这应该分开


所以问题是:有没有冷泉实用程序可以为我做到这一点?还是由我来写

所以我决定证明我没有那么懒。该解决方案有一些明显的限制,如1级属性或只能对基本类型进行操作。但是满足了我的需要。因此,作为记录:

属性文件:

student.1.firstName=Jan
student.1.lastName=Zyka
student.1.age=30
student.2.firstName=David
student.2.lastName=Kalita
student.2.age=55
学生班级:

package com.jan.zyka.test.dao;

public class Student {

    private String firstName;
    private String lastName;
    private int age;
    private String common;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getCommon() {
        return common;
    }

    public void setCommon(String common) {
        this.common = common;
    }

    @Override
    public String toString() {
        return "Student{" +
                "firstName='" + firstName + '\'' +
                ", lastName='" + lastName + '\'' +
                ", age=" + age +
                ", common='" + common + '\'' +
                '}';
    }
}
弹簧定义:

<bean id="studentFactory" class="com.jan.zyka.test.BeanListFactory">
    <property name="propertyPrefix" value="student" />
    <property name="commonProperties">
        <map>
            <entry key="common" value="testCommonValue" />
        </map>
    </property>
    <property name="targetType">
        <value type="java.lang.Class">com.jan.zyka.test.dao.Student</value>
    </property>
    <property name="properties">
        <util:properties location="classpath:testListFactory.properties" />
    </property>
</bean>
工厂bean本身:

package com.jan.zyka.test;

import com.google.common.primitives.Primitives;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.FactoryBean;

import java.beans.PropertyDescriptor;
import java.util.*;

/**
 * <p>
 *    Creates list of beans based on property file.
 * </p>
 * <p>
 *    Each object should be defined in the property file as follows:
 *    <pre>
 *        &lt;prefix&gt;.&lt;index&gt;.&lt;property&gt;
 *    </pre>
 *
 *    So one example might be:
 *    <pre>
 *        student.1.firstName=Paul
 *        student.1.lastName=Verlaine
 *        student.2.firstName=Alex
 *        student.2.lastName=Hamburger
 *    </pre>
 *
 *    The target class must provide default constructor and setter for each property defined in the configuration file as per
 *    bean specification.
 * </p>
 *
 * @param <T> type of the target object
 */
public class BeanListFactory<T> implements FactoryBean<List<T>> {

    private String propertyPrefix;
    private Map<String, Object> commonProperties = Collections.emptyMap();
    private Class<T> targetType;
    private Properties properties;

    private List<T> loadedBeans;

    public String getPropertyPrefix() {
        return propertyPrefix;
    }

    public void setPropertyPrefix(String propertyPrefix) {
        this.propertyPrefix = propertyPrefix;
    }

    public Map<String, Object> getCommonProperties() {
        return commonProperties;
    }

    public void setCommonProperties(Map<String, Object> commonProperties) {
        this.commonProperties = commonProperties;
    }

    public Class<T> getTargetType() {
        return targetType;
    }

    public void setTargetType(Class<T> targetType) {
        this.targetType = targetType;
    }

    public Properties getProperties() {
        return properties;
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    @Override
    public List<T> getObject() throws Exception {

        loadedBeans = new ArrayList<T>();
        int lastIndex = -1;

        T item = null;

        for (String property: prefixFilteredProperties()) {

            // The actual value
            final String propertyValue = properties.getProperty(property);

            // Remove Prefix
            property = property.substring(propertyPrefix.length() + 1);

            // Split by "."
            String tokens[] = property.split("\\.");

            if (tokens.length != 2) {
                throw new IllegalArgumentException("Each list property must be in form of: <prefix>.<index>.<property name>");
            }

            final int index = Integer.valueOf(tokens[0]);
            final String propertyName = tokens[1];

            // New index
            if (lastIndex != index) {
                if (lastIndex !=-1) {
                    loadedBeans.add(item);
                }
                lastIndex = index;

                item = targetType.newInstance();
                setCommonProperties(item, commonProperties);
            }

            // Set the property
            setProperty(item, propertyName, convertIfNecessary(propertyName, propertyValue));
        }

        // Add last item
        if (lastIndex != -1) {
            loadedBeans.add(item);
        }

        return loadedBeans;
    }

    @Override
    public Class<?> getObjectType() {
        return ArrayList.class;
    }

    @Override
    public boolean isSingleton() {
        return false;
    }

    private Object convertIfNecessary(String propertyName, String propertyValue) throws Exception {
        PropertyDescriptor descriptor = BeanUtils.getPropertyDescriptor(targetType, propertyName);
        Class<?> propertyType = Primitives.wrap(descriptor.getPropertyType());

        if (propertyType == String.class) {
            return propertyValue;
        }

        return propertyType.getDeclaredMethod("valueOf", String.class).invoke(propertyType, propertyValue);
    }

    private Set<String> prefixFilteredProperties() {
        Set<String> filteredProperties = new TreeSet<String>();

        for (String propertyName: properties.stringPropertyNames()) {
            if (propertyName.startsWith(this.propertyPrefix)) {
                filteredProperties.add(propertyName);
            }
        }

        return filteredProperties;
    }

    private void setCommonProperties(T item, Map<String, Object> commonProperties) throws Exception {
        for (Map.Entry<String, Object> commonProperty: commonProperties.entrySet()) {
            setProperty(item, commonProperty.getKey(), commonProperty.getValue());
        }
    }

    private static void setProperty(Object item, String propertyName, Object value) throws Exception {
        PropertyDescriptor descriptor = BeanUtils.getPropertyDescriptor(item.getClass(), propertyName);
        descriptor.getWriteMethod().invoke(item, value);
    }
}
package com.jan.zyka.test;
导入com.google.common.primitives.primitives;
导入org.springframework.beans.BeanUtils;
导入org.springframework.beans.factory.FactoryBean;
导入java.beans.PropertyDescriptor;
导入java.util.*;
/**
*
*基于属性文件创建bean列表。
*

* *每个对象应在属性文件中定义如下: * *prefix.index.property * * *因此,一个例子可能是: * *学生1.名字=保罗 *学生1.姓氏=Verlaine *学生2.名字=亚历克斯 *学生2.姓氏=汉堡包 * * *根据,目标类必须为配置文件中定义的每个属性提供默认构造函数和setter *bean规范。 *

* *@param目标对象的类型 */ 公共类BeanListFactory实现FactoryBean{ 私有字符串propertyPrefix; 私有映射commonProperties=Collections.emptyMap(); 私有类targetType; 私人物业;; 私有列表加载bean; 公共字符串getPropertyPrefix(){ 返回propertyPrefix; } 公共void setPropertyPrefix(字符串propertyPrefix){ this.propertyPrefix=propertyPrefix; } 公共映射getCommonProperties(){ 归还公共财产; } 公共void setCommonProperties(映射commonProperties){ this.commonProperties=commonProperties; } 公共类getTargetType(){ 返回目标类型; } public void setTargetType(targetType类){ this.targetType=targetType; } 公共属性getProperties(){ 归还财产; } 公共void集合属性(属性){ 这个。属性=属性; } @凌驾 公共列表getObject()引发异常{ loadedBeans=newArrayList(); int lastIndex=-1; T项=null; for(字符串属性:prefixFilteredProperties()){ //实际值 最后一个字符串propertyValue=properties.getProperty(property); //删除前缀 property=property.substring(propertyPrefix.length()+1); //以“.”分割 字符串标记[]=property.split(“\\”); if(tokens.length!=2){ 抛出新的IllegalArgumentException(“每个列表属性的格式必须为:…); } final int index=Integer.valueOf(令牌[0]); 最终字符串propertyName=tokens[1]; //新索引 if(lastIndex!=索引){ 如果(lastIndex!=-1){ loadedBeans.add(项); } lastIndex=指数; item=targetType.newInstance(); setCommonProperties(项目,commonProperties); } //设置属性 setProperty(item、propertyName、ConvertifEssential(propertyName、propertyValue)); } //添加最后一项 如果(lastIndex!=-1){ loadedBeans.add(项); } 返回加载的bean; } @凌驾 公共类getObjectType(){ 返回ArrayList.class; } @凌驾 公共布尔isSingleton(){ 返回false; } 私有对象ConvertifEssential(String propertyName、String propertyValue)引发异常{ PropertyDescriptor描述符=BeanUtils.getPropertyDescriptor(targetType,propertyName); 类propertyType=Primitives.wrap(descriptor.getPropertyType()); if(propertyType==String.class){ 返回属性值; } 返回propertyType.getDeclaredMethod(“valueOf”,String.class).invoke(propertyType,propertyValue); } 专用集prefixFilteredProperties(){ Set filteredProperties=new TreeSet(); 对于(String propertyName:properties.stringPropertyNames()){ if(propertyName.startsWith(this.propertyPrefix)){ filteredProperties.add(propertyName); } } 返回过滤器属性; } 私有void setCommonProperties(T项,映射commonProperties)引发异常{ 对于(Map.Entry commonProperty:commonProperties.entrySet()){ setProperty(item,commonProperty.getKey(),commonProperty.getValue()); } } 私有静态void setProperty(对象项、字符串propertyName、对象值)引发异常{ PropertyDescriptor描述符=BeanUtils.getPropertyDescriptor(item.getClass(),propertyName); descriptor.getWriteMethod().invoke(项,值); } }
发布这个针对Spring Boot的解决方案,希望能帮助其他仍在寻找答案的人。使用spring可以将属性文件映射到bean列表。您需要有一个包含bean集合的ConfigurationProperties bean,并使用适当的命名约定,如下所示

调查
<bean id="studentFactory" class="com.jan.zyka.test.BeanListFactory">
    <property name="propertyPrefix" value="student" />
    <property name="commonProperties">
        <map>
            <entry key="common" value="testCommonValue" />
        </map>
    </property>
    <property name="targetType">
        <value type="java.lang.Class">com.jan.zyka.test.dao.Student</value>
    </property>
    <property name="properties">
        <util:properties location="classpath:testListFactory.properties" />
    </property>
</bean>
[
 Student{firstName='Jan', lastName='Zyka', age=30, common='testCommonValue'},  
 Student{firstName='David', lastName='Kalita', age=55, common='testCommonValue'}
]
package com.jan.zyka.test;

import com.google.common.primitives.Primitives;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.FactoryBean;

import java.beans.PropertyDescriptor;
import java.util.*;

/**
 * <p>
 *    Creates list of beans based on property file.
 * </p>
 * <p>
 *    Each object should be defined in the property file as follows:
 *    <pre>
 *        &lt;prefix&gt;.&lt;index&gt;.&lt;property&gt;
 *    </pre>
 *
 *    So one example might be:
 *    <pre>
 *        student.1.firstName=Paul
 *        student.1.lastName=Verlaine
 *        student.2.firstName=Alex
 *        student.2.lastName=Hamburger
 *    </pre>
 *
 *    The target class must provide default constructor and setter for each property defined in the configuration file as per
 *    bean specification.
 * </p>
 *
 * @param <T> type of the target object
 */
public class BeanListFactory<T> implements FactoryBean<List<T>> {

    private String propertyPrefix;
    private Map<String, Object> commonProperties = Collections.emptyMap();
    private Class<T> targetType;
    private Properties properties;

    private List<T> loadedBeans;

    public String getPropertyPrefix() {
        return propertyPrefix;
    }

    public void setPropertyPrefix(String propertyPrefix) {
        this.propertyPrefix = propertyPrefix;
    }

    public Map<String, Object> getCommonProperties() {
        return commonProperties;
    }

    public void setCommonProperties(Map<String, Object> commonProperties) {
        this.commonProperties = commonProperties;
    }

    public Class<T> getTargetType() {
        return targetType;
    }

    public void setTargetType(Class<T> targetType) {
        this.targetType = targetType;
    }

    public Properties getProperties() {
        return properties;
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    @Override
    public List<T> getObject() throws Exception {

        loadedBeans = new ArrayList<T>();
        int lastIndex = -1;

        T item = null;

        for (String property: prefixFilteredProperties()) {

            // The actual value
            final String propertyValue = properties.getProperty(property);

            // Remove Prefix
            property = property.substring(propertyPrefix.length() + 1);

            // Split by "."
            String tokens[] = property.split("\\.");

            if (tokens.length != 2) {
                throw new IllegalArgumentException("Each list property must be in form of: <prefix>.<index>.<property name>");
            }

            final int index = Integer.valueOf(tokens[0]);
            final String propertyName = tokens[1];

            // New index
            if (lastIndex != index) {
                if (lastIndex !=-1) {
                    loadedBeans.add(item);
                }
                lastIndex = index;

                item = targetType.newInstance();
                setCommonProperties(item, commonProperties);
            }

            // Set the property
            setProperty(item, propertyName, convertIfNecessary(propertyName, propertyValue));
        }

        // Add last item
        if (lastIndex != -1) {
            loadedBeans.add(item);
        }

        return loadedBeans;
    }

    @Override
    public Class<?> getObjectType() {
        return ArrayList.class;
    }

    @Override
    public boolean isSingleton() {
        return false;
    }

    private Object convertIfNecessary(String propertyName, String propertyValue) throws Exception {
        PropertyDescriptor descriptor = BeanUtils.getPropertyDescriptor(targetType, propertyName);
        Class<?> propertyType = Primitives.wrap(descriptor.getPropertyType());

        if (propertyType == String.class) {
            return propertyValue;
        }

        return propertyType.getDeclaredMethod("valueOf", String.class).invoke(propertyType, propertyValue);
    }

    private Set<String> prefixFilteredProperties() {
        Set<String> filteredProperties = new TreeSet<String>();

        for (String propertyName: properties.stringPropertyNames()) {
            if (propertyName.startsWith(this.propertyPrefix)) {
                filteredProperties.add(propertyName);
            }
        }

        return filteredProperties;
    }

    private void setCommonProperties(T item, Map<String, Object> commonProperties) throws Exception {
        for (Map.Entry<String, Object> commonProperty: commonProperties.entrySet()) {
            setProperty(item, commonProperty.getKey(), commonProperty.getValue());
        }
    }

    private static void setProperty(Object item, String propertyName, Object value) throws Exception {
        PropertyDescriptor descriptor = BeanUtils.getPropertyDescriptor(item.getClass(), propertyName);
        descriptor.getWriteMethod().invoke(item, value);
    }
}
@Component
@ConfigurationProperties(prefix = "notification")
@Data
@Valid
public class NotificationProperties {
    @NotNull
    private Set<AlertThreshold> alertThresholds;
    @NotNull
    private Set<Subscriber> subscribers;
}
notification.alert-thresholds[0].step=PB
notification.alert-thresholds[0].status=RDY
notification.alert-thresholds[0].threshold=500

notification.alert-thresholds[1].step=LA
notification.alert-thresholds[1].status=RDY
notification.alert-thresholds[1].threshold=100

notification.subscribers[0].email=subscriber1@gmail.com
notification.subscribers[1].email=subscriber2@gmail.com
@Data
public class AlertThreshold {
    @NotNull
    private String step;
    @NotNull
    private String status;
    @NotNull
    private Integer threshold;
}

@Data
public class Subscriber {
    @Email
    private String email;
}