Create a web application using the Spring MVC framework

From Computing Study
Jump to: navigation, search

Introduction

This tutorial builds on the concepts introduced in the Create an object oriented dynamic website with Java and MySQL tutorial. It is more of an overview of the Spring Framework techniques than a line-by-line walk though. You will need to have completed the earlier tutorial first, or at least have a good understanding of the Java Servlets model, as well as object oriented programming, before you attempt this tutorial. It is designed to assist you in creating a complete application with the most common options. There is a huge amount of useful and more detailed information along with code examples in the Spring Framework documentation:
https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html

The editor used in this tutorial was Eclipse

Spring MVC Configuration

Create 'New Maven Project'
Create simple project
Group ID > com.infiniteskills.mvc
Artifact Id > course-project (address within Maven)

Convert to Web Project using Project Facets
Right click on project > configure > convert to faceted form > Dynamic Web Module (3.1)
Check Java Build Path Java library matches project facet
Add server runtime (Tomcat)

Delete 'Web Content'
src > main > add new folder 'webapp'
Project properties (ALT+Enter) Deployment Assembly delete 'Web Content' and add 'webapp'

New > File > jsp > home.jsp (inside 'webapp')
Right click on project > Java EE Tools > Generate Deployment Descriptor Stub (adds WEB-INF & web.xml)

pom.xml > Dependencies
Add org.springframework > spring-webmvc
Add slf4j.log4j13
Add log4j > change to jar file
(Add slf4j.log4j12 and org.codehause.plexus plexus-archiver as well to resolve issues)
Add jstl.jar

src/main/resources > New File > log4j.properties

# Root logger option
log4j.rootLogger=INFO, stdout
# Direct log messages to stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n

web.xml > remove welcome file list
Add:

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
	
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/spring/applicationContext.xml</param-value>
</context-param>
	
<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring/dispatcher-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

In WEB-INF > New File > spring bean configuration file > dispatcher-servlet.xml

Project Properties > Deployment Assembly > Add > Java Build Path Entries > Meven Dependencies

Move dispatcher-servlet.xml to /WEB-INF/spring/ (create folder)

In WEB-INF/spring/ > New File > spring bean configuration file > applicationContext.xml

dispatcher-servlet.xml > Namespaces tab
Add mvc namespace and context namespace
Add:

<mvc:annotation-driven/>
<context:component-scan base-package="com.infiniteskills.mvc.controllers"/>

src/main/java > New Class
Package > com.infiniteskills.mvc.controllers
Name > HomeControllers
The name of the method can be anything you like. It is normally a description of what the method does.
The return statement is the name of the JSP that you want to be displayed after this method is executed. No need to use the .jsp extension.

@Controller
public class HomeController {

	@RequestMapping("/")
	public String goHome(){
		return "home";
	}
}

dispatcher-servlet.xml

<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/views/"/>
    <property name="suffix" value=".jsp"/>
</bean>

New Folder > /WEB-INF/views/
Move home.jsp into views

home.jsp
Put h1 tags around Home
New Folders+File webapp/resources/css/home.css

h1 {
    color: red;
}

Before DOCTYPE

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>

Within <head>

<link rel="stylesheet" href="<spring:url value="/resources/css/home.css"/>" type="text/css"/>

dispatcher-servlet.xml

<mvc:resources location="/resources/" mapping="/resources/**"></mvc:resources>

Controller Basics

Spring controllers allow you to have several different methods within the same controller class, instead of needing to have a different class for each method like you do with the Servlets model. They use the @RequestMapping annotation to determine the URL path that will direct to the controller, and subsequently to an individual method. For example, a controller might have a root @RequestMapping of "project", and the controller contains a method which has a @RequestMapping of "find". The URL would then look like domain.com/project/find with the 'project' part of the URL directing to this controller and the 'find' part directing to that method. This technique can be used to implement a RESTful interface.

In order to use the Controller annotation you need to have component scanning enabled in the dispatcher servlet

<context:component-scan base-package="com.infiniteskills.mvc.controllers"/>

Use @RequestMapping("/project") in the controller root to direct all URLs with domain.com/project to this controller.
The name of the method can be anything you like, normally it describes what the method does.
Use another @RequestMapping(value="/add") above the method inside the class to target the next level in the URL. e.g. this would now link to {domain.com}/project/add
The return statement is the name of the JSP that you want to be displayed after this method is executed. No need to use the .jsp extension.

