JavaBeans PropertyEditors

For those of you not entirely familiar with JavaBeans concepts, a PropertyEditor is a class that converts a property's value to and from its native type representation into a String. Originally, this was conceived as a way to allow property values to be entered, as String values, into an editor and have them transformed into the correct type. However, because PropertyEditors are inherently lightweight classes, they have found uses in many different settings, including Spring.

Because a good portion of property values in a Spring-based application start life in the BeanFactory configuration file, they are essentially Strings. However, the property that these values are set on may not be String-typed. So, to save you from having to create a load of String-typed properties artificially, Spring allows you to define PropertyEditors to manage the conversion of String-based property values into the correct types.

The Built-in PropertyEditors

Spring comes with seven built-in PropertyEditor implementations that are preregistered with the BeanFactory. Listing 5-34 shows a simple bean that declares seven properties, one for each of the types supported by the built-in PropertyEditors.

Listing 5-34: Using the Built-in PropertyEditors

image from book
package com.apress.prospring.ch5.pe;      import java.io.File; import java.net.URL; import java.util.Locale; import java.util.Properties;      import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.FileSystemResource;      public class PropertyEditorBean {          private Class cls;          private File file;          private URL url;          private Locale locale;          private Properties properties;          private String[] strings;          private byte[] bytes;          public void setClass(Class cls) {         System.out.println("Setting class: " + cls.getName());         this.cls = cls;     }          public void setFile(File file) {         System.out.println("Setting file: " + file.getName());         this.file = file;     }          public void setLocale(Locale locale) {         System.out.println("Setting locale: " + locale.getDisplayName());         this.locale = locale;     }          public void setProperties(Properties properties) {         System.out.println("Loaded " + properties.size() + " properties");         this.properties = properties;     }          public void setStrings(String[] strings) {         System.out.println("Loaded " + strings.length + " Strings");         this.strings = strings;     }          public void setUrl(URL url) {         System.out.println("Setting URL: " + url.toExternalForm());         this.url = url;     }          public void setBytes(byte[] bytes) {         System.out.println("Adding " + bytes.length + " bytes");         this.bytes = bytes;     }          public static void main(String[] args) {         BeanFactory factory = new XmlBeanFactory(new FileSystemResource(                 "./ch5/src/conf/pe/builtin.xml"));         PropertyEditorBean bean = (PropertyEditorBean) factory                 .getBean("builtInSample");     } }
image from book

In Listing 5-34, you can see that PropertyEditorBean has seven properties, each corresponding to one of the built-in PropertyEditors. In Listing 5-35, you can see a simple BeanFactory configuration specifying values for all of these properties.

Listing 5-35: Configuration Using PropertyEditors

image from book
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"  "http://www.springframework.org/dtd/spring-beans.dtd"> <beans>     <bean             >         <property name="class">             <value>java.lang.String</value>         </property>         <property name="file">             <value>d:/tmp/test.txt</value>         </property>         <property name="locale">             <value> en-GB </value>         </property>              <property name="url">             <value>http://www.springframework.org</value>         </property>         <property name="properties">             <value>                  name=foo                  age=19              </value>         </property>         <property name="strings">             <value>rob,jan,rod,jurgen,alef</value>         </property>         <property name="bytes">             <value>Hello World</value>         </property>     </bean> </beans>
image from book

As you can see, although all the properties on the PropertyEditorBean are not Strings, the values for the properties are specified as simple Strings. Running this example yields the following output:

Setting class: java.lang.String Setting file: test.txt Setting locale:  en-gb  Setting URL: http://www.springframework.org Loaded 2 properties Loaded 5 Strings Adding 11 bytes

As you can see, Spring has, using the built-in PropertyEditors, converted the String representations of the various properties to the correct types. Table 5-1 summarizes the built-in PropertyEditors available in Spring.

Table 5-1: Spring PropertyEditors

PropertyEditor

Description

ByteArrayPropertyEditor

This PropertyEditor converts a String value into an array of bytes.

ClassEditor

The ClassEditor converts from a fully qualified class name into a Class instance. When using this PropertyEditor, be careful not to include any extraneous spaces on either side of the class name when using XmlBeanFactory, because this results in a ClassNotFoundException.

FileEditor

The FileEditor converts a String file path into a File instance. Spring does not check to see if the file exists.

LocaleEditor

The LocaleEditor converts the String representation of a locale, such as en-GB, into a java.util.Locale instance.

