`

spring3 Validation, Data Binding, and Type Conversion

阅读更多

5. Validation, Data Binding, and Type Conversion

5.1 Introduction

There are pros and cons for considering validation as business logic, and Spring offers a design for validation (and data binding) that does not exclude either one of them. Specifically validation should not be tied to the web tier, should be easy to localize and it should be possible to plug in any validator available. Considering the above, Spring has come up with a Validator interface that is both basic ands eminently usable in every layer of an application.

Data binding is useful for allowing user input to be dynamically bound to the domain model of an application (or whatever objects you use to process user input). Spring provides the so-called DataBinder to do exactly that. The Validator and the DataBinder make up the validation package, which is primarily used in but not limited to the MVC framework.

The BeanWrapper is a fundamental concept in the Spring Framework and is used in a lot of places. However, you probably will not have the need to use the BeanWrapper directly. Because this is reference documentation however, we felt that some explanation might be in order. We will explain the BeanWrapper in this chapter since, if you were going to use it at all, you would most likely do so when trying to bind data to objects.

Spring's DataBinder and the lower-level BeanWrapper both use PropertyEditors to parse and format property values. The PropertyEditor concept is part of the JavaBeans specification, and is also explained in this chapter. Spring 3 introduces a "core.convert" package that provides a general type conversion facility, as well as a higher-level "format" package for formatting UI field values. These new packages may be used as simpler alternatives to PropertyEditors, and will also be discussed in this chapter.

5.2 Validation using Spring's Validator interface

Spring features a Validator interface that you can use to validate objects. The Validator interface works using an Errors object so that while validating, validators can report validation failures to the Errors object.

Let's consider a small data object:

public
 class
 Person {

  private
 String name;
  private
 int
 age;

  // the usual getters and setters...

}

We're going to provide validation behavior for the Person class by implementing the following two methods of the org.springframework.validation.Validator interface:

  • supports(Class) - Can this Validator validate instances of the supplied Class ?

  • validate(Object, org.springframework.validation.Errors) - validates the given object and in case of validation errors, registers those with the given Errors object

 

Implementing a Validator is fairly straightforward, especially when you know of the ValidationUtils helper class that the Spring Framework also provides.

public
 class
 PersonValidator implements
 Validator {

    /**
    * This Validator validates just Person instances
    */

    public
 boolean
 supports(Class clazz) {
        return
 Person.class
.equals(clazz);
    }

    public
 void
 validate(Object obj, Errors e) {
        ValidationUtils.rejectIfEmpty(e, "name"
, "name.empty"
);
        Person p = (Person) obj;
        if
 (p.getAge() < 0) {
            e.rejectValue("age"
, "negativevalue"
);
        } else
 if
 (p.getAge() > 110) {
            e.rejectValue("age"
, "too.darn.old"
);
        }
    }
}

As you can see, the static rejectIfEmpty(..) method on the ValidationUtils class is used to reject the 'name' property if it is null or the empty string. Have a look at the Javadoc for the ValidationUtils class to see what functionality it provides besides the example shown previously.

While it is certainly possible to implement a single Validator class to validate each of the nested objects in a rich object, it may be better to encapsulate the validation logic for each nested class of object in its own Validator implementation. A simple example of a 'rich' object would be a Customer that is composed of two String properties (a first and second name) and a complex Address object. Address objects may be used independently of Customer objects, and so a distinct AddressValidator has been implemented. If you want your CustomerValidator to reuse the logic contained within the AddressValidator class without resorting to copy-and-paste, you can dependency-inject or instantiate an AddressValidator within your CustomerValidator , and use it like so:

public
 class
 CustomerValidator implements
 Validator {

    private
 final
 Validator addressValidator;

    public
 CustomerValidator(Validator addressValidator) {
        if
 (addressValidator == null) {
            throw
 new
 IllegalArgumentException(
              "The supplied [Validator] is required and must not be null."
);
        }
        if
 (!addressValidator.supports(Address.class
)) {
            throw
 new
 IllegalArgumentException(
              "The supplied [Validator] must support the validation of [Address] instances."
);
        }
        this
.addressValidator = addressValidator;
    }

    /**
    * This Validator validates Customer instances, and any subclasses of Customer too
    */

    public
 boolean
 supports(Class clazz) {
        return
 Customer.class
.isAssignableFrom(clazz);
    }

    public
 void
 validate(Object target, Errors errors) {
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName"
, "field.required"
);
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "surname"
, "field.required"
);
        Customer customer = (Customer) target;
        try
 {
            errors.pushNestedPath("address"
);
            ValidationUtils.invokeValidator(this
.addressValidator, customer.getAddress(), errors);
        } finally
 {
            errors.popNestedPath();
        }
    }
}

Validation errors are reported to the Errors object passed to the validator. In case of Spring Web MVC you can use <spring:bind/> tag to inspect the error messages, but of course you can also inspect the errors object yourself. More information about the methods it offers can be found from the Javadoc.

5.3 Resolving codes to error messages

We've talked about databinding and validation. Outputting messages corresponding to validation errors is the last thing we need to discuss. In the example we've shown above, we rejected the name and the age field. If we're going to output the error messages by using a MessageSource , we will do so using the error code we've given when rejecting the field ('name' and 'age' in this case). When you call (either directly, or indirectly, using for example the ValidationUtils class) rejectValue or one of the other reject methods from the Errors interface, the underlying implementation will not only register the code you've passed in, but also a number of additional error codes. What error codes it registers is determined by the MessageCodesResolver that is used. By default, the DefaultMessageCodesResolver is used, which for example not only registers a message with the code you gave, but also messages that include the field name you passed to the reject method. So in case you reject a field using rejectValue("age", "too.darn.old") , apart from the too.darn.old code, Spring will also register too.darn.old.age and too.darn.old.age.int (so the first will include the field name and the second will include the type of the field); this is done as a convenience to aid developers in targeting error messages and suchlike.

More information on the MessageCodesResolver and the default strategy can be found online with the Javadocs for MessageCodesResolver and DefaultMessageCodesResolver respectively.

5.4 Bean manipulation and the BeanWrapper

The org.springframework.beans package adheres to the JavaBeans standard provided by Sun. A JavaBean is simply a class with a default no-argument constructor, which follows a naming convention where (by way of an example) a property named bingoMadness would have a setter method setBingoMadness(..) and a getter method getBingoMadness() . For more information about JavaBeans and the specification, please refer to Sun's website ( java.sun.com/products/javabeans ).

One quite important class in the beans package is the BeanWrapper interface and its corresponding implementation (BeanWrapperImpl ). As quoted from the Javadoc, the BeanWrapper offers functionality to set and get property values (individually or in bulk), get property descriptors, and to query properties to determine if they are readable or writable. Also, the BeanWrapper offers support for nested properties, enabling the setting of properties on sub-properties to an unlimited depth. Then, the BeanWrapper supports the ability to add standard JavaBeans PropertyChangeListeners and VetoableChangeListeners , without the need for supporting code in the target class. Last but not least, the BeanWrapper provides support for the setting of indexed properties. The BeanWrapper usually isn't used by application code directly, but by the DataBinder and the BeanFactory .

The way the BeanWrapper works is partly indicated by its name: it wraps a bean to perform actions on that bean, like setting and retrieving properties.

5.4.1 Setting and getting basic and nested properties

Setting and getting properties is done using the setPropertyValue(s) and getPropertyValue(s) methods that both come with a couple of overloaded variants. They're all described in more detail in the Javadoc Spring comes with. What's important to know is that there are a couple of conventions for indicating properties of an object. A couple of examples:

Table 5.1. Examples of properties

Expression Explanation
name Indicates the property name corresponding to the methods getName() or isName() and setName(..)
account.name Indicates the nested property name of the property account corresponding e.g. to the methods getAccount().setName() or getAccount().getName()
account[2] Indicates the third element of the indexed property account . Indexed properties can be of type array , list or other naturally ordered collection
account[COMPANYNAME] Indicates the value of the map entry indexed by the key COMPANYNAME of the Map property account

Below you'll find some examples of working with the BeanWrapper to get and set properties.

(This next section is not vitally important to you if you're not planning to work with the BeanWrapper directly. If you're just using the DataBinder and the BeanFactory and their out-of-the-box implementation, you should skip ahead to the section about PropertyEditors .)

Consider the following two classes:

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;
    }
}
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;
    }
}

The following code snippets show some examples of how to retrieve and manipulate some of the properties of instantiated Companies and Employees :

BeanWrapper company = 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);

// ok, let's create the director and tie it to the company:

BeanWrapper jim = BeanWrapperImpl(new
 Employee());
jim.setPropertyValue("name"
, "Jim Stravinsky"
);
company.setPropertyValue("managingDirector"
, jim.getWrappedInstance());

// retrieving the salary of the managingDirector through the company

Float salary = (Float) company.getPropertyValue("managingDirector.salary"
);

5.4.2 Built-in PropertyEditor implementations

Spring uses the concept of PropertyEditors to effect the conversion between an Object and a String . If you think about it, it sometimes might be handy to be able to represent properties in a different way than the object itself. For example, a Date can be represented in a human readable way (as the String '2007-14-09 '), while we're still able to convert the human readable form back to the original date (or even better: convert any date entered in a human readable form, back to Date objects). This behavior can be achieved by registering custom editors , of type java.beans.PropertyEditor . Registering custom editors on a BeanWrapper or alternately in a specific IoC container as mentioned in the previous chapter, gives it the knowledge of how to convert properties to the desired type. Read more about PropertyEditors in the Javadoc of the java.beans package provided by Sun.

A couple of examples where property editing is used in Spring:

  • setting properties on beans is done using PropertyEditors . When mentioning java.lang.String as the value of a property of some bean you're declaring in XML file, Spring will (if the setter of the corresponding property has a Class -parameter) use the ClassEditor to try to resolve the parameter to a Class object.

  • parsing HTTP request parameters in Spring's MVC framework is done using all kinds of PropertyEditors that you can manually bind in all subclasses of the CommandController .

 

Spring has a number of built-in PropertyEditors to make life easy. Each of those is listed below and they are all located in the org.springframework.beans.propertyeditors package. Most, but not all (as indicated below), are registered by default by BeanWrapperImpl . Where the property editor is configurable in some fashion, you can of course still register your own variant to override the default one:

Table 5.2. Built-in PropertyEditors

Class Explanation
ByteArrayPropertyEditor Editor for byte arrays. Strings will simply be converted to their corresponding byte representations. Registered by default by BeanWrapperImpl .
ClassEditor Parses Strings representing classes to actual classes and the other way around. When a class is not found, an IllegalArgumentException is thrown. Registered by default by BeanWrapperImpl .
CustomBooleanEditor Customizable property editor for Boolean properties. Registered by default by BeanWrapperImpl , but, can be overridden by registering custom instance of it as custom editor.
CustomCollectionEditor Property editor for Collections, converting any source Collection to a given target Collection type.
CustomDateEditor Customizable property editor for java.util.Date, supporting a custom DateFormat. NOT registered by default. Must be user registered as needed with appropriate format.
CustomNumberEditor Customizable property editor for any Number subclass like Integer , Long , Float , Double . Registered by default by BeanWrapperImpl , but can be overridden by registering custom instance of it as a custom editor.
FileEditor Capable of resolving Strings to java.io.File objects. Registered by default by BeanWrapperImpl .
InputStreamEditor One-way property editor, capable of taking a text string and producing (via an intermediate ResourceEditor and Resource ) an InputStream , so InputStream properties may be directly set as Strings. Note that the default usage will not close the InputStream for you! Registered by default by BeanWrapperImpl .
LocaleEditor Capable of resolving Strings to Locale objects and vice versa (the String format is [language]_[country]_[variant], which is the same thing the toString() method of Locale provides). Registered by default by BeanWrapperImpl .
PatternEditor Capable of resolving Strings to JDK 1.5 Pattern objects and vice versa.
PropertiesEditor Capable of converting Strings (formatted using the format as defined in the Javadoc for the java.lang.Properties class) to Properties objects. Registered by default by BeanWrapperImpl .
StringTrimmerEditor Property editor that trims Strings. Optionally allows transforming an empty string into a null value. NOT registered by default; must be user registered as needed.
URLEditor Capable of resolving a String representation of a URL to an actual URL object. Registered by default by BeanWrapperImpl .

Spring uses the java.beans.PropertyEditorManager to set the search path for property editors that might be needed. The search path also includes sun.bean.editors , which includes PropertyEditor implementations for types such as Font , Color , and most of the primitive types. Note also that the standard JavaBeans infrastructure will automatically discover PropertyEditor classes (without you having to register them explicitly) if they are in the same package as the class they handle, and have the same name as that class, with 'Editor' appended; for example, one could have the following class and package structure, which would be sufficient for the FooEditor class to be recognized and used as the PropertyEditor for Foo -typed properties.

com
  chank
    pop
      Foo
      FooEditor   // the PropertyEditor
 for the Foo
 class

Note that you can also use the standard BeanInfo JavaBeans mechanism here as well (described in not-amazing-detail here ). Find below an example of using the BeanInfo mechanism for explicitly registering one or more PropertyEditor instances with the properties of an associated class.

com
  chank
    pop
      Foo
      FooBeanInfo   // the BeanInfo
 for the Foo
 class

Here is the Java source code for the referenced FooBeanInfo class. This would associate a CustomNumberEditor with the age property of the Foo class.

public
 class
 FooBeanInfo extends
 SimpleBeanInfo {

    public
 PropertyDescriptor[] getPropertyDescriptors() {
        try
 {
            final
 PropertyEditor numberPE = new
 CustomNumberEditor(Integer.class
, true);
            PropertyDescriptor ageDescriptor = new
 PropertyDescriptor("age"
, Foo.class
) {
                public
 PropertyEditor createPropertyEditor(Object bean) {
                    return
 numberPE;
                };
            };
            return
 new
 PropertyDescriptor[] { ageDescriptor };
        }
        catch
 (IntrospectionException ex) {
            throw
 new
 Error(ex.toString());
        }
    }
}

5.4.2.1 Registering additional custom PropertyEditors

When setting bean properties as a string value, a Spring IoC container ultimately uses standard JavaBeans PropertyEditors to convert these Strings to the complex type of the property. Spring pre-registers a number of custom PropertyEditors (for example, to convert a classname expressed as a string into a real Class object). Additionally, Java's standard JavaBeans PropertyEditor lookup mechanism allows a PropertyEditor for a class simply to be named appropriately and placed in the same package as the class it provides support for, to be found automatically.

If there is a need to register other custom PropertyEditors , there are several mechanisms available. The most manual approach, which is not normally convenient or recommended, is to simply use the registerCustomEditor() method of the ConfigurableBeanFactory interface, assuming you have a BeanFactory reference. Another, slightly more convenient, mechanism is to use a special bean factory post-processor called CustomEditorConfigurer . Although bean factory post-processors can be used with BeanFactory implementations, the CustomEditorConfigurer has a nested property setup, so it is strongly recommended that it is used with the ApplicationContext , where it may be deployed in similar fashion to any other bean, and automatically detected and applied.

Note that all bean factories and application contexts automatically use a number of built-in property editors, through their use of something called a BeanWrapper to handle property conversions. The standard property editors that the BeanWrapper registers are listed in the previous section . Additionally, ApplicationContexts also override or add an additional number of editors to handle resource lookups in a manner appropriate to the specific application context type.

Standard JavaBeans PropertyEditor instances are used to convert property values expressed as strings to the actual complex type of the property. CustomEditorConfigurer , a bean factory post-processor, may be used to conveniently add support for additional PropertyEditor instances to an ApplicationContext .

Consider a user class ExoticType , and another class DependsOnExoticType which needs ExoticType set 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;
    }
}

When things are properly set up, we want to be able to assign the type property as a string, which a PropertyEditor will behind the scenes convert into an actual ExoticType instance:

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

The PropertyEditor implementation could look similar to this:

// converts string representation to ExoticType object

package
 example;

public
 class
 ExoticTypeEditor extends
 PropertyEditorSupport {

    public
 void
 setAsText(String text) {
        setValue(new
 ExoticType(text.toUpperCase()));
    }
}

Finally, we use CustomEditorConfigurer to register the new PropertyEditor with the ApplicationContext , which will then be able to 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 PropertyEditorRegistrars

Another mechanism for registering property editors with the Spring container is to create and use a PropertyEditorRegistrar . This interface is particularly useful when you need to use the same set of property editors in several different situations: write a corresponding registrar and reuse that in each case. PropertyEditorRegistrars work in conjunction with an interface called PropertyEditorRegistry , an interface that is implemented by the Spring BeanWrapper (and DataBinder ). PropertyEditorRegistrars are particularly convenient when used in conjunction with the CustomEditorConfigurer (introduced here ), which exposes a property called setPropertyEditorRegistrars(..) : PropertyEditorRegistrars added to a CustomEditorConfigurer in this fashion can easily be shared with DataBinder and Spring MVC Controllers . Furthermore, it avoids the need for synchronization on custom editors: a PropertyEditorRegistrar is expected to create fresh PropertyEditor instances for each bean creation attempt.

Using a PropertyEditorRegistrar is perhaps best illustrated with an example. First off, you need to create your own PropertyEditorRegistrar implementation:

package
 com.foo.editors.spring;

public
 final
 class
 CustomPropertyEditorRegistrar implements
 PropertyEditorRegistrar {

    public
 void
 registerCustomEditors(PropertyEditorRegistry registry) {

        // it is expected that new PropertyEditor instances are created

        registry.registerCustomEditor(ExoticType.class
, new
 ExoticTypeEditor());

        // you could register as many custom property editors as are required here...

    }
}

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

Next we configure a CustomEditorConfigurer and inject an instance of our CustomPropertyEditorRegistrar into it:

<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 in a bit of a departure from the focus of this chapter, for those of you using Spring's MVC web framework , using PropertyEditorRegistrars in conjunction with data-binding Controllers (such as SimpleFormController ) can be very convenient. Find below an example of using a PropertyEditorRegistrar in the implementation of an initBinder(..) method:

public
 final
 class
 RegisterUserController extends
 SimpleFormController {

    private
 final
 PropertyEditorRegistrar customPropertyEditorRegistrar;

    public
 RegisterUserController(PropertyEditorRegistrar propertyEditorRegistrar) {
        this
.customPropertyEditorRegistrar = propertyEditorRegistrar;
    }

    protected
 void
 initBinder(HttpServletRequest request, ServletRequestDataBinder binder)
                        throws
 Exception {
        this
.customPropertyEditorRegistrar.registerCustomEditors(binder);
    }

    // other methods to do with registering a User

}

This style of PropertyEditor registration can lead to concise code (the implementation of initBinder(..) is just one line long!), and allows common PropertyEditor registration code to be encapsulated in a class and then shared amongst as many Controllers as needed.

5.5 Spring 3 Type Conversion

Spring 3 introduces a core.convert package that provides a general type conversion system. The system defines an SPI to implement type conversion logic, as well as an API to execute type conversions at runtime. Within a Spring container, this system can be used as an alternative to PropertyEditors to convert externalized bean property value strings to required property types. The public API may also be used anywhere in your application where type conversion is needed.

5.5.1 Converter SPI

The SPI to implement type conversion logic is simple and strongly typed:

package
 org.springframework.core.convert.converter;

public
 interface
 Converter<S, T> {

    T convert(S source);

}

To create your own Converter, simply implement the interface above. Parameterize S as the type you are converting from, and T as the type you are converting to. For each call to convert(S), the source argument is guaranteed to be NOT null. Your Converter may throw any Exception if conversion fails. An IllegalArgumentException should be thrown to report an invalid source value. Take care to ensure your Converter implementation is thread-safe.

Several converter implementations are provided in the core.convert.support package as a convenience. These include converters from Strings to Numbers and other common types. Consider StringToInteger as an example Converter implementation:

package
 org.springframework.core.convert.support;

final
 class
 StringToInteger implements
 Converter<String, Integer> {

    public
 Integer convert(String source) {
        return
 Integer.valueOf(source);
    }

}

5.5.2 ConverterFactory

When you need to centralize the conversion logic for an entire class hierarchy, for example, when converting from String to java.lang.Enum objects, implement ConverterFactory :

package
 org.springframework.core.convert.converter;

public
 interface
 ConverterFactory<S, R> {

    <T extends
 R> Converter<S, T> getConverter(Class<T> targetType);

}

Parameterize S to be the type you are converting from and R to be the base type defining the range of classes you can convert to. Then implement getConverter(Class<T>), where T is a subclass of R.

Consider the StringToEnum ConverterFactory as an example:

package
 org.springframework.core.convert.support;

final
 class
 StringToEnumConverterFactory implements
 ConverterFactory<String, Enum> {

    public
 <T extends
 Enum> Converter<String, T> getConverter(Class<T> targetType) {
        return
 new
 StringToEnumConverter(targetType);
    }

    private
 final
 class
 StringToEnumConverter<T extends
 Enum> implements
 Converter<String, T> {

        private
 Class<T> enumType;

        public
 StringToEnumConverter(Class<T> enumType) {
            this
.enumType = enumType;
        }

        public
 T convert(String source) {
            return
 (T) Enum.valueOf(this
.enumType, source.trim());
        }
    }
}

5.5.3 GenericConverter

When you require a sophisticated Converter implementation, consider the GenericConverter interface. With a more flexible but less strongly typed signature, a GenericConverter supports converting between multiple source and target types. In addition, a GenericConverter makes available source and target field context you can use when implementing your conversion logic. Such context allows a type conversion to be driven by a field annotation, or generic information declared on a field signature.

package
 org.springframework.core.convert.converter;

public
 interface
 GenericConverter {

    public
 Set<ConvertiblePair> getConvertibleTypes();

    Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);

}

To implement a GenericConverter, have getConvertibleTypes() return the supported source->target type pairs. Then implement convert(Object, TypeDescriptor, TypeDescriptor) to implement your conversion logic. The source TypeDescriptor provides access to the source field holding the value being converted. The target TypeDescriptor provides access to the target field where the converted value will be set.

A good example of a GenericConverter is a converter that converts between a Java Array and a Collection. Such an ArrayToCollectionConverter introspects the field that declares the target Collection type to resolve the Collection's element type. This allows each element in the source array to be converted to the Collection element type before the Collection is set on the target field.

Note
[Note]

Because GenericConverter is a more complex SPI interface, only use it when you need it. Favor Converter or ConverterFactory for basic type conversion needs.

5.5.3.1 ConditionalGenericConverter

Sometimes you only want a Converter to execute if a specific condition holds true. For example, you might only want to execute a Converter if a specific annotation is present on the target field. Or you might only want to execute a Converter if a specific method, such as static valueOf method, is defined on the target class. ConditionalGenericConverter is an subinterface of GenericConverter that allows you to define such custom matching criteria:

public
 interface
 ConditionalGenericConverter extends
 GenericConverter {

    boolean
 matches(TypeDescriptor sourceType, TypeDescriptor targetType);

}

A good example of a ConditionalGenericConverter is an EntityConverter that converts between an persistent entity identifier and an entity reference. Such a EntityConverter might only match if the target entity type declares a static finder method e.g. findAccount(Long). You would perform such a finder method check in the implementation of matches(TypeDescriptor, TypeDescriptor).

5.5.4 ConversionService API

The ConversionService defines a unified API for executing type conversion logic at runtime. Converters are often executed behind this facade interface:

package
 org.springframework.core.convert;

public
 interface
 ConversionService {

    boolean
 canConvert(Class<?> sourceType, Class<?> targetType);

    <T> T convert(Object source, Class<T> targetType);

    boolean
 canConvert(TypeDescriptor sourceType, TypeDescriptor targetType);

    Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);

}

Most ConversionService implementations also implement ConverterRegistry , which provides an SPI for registering converters. Internally, a ConversionService implementation delegates to its registered converters to carry out type conversion logic.

A robust ConversionService implementation is provided in the core.convert.support package. GenericConversionService is the general-purpose implementation suitable for use in most environments. ConversionServiceFactory provides a convenient factory for creating common ConversionService configurations.

5.5.5 Configuring a ConversionService

A ConversionService is a stateless object designed to be instantiated at application startup, then shared between multiple threads. In a Spring application, you typically configure a ConversionService instance per Spring container (or ApplicationContext). That ConversionService will be picked up by Spring and then used whenever a type conversion needs to be performed by the framework. You may also inject this ConversionService into any of your beans and invoke it directly.

Note
[Note]

If no ConversionService is registered with Spring, the original PropertyEditor-based system is used.

To register a default ConversionService with Spring, add the following bean definition with id conversionService :

<h

  


  
分享到:
评论

相关推荐

    spring framework4

    Core technologies: dependency injection, events, resources, i18n, validation, data binding, type conversion, SpEL, AOP. Testing: mock objects, TestContext framework, Spring MVC Test, WebTestClient. ...

    spring-boot-reference.pdf

    29.3. JPA and “Spring Data” 29.3.1. Entity Classes 29.3.2. Spring Data JPA Repositories 29.3.3. Creating and Dropping JPA Databases 29.3.4. Open EntityManager in View 29.4. Using H2’s Web Console ...

    spring-framework-4-reference

    Validation, Data Binding, and Type Conversion部分讲解了Spring中如何进行验证、数据绑定和类型转换。Spring提供了一个Validator接口,可以用来执行验证操作,并且可以将错误代码解析为错误信息。BeanWrapper类...

    spring4 中文API

    #### Validation, Data Binding, and Type Conversion - **Introduction**:概述验证、数据绑定和类型转换的基本概念。 - **Validation using Spring’s Validator interface**:介绍了如何使用Spring的Validator...

    spring MVC .docx

    10. **Conversion and Validation**: Spring MVC提供了数据转换和验证功能,如使用`@RequestParam`、`@PathVariable`注解自动转换参数,以及使用`@Valid`和`BindingResult`进行表单验证。 11. **Tiles or Layouts**...

    spring-frame-4-reference

    #### Validation, Data Binding, and Type Conversion Spring提供了强大的验证、数据绑定和类型转换功能,这对于Web开发尤为重要。主要包括以下几个方面: - **Validation using Spring’s Validator interface**...

    java8集合源码分析-thinking-in-spring:小马哥极客时间Spring编程思想示例项目

    校验(Validation) 数据绑定(Data Binding) 类型装换(Type Conversion) Spring 表达式(Spring Express Language) 面向切面编程(AOP) 数据存储(Data Access) JDBC 事务抽象(Transactions) DAO 支持(DAO Support) O/R...

    48 Spring展望及后续(未完)慕课专栏(1)1

    国际化(i18n)支持使应用能够适应不同地区的语言环境,数据校验(Validation)和数据绑定(Data Binding)确保了输入数据的准确性和一致性。类型转换(Type Conversion)系统则允许不同数据类型的自动转换,而...

    spring mvc

    9. **Conversion and Formatting**:Spring MVC有内置的转换服务,可以自动将不同类型的值转换为期望的类型,如日期、数字等。 Spring MVC与Mybatis的结合使用则进一步增强了应用程序的功能。Mybatis是一个轻量级的...

    Spring-Framework-4.x参考文档.pdf

    **3.3 Validation, Data Binding, and Type Conversion** - **3.3.1 验证介绍**: 介绍了 Spring 提供的验证支持。 - **3.3.2 使用 Spring 的 Validator 接口进行验证**: 详细解释了如何使用 Validator 接口进行数据...

    springMVC官方源码

    7. **Data Binding and Validation**: Spring MVC提供数据绑定功能,可以自动将请求参数绑定到Controller方法的参数上。同时,使用@Valid和Validator接口可以进行数据验证,确保输入的有效性。 8. **Conversion and...

    spring-framework-reference-4.1.2

    3. New Features and Enhancements in Spring Framework 4.0 ............................................ 17 3.1. Improved Getting Started Experience .........................................................

    spingMVCjar

    13. **Conversion and Validation**:Spring MVC提供了转换器和验证器,用于处理不同类型的数据输入和确保数据的正确性。 通过这些组件和机制,Spring MVC能够灵活地处理Web请求,提供高效的开发体验。在实际应用中...

    Beanutils.rar

    2. **验证和数据绑定(Validation and Data Binding)**:可以配合Java的校验框架(如Hibernate Validator)进行数据验证,然后将验证结果封装进Bean。 3. **减少代码冗余**:使用BeanUtils可以避免在控制器层手动...

    spring-framework-reference4.1.4

    3. New Features and Enhancements in Spring Framework 4.0 ............................................ 17 3.1. Improved Getting Started Experience .........................................................

    JavaServer Faces

    3. **数据绑定(Data Binding)**:JSF允许将UI组件的值与后端bean的属性直接绑定,使得数据在视图和模型之间自动同步。 4. **生命周期(Life Cycle)**:JSF有自己的一套处理请求、更新模型、渲染响应的生命周期。...

Global site tag (gtag.js) - Google Analytics