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:
Expression | Explanation |
---|---|
|
Specifies the |
|
Specifies the |
|
Specifies the third element of the indexed property |
|
Indicates the value of the associative array entry indexed by the |
(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:
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;
}
}
class Company {
var name: String? = null
var managingDirector: Employee? = null
}
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;
}
}
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
Company
and Employees
instances:
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");
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 useString
as the property value of any bean you declare in an XML file, Spring (if the corresponding property's setter has aClass
parameter) uses aClassEditor
to try to resolve the parameter to aClass
object.The parsing of HTTP request parameters in the Spring MVC framework is done using various implementations
PropertyEditor
, which can be manually bound in allCommandController
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:
Class | Explanation |
---|---|
|
Editor for byte arrays. Converts strings to their corresponding byte representations. By default, |
|
Parses strings representing classes into real classes and vice versa. If the class is not found, an
|
|
Custom property editor for |
|
Property editor for collections that converts any source |
|
Custom property editor for |
|
Customizable property editor for any |
|
Resolves strings to |
|
A unidirectional property editor that can accept a string and create (via an intermediate |
|
Can convert strings to |
|
Can resolve strings to |
|
Can convert strings (formatted in the format defined in the javadoc of the
|
|
Property editor that trims lines. Optionally allows you to transform an empty string into a
|
|
Can convert a string representation of some URL into an actual |
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:
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());
}
}
}
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, ApplicationContext
also
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:
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;
}
}
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:
// 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()));
}
}
// 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
:
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...
}
}
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:
@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
}
@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.
GO TO FULL VERSION