PropertiesEditor

PropertiesEditor converts a String in the format key1=value1\n key2=value2\n keyn=valuen into an instance of java.util.Properties with the corresponding properties configured.

StringArrayPropertyEditor

The StringArrayPropertyEditor class converts a comma- separated list of Strings into a String array.

URLEditor

The URLEditor converts a String representation of a URL into an instance of java.net.URL.

This set of PropertyEditors provides a good base for working with Spring and makes configuring your application with common components such as Files and URLs much simpler.

Creating a Custom PropertyEditor

Although the built-in PropertyEditors cover some of the standard cases of property type conversion, there may come a time when you need to create your own PropertyEditor to support a class or a set of classes you are using in your application.

Spring has full support for registering custom PropertyEditors; the only downside is that the java.beans.PropertyEditor interface has a lot of methods, many of which are irrelevant to the task at hand—converting property types. Thankfully, Spring provides the PropertyEditorSupport class, which your own PropertyEditors can extend, leaving you to implement only a single method: setAsText().

Many of the applications that we build use regular expressions. In a recent application we built using Spring, we wanted to externalize the regular expressions we used in the application. At first glance, we thought the way to do this was to expose the regular expressions as String-typed properties and then, in an initialization callback, compile the String patterns in java.util.regex.Pattern objects. However, by employing a custom PropertyEditor, we discovered that we could expose Pattern type properties and have the PropertyEditor perform the compilation as the property values are set.

Listing 5-36 shows the code for the PatternPropertyEditor class.

Listing 5-36: The PatternPropertyEditor Class

image from book
package com.apress.prospring.ch5.pe;      import java.beans.PropertyEditorSupport; import java.util.regex.Pattern;      public class PatternPropertyEditor extends PropertyEditorSupport {          public void setAsText(String text) throws IllegalArgumentException {         Pattern pattern = Pattern.compile(text);         setValue(pattern);     } } 
image from book

As you can see, very little code is involved in the custom PropertyEditor; we simply use the supplied text as the source of the Pattern.compile() method. Calling the setValue() method sets the value of the underlying property.

In Listing 5-37, you can see a simple bean that searches a String using a Pattern instance and displays the result.

Listing 5-37: The CustomEditorExample Bean

image from book
package com.apress.prospring.ch5.pe;      import java.util.regex.Matcher; import java.util.regex.Pattern;      import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.CustomEditorConfigurer; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.FileSystemResource;      public class CustomEditorExample {          private Pattern searchPattern;          private String textToSearch;          public static void main(String[] args) {         ConfigurableListableBeanFactory factory = new XmlBeanFactory(                 new FileSystemResource("./ch5/src/conf/pe/custom.xml"));              CustomEditorConfigurer config =  (CustomEditorConfigurer) factory.getBean("customEditorConfigurer");                  config.postProcessBeanFactory(factory);                  CustomEditorExample bean =  (CustomEditorExample) factory.getBean("exampleBean");              System.out.println(bean.getMatchCount());     }          public void setSearchPattern(Pattern searchPattern) {         this.searchPattern = searchPattern;     }          public void setTextToSearch(String textToSearch) {         this.textToSearch = textToSearch;     }          public int getMatchCount() {         Matcher m = searchPattern.matcher(textToSearch);              int count = 0;         while (m.find()) {             count++;         }              return count;     } }
image from book

The most complex part of using a custom PropertyEditor is the registration process. You can register your PropertyEditor with Spring in one of two ways. The first is by calling ConfigurableBeanFactory.registerCustomEditor() and passing the type for which the editor should be used and an instance of the editor itself. The second, and preferred, mechanism is to define a bean of type CustomEditorConfigurer in your BeanFactory configuration, specifying the editors in a Map-typed property of that bean.

The CustomEditorConfigurer is an example of a BeanFactoryPostProcessor, a class that can make changes to a BeanFactory's configuration before the application uses it. Other common BeanFactoryPostProcessors are PropertyPlaceholderConfigurer and PropertyOverrideConfigurer. You can find more information about these classes in the Spring documentation. The only drawback of using a BeanFactoryPostProcessor with a BeanFactory is that they are not applied automatically. However, as you will see shortly, this is not a problem you encounter when using the ApplicationContext, which is one of the reasons ApplicationContext is preferred over BeanFactory for most applications.

Listing 5-38 shows a BeanFactory configuration that configures a CustomEditorConfigurer and the PatternPropertyEditor.

