Loading a List from a file in Spring Application Context
Have you ever wanted to load a List in your application context from a file? Perhaps you want to define a variable number of settings to be injected in, or in my case I wanted to pull in the Hibernate mapping files into an AnnotationSessionFactoryBean. Spring doesn't appear to have a way to do this out of the box, so here is how I have done it.
And that's about all. Hope this is of help to someone.
So you want to load a list of strings defined in a file called names.txt such as:
James Adam Peter PaulYour application context would look something like:
<bean id="resourceList" class="com.fizzpod.spring.ResourceListFactoryBean"> <property name="targetListClass"> <value>java.util.ArrayList</value> </property> <property name="locations"> <list> <value>classpath:names.txt</value> </list> </property> </bean>The ResourceListFactoryBean is (obviously) a factory bean for creating a list from a Spring resource. Now this uses some of Springs magic to turn the classpath:names.txt into a spring Resource object. To take advantage of the normal ListFactoryBean by extending it so that you can define a list using the normal ListFactoryBean (why you would want to do that rather than using the util:list tag). Anyway the class looks like this:
/** * Creates a List of values from the specified resources. */ public class ResourceListFactoryBean extends ListFactoryBean { /** * The List implementation class to create */ private Class targetListClass = null; /** * The resources to create the list from */ private Resource[] locations = null; /** * Flag for whether the source list is set */ private boolean sourceListSet = false; @Override /** * Set the source List, just used to detect whether * a sourceList is set * @param sourceList the source list to create the list from. */ public void setSourceList(List sourceList) { super.setSourceList(sourceList); sourceListSet = true; } @Override /** * To allow the creation of the list class * @param targetListClass the class of the list to instantiate */ public void setTargetListClass(Class targetListClass) { super.setTargetListClass(targetListClass); this.targetListClass = targetListClass; } /** * Set a location of the list file to load. * @param location the resource to load the settings f */ public void setLocation(Resource location) { this.locations = new Resource[] {location}; } /** * Set locations of list files to be loaded. * @param locations the locations of the resources */ public void setLocations(Resource[] locations) { this.locations = locations; } @Override @SuppressWarnings("unchecked") /** * Create a list from the resources, delegating the * super class if any source list has been set. * @return the list instantiated from the context definition */ protected List createInstance() { List result = null; if(this.sourceListSet) { result = super.createInstance(); } if(this.locations != null) { List resourceList = null; if(result == null) { result = instantiateListClass(); } Class valueType = null; for(Resource resource: locations) { List contents = this.resolveList(resource); if (this.targetListClass != null) { valueType = GenericCollectionTypeResolver.getCollectionType(this.targetListClass); } if (valueType != null) { TypeConverter converter = getBeanTypeConverter(); for (Object elem : contents) { result.add(converter.convertIfNecessary(elem, valueType)); } } else { result.addAll(contents); } } } if(result == null) { throw new IllegalArgumentException("You must set either the sourceList, locations or location parameters"); } return result; } /** * Instantiates the list class * @return the list class as defined by the context */ private List instantiateListClass() { List result; if (this.targetListClass != null) { result = (List) BeanUtils.instantiateClass(this.targetListClass); } else { result = new ArrayList(); } return result; } @SuppressWarnings("unchecked") /** * Loads the resource into a list of strings * @return the list of strings loaded from the resource */ private ListNote that the above code relies on the apache commons io to parse and load the resource into a list. Also note that the some of the ListFactoryBean methods are overriden so that we can detect if the user is just defining a normal list. Why, well like I said above if you just want a list use the util:list tag, but by doing this in the above code it allows for a mixed list of items from a resource and sources defined in the xml to be merged together. The following example shows that and as a bonus how to define a single resource for example:resolveList(Resource resource) { InputStream inStream = null; List lines = null; try { inStream = resource.getInputStream(); lines = IOUtils.readLines(inStream); } catch (IOException e) { throw new IllegalArgumentException("Could not read resource " + resource.getDescription(), e); } finally { IOUtils.closeQuietly(inStream); } return lines; } }
<bean id="resourceList" class="com.fizzpod.ResourceListFactoryBean"> <property name="targetListClass"> <value>java.util.ArrayList</value> </property> <property name="sourceList"> <list> <value>Sarah</value> <value>Helen</value> <value>Debbie</value> </list> </property> <property name="locations" value="classpath:names.txt"></property> </bean>
And that's about all. Hope this is of help to someone.
Comments
Post a Comment