@Controller
@RequestMapping("/project")
public class ProjectController {

    // Maps to domain.com/project/add
    @RequestMapping("/add")
    public String addProject(){
        return "project_add";
    }
}

Use method=RequestMethod.GET (or POST, PUT, DELETE) inside @RequestMapping to define the type of request that will be sent to this method.
Inside @RequestMapping define parameters that must be present using params=("paramName=optionalParamValue")
In this way two methods can have the same @RequestMapping value and be reached based on their method and/or parameters.

// Maps to domain.com/project/add
@RequestMapping(value="/add", method=RequestMethod.POST, params={"paramName=paramValue","paramName"})
public String saveProject(){
    // save project methods
    // then return to add another project
    return "project_add";
}

To pass an object to the View, add Model as an argument to the controller method. Then use model.addAttribute() in much the same way as request.setAttribute() works in the Servlet example.

// Maps to domain.com/
@RequestMapping("/")
public String goHome(Model model){
		
    Project project = new Project();
    project.setName("Sample Project");
    project.setSponsor("Nasa");
    project.setDescription("This is a simple project sponsored by NASA");
		
    model.addAttribute("currentProject", project);
		
    return "home";
}

Can then access the values in JSP using JSP Expression Language

<ul>
    <li><label>Project Name:</label><span>${currentProject.name }</span></li>
    <li><label>Sponsor:</label><span>${currentProject.sponsor }</span></li>
    <li><label>Description:</label><span>${currentProject.description }</span></li>
</ul>

@Autowired

Establish a service (possibly a DAO) that returns data as a bean and link to it inside the root application context (applicationContext.xml). This makes it available to the dispatcher service as well as the controllers.

<bean id="projectService" class="com.infiniteskills.mvc.data.services.ProjectService"/>

Create a method in the controller that points to the jsp and takes the model as an argument. Outside the method create an instance of the service class and add the @Autowired annotation. Then inside the method set an attribute to the model as this.projectService.invokeMethod(). Then all objects returned by the invoked method will be placed in the model.

@Autowired
private ProjectService projectService;

// Maps to domain.com/project/find
@RequestMapping(value="/find")
public String find(Model model){
    model.addAttribute("projects", this.projectService.findAll());
    return "projects";
}

URI templates

A RESTful URL may look like /projects/{projectId}. Create a controller method that adds an object to the model called by the @Autowired project service. This service method will take an argument, which is extracted from the URL in the @RequestMapping by using curly braces. Then in the controller method argument use @PathVariable annotation to assign this variable in the URL to a Java variable.

// Maps to domain.com/project/{projectId}
@RequestMapping(value="/{projectId}")
public String findProject(Model model, @PathVariable("projectId") Long projectId){
    model.addAttribute("project", this.projectService.find(projectId));
    return "project";
}

https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-arguments

Contains (among other things) all the method arguments that can be passed into a controller method.
HttpSession session - no need to then get the session, Spring will do this automatically.
HttpServletRequest request - can then be used to grab parameters like a servlet, e.g. from a form.
Another way to get parameters is to use @RequestParam("paramName") and assign it to a Java variable.
@RequestParam is able to convert the parameter to a relevant type. e.g. String to int.

Data Binding

Use @ModelAttribute annotation to add a Java object to the argument. Spring will then automatically assign all the values from the parameter names being sent by the form to their matching equivalents in the object. This only works if your parameter names in your form (or whatever else you are sending) are the same as the variable names in the object that you are passing as argument.

// Maps to domain.com/project/add
@RequestMapping(value="/add", method=RequestMethod.POST)
public String saveProject(@ModelAttribute Project project){
    System.out.println("Saving Project");
    System.out.println(project);
    return "project_add";
}

The difference between @ModelAttribute and @Request Attribute is that @RequestAttribute binds to a stand-alone variable of the same name, whereas @ModelAttribute creates an instance of the class and binds the incoming parameters to the variables belonging to that class. @ModelAttribute gives you additional features such as validation and form pre-population.

Spring Tag Library

URL tag

In the JSP enable the tag library by adding at the top of the page:

<%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>

Then in the form action you can use the Spring link

<spring:url value="/resource/save"/>.

This provides a context root for the application. You can use htmlEscape="true" to automatically escape any values coming from the form. There are also options for JavaScript etc.

Add the tag library for the Spring form tag.

<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>

