PropertyEditors in Spring 3

PropertyEditor is an interface that provides a support for the conversion of the value of a property (usually of type String) in an object that can be Date, URL, etc.
In the Spring beans are defined in a configuration file and the values of their properties are always written as strings such as:

<property name="number">
	<value>10</value>
</property>

where the property “number” contains the value “10” as string and not as a number, even if in the java class of the bean the member “number” is defined as int as shown by the following example:

  1. create the file pom.xml
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
      <groupId>PropertyEditors</groupId>
      <artifactId>PropertyEditors</artifactId>
      <version>0.0.1-SNAPSHOT</version>
      <build>
        <sourceDirectory>src</sourceDirectory>
        <plugins>
          <plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>2.3.2</version>
            <configuration>
              <source/>
              <target/>
            </configuration>
          </plugin>
        </plugins>
      </build>
      
      	<dependencies>
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-context</artifactId>
    			<version>3.1.2.RELEASE</version>
    		</dependency>
    
    		<!-- log4j -->
    		<dependency>
    			<groupId>log4j</groupId>
    			<artifactId>log4j</artifactId>
    			<version>1.2.17</version>
    		</dependency>
    	</dependencies>
    </project>
    

    alternatively, if you do not use Maven, you can import the following .jar files in the folder “lib”:

    • spring-context-3.1.2.RELEASE.jar
    • spring-aop-3.1.2.RELEASE.jar
    • aopalliance-1.0.jar
    • spring-beans-3.1.2.RELEASE.jar
    • spring-core-3.1.2.RELEASE.jar
    • commons-logging-1.1.1.jar
    • spring-expression-3.1.2.RELEASE.jar
    • spring-asm-3.1.2.RELEASE.jar
    • log4j-1.2.17.jar
  2. create the file conf/app-context.xml (the folder “conf” must be in the classpath)
    <?xml version="1.0" encoding="UTF-8"?>
    <beans
    	xmlns="http://www.springframework.org/schema/beans"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xmlns:context="http://www.springframework.org/schema/context"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd"
    	default-lazy-init="true">
    
    	<bean
    		id="simpleBean"
    		class="eu.lucazanini.propertyeditors.SimpleBean">
    		<property name="number">
    			<value>10</value>
    		</property>
    		<property name="url">
    			<value>http://www.lucazanini.eu</value>
    		</property>
    	</bean>
    
    </beans>
    
  3. create the file conf/log4j.xml (the folder “conf” must be in the classpath)
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE log4j:configuration PUBLIC "-//LOGGER" "log4j.dtd">
    
    <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"> 
        <appender name="stdout" class="org.apache.log4j.ConsoleAppender">
            <layout class="org.apache.log4j.PatternLayout">
                <param name="ConversionPattern" value="%d %-5p %c{1}:%L %m %n" />
    <!--
    ConversionPattern format specification
    %d      inserts the date; you can specify the format (%d{yyyy-MM-dd HH:mm:ss,SSS})
    %-5p    inserts the priority log level, 5 characters, left justified
    %c{1}   inserts the name of the class
    %L      inserts the line number
    %m      inserts the user message
    %n      inserts the separator (for example, a new line)
    -->
            </layout>
        </appender>
    
        <appender name="fileAppender" class="org.apache.log4j.RollingFileAppender">
            <param name="append" value="false" />
            <param name="Threshold" value="DEBUG" />
            <param name="File" value="logs/app.log"/>
            <param name="MaxFileSize" value="512KB" />
            <param name="MaxBackupIndex" value="10" />
            <layout class="org.apache.log4j.PatternLayout">
                <param name="ConversionPattern" value="%d %-5p %c{1}:%L %m %n" />
            </layout>
        </appender>
    
    <!--sets the priority log level for org.springframework-->
        <logger name="org.springframework">
            <level value="info"/>
        </logger>
    
    <!--sets the priority log level for eu.lucazanini-->
        <logger name= "eu.lucazanini">
            <level value="debug"/>
        </logger>
    
    <!--sets the default priority log level-->
        <root>
            <priority value="debug"></priority>
            <appender-ref ref="stdout"/>
            <appender-ref ref="fileAppender"/>
        </root>
    </log4j:configuration>
    
  4. create the file eu/lucazanini/propertyeditors/Main.java
    package eu.lucazanini.propertyeditors;
    
    import org.apache.log4j.Logger;
    import org.springframework.context.support.*;
    
    public class Main {
    
        private static Logger log = Logger.getLogger(Main.class);
    
        public static void main(String[] args) {
    
    	GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
    	ctx.load("classpath:app-context.xml");
    	ctx.refresh();
    
    	log.debug("creating bean");
    	SimpleBean bean = (SimpleBean) ctx.getBean("simpleBean");
    	log.debug("number is " + bean.getNumber());
    	log.debug("date is " + bean.getDate());
    	log.debug("url is " + bean.getUrl());
    	log.debug("created bean");
    
        }
    }
    
  5. create the file eu/lucazanini/propertyeditors/SimpleBean.java
    package eu.lucazanini.propertyeditors;
    
    import java.net.URL;
    import java.util.Date;
    
    public class SimpleBean {
    
        private Date date;
        private int number;
        private URL url;
    
        public Date getDate() {
    	return date;
        }
    
        public int getNumber() {
    	return number;
        }
    
        public URL getUrl() {
    	return url;
        }
    
        public void setDate(Date date) {
    	this.date = date;
        }
    
        public void setNumber(int number) {
    	this.number = number;
        }
    
        public void setUrl(URL url) {
    	this.url = url;
        }
    
    }
    

