Bean Manipulation

Available

The org.springframework.beans package follows the JavaBeans class standard. A JavaBean is a class with a default no-argument constructor that follows a naming convention where (for example) a property called bingoMadness will have a setter setBingoMadness(...) and a getter getBingoMadness(). For more information about JavaBeans and the specification, see javabeans.

One of the important classes in the bean package is the BeanWrapper interface and its corresponding implementation (BeanWrapperImpl). As stated in the javadoc, BeanWrapper offers functionality for setting and getting property values (individually or in bulk), getting property handles, and querying properties to determine whether they are readable or writable. Additionally, BeanWrapper offers support for nested properties, allowing you to set properties on subproperties to an unlimited depth. BeanWrapper also supports the ability to add standard JavaBeans PropertyChangeListeners and VetoableChangeListeners without the need for additional code in the target class. Last but not least, BeanWrapper provides support for setting indexed properties. BeanWrapper is not typically used directly by application code, but is used by DataBinder and BeanFactory.

This is how BeanWrapper works is somewhat clear from its name: it wraps a bean to perform certain actions on it, such as setting and getting properties.

Setting and getting base and nested properties

Setting and getting properties is done using the overloaded setPropertyValue and getPropertyValue methods in BeanWrapper. See their Javadoc for more details. The table below shows some examples of these conventions:

Table 11. Property Examples
Expression Explanation

name

Specifies the name property that matches methods getName() or isName() and setName(..).

account.name

Specifies the name of the account nested property , which corresponds to (for example) the methods getAccount().setName() or getAccount().getName().

account[2]

Specifies the third element of the indexed property account property. Indexed properties can be of type array, list, or other naturally ordered collection.

account[COMPANYNAME]

Indicates the value of the associative array entry indexed by the COMPANYNAME key of the Account Map property.

(This next section is not extremely important unless you plan to work with BeanWrapper directly. If you only use DataBinder and BeanFactory and their default implementations, then you should go straight to PropertyEditors.)

Next two example classes use BeanWrapper to get and set properties:

Java
public class Company {
    private String name;
    private Employee managingDirector;
    public String getName() {
        return this.name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Employee getManagingDirector() {
        return this.managingDirector;
    }
    public void setManagingDirector(Employee managingDirector) {
        this.managingDirector = managingDirector;
    }
}
Kotlin
class Company {
    var name: String? = null
    var managingDirector: Employee? = null
}
Java
public class Employee {
    private String name;
    private float salary;
    public String getName() {
        return this.name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public float getSalary() {
        return salary;
    }
    public void setSalary(float salary) {
        this.salary = salary;
    }
}
Kotlin
class Employee {
    var name: String? = null
    var salary: Float? = null
}

The following code snippets demonstrate examples of obtaining and manipulating some properties of the created Companyand Employees instances:

Java
BeanWrapper company = new BeanWrapperImpl(new Company());
// setting the company name...
company.setPropertyValue("name", "Some Company Inc.");
// ... can also be done like this:
PropertyValue value = new PropertyValue("name", "Some Company Inc.");
company.setPropertyValue(value);
// Okay, let's create a director and bind him to the company:
BeanWrapper jim = new BeanWrapperImpl(new Employee());
jim.setPropertyValue("name", "Jim Stravinsky");
company.setPropertyValue("managingDirector", jim.getWrappedInstance());
// getting salary for managingDirector via company
Float salary = (Float) company.getPropertyValue("managingDirector.salary");
Kotlin
val company = BeanWrapperImpl(Company())
// setting the company name...
company.setPropertyValue("name", "Some Company Inc.")
// ... can also be done like this:
val value = PropertyValue("name", "Some Company Inc.") company.setPropertyValue(value)
// Okay, let's create a director and bind him to the company:
val jim = BeanWrapperImpl(Employee())
jim.setPropertyValue("name" , "Jim Stravinsky")
company.setPropertyValue("managingDirector", jim.wrappedInstance)
// getting salary for managingDirector via company
val salary = company.getPropertyValue("managingDirector.salary") as Float?

Built-in implementations of PropertyEditor

Spring uses the PropertyEditor concept to perform conversion between Object and String. It can be convenient to represent properties differently than the object itself. For example, Date can be represented in human-readable form (as String: '2007-14-09'), while we all We can also convert human-readable form back to the original date (or, better yet, convert any date entered in human-readable form back into Date objects). This operating logic can be achieved by registering special editors like java.beans.PropertyEditor. Registering special editors for the BeanWrapper, or alternatively in a specific IoC container (as mentioned in the previous chapter), tells it how to convert the properties to the desired type. For more information about PropertyEditor, see javadoc for the java.beans package from Oracle.

Some examples of using the property editing feature in Spring:

