geeky · java

Dynamically invoke methods using spring config

A business rule at work requires some flexible configurations. I attempted to fulfill the need by create different spring batch steps/tasklets to execute each requirement based on a property value.

For example, step 1 will need to execute methodOne on the class Repository with the appropriate parameters. The method signature & parameters are determined by the tasklet1.chocie value in the env.properties.

I could write a bunch of if statements in each step. I wanted to see if there is a more dynamic way to do this.

I tried using Spring Framework and Java reflection.

My Git Repository for the example

Repository Class link
It has four methods: methodOne, methodOne(overloaded), methodTwo, methodThree.

TaskletExecution link
The POJO to hold the configuration needed to invoke the method using reflection.

  • String methodName
  • Object[] parameters
  • public Class[] getParameterClasses() {
    	List<Class> parameterClasses = new ArrayList<Class>();
    	for (Object parameter : parameters) {
    	    parameterClasses.add(parameter.getClass());
    	}
    	return parameterClasses.toArray(new Class[] {});
    }
    

AbstractTasklet Class link
The parent class for all of the tasklets. It has the execute method that uses reflection to invoke the method using the TaskletExecution.

public void execute() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
	log.info(String.format("in '%s' execute with choice '%s'", getTaskletName(), choice));
	Method method = Repository.class.getMethod(taskletExecution().getMethodName(), taskletExecution().getParameterClasses());
	Integer repositoryResult =(Integer)method.invoke(repository, taskletExecution().getParameters());
	log.info(String.format("repository result = '%s'", repositoryResult));
}

The TaskletExecution is determined by the value of the tasklet#.choice property in the env.properties. It uses the property to look up the taskletExecution value in the taskletExecutions map that’s autowired to each tasklet via spring.

Example of a TaskletExecutions Map link

<util:map id="tasklet5Executions" value-type="net.pureessence.example.TaskletExecution">
    <entry key-ref="choice1">
        <bean class="net.pureessence.example.TaskletExecution">
            <property name="methodName" value="methodThree"/>
            <property name="parameters">
                <list>
                    <bean class="java.lang.String">
                        <constructor-arg>
                            <value>51</value>
                        </constructor-arg>
                    </bean>
                    <bean class="java.lang.Integer">
                        <constructor-arg>
                            <value>52</value>
                        </constructor-arg>
                    </bean>
                    <bean class="java.lang.String">
                        <constructor-arg>
                            <value>tasklet5 arg3</value>
                        </constructor-arg>
                    </bean>
                    <bean class="net.pureessence.example.Type" factory-method="valueOf">
                        <constructor-arg>
                            <value>TYPE_3</value>
                        </constructor-arg>
                    </bean>
                </list>
            </property>
        </bean>
    </entry>
    <entry key-ref="choice2">
        <bean class="net.pureessence.example.TaskletExecution">
            <property name="methodName" value="methodOne"/>
            <property name="parameters">
                <list>
                    <bean class="java.lang.String">
                        <constructor-arg>
                            <value>tasklet5 arg1</value>
                        </constructor-arg>
                    </bean>
                    <bean class="java.lang.Integer">
                        <constructor-arg>
                            <value>123599</value>
                        </constructor-arg>
                    </bean>
                    <bean class="net.pureessence.example.Type" factory-method="valueOf">
                        <constructor-arg>
                            <value>TYPE_3</value>
                        </constructor-arg>
                    </bean>
                </list>
            </property>
        </bean>
    </entry>
</util:map>

Personally I’m not overly thrilled with how verbose the spring config is. It took me a long time to figure out how to make it syntactically correct. The spring in depth site is a very good resource. In general, it takes a lot of xml to create maps in spring. I think at this point, it maybe a better idea to create static maps in each tasklet in Java for configurations.

My Git Repository for the example

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s