Search code examples
javaspringspring-beanxml-configurationmethod-invocation

Using MethodInvokingFactoryBean for XMLConfiguration to loading file in Spring bean


I have a xmlConfiguration bean to loading a SystemProperty.xml file as below

<bean
    id="xmlConfiguration"
    class="org.apache.commons.configuration.XMLConfiguration"
    lazy-init="true">
    <constructor-arg type="java.lang.String">
        <value>SystemProperty.xml</value>
    </constructor-arg>
    <property name="expressionEngine">
        <bean class="org.apache.commons.configuration.tree.xpath.XPathExpressionEngine" />
    </property>
</bean>

It works fine, however, I have to set delimiterParsingDisabled to true in XMLConfiguration, so I change bean for adding a propery delimiterParsingDisabled

<bean
    id="xmlConfiguration"
    class="org.apache.commons.configuration.XMLConfiguration"
    lazy-init="true">
    <constructor-arg type="java.lang.String">
        <value>SystemProperty.xml</value>
    </constructor-arg>
    <property name="expressionEngine">
        <bean class="org.apache.commons.configuration.tree.xpath.XPathExpressionEngine" />
    </property>
    <property name="delimiterParsingDisabled">
        <value type="java.lang.Boolean">true</value>
    </property>
</bean>

But, this wouldn't work well. Due to the setDelimiterParsingDisabled() must be called before loading in the file. Therefore, I have to call load(String fileName) of XMLConfiguration after called setDelimiterParsingDisabled()

I have used MethodInvokingFactoryBean for this way, but got any exception as below

org.apache.commons.configuration.ConfigurationException: No file name has been set!
at org.apache.commons.configuration.AbstractFileConfiguration.save(AbstractFileConfiguration.java:409)
at org.apache.commons.configuration.AbstractHierarchicalFileConfiguration.save(AbstractHierarchicalFileConfiguration.java:214)
at devicemanage.system.SystemConfigurationServiceImpl.saveSystemProperty(SystemConfigurationServiceImpl.java:232)
at datacollection.service.DataCollectionServiceImpl.syncWithDataCollection(DataCollectionServiceImpl.java:786)
at devicemanage.utility.SyncWithDCListener$1.run(SyncWithDCListener.java:51)

It seems that the file doesn't be set into XMLConfiguration, my MethodInvokingFactoryBean has described as below

<bean id="xmlConfigurationMethodInvokingBean"  
     class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">  
     <property name="targetObject" ref="xmlConfiguration" />  
     <property name="targetMethod" value="load" />
     <property name="arguments" value="SystemProperty.xml" />  
 </bean>

And of cause, change my xmlConfiguration bean to not loading file while new the constructor as below

<bean
    id="xmlConfiguration"
    class="org.apache.commons.configuration.XMLConfiguration"
    lazy-init="true">
    <property name="expressionEngine">
        <bean class="org.apache.commons.configuration.tree.xpath.XPathExpressionEngine" />
    </property>
    <property name="delimiterParsingDisabled">
        <value type="java.lang.Boolean">true</value>
    </property>
</bean>

Not sure is that I have the wrong way to using MethodInvokingFactoryBean or I have an error used of the arguments to passing a fileName String into load()

Any help is appreciated.


Solution

  • First way

    I would suggest you to create your own inherited class and declare init-method:

    package beans;
    
    import org.apache.commons.configuration.ConfigurationException;
    import org.apache.commons.configuration.XMLConfiguration;
    
    public class CustomXMLConfiguration extends XMLConfiguration {
    
        private String loadFileName;
    
        private void init() throws ConfigurationException {
            this.load(fileName);
        }
    
        public String getLoadFileName() {
            return loadFileName;
        }
    
        public void setLoadFileName(String fileName) {
            this.loadFileName = fileName;
        }
    }
    

    And into the configuration you can use this class in the following way:

    <bean id="xmlConfiguration" class="beans.CustomXMLConfiguration" lazy-init="true"
                                init-method="init">
        <property name="expressionEngine">
            <bean class="org.apache.commons.configuration.tree.xpath.XPathExpressionEngine" />
        </property>
        <property name="delimiterParsingDisabled">
            <value type="java.lang.Boolean">true</value>
        </property>
        <property name="loadFileName" value="SystemProperty.xml"/>
    </bean>
    

    init() method will be invoked right after bean will be initialized.

    Second way

    You can use bean of MethodInvokingBean class. Bean of this class just invoking of target method:

    <bean class="org.springframework.beans.factory.config.MethodInvokingBean">
        <property name="targetObject" ref="xmlConfiguration"/>
        <property name="targetMethod" value="load"/>
        <property name="arguments" value="SystemProperty.xml"/>
    </bean>
    

    Personally I would prefer first variant, because of more flexible customization and there is not redundant instance of bean. But you can choose anyone.

    Hope this will be helpful.