...
<%@ taglib prefix="sf" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
...
<div class="person">
<spring:url value="/persons/{id}" var="editUrl">
<spring:param name="id" value="${person.id}"/>
</spring:url>
<sf:form modelAttribute="person" action="${editUrl}" method="POST">
<table>
<tr>
<th>
<label for="firstName">
<span class="man">*</span>
<spring:message code="label.Person.firstname"/> :
</label>
</th>
<td><sf:input path="firstName"/>
<sf:errors cssClass="error" path="firstName"/></td>
</tr>
...
<tr>
<th>
<label for="dateOfBirth">
<span class="man">*</span>
<spring:message code="label.Person.dob"/> :
</label>
</th>
<td><sf:input path="dateOfBirth"/>
<sf:errors cssClass="error" path="dateOfBirth"/></td>
</tr>
<tr>
<th>
<label for="gender">
<spring:message code="label.Person.gender"/> :
</label>
</th>
<td>
<sf:radiobutton path="gender" value="MALE"/>
<spring:message code="label.Person.male"/>
<sf:radiobutton path="gender" value="FEMALE"/>
<spring:message code="label.Person.female"/>
</td>
</tr>
<tr>
<th>
<label for="hospital">
<span class="man">*</span>
<spring:message code="label.Hospital"/> :
</label>
</th>
<td>
<sf:select path="hospital">
<c:choose>
<c:when test="${person == null}">
<sf:option value="">
<spring:message code="label.choose"/></sf:option>
</c:when>
<c:otherwise>
<sf:option value="${person.hospital.id}">
${person.hospital.name}
</sf:option>
</c:otherwise>
</c:choose>
<sf:options items="${hospitalList}"
itemValue="id" itemLabel="name"/>
</sf:select>
</td>
</tr>
<tr>
<td>
<button id="saveButton" type="submit">
<spring:message code="command.save"/>
</button>
</td>
<td>
<a href="${editUrl}">
<spring:message code="command.cancel"/>
</a>
</td>
</tr>
</table>
</sf:form>
</div>
In the previous code there are a few elements that are underlined. They are Spring form tags equivalent to HTML form tags. The only difference is that they are fully integrated with Spring MVC and their content is populated from the modelAttribute and other Spring form–specific objects, which are covered a little bit later.
The behavior of a Spring form can be described using this succession of steps:
Without the POST-Redirect-GET behavior, an application will behave incorrectly, because subsequent POST requests could lead to duplicate data and/or data corruption. The forms used for search operations do not need a POST request, because submitting a search query has no side effects and it redirects to the results page.
When working with forms, all the fields in a form should map to the fields of an object called a data transfer object (DTO). In the example, this is done for the Search Person form, which is covered later. When editing a person, it is more suitable to use a Person object as a data transfer object. The Person object is an instance of an @Entity class, and maps to a row in the person table in the database. These types of objects are called domain objects. This requires that the object used for this purpose should have a default constructor, getters, and setters for all the fields used in the form. Although using a domain object as a form object welcomes the undesired possibility that some web logic–specific operations might creep in, it is practical to use when the objects handled are simple20 and entity fields are annotated with specific validation annotations (example: @NotEmpty, NotNull, etc.), because implementing a validator class at the web layer may no longer be necessary.
Form-specific data transfer objects are also required when the information from a form is aggregated from multiple domain objects. This allows the form object to encapsulate only what is needed to be displayed on the screen: web layer logic, validation logic, logic for information transfer between the form object, and domain object.
Figure 3-15 shows the correspondence between the form tags and the fields in the CriteriaDto object.
Figure 3-15. Form to search a Person
The following can be said when using Spring forms:
<sf:form action="${personsUrl}" modelAttribute="criteriaDto" method="get">
....
</sf:form>
<spring:url value="/persons/go" var="personsUrl"/>
<sf:form action="${personsUrl}" modelAttribute="criteriaDto" method="get">
....
</sf:form>
<sf:select path="hospital" itemValue="id" itemLabel="name"
items="${hospitalList}"/>
The hospitalList has to be added to the model by the controller as an attribute:
model.addAttribute("hospitalList", hospitalRepo.findAll());
For example, if the same form is used for editing and creating a person instance, when a new person is created, you might want to display a default option in the hospital drop-down list, making it obvious for the user that a value has to be selected. That default option is not part of the hospital list model attribute. When a person is edited, you want to select the hospital where that person was born.
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
....
<sf:select path="hospital">
<c:choose>
<!-- no person attribute, form is used to create a person -->
<c:when test="${person == null}">
<sf:option value="">
<!-- default option not in the hospital list model attribute -->
<spring:message code='label.choose'/>
</sf:option>
</c:when>
<!-- form is used to edit a person, person model attribute is set -->
<c:otherwise>
<sf:option value="${person.hospital.id}">
${person.hospital.name}
</sf:option>
</c:otherwise>
</c:choose>
<!-- Dynamic list of options -->
<sf:options items="${hospitalList}" itemValue="id" itemLabel="name"/>
</sf:select>
The preceding example is rendered as an HTML select element containing all hospitals in the hospitalList model attribute and an extra static Choose hospital option when the form is used to create a person. The names of the hospitals are used as labels for the available options in the select element. The JSP c taglib is used for conditional operations.
When it comes to using Spring forms, three key subjects must be well understood to use them like an expert: formatting, data binding, and validation; each of these is given the proper coverage in its own section.
Data Formatting
In the Person edit form, you need to display and eventually edit a java.util.Date instance and a Hospital instance. These are complex types and the Spring tag library does not know how to handle them on its own. The developer must provide implementations for the org.springframework.format.Formatter<T> interface for the specific type of object handled in the form. Formatter classes parse text data, turn them into objects, and transform beans into text data ready for rendering. In the 05-pr-mvc-form-solution module, which is the project specific to this section, two formatters are used in the Edit person form; where they are used is shown in Figure 3-16.
Figure 3-16. Formatters used in the form to edit a person
Formatters can be used in four places in the application:
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date dateOfBirth;
<fmt:formatDate value="${person.dateOfBirth}" pattern="yyyy-MM-dd" />
<mvc:annotation-driven conversion-service="typeConversionService" "/>
<!-- Define a custom ConversionService -->
<bean id="typeConversionService"
class="o.s.format.support.FormattingConversionServiceFactoryBean">
<property name="formatters">
<set>
<bean class="com.pr.util.DateFormatter"/>
<bean class="com.pr.util.HospitalFormatter"/>
</set>
</property>
</bean>
The DateFormatter implementation used in the personal-records project looks like this:
public class DateFormatter implements Formatter<Date>{
public static final SimpleDateFormat formatter =
new SimpleDateFormat("yyyy-MM-dd");
@Override
public Date parse(String s, Locale locale) throws ParseException {
return formatter.parse(s);
}
@Override
public String print(Date date, Locale locale) {
return formatter.format(date);
}
}
@Component
public class IdValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return String.class.equals(clazz);
}
@Override
public void validate(Object target, Errors errors) {
String id = (String) target;
if(!id.matches("/{id:\d*}")) {
errors.reject("id.malformed");
}
}
}
The controller method to retrieve a person’s data and using that validator looks like this:
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public String show(@Validated(IdValidator.class)@PathVariable String id,
Model model) throws NotFoundException {
Long theId = Long.parseLong(id);
Person person = personManager.findById(theId);
if(person == null) {
throw new NotFoundException(Person.class, theId);
}
model.addAttribute("person", person);
return "persons/show";
}
In the examples in this book, only Date and Hospital formatters are covered because they are the only ones needed in the associated projects, but any complex field type can have a formatter. For example:
@NumberFormat(style=Style.NUMBER, pattern="#,###.###")
private final BigDecimal amount;
@NumberFormat(style=Style.CURRENCY)
private final BigDecimal amount;
When field values are formatted inside a JSP page, formatting annotations are no longer needed. When formatters are registered using the FormattingConversionServiceFactoryBean bean, the specific types are automatically converted without another annotation or tag.
The <mvc:annotation-driven/> and @EnableWebMVC registers default formatters for Numbers and Dates by offering support for specific annotations: @NumberFormat and @DateTimeFormat. If Joda Time is in the classpath, support for it is also enabled.
Java configuration to register custom formatters can be done in two ways: by defining a @Bean of type ConversionService, or by implementing the addFormatters method defined in WebMvcConfigurer (or by overriding it when implementations of this interface are used).
The second way is easier:
\in the @Configuration & @EnableWebMvc annotated class
@Override
public void addFormatters(FormatterRegistry formatterRegistry)
{
formatterRegistry.addFormatter(getDateFormatter());
formatterRegistry.addFormatter(getHospitalFormatter());
}
@Bean
public DateFormatter getDateFormatter(){
return new DateFormatter();
}
@Bean
public HospitalFormatter getHospitalFormatter(){
return new HospitalFormatter();
}
The first way is more complicated. The following is an example of an annotated ConversionService bean definition:
\ in the @Configuration annotated class
public ConversionService conversionService() {
FormattingConversionServiceFactoryBean bean =
new FormattingConversionServiceFactoryBean();
bean.setFormatters(getFormatters());
bean.afterPropertiesSet();
ConversionService object = bean.getObject();
return object;
}
private Set<Formatter> getFormatters() {
Set<Formatter> formatters = new HashSet<>();
formatters.add(dateFormatter);
formatters.add(dateFormatter);
return formatters;
}
// definition for formatter beans as in the previous example
Now you have the conversionService bean. Let’s look at the equivalent for <mvc:annotation-driven conversion-service="conversionService" />:
@FeatureConfiguration
class MvcFeatures {
@Feature
public MvcAnnotationDriven annotationDriven(
ConversionService conversionService) {
return new MvcAnnotationDriven().conversionService(conversionService)
}
...
}
MvcAnnotationDriven provides the same options as the XML elements using a conveniently chained method API. But who needs a complicated way to do this when there is an easier way, right?
HospitalFormatter is a custom formatter specifically created to be used in projects attached to this book. It basically transforms a Hospital instance into its name so that it can be rendered in a view. And it takes a hospital id and retrieves the Hospital instance from the database to be returned to the controller, where it is used further. As the HospitalFormatter is a bean like any other, the HospitalManager bean can be injected into it to make this happen. So the custom implementation looks like this:
public class HospitalFormatter implements Formatter<Hospital> {
@Autowired
HospitalManager hospitalManager;
@Override
public Hospital parse(String text, Locale locale)
throws ParseException {
Long id = Long.parseLong(text);
return hospitalManager.findOne(id);
}
@Override
public String print(Hospital hospital, Locale locale) {
return hospital.getName();
}
}
Data Binding
Form objects, data transfer objects, and domain objects have been mentioned so far. But how are they linked together? How does the information from a form object get transferred to a data transfer object or to a domain object? How does Spring MVC know how to do this? The answer to these three questions is a process named data binding.
Spring MVC binds the request to the form object. When a form is submitted, string data is transformed into objects that are injected into the form object using getters and setters. A POST request or form submission means setters for the form model attribute are called. A GET or page/form load means getters are called upon the form model attribute to populate the view. Each object is identified using the path attribute value in the corresponding Spring element tag. The form object it tightly bound to the JSP page, and if the form object cannot be created, the JSP page won’t be rendered correctly. The form object is linked to the JSP page using the modelAttribute attribute in the <sf:form/> tag:
<sf:form modelAttribute="person" action="${editUrl}" method="POST">
...
</sf:form>
In the controller, the form object can be accessed in multiple ways. It can be received as an argument to the method mapped to the ${editUrl}.
@Controller
public class PersonsController {
@RequestMapping(method=RequestMethod.POST)
public String update(Person person) {
...
}
In this case, data is copied automatically into the object, and the object is re-created on every request. You can annotate the form object with the @ModelAttribute annotation.
@Controller
public class PersonsController {
@RequestMapping(method=RequestMethod.POST)
public String edit(@ModelAttribute("person") Person person) {
...
}
CC When the name of the modelAttribute is the same as the name of the argument in a handler method, the value for @ModelAttribute can be skipped. So in the previous case, public String update(@ModelAttribute("person") Person person) is equivalent to public String update(@ModelAttribute Person person).
This annotation was mentioned in the “Redirecting” section; it can be used the same way for forms too, because in this case, you have a controller that handles the edit and show requests for a person instance. @ModelAttribute annotated methods are executed before the chosen @RequestMapping annotated handler method. They effectively pre-populate the implicit model with specific attributes, in this case, the person instance to be displayed or edited.
So you can simplify the controller like this:
@Controller
@RequestMapping("/persons/{id}")
public class PersonsController {
@ModelAttribute
public Person findPerson(@PathVariable Long id) {
return personManager.findOne(id);
}
@RequestMapping(method = RequestMethod.GET)
public String show() {
return "persons/show";
}
@RequestMapping(value="/edit", method = RequestMethod.GET)
public String edit(Model model) {
//we add the hospitalList to show in the Hospital drop-down list
model.addAttribute(hospitalRepo.findAll());
return "persons/edit";
}
@RequestMapping(method = RequestMethod.POST)
public String save(Person person, BindingResult result, Model model) {
if (result.hasErrors()) {
// we need to add this here as the dropdown list
// has to be populated correctly
// and "hospitalList" is not a model attribute
model.addAttribute(hospitalRepo.findAll());
return "persons/edit";
}
personManager.save(person);
return "redirect:/persons/".concat(person.getId());
}
}
In this implementation, you do not have to concern yourself with the existence of the form object because the methods of this controller are only accessible when the URL is constructed with a valid person id.
By default, all fields in a form are binded to the form object, but Spring MVC offers the possibility to modify the default behavior by customizing a WebDataBinder object. Some fields can be blacklisted or whitelisted for the binding process:
@InitBinder
public void initBinder(WebDataBinder binder) {
//allowed fields
binder.setAllowedFields("firstName", "lastName");
//disallowed fields
binder.setDisallowedFields("pk", "*Pk");
}
The recommended behavior is to whitelist only the necessary fields, even if there might be a lot of them to minimize the security holes.
The validation errors are binded to the form object too using a BindingResult object.
@RequestMapping(method = RequestMethod.POST)
public String save(@Valid Person person, BindingResult result, Model model) {
if (result.hasErrors()) {
return "persons/edit";
}
...
}
If you look at the beginning of this section, where the Edit person form code is, you see that some elements look like this:
<sf:errors cssClass="error" path="dateOfBirth"/></td>
They are right next to their analogue elements:
<sf:input path="dateOfBirth"/>
And they have the exact path attribute value. These elements are used to display validation errors when the POST handler method returns back to the edit view because the BindingResult object was populated by an existing validator bean. When returning to the form, the submitted data is still there, but there is extra information about the state and condition of the submitted data, something more or less like what you see in Figure 3-17.
Figure 3-17. Spring default validation errors displayed after a form failed submission
Spring MVC has its own validator messages, but supports externally provided validator messages too. Data binding error messages can be customized and internationalized. The following are some examples; depending on the validation library used, the message keys could be different:
NotEmpty.person.firstName=Please insert First Name Value
Size.person.firstName=Length must be between {2} and {1}
typeMismatch.dateOfBirth=Invalid format, should be 'yyyy-mm-dd'
typeMismatch.amount=Incorrect amount
And after the customization, when a submit fails, the invalidated form looks like what’s shown in Figure 3-18.
Figure 3-18. Customized validation errors displayed after a form failed submission
Spring MVC supports JSR 303/349 Bean Validation for validating form objects. If the library javax.validation:validation-api:[version] is in the classpath and the application is configured using <mvc:annotation-driven/> or @EnableWebMvc, it is automatically detected and enabled.
Spring 4+ also supports Hibernate Validator 4.3+, but for the org.hibernate:hibernate-validator:[version] library to be used, a custom validator that implements org.springframework.validation.Validator must be set in the configuration; for example:
<!-- Enables hibernate validator -->
<bean id="validator"
class="o.s.validation.beanvalidation.LocalValidatorFactoryBean"/>
<!-- Defines basic MVC defaults (handler adapter, mapping,
date formatting, etc) -->
<mvc:annotation-driven validator="validator"/>
The Hibernate Validator is an extension of the default set of validation annotations provided by the validation-api library, that’s why when using Hibernate Validator, validation-api is enabled by default, as validation-api is a dependency of the Hibernate Validator.
! To depict this, a special Gradle task was created for you in the 00-pr-dao module: allCompileDeps. When executed, Gradle prints the dependency tree for the 00-pr-dao module in the Intellij IDEA console. If you analyze the output, you will find the following snippet.
+--- org.hibernate:hibernate-validator:5.1.3.Final
| +--- javax.validation:validation-api:1.1.0.Final
| +--- org.jboss.logging:jboss-logging:3.1.3.GA
| --- com.fasterxml:classmate:1.0.0
The following are examples of validation annotations:
They are used on the fields of interest in the domain object or data transfer object:
public class Person extends AbstractEntity {
@Size(min=2, max=50)
public String firstName;
@Size(min=2, max=50)
public String lastName;
@NotNull
// comment the following if a custom formatter is registered
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date dateOfBirth;
...
}
The validation is invoked by annotating the form object with @Valid and the errors are registered in the BindingResult object too, alongside the binding errors.
In the JSP form, the way the errors are displayed can also be customized. In the previous section, each error was mapped to its field, but you can also print all the errors in the same place by using the following syntax:
<sf:form modelAttribute="person">
<form:errors path="*"/>
...
</sf:form>
This approach is not recommended for big forms. It is also quite annoying for the user to have to search for the form field he has to correct. By linking the error to the form field, it becomes quite obvious where the correction must be applied.
The Hibernate Validator contains its own set of internationalization files with default internationalized messages. The Resource bundle is named ValidationMessages; it is located in the hibernate-valdiator.jar under the org.hibernate.validator package. You can expand the hibernate-validator.jar and look at it contents in Intellij IDEA, as shown in Figure 3-19.
Figure 3-19. Contents of the hibernate-validator.jar
The message keys in the ValidationMessages.properties files are the message keys set by default in the definition of each annotation. For example, the following is a snippet of code for the @NotEmpty annotation:
@Constraint(validatedBy = { })
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@ReportAsSingleViolation
@NotNull
@Size(min = 1)
public @interface NotEmpty {
String message() default "{org.hibernate.validator.constraints.NotEmpty.message}";
...
}
For every field that fails, the @NotEmpty validation has the default error message printed next to it (if configured so), read from the Hibernate Validator resource bundle files. These messages can be overridden by creating your own ValidationMessages resource bundle in the classpath of the project. Also, the message keys can be customized by making the new message key a parameter for the message property when using the annotation; this allows specific messages to be displayed when the same annotation is used on different fields:
// in the Person entity class
@NotEmpty(message="lastname.notempty")
public String lastName;
#in the ValidationMessages.properties
lastname.notempty=Lastname cannot be empty!
When using Spring forms, the error messages can be part of the application resource bundle under WEB-INFmessages; the message keys usually respect the following template:
constraintName.modelAttributeName.propertyName
Each part of the Spring message key is linked to elements in the application, as depicted in Figure 3-20.
Figure 3-20. Spring message keys and linked elements
The message samples at the end of the previous section include customized validation messages, used in the 05-pr-mvc-form-practice and solution modules.
Spring also supports the JEE @Constraint21 annotation, which can be used to define customized validation annotations.
// Pnc.java
@Constraint(validatedBy = [PncValidator.class])
@Target( { ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface Pnc {
//using specific message key
String message() default "{pncFormatExpected}";
Class<?>[] groups() default {};
}
//In PncValidator.java
public class PncValidator implements
ConstraintValidator<Pnc, String> {
@Override
public void initialize(Pnc constraintAnnotation) {
// nothing to initialize
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return (value == null) || value.matches("[1-2][0-9]*");
}
}
// In Resouce bundle global.properties files
pncFormatExpected= A valid Personal Numerical Code is required!
// Usage in IdentityCard.java
public class IdentityCard extends AbstractEntity {
...
@Pnc
private String pnc;
...
}
Or the org.springframework.validation.Validator interface can be implemented to provide a custom validator implementation, which can be specific to a controller and can be set using @InitBinder:
class PncValidator extends Validator {
public void validate(Object target, Errors errors) {
if ((Person)target)
.identityCard.pnc.matches("[1-2][0-9]*") )
errors.rejectValue("pnc", "pncFormatExpected");
}
public boolean supports(Class<?> clazz) {
return clazz instanceof Person.class;
}
}
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.setValidator(new PncValidator());
}
Or a validation method can be implemented directly into the data transfer object and called from the controller:
public class CriteriaDto {
public void validate(Errors errors) {
if (fieldValue == null || fieldValue.isEmpty())
errors.rejectValue("fieldValue", "valueExpected");
}
}
...
@RequestMapping(method=RequestMethod.Get)
public String search(CriteriaDto criteriaDto, BindingResult result) {
criteriaDto.validate(result);
// process failure or success normally ...
}
Managing the Form Object
Using a form implies multiple requests, which means that the form object has to be the same across two or more requests (when validations fail, for example). There are more ways to manage the form object:
In the first case, the object is created in the initial GET request, and then Spring MVC takes care of creating it again and binding all request parameters to it.
@Controller
@RequestMapping("/persons/{id}")
public class PersonsController {
//1. object is being created on every request
// initial GET request, object is created
@RequestMapping(value="/new", method=RequestMethod.GET)
public String new(Model model) {
model.add(new Person());
}
//2. object is being retrieved
@ModelAttribute
public Person findPerson(@PathVariable Long id) {
return personManager.findOne(id);
}
// the POST request - Spring MVC takes care of creating the object and binding
@RequestMapping(method=RequestMethod.POST)
public String save(Person person) {
...
}
}
In the second case, the object is retrieved by a manager (service class) before every request and @ModelAttribute annotation on a method is used for this; you can see this in the code sample at the beginning of the “Data Binding” section. The last two cases were covered in the “Redirecting” section.
Summary
After reading this chapter, you should have a wide understanding of Spring Web MVC and all it has to offer. Here is a simple list of topics that you should keep handy when reviewing your new knowledge:
Quick Quiz
Question 1: Considering the following configuration in web.xml, what is the name of the parameter that holds the location of the Spring MVC configuration file?
<servlet>
<servlet-name>mvc-dispatcher</servlet-name>
<servlet-class>o.s.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>?????</param-name>
<param-value>
/WEB-INF/spring/mvc-config.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
Question 2: Considering the following configuration in web.xml, what is the name of the parameter that points to the Spring infrastructure bean that enables Java configuration?
<servlet>
<servlet-name>mvc-dispatcher</servlet-name>
<servlet-class>o.s.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>???</param-name>
<param-value>
o.s.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
com.book.config.WebConfig
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
Question 3: In web.xml, the servlet name has been configured to mvc-dispatcher. What is the default name of the MVC configuration file that Spring looks for?
Question 4: As a developer, what do you need to do to configure Spring Web MVC application without using an web.xml file ?
Question 5: Which of the following are Spring MVC infrastructure components?
Question 6: The purpose of HandlerMapping implementations is to map incoming requests to the appropriate handlers and a list of pre- and post-processor interceptors. Is this statement true?
Question 7: RequestMappingHandlerMapping is registered by default when the following configuration style is used for a Spring web application:
Question 8: What are the key interfaces used by Spring to render responses without tying itself to a specific view technology?
Question 9: Which of the following is an out-of-the-box view technology supported by Spring?
Question 10: What is the default ViewResolver implementation configured by Spring?
Question 11: What is the difference between chaining ViewResolver beans and content-type negotiation?
Question 12: What is true about the HTTP Accept header?
Question 13: From the following list, select the Spring infrastructure bean types responsible with application personalization:
Question 14: What is true about the @ExceptionHandler and @ControllerAdvice annotations?
Question 15: Given the following controller, to what request will the call method be mapped to?
@Controller
@RequestMapping("/persons")
public class PersonsController {
@RequestMapping("/list")
public String call(Model model,HttpServletRequest rq) {
...
}
}
Question 16: Given the following controller, is the declaration of the show method correct?
@Controller
@RequestMapping("/persons")
public class PersonsController {
@RequestMapping("/{id}")
public String show(@PathVariable String number, Model model) {
...
}
}
Question 17: What of the following is something that a Spring MVC handler method could not return?
Question 18: Which of the following statements regarding annotation-based configuration are true?
Question 19: What is true about @ModelAttribute ?
Question 20: What is @InitBinder used for?
Question 21: Which is true when a new view technology is added to a Spring web application?
Question 22: When working with Spring forms, which is the recommended workflow?
Question 23: Given the following Spring form definition, what is wrong with it?
<%@ taglib prefix="sf" uri="http://www.springframework.org/tags/form" %>
...
<sf:form action="${personsUrl}" method="GET">
...
</sf:form>
Question 24: Does Spring MVC support validation of form data?
Question 25: Which of the following are validation annotations used on form object fields?
Practical Exercise
This chapter is quite big, so it has four module projects associated with it and each of the modules covers a specific section. Figure 3-21 depicts the eight module projects attached to this chapter: four practice projects and four proposed solutions for them. You are welcome to analyze the proposed solutions and compare them to your solutions to test your understanding of Spring MVC.
Figure 3-21. Practice projects for Chapter 3
All web modules depend on the 01-pr-service module. This project contains the @Service classes and the repositories used to manage data. The 01-pr-service depends on the 00-pr-dao module that contains entity classes and common classes (formatters, enums, and utility classes) used by all other modules. The service module was created to respect the standardized three-tiered software architecture pattern, depicted in Figure 3-22. Each tier has a specific responsibility:
Figure 3-22. Typical standardized three-tiered architecture
Because the Personal Records Manager is quite a small project, the service classes do not do much besides calling analogous methods form repository beans.
The HospitalFormatter is part of the 01-pr-service module because it needs a manager instance to retrieve the hospital instance from the repository.
The DBInitializer class is also located in the service class; it is used to populate the database with some sample entries when a web application starts. This class is a simple bean with access to all the service components used in the application, and with a @PostConstruct annotated method that uses those service classes to insert data. This is the most practical way to initialize a small test-scoped database that does not require external configuration files or additional libraries in the classpath. The bean is annotated with @Component and it is automatically discovered, created, and initialized at application boot time.
The @Service annotated classes are organized in the hierarchy depicted in Figure 3-23.
Figure 3-23. Service classes hierarchy
They are all named [EntityType]ManagerImpl, where EntityType is the type of object managed by the class. The Impl suffix is used to emphasize that the class is a concrete implementation for the [EntityType]Manager interface.
The BaseManager interface contains all the basic method skeletons common to all service classes.
@Transactional
public interface BaseManager<E extends AbstractEntity> {
@Transactional(readOnly = true)
List<E> findAll();
@Transactional(readOnly = true)
E findById(Long id);
E save(E e);
void delete(E e);
void deleteById(Long id);
}
The manager interfaces extending it add method skeletons specific to each managed entity-type.
The repository components are created using Spring Data JPA and are in fact interfaces extending the JpaRepository interface. This interface extends CrudRepository, which provides sophisticated CRUD functionality for the entity class being managed. All other method definitions that are needed, but not provided, are defined in the interface extending JpaRepository. They are annotated with the @Query annotation, and the query to be executed is set through it; for example:
@Query("select p from Person p where p.lastName = :lastName")
List<Person> getByLastname(@Param("lastName") String lastName);
For Spring to provide proxy repositories with the configured implementation for the interfaces that extend JpaRepository, the following line has been added to the app-dao-config.xml configuration file:
<beans xmlns="http://www.springframework.org/schema/beans"
...
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="http://www.springframework.org/schema/beans
...
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
...">
<jpa:repositories base-package="com.pr.repos"/>
</beans>
The equivalent Java configuration makes use of the @EnableJpaRepositories annotation:
@Configuration
@EnableJpaRepositories("com.pr.repos")
class ApplicationConfiguration {
@Bean
public EntityManagerFactory entityManagerFactory() { ... }
@Bean
public DataSource dataSource() {...}
...
{
The basic configuration *.gradle file for each web module project looks like this:
(1) apply plugin: 'war'
apply from: 'https://raw.github.com/akhikhl/gretty/master/pluginScripts/gretty.plugin'
dependencies {
(2) compile project(':01-pr-service')
compile misc.slf4jApi, misc.slf4jJcl, misc.logback,
hibernate.ehcache, hibernate.em, hibernate.core, hibernate.validator,
spring.jdbc, spring.orm, spring.contextSupport, spring.data,
spring.webmvc,
misc.dbcp, misc.h2, misc.joda, misc.jstl, misc.tilesJsp,
misc.tilesReqApi, misc.javaxEl
testCompile tests.junit, tests.mockito, spring.test,
tests.hamcrestCore, tests.hamcrestLib
}
gretty {
port = 8080
(3) contextPath = '/mvc-layout'
}
All the projects can be built without the tests by running the allCompile task, as mentioned earlier in the book. In case you forgot, the task can be found directly under the project root, in this case the personal-records project, in the Intellij IDEA Gradle task tab, as you can see in Figure 3-24.
Figure 3-24. The Gradle allCompileTask
The other two tasks you need to use are the appStart and appStop under the module-project name in the Intellij IDEA Gradle task tab; they start and stop the web application, as depicted in Figure 3-25.
Figure 3-25. The Gerry start and stop web application tasks
All the modules in personal-records can be built and run separately when using Gradle outside of Intellij IDEA, by using the command line in a terminal and running specific Gradle tasks:
$ cd personal-records
$ gradle :02-pr-mvc-basic-practice:build
$ gradle :02-pr-mvc-basic-practice:run
In the previous examples, the :02-pr-mvc-basic-practice is the name of the submodule, and :build and run are Gradle tasks to be run for the modules.
! When running examples in the command line, the run task is used instead of appStart to run the web modules in Gretty, so the execution can be ended by pressing any key in the terminal.
Each of the projects suffixed with -practice is incomplete, missing either a bean definition or a configuration. In its place there is a TODO comment explaining what you have to do to make the project build. Each project covers a specific topic from the chapter, as follows:
Figure 3-26. Path of the Tiles layout template
There are two controllers defined PersonsController and HospitalsController. Each of the controllers has the responsibility of populating the corresponding list.jsp views and the HospitalsController has a method that uses redirect: and redirectAttributes. This project also contains unit and integration tests designed to test you controllers.
All the applications are internationalized and themed. There are two languages available—English and German, and two colored themes—blue and green.
When all the tests pass, all the applications start successfully and provide the expected functionality, and you feel confident that you have a solid grasp of the Spring Web MVC, you can continue to the next chapter.
________________________
1Thymeleaf is the new sheriff in Web town. It is an XML/XHTML /HTML5 template engine that works both in web and non-web environments. It is really easy to integrate it with Spring. If you want to read more about it before using it in the practice code for this chapter, go to the official site at http://www.thymeleaf.org/.
2If it looks as if Spring MVC resembles Struts, you are definitely not imagining things. The Spring Web MVC was inspired by Struts, which was one of the first MVC-based frameworks. The DispatcherServlet in Spring has the same responsibilities as the ActionServlet in Struts, as both are implementations of the Front Controller Pattern. You can read more about this software design pattern at http://www.martinfowler.com/eaaCatalog/frontController.html.
3If you want, you can look in the API documentation for detail information about this class, which is available at http://docs.spring.io/spring/docs/current/javadoc- api/.
4The code for this class is at https://github.com/spring-projects/spring-framework/blob/master/spring-web/src/main/java/org/springframework/web/ SpringServletContainerInitializer.java.
5The contents can be accessed directly on GitHub at https://github.com/spring-projects/ spring-framework/blob/master/spring-webmvc/src/main/resources/org/springframework/web/ servlet/DispatcherServlet.properties.
6The full list of configuration options for Gretty can be found at http://akhikhl.github.io/gretty-doc/Gretty-configuration.html.
7http://docs.spring.io/spring/docs/current/spring- framework-reference/htmlsingle/#mvc-ann-requestmapping-31-vs-30, http://docs.spring.io/spring/docs/current/spring- framework-reference/htmlsingle/#mvc-config-enable.
8http://docs.spring.io/spring/docs/4.1.x/spring- framework-reference/htmlsingle/#mvc-viewresolver.
9You can read more about it in the official documentation at http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html#beans- p-namespace.
10By default, strategies for checking the extension of the request path and the Accept header are registered. The path extension check performs lookups through the ServletContext and the JavaBeans Activation Framework (if present) unless media types are configured. In order to use the JavaBeans Activation Framework, the activation.jar has to be in the classpath of the application.
11A detailed explanation of exception handling using Spring MVC is at https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc.
12A RESTful URI identifies a domain resource (like a book, or a person, in this case) rather than an application resource like a web page or a form. URI is the acronym for Uniform Resource Identifier. URL is the acronym for Uniform Resource Locator. REST services work only with URIs and @PathVariable.
13More information about it is at http://mockito.org.
14Read more about it on the official site at http://hamcrest.org/.
15You can take a look at these interceptors’ code on GitHub at https://github. com/spring-projects/spring-framework/blob/master/spring- webmvc/src/main/java/org/ springframework/web/servlet/theme/ThemeChangeInterceptor.java and https://github.com/spring-projects/spring-framework/blob/master/spring-webmvc/src/main/java/org/springframework/web/servlet/i18n/LocaleChangeInterceptor.java.
16This approach is described by the Composite View pattern that introduces the notions of composite and atomic views. A composite view is a tree structure of atomic views. An atomic view can be included dynamically and it changes based on the context.
17Detailed API information on tiles:insertAttribute is at https://tiles.apache.org/framework/tiles-jsp/tlddoc/tiles/insertAttribute.html.
18Detailed API information on tiles:importAttribute is at https://tiles.apache.org/framework/tiles-jsp/tlddoc/tiles/insertAttribute.html.
19A full discussion is at http://forum.thymeleaf.org/why-Thymeleaf-td3412902.html.
20Adam Bien, one of the most respected Java developers in the world, has named DTOs objects “anemic in general and do not contain any business logic” on his blog at http://www.adam-bien.com/roller/ abien/entry/value_object_vs_data_transfer.
21See http://docs.oracle.com/javaee/7/api/javax/validation/Constraint.html.
22In the PErsonal REcords Manager project, because Spring DATA JPA is used, the implementation of the repositories API is reduced to interfaces extending the JpaRepository interface. They are placed in the logic tier/service layer.
18.119.19.174