Announcement Announcement Module
Collapse
No announcement yet.
Inject a Map defined in a properties file Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Inject a Map defined in a properties file

    I want to inject a Map from a property file into my Bean. What is the best way to configure that?

    My props:
    Code:
    a.b.c.1=foo
    a.b.c.2=bar
    Inside my bean I want a Map with the pairs (1, foo) (2, foo).

    I have found a post on stackoverflow.com which stated:
    - implement ApplicationContextAware and set the ApplicationContext as a field in your bean.
    - in a @PostConstruct method call context.getBean("${aaa.props}")
    - parse the result manually and set it to the desired fields
    Only it seems that
    Code:
    context.getBean("${a.b.c.1}");
    doesn't resolve to my property.

    I also tried to add a property which was:
    a.b.c.list=1,2,3
    but I can't get those properties from the context.

    Is there a way to solve this?

  • #2
    Maybe it's a silly question, but just to be sure, did you add the line
    HTML Code:
    <context:property-placeholder location="classpath:/META-INF/....." />
    to your app config?

    Regards,
    Stefano

    Comment


    • #3
      Originally posted by zakhrim View Post
      Maybe it's a silly question, but just to be sure, did you add the line
      HTML Code:
      <context:property-placeholder location="classpath:/META-INF/....." />
      to your app config?

      Regards,
      Stefano
      Yes, I added the property-placeholder, other properties were being resolved.

      My current solution is:
      Code:
      /**
       * Allows for making a Map from a properties file from a classpath location available in a bean factory. Can be used to
       * populate any bean property of type Map via a bean reference.
       * 
       * <p>
       * Supports loading from a properties file and/or setting local properties on this FactoryBean. The created Properties
       * instance will be merged from loaded and local values. If neither a location nor local properties are set, an
       * exception will be thrown on initialization.
       * 
       * @see #setLocation
       * @see #setProperties
       * @see #setLocalOverride
       * @see java.util.Properties
       */
      public class MapFactoryBean extends PropertiesLoaderSupport implements FactoryBean<Map<String, String>> {
      
          private static final Logger LOG = LoggerFactory.getLogger(MapFactoryBean.class);
          private String mapPropertyName;
      
          @Override
          public Map<String, String> getObject() throws IOException {
              Properties props = mergeProperties();
              Map<String, String> map = Maps.newHashMap();
              boolean valid = true;
      
              String keyList = props.getProperty(mapPropertyName + ".list");
              if (StringUtils.isEmpty(keyList)) {
                  LOG.error("Failed to read map '{}', missing list property.", mapPropertyName);
                  valid = false;
              } else {
                  valid = createMap(props, map, keyList);
              }
              if (valid) {
                  return map;
              } else {
                  throw new IllegalStateException("Failed to read map '" + mapPropertyName + "'.");
              }
          }
      
          private boolean createMap(final Properties props, final Map<String, String> map, final String keyList) {
              boolean valid = true;
              String[] keys = StringUtils.split(keyList, ",");
              for (String key : keys) {
                  String value = props.getProperty(mapPropertyName + "." + key);
                  if (value == null) {
                      LOG.error("Failed to read map '{}': Missing value for key '{}'", mapPropertyName, key);
                      valid = false;
                  } else {
                      map.put(key, value);
                  }
              }
              return valid;
          }
      
          @Override
          public Class<?> getObjectType() {
              return Map.class;
          }
      
          @Override
          public boolean isSingleton() {
              return true;
          }
      
          /**
           * @param mapPropertyName
           *            the mapPropertyName to set
           */
          public void setMapPropertyName(final String mapPropertyName) {
              this.mapPropertyName = mapPropertyName;
          }
      }
      And configure it like this:
      Code:
          <bean id="sampleBean" class="com.example.SampleBeanl">
              <property name="mapName">
                  <bean class="com.example.spring.MapFactoryBean">
                      <property name="location" value="classpath:example.properties" />
                      <property name="mapPropertyName" value="com.example.map" />
                  </bean>
              </property>
          </bean>
      And this works. (And I now see I won't need the list property, I could just iterate over all the properties and only pick the ones starting with the mapPropertyName.)

      Comment


      • #4
        Spring doesn't have any in-built support for use-cases like this and your solution seems fine if its working correctly as per expectations in your code-base.

        Comment

        Working...
        X