diff --git a/src/asciidoc/core-validation.adoc b/src/asciidoc/core-validation.adoc index 5c95729ae3..d493a70b52 100644 --- a/src/asciidoc/core-validation.adoc +++ b/src/asciidoc/core-validation.adoc @@ -1,4 +1,3 @@ - [[validation]] = Validation, Data Binding, and Type Conversion @@ -172,6 +171,7 @@ methods it offers can be found in the javadocs. [[validation-conversion]] == 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 @@ -228,6 +228,7 @@ perform actions on that bean, like setting and retrieving properties. [[beans-beans-conventions]] === 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 javadocs Spring comes with. What's important @@ -701,6 +702,7 @@ registration code to be encapsulated in a class and then shared amongst as many [[core-convert]] == Spring 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 @@ -712,6 +714,7 @@ application where type conversion is needed. [[core-convert-Converter-API]] === Converter SPI + The SPI to implement type conversion logic is simple and strongly typed: [source,java,indent=0] @@ -722,7 +725,6 @@ The SPI to implement type conversion logic is simple and strongly typed: public interface Converter { T convert(S source); - } ---- @@ -751,7 +753,6 @@ Consider `StringToInteger` as an example for a typical `Converter` implementatio public Integer convert(String source) { return Integer.valueOf(source); } - } ---- @@ -759,6 +760,7 @@ Consider `StringToInteger` as an example for a typical `Converter` implementatio [[core-convert-ConverterFactory-SPI]] === 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`: @@ -771,7 +773,6 @@ example, when converting from String to java.lang.Enum objects, implement public interface ConverterFactory { Converter getConverter(Class targetType); - } ---- @@ -811,6 +812,7 @@ Consider the `StringToEnum` ConverterFactory as an example: [[core-convert-GenericConverter-SPI]] === 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 @@ -828,7 +830,6 @@ by a field annotation, or generic information declared on a field signature. public Set getConvertibleTypes(); Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType); - } ---- @@ -853,6 +854,7 @@ Favor Converter or ConverterFactory for basic type conversion needs. [[core-convert-ConditionalGenericConverter-SPI]] ==== 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, @@ -866,12 +868,9 @@ such as a `static valueOf` method, is defined on the target class. public interface ConditionalConverter { boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType); - } - public interface ConditionalGenericConverter - extends GenericConverter, ConditionalConverter { - + public interface ConditionalGenericConverter extends GenericConverter, ConditionalConverter { } ---- @@ -885,6 +884,7 @@ might only match if the target entity type declares a static finder method e.g. [[core-convert-ConversionService-API]] === ConversionService API + The ConversionService defines a unified API for executing type conversion logic at runtime. Converters are often executed behind this facade interface: @@ -919,6 +919,7 @@ creating common ConversionService configurations. [[core-convert-Spring-config]] === 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). @@ -971,6 +972,7 @@ In certain situations you may wish to apply formatting during conversion. See [[core-convert-programmatic-usage]] === Using a ConversionService programmatically + To work with a ConversionService instance programmatically, simply inject a reference to it like you would for any other bean: @@ -1024,6 +1026,7 @@ no need to create a specific converter to convert from a `Collection` of `S` to [[format]] == Spring Field Formatting + As discussed in the previous section, <> is a general-purpose type conversion system. It provides a unified ConversionService API as well as a strongly-typed Converter SPI for implementing conversion logic from one type @@ -1051,6 +1054,7 @@ ConversionService provides a unified type conversion API for both SPIs. [[format-Formatter-SPI]] === Formatter SPI + The Formatter SPI to implement field formatting logic is simple and strongly typed: [source,java,indent=0] @@ -1068,6 +1072,7 @@ Where Formatter extends from the Printer and Parser building-block interfaces: [subs="verbatim,quotes"] ---- public interface Printer { + String print(T fieldValue, Locale locale); } ---- @@ -1078,6 +1083,7 @@ Where Formatter extends from the Printer and Parser building-block interfaces: import java.text.ParseException; public interface Parser { + T parse(String clientValue, Locale locale) throws ParseException; } ---- @@ -1091,8 +1097,8 @@ should throw a ParseException or IllegalArgumentException if a parse attempt fai care to ensure your Formatter implementation is thread-safe. Several Formatter implementations are provided in `format` subpackages as a convenience. -The `number` package provides a `NumberFormatter`, `CurrencyFormatter`, and -`PercentFormatter` to format `java.lang.Number` objects using a `java.text.NumberFormat`. +The `number` package provides a `NumberStyleFormatter`, `CurrencyStyleFormatter`, and +`PercentStyleFormatter` to format `java.lang.Number` objects using a `java.text.NumberFormat`. The `datetime` package provides a `DateFormatter` to format `java.util.Date` objects with a `java.text.DateFormat`. The `datetime.joda` package provides comprehensive datetime formatting support based on the http://joda-time.sourceforge.net[Joda-Time library]. @@ -1131,7 +1137,6 @@ Consider `DateFormatter` as an example `Formatter` implementation: dateFormat.setLenient(false); return dateFormat; } - } ---- @@ -1142,6 +1147,7 @@ https://jira.spring.io/browse/SPR[jira.spring.io] to contribute. [[format-CustomFormatAnnotations]] === Annotation-driven Formatting + As you will see, field formatting can be configured by field type or annotation. To bind an Annotation to a formatter, implement AnnotationFormatterFactory: @@ -1157,7 +1163,6 @@ an Annotation to a formatter, implement AnnotationFormatterFactory: Printer getPrinter(A annotation, Class fieldType); Parser getParser(A annotation, Class fieldType); - } ---- @@ -1191,18 +1196,17 @@ specified: return configureFormatterFrom(annotation, fieldType); } - private Formatter configureFormatterFrom(NumberFormat annotation, - Class fieldType) { + private Formatter configureFormatterFrom(NumberFormat annotation, Class fieldType) { if (!annotation.pattern().isEmpty()) { - return new NumberFormatter(annotation.pattern()); + return new NumberStyleFormatter(annotation.pattern()); } else { Style style = annotation.style(); if (style == Style.PERCENT) { - return new PercentFormatter(); + return new PercentStyleFormatter(); } else if (style == Style.CURRENCY) { - return new CurrencyFormatter(); + return new CurrencyStyleFormatter(); } else { - return new NumberFormatter(); + return new NumberStyleFormatter(); } } } @@ -1218,13 +1222,13 @@ To trigger formatting, simply annotate fields with @NumberFormat: @NumberFormat(style=Style.CURRENCY) private BigDecimal decimal; - } ---- [[format-annotations-api]] ==== Format Annotation API + A portable format annotation API exists in the `org.springframework.format.annotation` package. Use @NumberFormat to format java.lang.Number fields. Use @DateTimeFormat to format java.util.Date, java.util.Calendar, java.util.Long, or Joda-Time fields. @@ -1239,7 +1243,6 @@ The example below uses @DateTimeFormat to format a java.util.Date as a ISO Date @DateTimeFormat(iso=ISO.DATE) private Date date; - } ---- @@ -1247,6 +1250,7 @@ The example below uses @DateTimeFormat to format a java.util.Date as a ISO Date [[format-FormatterRegistry-SPI]] === FormatterRegistry SPI + The FormatterRegistry is an SPI for registering formatters and converters. `FormattingConversionService` is an implementation of FormatterRegistry suitable for most environments. This implementation may be configured programmatically or @@ -1270,7 +1274,6 @@ Review the FormatterRegistry SPI below: void addFormatterForFieldType(Formatter formatter); void addFormatterForAnnotation(AnnotationFormatterFactory factory); - } ---- @@ -1286,6 +1289,7 @@ these rules once and they are applied whenever formatting is needed. [[format-FormatterRegistrar-SPI]] === FormatterRegistrar SPI + The FormatterRegistrar is an SPI for registering formatters and converters through the FormatterRegistry: @@ -1297,7 +1301,6 @@ FormatterRegistry: public interface FormatterRegistrar { void registerFormatters(FormatterRegistry registry); - } ---- @@ -1320,6 +1323,7 @@ See <> in the Spring MVC chapter. [[format-configuring-formatting-globaldatetimeformat]] == Configuring a global date & time format + By default, date and time fields that are not annotated with `@DateTimeFormat` are converted from strings using the `DateFormat.SHORT` style. If you prefer, you can change this by defining your own global format. @@ -1413,6 +1417,7 @@ For XML you should use the `'conversion-service'` attribute of the [[validation-beanvalidation]] == Spring Validation + Spring 3 introduces several enhancements to its validation support. First, the JSR-303 Bean Validation API is now fully supported. Second, when used programmatically, Spring's DataBinder can now validate objects as well as bind to them. Third, Spring MVC now has @@ -1422,6 +1427,7 @@ support for declaratively validating `@Controller` inputs. [[validation-beanvalidation-overview]] === Overview of the JSR-303 Bean Validation API + JSR-303 standardizes validation constraint declaration and metadata for the Java platform. Using this API, you annotate domain model properties with declarative validation constraints and the runtime enforces them. There are a number of built-in @@ -1451,7 +1457,6 @@ JSR-303 allows you to define declarative validation constraints against such pro @Min(0) private int age; - } ---- @@ -1468,6 +1473,7 @@ bean, keep reading. [[validation-beanvalidation-spring]] === Configuring a Bean Validation Provider + Spring provides full support for the Bean Validation API. This includes convenient support for bootstrapping a JSR-303/JSR-349 Bean Validation provider as a Spring bean. This allows for a `javax.validation.ValidatorFactory` or `javax.validation.Validator` to @@ -1489,6 +1495,7 @@ is expected to be present in the classpath and will be detected automatically. [[validation-beanvalidation-spring-inject]] ==== Injecting a Validator + `LocalValidatorFactoryBean` implements both `javax.validation.ValidatorFactory` and `javax.validation.Validator`, as well as Spring's `org.springframework.validation.Validator`. You may inject a reference to either of @@ -1522,13 +1529,13 @@ the Spring Validation API: @Autowired private Validator validator; - } ---- [[validation-beanvalidation-spring-constraints]] ==== Configuring Custom Constraints + Each Bean Validation constraint consists of two parts. First, a `@Constraint` annotation that declares the constraint and its configurable properties. Second, an implementation of the `javax.validation.ConstraintValidator` interface that implements the constraint's @@ -1574,6 +1581,7 @@ As you can see, a ConstraintValidator implementation may have its dependencies [[validation-beanvalidation-spring-method]] ==== Spring-driven Method Validation + The method validation feature supported by Bean Validation 1.1, and as a custom extension also by Hibernate Validator 4.3, can be integrated into a Spring context through a `MethodValidationPostProcessor` bean definition: @@ -1592,6 +1600,7 @@ for setup details with Hibernate Validator and Bean Validation 1.1 providers. [[validation-beanvalidation-spring-other]] ==== Additional Configuration Options + The default `LocalValidatorFactoryBean` configuration should prove sufficient for most cases. There are a number of configuration options for various Bean Validation constructs, from message interpolation to traversal resolution. See the @@ -1601,6 +1610,7 @@ constructs, from message interpolation to traversal resolution. See the [[validation-binder]] === Configuring a DataBinder + Since Spring 3, a DataBinder instance can be configured with a Validator. Once configured, the Validator may be invoked by calling `binder.validate()`. Any validation Errors are automatically added to the binder's BindingResult. @@ -1636,4 +1646,3 @@ locally on a DataBinder instance. See <>. === Spring MVC 3 Validation See <> in the Spring MVC chapter. -