  • Setting bean properties is done using PropertyEditor implementations. If you use String as the property value of any bean you declare in an XML file, Spring (if the corresponding property's setter has a Class parameter) uses a ClassEditor to try to resolve the parameter to a Class object.

  • The parsing of HTTP request parameters in the Spring MVC framework is done using various implementations PropertyEditor, which can be manually bound in all CommandController subclasses.

Spring has a number of built-in PropertyEditor implementations, which make your work easier. They are all in the org.springframework.beans.propertyeditors package. Most of them (but not all, as listed in the following table) are registered by BeanWrapperImpl by default. In cases where the property editor is configured in a particular way, you can register your own variation to override the default variation. The following table describes the various PropertyEditor implementations that Spring provides:

Table 12. Built-in implementations PropertyEditor
Class Explanation

ByteArrayPropertyEditor

Editor for byte arrays. Converts strings to their corresponding byte representations. By default, BeanWrapperImpl is registered.

ClassEditor

Parses strings representing classes into real classes and vice versa. If the class is not found, an IllegalArgumentException exception is thrown. By default, BeanWrapperImpl is registered.

CustomBooleanEditor

Custom property editor for Boolean properties. By default, BeanWrapperImpl is registered, but can be overridden by registering a custom instance of it as a custom editor.

CustomCollectionEditor

Property editor for collections that converts any source Collection to a given target Collection type.

CustomDateEditor

Custom property editor for java.util.Date, which supports a special DateFormat. NOT registered by default. The user must register it in the appropriate format as needed.

CustomNumberEditor

Customizable property editor for any Number subclass such as Integer, Long, Float or Double. By default, BeanWrapperImpl is registered, but can be overridden by registering a custom instance of it as a custom editor.

FileEditor

Resolves strings to java.io.File objects. By default, BeanWrapperImpl is registered.

InputStreamEditor

A unidirectional property editor that can accept a string and create (via an intermediate ResourceEditor and Resource) an InputStream so that the properties InputStreams could be directly set as strings. Note that when used by default, InputStream does not close. By default, BeanWrapperImpl is registered.

LocaleEditor

Can convert strings to Locale objects and vice versa (string format is [language]_[country]_[variant], same as toString() in Locale). Also accepts spaces as delimiters, as an alternative to underscores. By default, BeanWrapperImpl is registered.

PatternEditor

Can resolve strings to java.util.regex.Pattern objects and vice versa.

PropertiesEditor

Can convert strings (formatted in the format defined in the javadoc of the java.util.Properties class) into Propertiesobjects. By default, BeanWrapperImpl is registered.

StringTrimmerEditor

Property editor that trims lines. Optionally allows you to transform an empty string into a null value. Default is NOT registered - must be registered by user.

URLEditor

Can convert a string representation of some URL into an actual URL object. By default, BeanWrapperImpl is registered.

Spring uses java.beans.PropertyEditorManager for setting the search path for property editors that may be needed. The search path also includes sun.bean.editors, which includes PropertyEditor implementations for types such as Font, Color and most primitive types. Note that the standard JavaBeans class framework automatically discovers PropertyEditor classes (without having to register them explicitly) if they are in the same package as the class they process and have the same name as that class, with the addition of Editor. For example, you could have the following class and package structure, which would be enough to ensure that the SomethingEditor class is recognized and used as a PropertyEditor for properties of type Something.

com
  chank
    pop
      Something
      SomethingEditor //  PropertyEditor for class Something

Note that in this case you can also use the standard BeanInfo mechanism of the JavaBeans class (described to some extent by here). The following example uses the BeanInfo mechanism to explicitly register one or more PropertyEditor instances with the properties of the associated class:

com
  chank
    pop
      Something
      SomethingBeanInfo // BeanInfo for class Something

The following Java source code for The referencing class SomethingBeanInfo binds the CustomNumberEditor to the age property of the Something class:

Java
public class SomethingBeanInfo extends SimpleBeanInfo {
    public PropertyDescriptor[] getPropertyDescriptors() {
        try {
            final PropertyEditor numberPE = new CustomNumberEditor(Integer.class, true);
            PropertyDescriptor ageDescriptor = new PropertyDescriptor("age", Something.class) {
                @Override
                public PropertyEditor createPropertyEditor(Object bean) {
                    return numberPE;
                }
            };
            return new PropertyDescriptor[] { ageDescriptor };
        }
        catch (IntrospectionException ex) {
            throw new Error(ex.toString());
        }
    }
}
Kotlin
class SomethingBeanInfo : SimpleBeanInfo() {
    override fun getPropertyDescriptors(): Array {
        try {
            val numberPE = CustomNumberEditor(Int::class.java, true)
            val ageDescriptor = object : PropertyDescriptor("age", Something::class.java) {
                override fun createPropertyEditor(bean: Any): PropertyEditor {
                    return numberPE
                }
            }
            return arrayOf(ageDescriptor)
        } catch (ex: IntrospectionException) {
            throw Error(ex.toString())
        }
    }
}

Registering additional implementations of the special PropertyEditor

When specifying bean properties as string values, the Spring IoC container ends up using standard JavaBeans PropertyEditor implementations to convert those strings into a complex property type. Spring pre-registers a number of custom PropertyEditor implementations (for example, to convert a class name expressed as a string to a Class object). Additionally, the standard PropertyEditor lookup mechanism in JavaBeans allows you to name the PropertyEditor for a class appropriately and place it in the same package as the class for which it provides support, so that it could be found automatically.

If there is a need to register other custom PropertyEditors, several mechanisms are available. The most manual approach, and not generally considered convenient or recommended, is to use the registerCustomEditor() method of the ConfigurableBeanFactory interface, provided that there is a reference to BeanFactory. Another (slightly more convenient) mechanism is to use a specialized bean factory post-processor called CustomEditorConfigurer. Although you cannot use bean factory postprocessors using the BeanFactory implementation, the CustomEditorConfigurer has nested property setting, so we strongly recommend using it with the ApplicationContext in cases where in which it can be deployed in the same way as any other bean and in which it can be automatically discovered and applied.

Note that all bean factories and application contexts automatically use a number of built-in property editors through the use of BeanWrapper to handle property conversions. In addition, ApplicationContextalso overrides or adds additional editors to handle resource lookups according to the specific application context type.

Standard instances of the PropertyEditor class of JavaBeans are used to converting property values expressed as strings to the actual complex property type. You can use CustomEditorConfigurer, a bean factory post-processor, to conveniently add support for additional PropertyEditor instances in the ApplicationContext.

Consider the following example, which defines a custom class ExoticType and another class DependsOnExoticType that needs to set ExoticType as a property:

Java
package example;
public class ExoticType {
    private String name;
    public ExoticType(String name) {
        this.name = name;
    }
}
public class DependsOnExoticType {
    private ExoticType type;
    public void setType(ExoticType type) {
        this.type = type;
    }
}
Kotlin
package example
class ExoticType(val name: String)
class DependsOnExoticType {
    var type: ExoticType? = null
}

If everything is configured correctly, we need to be able to assign a type property to a string, which the PropertyEditor will convert to a real instance of ExoticType. The following bean definition shows how to establish this relationship:

<bean id="sample" class="example.DependsOnExoticType">
    <property name="type" value="aNameForExoticType"/>
</bean>

The PropertyEditor implementation might look like this:

Java
// converts the string representation to an ExoticType object
package example;
public class ExoticTypeEditor extends PropertyEditorSupport {
    public void setAsText(String text) {
        setValue(new ExoticType(text.toUpperCase()));
    }
}
Kotlin
// Converts a string representation to an ExoticType object
package example
import java.beans.PropertyEditorSupport
class ExoticTypeEditor : PropertyEditorSupport() {
    override fun setAsText(text: String) {
        value = ExoticType(text.toUpperCase())
    }
}

Finally, in the following The example shows how to use the CustomEditorConfigurer to register a new PropertyEditor with the ApplicationContext, which can then use it as needed:

<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
    <property name="customEditors">
        <map>
            <entry key="example.ExoticType" value="example.ExoticTypeEditor"/>
        </map>
    </property>
</bean>
Using PropertyEditorRegistrar

Another mechanism for registering property editors in a Spring container is to create and use PropertyEditorRegistrar. This interface is especially useful if you want to use the same set of property editors in several different situations. You can write a corresponding registrar and reuse it in each case. PropertyEditorRegistrar instances operate in conjunction with the PropertyEditorRegistry interface, which is implemented by Spring's BeanWrapper (and DataBinder). PropertyEditorRegistrar instances are especially useful when used in conjunction with the CustomEditorConfigurer, which exposes the setPropertyEditorRegistrars(..) property. PropertyEditorRegistrar instances added to CustomEditorConfigurer this way can be easily used with DataBinder and Spring MVC controllers. In addition, this avoids the need for synchronization in custom editors: PropertyEditorRegistrar is expected to create new instances of PropertyEditor each time a bean is created.

In the following The example shows how to create your own implementation of PropertyEditorRegistrar:

Java
package com.foo.editors.spring;
public final class CustomPropertyEditorRegistrar implements PropertyEditorRegistrar {
    public void registerCustomEditors(PropertyEditorRegistry registry) {
        // expect new PropertyEditor instances to be created
        registry.registerCustomEditor(ExoticType.class, new ExoticTypeEditor());
        // in this case, you can register as many custom property editors as required...
    }
}
Kotlin
package com.foo.editors.spring
import org.springframework.beans.PropertyEditorRegistrar
import org.springframework.beans.PropertyEditorRegistrar
class CustomPropertyEditorRegistrar : PropertyEditorRegistrar {
    override fun registerCustomEditors(registry: PropertyEditorRegistry) {
        // expect new instances of PropertyEditor registry to be created
        register.CustomEditor(ExoticType::class.java, ExoticTypeEditor())
        // in this case, you can register as many custom property editors as you need...
    }
}

See also an example implementation of PropertyEditorRegistrar in org.springframework.beans.support.ResourceEditorRegistrar. Notice how in its implementation of the registerCustomEditors(...) method it creates new instances of each property editor.

The following example shows how to configure the CustomEditorConfigurer and implement into it an instance of our CustomPropertyEditorRegistrar:

<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
    <property name="propertyEditorRegistrars">
        <list>
            <ref bean="customPropertyEditorRegistrar"/>
        </list>
    </property>
</bean>
<bean id="customPropertyEditorRegistrar"
    class="com.foo.editors.spring.CustomPropertyEditorRegistrar"/>

Finally (and slightly off topic this chapter) for those of you using the MVC web framework from Spring, using PropertyEditorRegistrar in combination with data binding controllers in a web application can be very convenient. In the following example uses PropertyEditorRegistrar in the implementation of the @InitBinder method:

Java
@Controller
public class RegisterUserController {
    private final PropertyEditorRegistrar customPropertyEditorRegistrar;
    RegisterUserController(PropertyEditorRegistrar propertyEditorRegistrar) {
        this.customPropertyEditorRegistrar = propertyEditorRegistrar;
    }
    @InitBinder
    void initBinder(WebDataBinder binder) {
        this.customPropertyEditorRegistrar.registerCustomEditors(binder);
    }
    // other methods related to user registration
}
Kotlin
@Controller
class RegisterUserController(
    private val customPropertyEditorRegistrar: PropertyEditorRegistrar) {
    @InitBinder
    fun initBinder(binder: WebDataBinder) {
        this.customPropertyEditorRegistrar.registerCustomEditors(binder)
    }
    // other methods related to user registration
}

This style of PropertyEditor registration can keep the code concise (the implementation of the @InitBinder method takes just one line) and allows you to encapsulate the general PropertyEditor registration code in a class, and then allow many controllers to share it.

Comments
  • Popular
  • New
  • Old
You must be signed in to leave a comment
This page doesn't have any comments yet