Spring study notes data binding, validation, BeanWrapper and property editor

Spring binding data, parity, the BeanWrapper, the Attribute Editor


Data Binding

Binding data (Data binding) is useful because it can dynamically to a user input domain model with the application (or the objects for processing user input your) bind. Spring provides the so-called DataBinder to complete this function. Validator and DataBinder up the validation package is mainly used in the Spring Framework MVC. Of course, they can also be used for other local needs.

Validation

From the beginning of Spring 4.0 version, Spring Framework supports Bean Vaildation 1.0 (JSR-303) and Bean Vaildation 1.1 (JSR-349)

Spring Validator interfaces using
the Spring framework provides an interface to the Validator object. You can use it to verify the object, when the Validator Errors by performing verification in order to work, Validator can verify that fail to report the error object.
Errors: for storing and exposure data bound to an object and associated error checking information.

public interface Validator {
    boolean supports(Class<?> var1);
    void validate(Object var1, Errors var2);
}

supports (Class var1): authentication parameters class supports validate the instance of this class.
validate (Object var1, Errors var2) : Verify the given object, if the validation error occurs, it will check all of the failures to the Errors object to

Examples

public class Person implements Validator {
	private String name;
	private int    age;

	@Override public boolean supports(Class<?> aClass) {
		return Person.class.equals(aClass);
	}

	@Override public void validate(Object o, Errors errors) {
		// 校验 name 属性是否为空
		ValidationUtils.rejectIfEmpty(errors, "name", "name.empty");
		Person p = (Person) o;
		// 校验 age 是否符合业务规则
		if (p.getAge() < 0) {
			errors.rejectValue("age", "negativevalue");
		} else if (p.getAge() > 110) {
			errors.rejectValue("age", "too.darn.old");
		}
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}
}

We used a static method rejectIfEmpty ValidationUtils in (...) to check on the name attribute, if the 'name' attribute is null or empty string, then it is rejected by the authentication.

Implement a single class Validator to verify their built-in property class is certainly possible, but is preferably a built-in class for each implement the following Validator. For example, a Customer class has two String properties fristName and secondName, there is a reference to the object attribute Address class. Address objects may be independent of the Customer object, so to achieve an independent AddressValidator. If you want your CustomerValidator to reuse the logic of the internal AddressValidator, but do not want to copy-paste, you can dependency injection AddressValidator objects in your CustomerValidator in, or create one.

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


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

Bean processing and BeanWrapper

org.springframework.beans package follows the standard JavaBean, JavaBean class is a default constructor has no parameters. Class attributes follow the naming convention, there getter setter methods. Which has such a property by the method will be called bingoMadness getBingoMadness and setBingoMadness.

org.springframework.beans package has a very important class BeanWrapper interface and its implementation class corresponding BeanWrapperImpl.
BeanWrapper provided to set and get property values (individually or in bulk), get property descriptors, the query read-only or writable and other functions, but also supports nested BeanWrapper properties.
BeanWrapper also supports the ability to add standard javabean PropertyChangeListeners and VetoableChangeListeners without the need for supporting code in the target class. BeanWrapper also provides a set of indexed properties. Under normal circumstances, we do not directly use BeanWrapper but DataBinder and BeanFactory applications.
BeanWrapper The name itself suggests its function: the behavior of a package of bean, such as setting and retrieving properties.

Set and retrieve attribute values and attribute references
set and get the properties by using setPropertyValue, setPropertyValues, getPropertyValue getPropertyValues and methods to complete.

Property Example

expression Explanation
name Show property name and method getName () isName, or () and the setName (...), respectively.
account.name Reference point attribute property account name, the corresponding getAccount (). SetName () and getAccount (). GetName ()
account[2] Reference attributes point to account third element, indexed property may be an array (Array), a list (list) or other naturally ordered.
account[COMPANYNAME] Map points to a COMPANYNAME of account entity key (key) value corresponding to

Sample Class

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

Examples of how to get and set properties of the above two examples of classes of Companies and Employees:

BeanWrapper company = new BeanWrapperImpl(new Company());
company.setPropertyValue("name", "Some Company Inc.");
PropertyValue value = new PropertyValue("name", "Some Company Inc.");
company.setPropertyValue(value);

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

Built-in PropertyEditor achieved
Spring use PropertyEditor between the object and for efficient conversion of String. For example, Date employer can more readily appreciated manner indicates (string: '2007-14-09'). At the same time we can understand that people are more likely to form into the original primitive type Date (or even for any date in the form of a human readable input can be converted into the corresponding Date object). If you need to do, then this behavior can be achieved by registering java.bean.propertyeditor type of custom editor.

Attribute editing is used in two ways:

  1. Use PropertyEditor to set bean properties. When using a String as the value of a property on a bean declared in an XML file, Spring (if the setter of the corresponding property of a class parameter) using ClassEditor try to resolve the parameter to a class object.
  2. Parse HTTP request parameters are implemented by using various PropertyEditor done, can be implemented manually bind these CommandController all subclasses of the MVC framework Spring's.