This is best used when you have a backing object for the form, you can take advantage of object binding, configurable field formatting, redisplay of values on errors, binding of error messages. Has similar methods to HTML forms, such as method="POST" and action. The value for the action cannot be a nested Spring tag so you need to assign the Spring URL to a variable first.

<spring:url value="/resource/save" var="formUrl" />

Can then use this in the form action using JSP EL

<form:form action="${formUrl}" method="POST" modelAttribute="className" >

In the controller method that loads the JSP add a model attribute of a new instance of the model class that you want associated with the form. This binds the model attributes with the request parameters send with the form.

model.addAttribute("className", new className());

Form Tag

Add the tag library for the Spring form tag.

<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>

This is best used when you have a backing object for the form, you can take advantage of object binding, configurable field formatting, redisplay of values on errors, binding of error messages. Has similar methods to HTML forms, such as method="POST" and action. The value for the action cannot be a nested Spring tag so you need to assign the Spring URL to a variable first. Put this line outside of the form.

<spring:url value="/resource/save" var="formUrl" />

Can then use this variable in the form action using JSP EL

<form:form action="${formUrl}" method="POST" modelAttribute="className" >

In the controller method that loads the JSP add a model.attribute() of a new instance of the class that you want associated with the form. This binds the class fields with the request parameters sent with the form. The form parameter names must match the fields of the class that you are linking with it.

model.addAttribute("className", new className());

Input Tag

Helps you to more easily perform data binding to the modelAttribute in a form

<form:input path="attributeName" cssClass="" id="resource-name"/>

path is the field name on the modelAttribute - equates to the 'name' attribute in HTML
cssClass is the class in CSS
id matches up with the label 'for' attribute
escapeHTML
max length
JavaScript event handlers
In the controller method arguments import the @ModelAttribute annotation and add a model class. Then the parameters from the form will automatically invoke the appropriate setter method in the model class and assign the value to the model field.

// Maps to domain.com/resource/save
@RequestMapping("/save")
public String save(@ModelAttribute Resource resource){
    System.out.println(resource);
    return "resource_add";
}

Select Tag

Replace all the HTML <select><option></option></select> code with the following:

<form:select path="attributeName" items="${listOptions}"/>

path corresponds with the field that you want to data bind on the modelAttribute
id matches up with the label 'for' attribute
items ends up being the options in the dropdown.

You need to supply these values with the model as a list in the controller method that loads the page. In the following examples the lists are hard-coded but they could easily be created dynamically.

@RequestMapping("/add")
public String add(Model model) {
    List<String> options = new LinkedList<>(Arrays.asList(new String[] {"First Option", "Second Option", "Third Option", "Fourth Option" }));
    model.addAttribute("listOptions", options);
    model.addAttribute("resource", new Resource());
    return "resource_add";
}

With these in place there is no need to have the other HTML 'Select' code, the dropdown list is created automatically.

If you want to pass a list of objects to the dropdown, you need to add itemLabel and itemValue. You can also have the form:select and form:options on separate lines for clarity.

<form:select path="attributeName">
    <form:options items="${listOptions}" itemLabel="displayedText" itemValue="value" />
</form:select>

Checkboxes and Radio Buttons

In the model class have two fields, one that holds a single value and one that holds an array.
In the controller method that loads the page have a list of items that is passed to the model for each of the radio options and the checkbox options.

List<String> radios = new LinkedList<>(Arrays.asList(new String[]{"Radio1", "Radio2", "Radio3"}));
model.addAttribute("radioItems", radios);
List<String> checks = new LinkedList<>(Arrays.asList(new String[]{"CheckOption1", "CheckOption2", "CheckOption3"}));
model.addAttribute("checkItems", checks);

In the View:

<form:radiobutton path="attributeName" items="${radioItems}"/>
<form:checkboxes path="arrayAttributeName" items="${checkItems}"/>

path is the field that you want to data bind to in the modelAttribute
items is the list of items that you passed in through the model
id matches up with the label 'for' attribute

Similar to the Spring select, no further code is needed. The application will create a radio button or checkbox for each of the options in the list provided. In the data returned to the controller, the value of the selected radio button will be stored in the associated field, and a list of the selected checkboxes are stored in the array of its associated field.

Text Area Tag

Fairly self explanatory, this is a Spring textarea and the text from is is stored in the field associated with it.

<form:textarea id="id" path="attributeName" cssClass="className"/>

Advanced Controllers

Data Binding Composite Objects