The output is:

DEBUG Main:16 creating bean 
DEBUG Main:18 number is 10 
DEBUG Main:19 date is null 
DEBUG Main:20 url is http://www.lucazanini.eu 
DEBUG Main:21 created bean 

Note that the string “10” has been converted into the corresponding integer and the string “http://www.lucazanini.eu” has been used to create an object of class URL.
This happens because Spring registers and implicitly uses some PropertyEditors and among them there are CustomNumberEditor and URLEditor.
There are other PropertyEditors you must register explicitly as CustomDateEditor that requires you specify the date format.
To check this, add the following property to simpleBean in app-context.xml:

<property name="date">
	<value>2012-10-21</value>
</property>

If you run the application you get the following error:

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: 
Error creating bean with name 'simpleBean' defined in class path resource [app-context.xml]: 
Initialization of bean failed; nested exception is org.springframework.beans.ConversionNotSupportedException: 
Failed to convert property value of type 'java.lang.String' to required type 'java.util.Date' for property 'date'; nested exception is java.lang.IllegalStateException: 
Cannot convert value of type [java.lang.String] to required type [java.util.Date] for property 'date': 
no matching editors or conversion strategy found

In order to register a CustomDateEditor and thus avoid this error, replace the file app-context.xml

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

	<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
		<property name="customEditors">
			<map>
				<entry key="java.util.Date">
					<bean
						id="dateEditor"
						class="org.springframework.beans.propertyeditors.CustomDateEditor">

						<constructor-arg>
							<bean class="java.text.SimpleDateFormat">
								<constructor-arg value="yyyy-MM-dd" />
							</bean>
						</constructor-arg>
						<constructor-arg value="true" />

					</bean>
				</entry>
			</map>
		</property>
	</bean>

	<bean
		id="simpleBean"
		class="eu.lucazanini.propertyeditors.SimpleBean">
		<property name="number">
			<value>10</value>
		</property>
		<property name="url">
			<value>http://www.lucazanini.eu</value>
		</property>
		<property name="date">
			<value>2012-10-21</value>
		</property>
	</bean>

</beans>

Now the output is:

DEBUG Main:16 creating bean 
DEBUG Main:18 number is 10 
DEBUG Main:19 date is Sun Oct 21 00:00:00 CEST 2012 
DEBUG Main:20 url is http://www.lucazanini.eu 
DEBUG Main:21 created bean 

References:
Validation, Data Binding, and Type Conversion
Package org.springframework.beans.propertyeditors


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.