Wednesday, June 16, 2010

Defining Custom Editors With Spring

Custom editors gives you a lot of flexibility and reduces XML configuration when defining your spring beans. Say for example i have the following Person object;


public class Person {

private String firstName;

private String lastName;

private int age;

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}


}


And the following Class called PersonManager is using this Person object as follows;


public class PersonManager {

private Person person;

public Person getPerson() {
return person;
}

public void setPerson(Person person) {
this.person = person;
}


}


If you wanted to inject a person object to the PersonManager class then you would first register the Person class as a spring bean and register that bean as a ref bean of PersonManager class.

But spring offers an easy way of injecting the Person object to the PersonManager by use of Custom PropertyEditors. With this you can define your values as a string value and create the Person object as you deem appropriate from the format you define. In my example i have defined the format to be;
firstname,lastname,age
hence is would be comma separated values which defined the values needed to create the Person object. Now in order to let spring understand how to handle this string value you need to define your own propertye editor. It is as follows;






public class PersonPropertyEditor extends PropertyEditorSupport{

@Override
public void setAsText(String arg0) throws IllegalArgumentException {

String[]vals = arg0.split(",");
Person p = new Person();
p.setFirstName(vals[0]);
p.setLastName(vals[1]);
p.setAge(Integer.valueOf(vals[2]));
setValue(p);
}
}



Here i simply split the string value and set the relevant values of the Person object and then call the setValue inherited method to set the value. Its as simple as that.

And finally you define your customer registar which binds your customer editor.


public class PersonConfiguratorWithRegistars implements PropertyEditorRegistrar {
@Override
public void registerCustomEditors(PropertyEditorRegistry arg0) {
arg0.registerCustomEditor(Person.class, new PersonPropertyEditor());

}
}



Now when letting spring know about your custom editor there were two ways initially before Spring 3.0. They are;

  1. Define as a customEditors property within org.springframework.beans.factory.config.CustomEditorConfigurer
  2. Define as a propertyEditorRegistrars within org.springframework.beans.factory.config.CustomEditorConfigurer
As of Spring 3.0 the customerEditors property is deprecated as they had a thread locking issue as explained here.

So lastly i show you how to wire your new customer editor and the PersonManager object by providing a string value. The application context XML is as follows;






<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

<!--

This is the OLD way of wiring your custom editor which is not thread safe

<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map>
<entry key="Person">
<bean id="personEditor" class="PersonPropertyEditor">
</bean>
</entry>
</map>
</property>
</bean>

-->

<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="propertyEditorRegistrars">
<list>
<ref bean="customPersonPropertyEditor" />
</list>
</property>
</bean>

<bean id="customPersonPropertyEditor"
class="PersonConfiguratorWithRegistars" />

<bean id="perMgt" class="PersonManager">
<property name="person" value="John,Alan,23" />
</bean>


</beans>



Thats it. You can use the following test class to test the same;


public class PropertyEditorTest {

/**
* @param args
*/
public static void main(String[] args) {
ApplicationContext appContext = new ClassPathXmlApplicationContext("appContext.xml");
PersonManager personMgr = (PersonManager)appContext.getBean("perMgt");
Person p = personMgr.getPerson();
System.out.println(p.getFirstName()+" "+p.getLastName()+" "+p.getAge());
}

}