When databinding a form to an object, sometimes one of the fields of that object is not a simple variable such as String or int, but another object. For example, if you have an order that is made up of a product, instead of having the product id as a field in the order you can have a product object, giving you direct access to all the fields of that product. To access this data in the JSP you simply use the dot notation in the path field.

<form:input id="product-name" path="product.name"/>

Then when the form is submitted the form object will contain another object nested within containing all its relevant fields.

Data Binding Lists

If the object that is data bound to the form contains a variable that is of type List, you can access this in the form using the index of the item in the list.

<form:input id="list-name" path="listName[0]"/>

Model Attributes

Model Attribute annotation has two contexts. The first is when you use the @ModelAttribute annotation as a method argument, which is saying that Spring should retrieve this argument from the model. If Spring cannot find an object of the type that is defined in the argument it instantiates a new one. Then it looks at all the parameters passed in and try to assign them to the object using the name.

The second context is to annotate a method in the controller. By using the @ModelAttribute on a method you are saying that that method should return an object of the type defined by the method itself.

@ModelAttribute("myList")
public List<String> getMyList() {
    return new LinkedList<>(Arrays.asList(new String[]{
        "List Item 1", "List Item 2", "List Item 3"
    }));
}

This method will return a list of type String, and the name of the list will be "myList". This List object will then be added to the model each time the controller is invoked. In this way, you can slim down the code for your controller method by having objects that are added to the model kept separate and then added automatically without needing to call them.

Session Attributes

@SessionAttributes is specified at the class level and the value is of the resource that you want stored in the session, which was defined as a ModelAttribute in the controller.

@Controller
@RequestMapping("/resource")
@SessionAttributes("resource")
public class ResourceController {

	@ModelAttribute("resource")
	public Resource getResource() {
		System.out.println("adding a new Resource to the model");
		return new Resource();
	}
	
}

In this example the resource object that is instantiated when the controller is called will be added to the session, which means that the controller will not create a new one each time it is called and instead reuse the one from the session (other methods removed from the controller for brevity).

@SessionAttribute can only be used on the same controller, so you can't use it to persist data between different controllers.

Session Status

To remove an object from the session so that you can continue to use it within the same controller but with new (or blank) information, add SessionStatus to the method argument and then use setComplete() within the method itself.

@RequestMapping("/save")
public String save(@ModelAttribute Resource resource, SessionStatus status) {
    // code to save the resource
    status.setComplete();
    return "redirect:/resource/add";
}

In this example, once the resource has been saved and the object is no longer needed by the application setComplete() is invoked and all session data related to this controller is cleared. Then the application returns to the sending page and the form is no longer populated with the data from the previous submission.

Request Body

The @RequestBody annotation indicates that a method parameter should be bound to the body of the HTTP request. It allows you to take the HTTP Request body and map it directly to one of the method parameters.

@RequestMapping(value="/incomingUrlPath", method=RequestMethod.PUT)
public void handle(@RequestBody String body) {
    System.out.println(body);
}

This example would print the request body to the console.

An example of how this annotation could be useful is if the data is then passed to a function that can parse it into JSON.

Response Body

The @ResponseBody annotation goes on a method, and indicates that the output of one of the controller methods should be written straight to the HTTP response body, and not placed in a model, interpreted as a View name or rednered by a JSP.

@RequestMapping(value="/incomingUrlPath", method=RequestMethod.PUT)
@ResponseBody
public String helloWorld() {
    return "Hello World";
}

In this simple method the text "Hello World" would be output in the browser window.

An example of how this could be useful is if the return data is captured by an Ajax function that is able to insert the response into a div or an alert box.

Error And Exception Handling

Validators

Create a new class for the validator in a new package, within the 'data' packages. You need to add the Validator Interface, be sure to choose the Spring Validator. The new class implements the Validation interface which by default has two methods. The first method is the 'supports' method. This method gets passed a class as an argument. What this means is when one of your methods gets passed a class (e.g. a controller method has a class as argument using the @ModelAttribute annotation) and the data coming in will be bounced against the validator class. This is a check to make sure that [for example] the data types are supported by the class variables. The 'supports' method returns a boolean to say that is does support the [e.g. Project] class.

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

The other method is the 'validate' method, which is what really does all of the work. This takes two arguments, the object that is used as a controller method argument (in this case a Project) and the errors. The following example will check to make sure that the project name that was supplied in the form has at least 5 characters, otherwise it will return an error.