Listing 5-38: Using CustomEditorConfigurer

image from book
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"  "http://www.springframework.org/dtd/spring-beans.dtd"> <beans>     <bean name="customEditorConfigurer"         >         <property name="customEditors">             <map>                 <entry key="java.util.regex.Pattern">                     <bean                          />                 </entry>             </map>         </property>     </bean>     <bean             >         <property name="searchPattern">             <value>(dog|fox)</value>         </property>         <property name="textToSearch">             <value>The quick brown fox jumped over the lazy dog.</value>         </property>     </bean> </beans>
image from book

You should notice three points in this configuration. The first is that the custom PropertyEditors are injected into the CustomEditorConfigurer class using the Map-typed customEditors property. The second point is that each entry in the Map represents a single PropertyEditor with the key of the entry being the name of the class for which the PropertyEditor is used. As you can see, the key for the PatternPropertyEditor is java.util.regex.Pattern, which signifies that this is the class for which the editor should be used. The final point of interest here is that we used an anonymous bean declaration as the value of the single Map entry. No other bean needs to access this bean, so it needs no name and as a result, you can declare it inside of the <entry> tag.

Listing 5-39 shows the code for CustomEditorExample class that is registered as a bean in Listing 5-38.

Listing 5-39: The CustomEditorExample Class

image from book
package com.apress.prospring.ch5.pe;      import java.util.regex.Matcher; import java.util.regex.Pattern;      import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.CustomEditorConfigurer; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.FileSystemResource;      public class CustomEditorExample {          private Pattern searchPattern;          private String textToSearch;          public static void main(String[] args) {         ConfigurableListableBeanFactory factory = new XmlBeanFactory(                 new FileSystemResource("./ch5/src/conf/pe/custom.xml"));              CustomEditorConfigurer config = (CustomEditorConfigurer) factory                 .getBean("customEditorConfigurer");                  config.postProcessBeanFactory(factory);                  CustomEditorExample bean = (CustomEditorExample) factory                 .getBean("exampleBean");              System.out.println(bean.getMatchCount());     }          public void setSearchPattern(Pattern searchPattern) {         this.searchPattern = searchPattern;     }          public void setTextToSearch(String textToSearch) {         this.textToSearch = textToSearch;     }          public int getMatchCount() {         Matcher m = searchPattern.matcher(textToSearch);              int count = 0;         while (m.find()) {             count++;         }              return count;     } }
image from book

Most of this code is fairly intuitive; the only point of interest is the call to CustomEditorConfigurer.postProcessBeanFactory(), which passes in the BeanFactory instance. This is where the custom editors are registered in Spring; you should call this before you attempt to access any beans that need to use the custom PropertyEditors.

Choosing a Registration Process

We did not show the programmatic registration process because we believe that in all cases, the declarative process is better suited to the needs of most, if not all, applications. When you are using the programmatic process, adding a new PropertyEditor means changing the application code, whereas with the declarative mechanism, you can simply update the configuration. Using the declarative mechanism also encourages you to define your editors as beans, which makes it so that you can configure them using Dependency Injection.

When using a BeanFactory, you require roughly the same amount of Java code for each mechanism as when you are registering a single PropertyEditor. When using an ApplicationContext, you require no Java code whatsoever to use the declarative mechanism, which strengthens the argument against using the programmatic mechanism.

PropertyEditors Summary

By using custom PropertyEditors, you can avoid exposing a lot of unnecessary String-typed properties on your beans. However, they are not always the ideal solution. We chose the regular expression editor to demonstrate this. Although we started off with a regular expression PropertyEditor, we eventually moved to a FactoryBean instead. The reason we chose a FactoryBean is because you can only have one PropertyEditor per type, and if you need different configurations, you are stuck. We reached a point where we needed one Pattern object configured to be in multiline mode and another to be configured in single-line mode. Because we could only have a single PropertyEditor registered for the Pattern type, we could only chose one of these options, thus we needed to move to a FactoryBean.

In general, you can use a PropertyEditor when the entire identity and configuration of the Object can be represented as a String, or where the configuration can be changed or derived from the identity. Pattern instances are immutable, so you cannot change the configuration, and it is impossible to tell whether a pattern should be in multiline or single-line mode. In all other cases, you may be better off with a FactoryBean.



Pro Spring
Pro Spring
ISBN: 1590594614
EAN: 2147483647
Year: 2006
Pages: 189

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net