Spring has many PropertyEditor implementation, simplify our work. In org.springframework.beans.propertyeditors package. By default, most have a good default registered in BeanWrapperImpl implementation class.

class Explanation
ByteArrayPropertyEditor Editor for byte arrays. String will simply be converted to their corresponding byte form. In BeanWrapperImpl Registered by default better.
ClassEditor It will appear as a string of class names to be resolved real Class object or the other way around. When this Class is not found, it will throw an IllegalArgumentException exception in BeanWrapperImpl Registered by default better.
CustomBooleanEditor For Boolean properties custom property editor. Registered by default in BeanWrapperImpl good, but can be user-defined editor instance covering their behavior.
CustomCollectionEditor Collection (Collection) editor, any source Collection (Collection) is converted into a collection of objects of type of target.
CustomDateEditor For the custom property editor for java.util.Date, supporting a custom DateFormat user. BeanWrapperImpl default has not been registered, the user needs to register by specifying the appropriate format.
CustomNumberEditor Custom subclass Integer, Long, Float, Double, etc. Number of Property Editor. Registered by default in BeanWrapperImpl good, but can be user-defined editor instance covering their behavior.
FileEditor Capable of resolving Strings to java.io.File objects. Registered by default in BeanWrapperImpl good.
InputStreamEditor A one-way property editor, the text string and can be InputStream (through an intermediary ResourceEditor and Resource), so InputStream properties may be directly set as Strings. Note By default, this property will not close the InputStream for you. In BeanWrapperImpl Registered by default better.
LocaleEditor Mutual conversion between the String object and the object Locale. (A String is [language] [national] [variables], the same result that the Local object's toString () method obtained) in BeanWrapperImpl Registered by default better.
PatternEditor JDK may be converted to a string of Pattern object, and vice versa.
PropertiesEditor String Properties object can be converted to (by JavaDoc java.lang.Properties predetermined type of format). In BeanWrapperImpl Registered by default better.
StringTrimmerEditor One for trimming (trim) String type property editor, to convert an empty string having a null value option. The default is not registered, must be registered by the user when needed.
URLEditor String representation of a URL can be converted to a specific URL object. In BeanWrapperImpl Registered by default better.

Register a custom PropertyEditor
when the bean property to a string value, Spring IoC container ultimately uses the JavaBeans PropertyEditor standard type of complex string into these attributes. Spring pre-registered many custom PropertyEditor implementation.

If you want to register their own definition of PropertyEditor, then there are several different mechanisms:

  1. Most manual method (usually inconvenient and recommended) is to use ConfigurableBeanFactory interface registerCustomEditor () method. Although bean factory post-processors can be implemented with BeanFactory used together, but there is a reference to establish property. Therefore, it is strongly recommended approach is to use it together with the ApplicationContext. This would make it the same as any other bean in a similar way to deploy while being perceived and used containers.
  2. Using special bean factory named CustomEditorConfigurer processor
public class CustomEditorConfigurer implements BeanFactoryPostProcessor, Ordered {}

Examples

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

Next we need to declare the value of a specified string bean to set the type attribute in xml, then PropertyEditor will behind the scenes to help you convert it to actual objects ExoticType

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

Realization of PropertyEditor

public class ExoticTypeEditor extends PropertyEditorSupport {

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

Finally, register the new PropertyEditor the ApplicationContext use CustomEditorConfigurer, then ApplicationContext can use it according to the needs

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

Use PropertyEditorRegistrars
to register property editors Spring container to another way is to create and use PropertyEditorRegistrar. The interface is particularly useful when you (e.g., a corresponding write register may then reuse it in many cases) in different cases require the same property editor. PropertyEditorRegistrars PropertyEditorRegistry interfaces with work, a PropertyEditorRegistry interfaces implemented by the Spring of the BeanWrapper (and DataBinder). When used in conjunction with CustomEditorConfigurer, PropertyEditorRegistrars especially convenient, CustomEditorConfigurer exposes called setPropertyEditorRegistrars (...) method: In this case and DataBinder Spring MVC Controllers can easily share the added CustomEditorConfigurer PropertyEditorRegistrars. In addition, custom editors no longer need to be synchronized: the bean PropertyEditorRegistrar created each time creates a new PropertyEditor.

How to create your own PropertyEditorRegistrar achieved when implementing registerCustomEditors (...) method, which creates a new instance of each property editor.

public final class CustomPropertyEditorRegistrar implements PropertyEditorRegistrar {

    public void registerCustomEditors(PropertyEditorRegistry registry) {
        registry.registerCustomEditor(ExoticType.class, new ExoticTypeEditor());
    }
}

How to configure CustomEditorConfigurer and inject instances 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 (For Spring MVC web frame for) the data binding PropertyEditorRegistrars controller (such as The SimpleFormController) is easy to use in combination. The following example uses the achieving of an initBinder propertyeditorregistry (...) 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);
    }
}

This can make the code registration mode PropertyEditor more concise (of an initBinder (...) is implemented only one line), and allows common PropertyEditor registration code encapsulated in a class, then shared among as many as needed Controllers.

Guess you like

Origin blog.csdn.net/qq_16830879/article/details/92086705