@Override
public void validate(Object obj, Errors errors) {
    Project project = (Project) obj;
		
    if(project.getName().length() < 5){
        errors.rejectValue("name", "project.name", "The name is too short");
    }
}

In this example of errors.rejectValue there are 3 arguments. The first is the name of the field, the second is for a code in a properties file but this example just uses project.name, the final argument is the message that you want passed back to the controller.

In order to use the validator, add a new void method to the controller that has an @InitBinder annotation and a WebDataBinder as an argument.

@InitBinder
public void initBinder(WebDataBinder binder) {
    binder.addValidators(new ProjectValidator());
}

Then when the controller is invoked it will consult this method as it is loading. On the binder object invoke the 'addValidators' object and pass in the new validator that you just created.

In order to use this validator on a controller method you need to add the @Valid annotation, but this is not included within Spring, so in the 'pom.xml' file add a new Dependency of validation-api and the one to choose is javax.validation. Now you can import the @Valid class into the controller and this signals to Spring that this method needs to be validated.

To collect the results from the validator you need to add either an 'Errors' or a 'BindingResult' argument directly after the object with @ModelAttribute annotation.

@RequestMapping(value="/add", method=RequestMethod.POST)
public String saveProject(@Valid @ModelAttribute Project project, Errors errors){
		
    if(!errors.hasErrors()) {
        System.out.println("The project validated");
    } else {
        System.out.println("The project did not validate");
    }
}

Bean Validation

You can use the JSR-303 bean validation api, which is the bean validation specification within the Java api. Two implementations of this api are 'org.hibernate hibernate-validation' and 'javax.validation validation-api'. Import these dependencies using 'pom.xml'.

This example specifies that the description field of the class must not be blank. If a form is submitted with a blank description field, Spring will reject the variable assignment and return the message instead.

@NotBlank(message="You must provide a description")
private String description;

Other options to use could be checking the min or max of a number, the length of a string field, or use Pattern to perform regex checking on the field. A regex example is provided below.

 @NotNull
 @Pattern(regexp = "{A-Za-z0-9}*")
 String name;

Form Errors

In Spring MVC you can use the 'errors' tag in the Form tag library to display errors to the user.

In the controller method that is generating the error, if an error is generated return the user to the form and the errors will be sent back to the View automatically. Then in the JSP View where you want the error to be displayed use the form errors tag and in the path supply the name of the field that you would like to display the errors for. This corresponds with the name of the variable in the Model object class.

<div class="form-group">
    <label for="project-name">Name</label>
    <form:input id="project-name" cssClass="form-control" path="name"/>
    <form:errors path="name"/>
</div>

Exception Handler

This handler will handle any server errors so that the stack track is not sent back to the browser, which can be a security risk and is also not a user friendly experience.

Create a new method in the controller that returns a JSP error page that will be displayed to the user. Annotate this method with the @ExceptionHandler annotation, and inside this you can specify all the different types of exceptions that this handler method will handle. If you just want to catch runtime exceptions you can use the Exception.class to find any generic exception. The example below will trigger on a NullPointerException.

@ExceptionHandler(NullPointerException.class)
public String handleError(HttpServletRequest request) {
    return "controller_error";
}

This strategy will only handle exceptions that are thrown by this particular controller.

Exception Handler Resolver

The Spring Handler Resolver interface will handle global exceptions. Create a new class (in its own package called resolvers?) and this class should implement the interface HandlerExceptionResolver. This class will have a resolveException method that needs to be overridden. The resolveException method takes 4 arguments: the first being the request, the second is the response, the third is the handler method and the fourth is the exception that was thrown. This method needs to return a ModelAndView that is the JSP you would like to display instead of the stack trace. The JSP name will be prefixed and suffixed using the view resolvers that have already been established in the Dispatcher Servlet. In addition you need to register this class as a bean within Spring, which you can do by adding the @Component annotation to the class. For this to work you will also need to component-scan this package within the dispatcher-servlet.

Below is the code for the exception resolver.

package com.infiniteskills.mvc.resolvers;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

@Component
public class GlobalHandlerExceptionResolver implements HandlerExceptionResolver {

	@Override
	public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
			Exception exception) {
		
		ModelAndView mav = new ModelAndView();
		mav.setViewName("global_error");
		return mav;
	}

}

And the code needed to add to the dispatcher-servlet.xml

<context:component-scan base-package="com.infiniteskills.mvc.resolvers"/>

This global exception handler will be overridden by any exception handlers within your controllers.

View Resolution

Chaining View Resolvers

