This post is about Java Web application development using the Spring MVC framework. It distills much of what I have learned from developing enterprise applications with Spring MVC, guiding usage of the components of Spring MVC that I most frequently encounter in practice and on discussion forums. This post provides a brief overview of these components by taking you through the development of an example Spring MVC application from scratch.
My goals in writing this post are to guide developers who are unfamiliar with Spring MVC, and to supply a convenient reference to more seasoned developers.
This post is written for developers who are interested in working with Spring MVC, whether they are newcomers or have been working with it for years. It assumes basic familiarity with Spring and Java Web applications, though I have included links to reference information to help fill these gaps.
I generally don't build a Spring MVC application from scratch, opting instead to build upon a framework I have already prepared, or a sample I have already developed. Since understanding the fundamental concepts is in no way the same as generating boilerplate code from memory, I find it valuable to maintain a barebones framework from which to springboard new project development.
The following components of Spring MVC are covered:
This post does not cover advanced, uncommon, or deep-dive topics into Spring MVC, Spring Web Flow, Spring JavaScript, JavaScript frameworks, etc., although there is some basic usage of both Spring JavaScript and the Dojo Toolkit's UI library Dijit. Instead, this post focuses on the development of an example application which features the common Spring MVC components.
This post is based on a minimal example application which serves two goals: to provide a useful foundation in something not unlike a real-world application, and to avoid diving into a specific problem domain which eclipses the concepts as they are presented.
The example application is a limited employee directory. A user interacts with a Web front-end to view a list of employees, add new employees, and edit or delete existing employees.
This shows the main view of the Employee Directory. It lists all employees in the directory, provides links to view and edit the details of each, and includes a button for adding a new employee to the directory.
This shows a detailed view of a single employee. When the details of an employee are viewed, or when a new employee is to be added, this view is presented.
The Employee Directory is structured as a Maven project. At the root
of the project is the POM, where dependencies and other project
configuration is maintained. Application source code is placed in
src/main/java
, with corresponding configuration in
src/main/resources
. Web application content and
configuration is within src/main/webapp
. This includes the
web.xml
deployment descriptor, the JSP view templates, and
other Web content (stylesheets, images, etc.).
Source code included in this post is formatted as follows.
CodeConventions.java:
package com.earldouglas.barebones.springmvc;
public class CodeConventions {
public static void main(String[] arguments) {
System.out.println("Hello World!");
}
}
web.xml
deployment descriptor, and the WAR file
format in general, are covered in Wikipedia.The core of the Employee Directory is the largest single segment of its construction. It consists of an in-memory repository of employees, with an HTML front-end for user interaction, using the following components:
Employee
: a domain class representing an employeeEmployeeService
: a simple CRUD-like interface for
fetching, saving, and deleting EmployeesInMemoryEmployeeService
: an EmployeeService
implementation which contains an in-memory collection of EmployeesBindableEmployee
: a flat class designed to bind to HTML
formsEmployeeController
: a Spring MVC controller to interact
with the useremployee.jsp
: a template for an HTML form for creating
or updating an employeeemployees.jsp
: a template for a list of employees in
the Employee Directorystyle.css
: template CSS configuration for the
viewspom.xml
: project dependencies and managementweb.xml
: the J2EE Web deployment descriptor containing
the Spring DispatcherServlet, the Front Controller for a Spring MVC
applicationspring-mvc-servlet.xml
: the Spring configuration for
the various Spring beans and Spring infrastructureAt the architectural bottom of the code is the domain class
Employee
.
Employee.java:
public class Employee {
private Long id;
private String name;
private String title;
public Edmployee() {
}
public Employee(Long id, String name, String title) {
this.id = id;
this.name = name;
this.title = title;
}
// Getters and setters omitted for brevity.
}
A service interface is defined by EmployeeService
, which
provides the standard CRUD behavior.
EmployeeService.java:
public interface EmployeeService {
public Collection<Employee> get();
public Employee get(Long id);
public Employee save(Employee employee);
public void delete(Long id);
}
A simple in-memory EmployeeService
is implemented by
InMemoryEmployeeService
.
InMemoryEmployeeService.java:
@Repository
public class InMemoryEmployeeService implements EmployeeService {
private long maxId = 3L;
private final Map<Long, Employee> employees = new HashMap<Long, Employee>() {
private static final long serialVersionUID = 1L;
{
put(1L, new Employee(1L, "Professor Jefe", "The Boss"));
put(2L, new Employee(2L, "Number Two", "Number Two"));
put(3L, new Employee(3L, "Johnny McDoe", "Work Man"));
}
};
@Override
public Employee get(Long id) {
return employees.get(id);
}
@Override
public Collection<Employee> get() {
return employees.values();
}
private synchronized long nextId() {
return ++maxId;
}
@Override
public Employee save(Employee employee) {
if (employee.getId() == null) {
.setId(nextId());
employee}
.put(employee.getId(), employee);
employeesreturn employee;
}
@Override
public void delete(Long employeeId) {
.remove(employeeId);
employees}
}
This service implementation will provide core services to the Web
front-end, implemented as EmployeeController
. The
EmployeeController
is responsible for handling HTTP
requests, and translating between the UI model and the domain model
classes.
EmployeeController.java:
@Controller
@RequestMapping("/employees")
public class EmployeeController {
@Autowired
private EmployeeService employeeService;
@RequestMapping(method = RequestMethod.GET)
public Collection<BindableEmployee> get() {
return BindableEmployee.bindableEmployees(employeeService.get());
}
@RequestMapping(value = "/new", method = RequestMethod.GET)
public String get(Model model) {
return get(null, model);
}
@RequestMapping(value = "/{employeeId}", method = RequestMethod.GET)
public String get(@PathVariable Long employeeId, Model model) {
= employeeService.get(employeeId);
Employee employee if (employee != null) {
.addAttribute(new BindableEmployee(employee));
model} else {
.addAttribute(new BindableEmployee());
model}
return "employee";
}
@RequestMapping(value = "/{employeeId}/delete", method = RequestMethod.GET)
public String deleteViaGet(@PathVariable Long employeeId) {
return delete(employeeId);
}
@RequestMapping(value = "/{employeeId}", method = RequestMethod.DELETE)
public String delete(@PathVariable Long employeeId) {
.delete(employeeId);
employeeServicereturn "redirect:../../employees";
}
@RequestMapping(method = RequestMethod.POST)
public String save(BindableEmployee bindableEmployee) {
.save(bindableEmployee.asEmployee());
employeeServicereturn "redirect:employees";
}
}
There are several things going on in EmployeeController
.
The class is annotated with @Controller
to indicate to
Spring its function as an MVC controller and its candidacy for component
scanning by the Spring container. In addition, it is annotated with a
class-level @RequestMapping
to base all of its method-level
@RequestMapping
s on a top-level URL pattern. Each method is
also annotated with @RequestMapping
to further constrain
their specific associated request patterns.
The get(Long, Model)
, deleteViaGet(Long)
,
and delete(Long)
methods are each configured to map to
RESTful URLs which contain the identifier of the Employee
object on which to operate.
The save(BindableEmployee
method contains no URL
information in its @RequestMapping
, so it will map simply
to /employees
, that of the class-level annotation. This is
in contrast to the other methods, such as get(Model)
, which
specifies /new
. This combines with the class-level
annotation to map to /employees/new
.
All of the methods specify a HTTP request method in the RESTful style.
EmployeeController
presents Employee
-like
data to the user both as textual data and as an HTML form. This is cause
for a special class to be designed with the Web UI in mind, specifically
to bind to the HTML form. This role is filled by
BindableEmployee
.
BindableEmployee.java:
public class BindableEmployee {
private Long id;
private String name;
private String title;
public BindableEmployee() {
}
public BindableEmployee(Employee employee) {
this.id = employee.getId();
this.name = employee.getName();
this.title = employee.getTitle();
}
// Getters and setters omitted for brevity.
public static Collection<BindableEmployee> bindableEmployees(
Collection<Employee> employees) {
Collection<BindableEmployee> bindableEmployees = new ArrayList<BindableEmployee>();
for (Employee employee : employees) {
.add(new BindableEmployee(employee));
bindableEmployees}
return bindableEmployees;
}
public Employee asEmployee() {
return new Employee(id, name, title);
}
}
BindableEmployee
knows both how to convert itself into
the domain class Employee
via its asEmployee()
method and how to convert a collection of instances of
Employee
into a collection of instances of
BindableEmployee
. This is a convenient location for this
functionality, and an important one as well. Because this conversion is
only concerned with connecting the domain to a thin Web layer, the
appropriate location for related computation is in the Web layer and out
of the domain.
That's all the Java code there is to write. Simple!
Next, the view templates are defined.
employee.jsp
uses Spring's form
tag library
to build a form with text inputs for the name and title of an
employee.
employee.jsp:
<c:url var="formUrl" value="/employees" />
<form:form action="${formUrl}" modelAttribute="bindableEmployee">
class="button">
<ul<c:if test="${not empty bindableEmployee.id}">
href="<c:url value="/employees/${bindableEmployee.id}/delete" />">
<li><a
Delete
</a></li></c:if>
id="submit" type="submit" value="Save" /></li>
<li><input
</ul><form:hidden path="id" />
<ul>
<li> for="name">Name</label>
<label<form:input path="name" />
</li>
<li> for="title">Title</label>
<label<form:input path="title" />
</li>
</ul></form:form>
employees.jsp
displays a list of the employees in the
system, provides links to edit each, and includes a button to add a new
employee to the system.
employees.jsp:
class="button">
<ul href="<c:url value="/employees/new" />">Add Employee</a></li>
<li><a
</ul> cellpadding="0" cellspacing="0">
<table
<thead>
<tr>
<th>Name</th>
<th>Title</th>
</tr>
</thead>
<tbody><c:forEach items="${bindableEmployeeList}" var="employee">
<tr>
<td> href="<c:url value="/employees/${employee.id}" />">
<a<c:out value="${employee.name}" />
</a>
</td><c:out value="${employee.title}" /></td>
<td>
</tr></c:forEach>
</tbody> </table>
Next, the Spring configuration is defined.
spring-mvc-servlet.xml:
<?xml version="1.0" encoding="UTF-8"?>
beans xmlns="http://www.springframework.org/schema/beans"
< xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
context:annotation-config />
<mvc:annotation-driven />
<mvc:default-servlet-handler />
<
bean class="com.earldouglas.barebones.springmvc.web.EmployeeController" />
<
beans> </
The <mvc:annotation-driven />
element tells Spring
to create a DefaultAnnotationHandlerMapping
bean to set up
handling of the @RequestMapping
annotations in
EmployeeController
, while the lone bean definition
registers a view resolver which looks for JSPs by view name. The
EmployeeController
is picked up by the
component-scan
, instantiated, and mapped to its applicable
requests by DefaultAnnotationHandlerMapping
.
Next, we have our Web deployment descriptor.
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
web-app xmlns="http://java.sun.com/xml/ns/javaee"
< xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
servlet>
<servlet-name>spring-mvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<servlet>
</
servlet-mapping>
<servlet-name>spring-mvc</servlet-name>
<url-pattern>/</url-pattern>
<servlet-mapping>
</
web-app> </
Note that since the DispatcherServlet
is named
spring-mvc
, by convention the Spring configuration is
retrieved from /WEB-INF/spring-mvc-servlet.xml
.
component-scan
tag, the @Controller
annotation, and classpath scanning is covered in the Spring
3 Reference, section 3.10.@RequestMapping
annotation is covered in the Spring
3 Reference, section 15.3.2.DefaultAnnotationHandlerMapping
and handler
mappings in general are covered in the Spring
3 Reference, section 18.5.ControllerClassNameHandlerMapping
class, covered in the Spring
3 Reference, section 15.10.Form validation goes hand-in-hand with Web applications, and server-side form validation is an easy addition to the core application. JSR-303 Bean Validation specifies annotations for declarative validation rules, which can be standardized across the layers of an enterprise application from the database to the user interface.
The following additions are required:
javax.validation
to the
Maven POM@Valid
annotation to controller method inputsErrors
objects to controller method inputs for view
error bindingBindableEmployee.java
Validator
to the Spring contexthibernate-validator
to the Maven POM<form:errors />
elements to
employee.jsp
There is only one controller method with input:
save(BindableEmployee)
. The BindableEmployee
parameter is annotated with @Valid
, which will trigger
Spring will use its configured JSR-303 Validator
to
validate the BindableEmployee
.
Spring needs a place to put the result of the validation, so a
BindingResult
is added to the controller method immediately
after the corresponding BindableEmployee
parameter. This
will make binding errors available to the view.
EmployeeController.java:
@Controller
@RequestMapping("/employees")
public class EmployeeController {
// Some methods omitted for brevity.
@RequestMapping(method = RequestMethod.POST)
public String save(@Valid BindableEmployee bindableEmployee,
) {
BindingResult bindingResult
if (bindingResult.hasErrors()) {
return "employee";
}
.save(bindableEmployee.asEmployee());
employeeServicereturn "redirect:employees";
}
}
JSR-303 annotations are added to BindableEmployee.java
to limit the pattern of name
to two words and the pattern
of title
to at least one word.
BindableEmployee.java:
public class BindableEmployee {
private Long id;
@Pattern(regexp = "\\w+ \\w+")
private String name;
@Pattern(regexp = "\\w+( \\w+)?")
private String title;
// Methods omitted for brevity.
}
The Spring MVC namespace will automatically configure a
JSR-303-backed Validator
as long as it is present on the
classpath.
Next, the Spring <form:errors />
element is added
to the view to show validation errors.
employee.jsp:
<ul>
<li> for="name"><spring:message code="heading.name" /></label>
<label<form:input path="name" />
<form:errors path="name" />
</li>
<li> for="title"><spring:message code="heading.title" /></label>
<label<form:input path="title" />
<form:errors path="title" />
</li> </ul>
When the form is submitted, the inputs are automatically validated, and any validation errors are displayed next to each corresponding input field in the form.
@Valid
annotation is covered
in the Spring
3 Reference, section 5.7.4.1.BindingResult
and data binding are covered in the
Spring
3 Reference, section 5.7.3.The counterpart to server-side validation is client-side validation, which is made easy by Spring JavaScript.
The following additions are required:
spring-js
to the Maven POMResourceServlet
to the Web deployment
descriptorSpring JavaScript includes ResourceServlet
, which
provides various scripts and CSS layouts from both Dojo and Spring
JavaScript. These add the functionality and look-and-feel needed for
rich client-side validation. The ResourceServlet
must be
added to the Web deployment descriptor.
web.xml:
servlet>
<servlet-name>Resources Servlet</servlet-name>
<servlet-class>org.springframework.js.resource.ResourceServlet</servlet-class>
<servlet>
</servlet-mapping>
<servlet-name>Resources Servlet</servlet-name>
<url-pattern>/resources/*</url-pattern>
<servlet-mapping> </
The Dojo and Spring JavaScript scripts and layout must be added to each view which will provide rich client behavior.
employee.jsp:
<head>
<title><spring:message code="application.title" /></title>
<script type="text/javascript"
src="<c:url value="/resources/dojo/dojo.js" />"></script>
<script type="text/javascript"
src="<c:url value="/resources/spring/Spring.js" />"></script>
<script type="text/javascript"
src="<c:url value="/resources/spring/Spring-Dojo.js" />"></script>
<link type="text/css" rel="stylesheet"
href="<c:url value="/resources/dijit/themes/tundra/tundra.css" />" />
<link type="text/css" rel="stylesheet"
href="<c:url value="/style.css" />" />
</head>
Spring JavaScript uses the decorator pattern to cleanly introduce rich client behavior into views. Script-free HTML is first built to create a fully functioning application, and Spring JavaScript decorators are added on top of the existing DOM to introduce rich behavior. This means that a view is fully functional on its own, which allows the application to run in an environment where JavaScript support might be limited or non-existent.
This practice, known as progressive enhancement, allows a Web
application to remain functional across a wealth of browsers, which may
vary in their level of support of JavaScript and CSS. The most important
takeaway from this idea is that the onclick
attribute is
never directly used in HTML code. It is only accessed by a decorator,
meaning its behavior is only used when the decorator script itself is
supported.
The form in employee.jsp
is updated to insert Spring
JavaScript decorators.
employee.jsp:
</form:form>
</div>
<script type="text/javascript">
.addDecoration(new Spring.ValidateAllDecoration( {
SpringelementId : "submit",
event : "onclick"
;
}))
.addDecoration(new Spring.ElementDecoration( {
SpringelementId : "name",
widgetType : "dijit.form.ValidationTextBox",
widgetAttrs : {
regExp : "\\w+ \\w+",
required : true
};
}))
.addDecoration(new Spring.ElementDecoration( {
SpringelementId : "title",
widgetType : "dijit.form.ValidationTextBox",
widgetAttrs : {
regExp : "\\w+( \\w+)?",
required : true
};
}))</script>
Field decorators have been added to all of the form input fields, and a global validation director has been added to the form submission button. These are just a few examples of the vast set of features provided by Dojo.
The employee form will now validate on the client, displaying any validation errors dynamically.
A Web application would seldom be complete without at least a minimal security layer to prohibit unauthenticated access to protected resources. In this section, basic security is introduced by adding HTML form-based authentication using Spring Security.
The following additions are required:
DelegatingFilterProxy
to the Web
deployment descriptorSpring Security's DelegatingFilterProxy
is essentially a
J2EE Filter
which nominally handles all requests and
determines how to allow or reject access.
web.xml:
filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>
<
org.springframework.web.filter.DelegatingFilterProxyfilter-class>
</filter>
</filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
<filter-mapping>
</
context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
<
/WEB-INF/spring-mvc-security.xmlparam-value>
</context-param>
</
listener>
<listener-class>
<
org.springframework.web.context.ContextLoaderListenerlistener-class>
</listener> </
Spring's ContextLoaderListener
is needed because there
is now a parent Spring context which is extended by the
spring-mvc
context of before. The
contextConfigLocation
parameter specifies the location of
the new parent configuration file.
spring-mvc-security.xml:
<!-- Enable Spring Security with HTTP basic authentication. -->
http auto-config="true">
<http-basic />
<intercept-url pattern="/**" access="ROLE_ADMIN" />
<form-login />
<http>
</
<!-- An AuthenticationProvider with sample users and roles. -->
authentication-manager>
<authentication-provider>
<user-service>
<user name="admin" password="password"
< authorities="ROLE_ADMIN" />
user-service>
</authentication-provider>
</authentication-manager> </
This nearly minimal configuration sets up an in-memory repository of roles, and enforces access to every resource against this repository. Here, a form-based login page is provided by Spring.
A major component of enterprise applications is information storage and retrieval via a relational database. Most of this behavior is confined to a special data tier, with a thin API exposed to the application tier for interaction with the domain. In this section, a Hibernate-based data tier is introduced for storage and retrieval of data.
The following additions are required:
hibernate
, hibernate-annotations
,
persistence-api
, jta
, spring-orm
,
commons-dbcp
, and hsqldb
Employee
EmployeeController
with the
repositorypersistence-context.xml
DataSource
via
JNDIA persistable type is created to represent the domain model. In this simple example, this will closely resemble the UI model, but it is important to draw a distinction between the two, as they serve two very different purposes.
The purpose of a domain model is to represent the domain model. The purpose of a UI model is to represent the UI model. This is intentionally redundant, because it is easy to forget. The domain model can include potentially complex object hierarchies as well as database-specific metadata. The UI model will have forms and other data structures which will tend to be very flat, and contain very specific information meant to be rendered in a view.
Attempting to merge the two models can get painfully cumbersome, as the domain model tends not to map directly to the UI model. Furthermore, the resulting tight coupling will force any changes in one to necessitate changes in the other. It is much simpler to create simple conversion logic in the service tier to translate between the two models.
The Employee
class is made persistable with JPA
annotations.
Employee.java:
@Entity
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column
private Long id;
@Column
private String name;
@Column
private String title;
public Employee() {
}
public Employee(Long id, String name, String title) {
this.id = id;
this.name = name;
this.title = title;
}
// Getters and setters omitted for brevity.
}
A repository serves the domain as an opaque entry point into the database. It provides accessors and mutators for database tables represented only by domain objects.
HibernateEmployeeService.java:
@Transactional
public class HibernateEmployeeService implements EmployeeService {
@Autowired
private SessionFactory sessionFactory;
private Session session() {
return sessionFactory.getCurrentSession();
}
@Override
public void delete(Long employeeId) {
session().delete(get(employeeId));
}
@Override
public Employee get(Long employeeId) {
return (Employee) session().createCriteria(Employee.class).add(
.idEq(employeeId)).uniqueResult();
Restrictions}
@SuppressWarnings("unchecked")
@Override
public Collection<Employee> get() {
return session().createCriteria(Employee.class).list();
}
@Override
public Employee save(Employee employee) {
session().saveOrUpdate(employee);
return employee;
}
}
This repository is meant to work with Hibernate, and so uses a
Hibernate SessionFactory
.
In this example, the service tier is contained entirely within
EmployeeController
, which is modified to interact with the
new repository.
BindableEmployee
provides an Employee
-based
constructor plus two helper methods, asEmployee()
and
bindableEmployee(Collection<Employee>)
, which do the
mapping between the UI model and the domain model.
BindableEmployee.java:
public class BindableEmployee {
private Long id;
private String name;
private String title;
public BindableEmployee() {
}
public BindableEmployee(Employee employee) {
this.id = employee.getId();
this.name = employee.getName();
this.title = employee.getTitle();
}
// Getters and setters omitted for brevity.
public static Collection<BindableEmployee> bindableEmployees(
Collection<Employee> employees) {
Collection<BindableEmployee> bindableEmployees = new ArrayList<BindableEmployee>();
for (Employee employee : employees) {
.add(new BindableEmployee(employee));
bindableEmployees}
return bindableEmployees;
}
public Employee asEmployee() {
return new Employee(id, name, title);
}
}
Next, a new global Spring context is created to manage the database-related objects.
persistence-context.xml:
tx:annotation-driven />
<
context:annotation-config />
<
bean id="transactionManager"
< class="org.springframework.orm.hibernate3.HibernateTransactionManager">
property name="sessionFactory" ref="sessionFactory" />
<bean>
</
bean class="com.earldouglas.barebones.springmvc.service.HibernateEmployeeService" />
<
bean id="sessionFactory"
< class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"
init-method="createDatabaseSchema">
property name="dataSource" ref="hsqlDataSource" />
<property name="packagesToScan" value="com.earldouglas.barebones.springmvc" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
<
org.hibernate.dialect.HSQLDialectprop>
</prop key="hibernate.show_sql">false</prop>
<props>
</property>
</bean>
</
bean id="hsqlDataSource" class="org.apache.commons.dbcp.BasicDataSource"
< destroy-method="close">
property name="driverClassName" value="org.hsqldb.jdbcDriver" />
<property name="url" value="jdbc:hsqldb:mem:spring-mvc" />
<property name="username" value="sa" />
<property name="password" value="" />
<bean> </
This context is made a parent context via
ContextLoaderListener
in the Web deployment descriptor.
web.xml:
context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
<
/WEB-INF/spring-mvc-security.xml
/WEB-INF/persistence-context.xml Barebones Spring MVC: Database Integrationparam-value>
</context-param>
</
listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
<listener> </
A minor change is required in spring-mvc-servlet.xml
to
enable service tier transaction management.
spring-mvc-servlet.xml:
<!--
<bean
class="com.earldouglas.barebones.springmvc.service.InMemoryEmployeeService"
/>
-->
The DataSource
can optionally be externalized from the
Spring configuration via JNDI. Configuration specifics, such as database
username and password, are then kept out of the Spring configuration and
delegated to the application server. This adds security by allowing the
application server protect these sensitive data. For Apache Tomcat, the
DataSource
is added to
META-INF/context.xml
.
context.xml:
<?xml version="1.0" encoding="UTF-8"?>
Context>
<Resource name="jdbc/hsqlDataSource"
< auth="Container"
type="javax.sql.DataSource"
username="sa"
password=""
driverClassName="org.hsqldb.jdbcDriver"
factory="org.apache.commons.dbcp.BasicDataSourceFactory"
url="jdbc:hsqldb:mem:mydatabase"/>
Context> </
The DataSource
bean is removed from the Spring
configuration, and replaced by a JNDI-lookup:
persistence-context.xml:
jee:jndi-lookup id="hsqlDataSource" jndi-name="java:comp/env/jdbc/hsqlDataSource" /> <
jee
namespace provides easy JNDI integration,
and is covered in the Spring
Reference, section C.2.3.One of the awesome features of the Spring MVC is its ability to easily support multiple types of request/response content. In fact, the same Spring MVC beans can be used to serve conventional HTML, RESTful XML, JSON, Atom, etc. usually with only some minor configuration changes.
This section introduces a RESTful Web service which utilizes the existing Spring MVC beans and configuration.
The following additions are required:
BindableEmployee
to define its XML
marshalling configurationspring-oxm
library to the Maven POMInternalResourceViewResolver
in the
Spring context with a ContentNegotiatingViewResolver
and
some JAXB marshalling configurationJAXB annotations are similar in use to Hibernate annotations. In this
example, BindableEmployee
is simple and flat enough that it
will marshal easily with a few JAXB annotations.
BindableEmployee.java:
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType
@XmlRootElement
public class BindableEmployee {
@XmlElement
private Long id;
@XmlElement
@Pattern(regexp = "\\w+ \\w+")
private String name;
@XmlElement
@Pattern(regexp = "\\w+( \\w+)?")
private String title;
// Methods omitted for brevity.
}
Spring MVC needs the ability to choose an appropriate view resolver
depending on the specifics of the request. When a conventional
text/html
request is made from a Web browser, Spring MVC
uses an InternalResourceViewResolver
to delegate to a JSP
view template as before. When an application/xml
request is
made by a Web service consumer, Spring MVC uses a
MarshallingView
with a JAXB marshaller to provide an XML
representation of the BindableEmployee
.
spring-mvc-servlet.xml:
bean
< class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
property name="viewResolvers">
<list>
<bean
< class="org.springframework.web.servlet.view.InternalResourceViewResolver">
property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
<bean>
</bean class="org.springframework.web.servlet.view.BeanNameViewResolver" />
<list>
</property>
</bean>
</
oxm:jaxb2-marshaller id="marshaller">
<oxm:class-to-be-bound
< name="com.earldouglas.barebones.springmvc.web.BindableEmployee" />
oxm:jaxb2-marshaller>
</
bean name="employee"
< class="org.springframework.web.servlet.view.xml.MarshallingView">
constructor-arg ref="marshaller" />
<bean> </
The HTML/XML duality of this example can be tested with
curl
:
> curl -H 'Accept: application/xml' localhost:8080/spring-mvc/employee
> curl -H 'Accept: text/html' localhost:8080/spring-mvc/employee
Message externalization in the Web view layer digs the various text out of view templates and keeps it centralized and manageable. It also provides a convenient launchpad for site internationalization. Spring MVC provides for easy introduction of message externalization and internationalization into the view layer.
The following additions are required:
ResourceBundleMessageSource
bean to the Spring
contextmessages.properties
into
Spanish in messages_es.properties
LocaleChangeInterceptor
and
SessionLocaleResolver
beans to the Spring contextSpring needs to know where it will find externalized messages. This
is done with Java's resource bundle functionality, encapsulated in a
Spring ResourceBundleMessageSource
.
spring-mvc-servlet.xml:
bean id="messageSource"
< class="org.springframework.context.support.ResourceBundleMessageSource">
property name="basename" value="messages" />
<bean> </
There isn't much in the way of messages in this example, but the
little that is there is moved into a properties file named in the above
ResourceBundleMessageSource
.
message.properties:
application.title=Employee Directory
button.add-employee=Add employee
button.delete=Delete
button.save=Save
heading.employees=Employees
heading.employee=Employee
heading.name=Name
heading.title=Title
english=English
spanish=Español
This is also done in Spanish. Translations were performed with the help of Google Translate, so as far as I know nothing below says "Your mother was a hamster."
messages_es.properties:
application.title=Directorio de Empleados
button.add-employee=Añadir empleado
button.delete=Eliminar
button.save=Guardar
heading.employees=Empleados
heading.employee=Empleado
heading.name=Nombre
heading.title=Título
Next, a couple of beans are added to the Spring context to allow detection of a user's desire to switch languages, and the ability to store that preference in the user's session.
spring-mvc-servlet.xml:
mvc:interceptors>
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" />
<mvc:interceptors>
</bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver" /> <
A user simply includes the HTTP request parameter
lang=es
to change the language to Spanish.