Tuesday, November 4, 2008

Initializing static fields of Spring beans

This solution is courtesy Chris Harris UK.

Step 1: Create a bean factory post processor as shown in Listing 1

Listing 1 : StaticInitializerBeanFactoryPostProcessor .java

/**
*
*/
package org.opengarage.spring.postprocessor;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Iterator;
import java.util.Map;

import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.BeansException;

import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;

/**
* @author dhrubo
*
*/
public class StaticInitializerBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    private Map classes;
    private BeanWrapperImpl bri;

    public StaticInitializerBeanFactoryPostProcessor() {
        bri = new BeanWrapperImpl();
    }

    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        for (Iterator classIterator = classes.keySet().iterator(); classIterator.hasNext(); ) {
            String className = (String)classIterator.next();
            //System.out.println("Class " + className + ":");
            Map vars = (Map)classes.get(className);
            Class c = null;
            try {
                c = Class.forName(className);
            } catch (ClassNotFoundException e) {
                throw new NoSuchBeanDefinitionException("Class not found for " + className);
            }
            Method[] methods = c.getMethods();
            for (Iterator fieldIterator = vars.keySet().iterator(); fieldIterator.hasNext(); ) {
                String fieldName = (String)fieldIterator.next();
                Object value = vars.get(fieldName);
                Method method = findStaticSetter(methods, fieldName);
                if (method == null) {
                    throw new RuntimeException("No static setter method found for class " +
                            className + ", field " + fieldName,null);
                }
                //System.out.println("\tFound method " + method.getName() + " for field " + fieldName + ", value " + value);
                Object newValue = bri.doTypeConversionIfNecessary(value, getPropertyType(method));
                try {
                    method.invoke(null, new Object[] {newValue});
                } catch (Exception e) {
                    throw new RuntimeException("Invocation of method " + method.getName() +
                            " on class " + className + " with value " + value + " failed.", e);
                }
            }
        }
    }

    private Class getPropertyType(Method setter) {
        Class params[] = setter.getParameterTypes();
        if (params.length != 1) {
            throw new RuntimeException("bad write method arg count: " + setter);
        }
        return  params[0];
    }

    /**
     * Look for a static setter method for field named fieldName in Method[].
     * Return null if none found.
     * @param methods
     * @param fieldName
     * @return
     */
    private Method findStaticSetter(Method[] methods, String fieldName) {
        String methodName = setterName(fieldName);
        for (int i=0; i<methods.length; i++) {
            if (methods[i].getName().equals(methodName) &&
                                                Modifier.isStatic(methods[i].getModifiers())) {
                return methods[i];
            }
        }
        return null;
    }

    /**
     * return the standard setter name for field fieldName
     * @param fieldName
     * @return
     */
    private String setterName(String fieldName) {
        String nameToUse = null;
        if (fieldName.length() == 1) {
            if (Character.isLowerCase(fieldName.charAt(0))) {
                nameToUse = fieldName.toUpperCase();
            } else {
                nameToUse = fieldName;
            }
        } else {
            if (Character.isLowerCase(fieldName.charAt(0)) && Character.isLowerCase(fieldName.charAt(1))) {
                nameToUse = fieldName.substring(0,1).toUpperCase() + fieldName.substring(1);
            }  else {
                nameToUse = fieldName;
            }
        }
        return "set" + nameToUse;
    }

    public void setClasses(Map classes) {
        this.classes = classes;
    }
}

Step 2 : Create your simple test class

Listing 2 : Test.java

/**
*
*/
package org.opengarage.spring.test;

import java.util.Properties;

/**
* @author dhrubo
*
*/
public class Test {
    private static Properties properties;

    /**
     * @return the properties
     */
    public static Properties getProperties() {
        return properties;
    }

    /**
     * @param properties the properties to set
     */
    public static void setProperties(Properties properties) {
        Test.properties = properties;
    }
}

Step 3 : Finally wire up everything in Spring configuration file

Listing 3 - Spring-config.xml

<bean class="org.opengarage.spring.postprocessor.StaticInitializerBeanFactoryPostProcessor">
        <property name="classes">
            <map>
                <entry key="org.opengarage.pingscape.test.Test">
                    <map>
                        <entry key="properties">
                            <bean class="org.springframework.beans.factory.config.PropertiesFactoryBean">
                                <property name="locations">
                                    <value>classpath:org/opengarage/spring/test/jdbc.properties</value>
                                </property>
                            </bean>
                        </entry>   
                    </map>
                </entry>
            </map>
        </property>
    </bean>

0 comments:

Post a Comment