In a web application you may want to return the View in something other than a JSP or HTML file, such as returning the data in XML or JSON.

Begin by creating a new view resolver in the dispacher-servlet.xml

<bean class="org.springframework.web.servlet.view.XmlViewResolver">
    <property name="location" value="/WEB-INF/spring/views.xml"/>
    <property name="order" value="1"/>
</bean>

Because there wre now more than one view resolvers in the dispatcher servlet Spring needs to know which order to process them in. This is defined by the property with the name "order". This example is given a property of 'order' with a value of '1', which means that it is consulted first by Spring. If a view resolver is not given an order property it automatically drops to the bottom of the chain.

The file views.xml contains a bean that is triggered when Spring is trying to resolve the name "welcome" from the controller. When Spring finds this bean it will return the file listed, in this case 'xml_welcome.jsp'.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	
	<bean id="welcome" class="org.springframework.web.servlet.view.InternalResourceView">
		<property name="url" value="/WEB-INF/views/xml_welcome.jsp"/>
	</bean>

</beans>

If none of the bean id match the string from the controller, the dispatcher servlet will move to the next resolver and try to resolve the file.

Content Negotiating View Resolver

The content negotiating view resolver delegates to other resolvers in order to find the appropriate strategy to represent a particular resource.

The main determinant of what view resolver will be used to resolve a resource is the file extension or the accept header that is provided with an HTTP request. The content negotiating view resolver is capable of returning the different views in formats such as JSON or XML. It's also capable of just returning the JSP pages. First add the "com.fasterxml.jackson.core jackson-databind" library to the 'pom.xml' file and change it from a bundle to a jar.

Create a new bean inside the dispatcher-servlet.xml that has a property name of "viewResolvers" and a list and then place all of the current view resolvers inside the list. Then specify a new property for the contentNegotiationManager and one for the default views.

<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
    <property name="viewResolvers">
        <list>
            <!-- existing view resolvers -->
        </list>			
    </property>
    <property name="contentNegotiationManager">
        <bean class="org.springframework.web.accept.ContentNegotiationManager">
            <constructor-arg>
                <bean class="org.springframework.web.accept.PathExtensionContentNegotiationStrategy">
                    <constructor-arg>
                        <map>
                            <entry key="json" value="application/json"/>
                            <entry key="xml" value="application/xml"/>
                        </map>
                    </constructor-arg>
                </bean>
            </constructor-arg>
        </bean>
    </property>
    <property name="defaultViews">
        <list>
            <bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"/>
        </list>			
    </property>
</bean>

To try this out create a new method in the controller that returns an object to the view. The method should be annotated with the @ResponseBody annotation. This method takes a project id from the URL, passes it to the project service which will return the project object. Then because the method is annotated with the @ResponseBody, this object will be passed directly to the view.

@RequestMapping(value="/find/{projectId}")
public @ResponseBody Project findProjectObject(Model model, @PathVariable("projectId") Long projectId) {
    return this.projectService.find(projectId);
}

On the Model that object is a type of, add the JAXB XML annotation @XmlRootElement, which allows you to control some of the XML elements that will be produced. You could also use the Jackson annotation if you wanted to control what the JSON will look like.

@XmlRootElement(name="project")
public class Project {

}

How this works is that when the user requests a page there is a String returned by the controller and Spring knows that the application is trying to display some HTML to the user. The view resolver in the dispatcher servlet looks in the mapping section of the ContentNegotiationManager, but there are no options for HTML there so it defaults to the view resolvers in the top half, and then when it has added the prefix and suffix defined there it is able to locate the JSP and display it to the user. Now if you enter a URL in the browser that points to the method in the controller (in this case it's project/find/{projectId}) including the id of the thing you are looking for and add an extension of .xml or .json the application will automatically convert and return the raw data in that format. This is very useful if you wanted to provide an API, for example. This will also work if you set the 'ACCEPT' header using JavaScript.

Redirects

When handling form posts, if you simply return a JSP and then the user tries to refresh the page, the browser will try to resubmit the form again (and it will show an alert box warning the user of this). Instead, a better strategy is to have a redirect to the page. This performs a HTTP 302 redirect behind the scenes and there is no opportunity for the user to re-post the form.

return "redirect:/{url-to-be-redirected-to}"

Redirect Attributes

Flash Attributes

Advanced Components

Interceptors

Bean Scopes

Jackson JSON Support

Controller Advice

Converters

File Upload

Extra Information