Mastering Spring MVC 4 - Sample Chapter
Mastering Spring MVC 4 - Sample Chapter
Mastering Spring MVC 4 - Sample Chapter
ee
You will then test, secure, and optimize your Spring web
application and design RESTful services that will be
consumed on the frontend.
$ 49.99 US
31.99 UK
P U B L I S H I N G
Geoffroy Warin
pl
C o m m u n i t y
Mastering Spring
MVC 4
Sa
m
E x p e r i e n c e
D i s t i l l e d
Mastering Spring
MVC 4
Gain expertise in designing real-world web applications using the
Spring MVC framework
Geoffroy Warin
Preface
As a web developer, I like to create new things, put them online quickly, and move
on to my next idea.
In a world where all our applications are connected to each other, we need to interact
with social media to promote our products and complex systems, to provide great
value for our users.
Until recently, all this was a distant and complicated world for Java developers. With
the birth of Spring Boot and the democratization of cloud platforms, we can now
create amazing applications and make them available to everyone in record time,
without spending a penny.
In this book, we will build a useful web application from scratch. An application with
a lot of neat features, such as internationalization, form validation, distributed sessions
and caches, social login, multithreaded programming, and many more.
Also, we will test it completely.
By the end of this book, we will have published our little application and made it
available on the Web.
If this sounds like fun to you, let's not waste any more time and get our hands on the
code!
Preface
Preface
Chapter 9, Deploying Your Web Application to the Cloud, guides us through publishing
our application. It shows how the different PaaS solutions can be compared to each
other. Then, it demonstrates how to deploy the application on both Cloud Foundry
and Heroku.
Chapter 10, Beyond Spring Web, discusses the Spring ecosystem in general, what
modern web applications are made of, and where to go from there.
[ 33 ]
The MVC pattern became wildly popular after emerging from the world of Smalltalk
and landing in the Ruby on Rails framework.
The architectural pattern features three layers:
The View: This is made up of several representations of the data that will be
displayed to your users.
The Controller: This is the part of the application that will handle user
interactions. It's a bridge between the model and the view.
[ 34 ]
Chapter 2
The idea behind MVC is to decouple the View from the Model. The model must
be self-contained and ignorant of the UI. This basically allows the same data to be
reused across multiple views. These views are different way to look at the data.
Drill down or using different renderers (HTML, PDF) are good illustrations of this
principle.
The Controller acts as a mediator between the user and the data. Its role is to control
actions available to the end user, as well as routing through the different views of the
application.
The model is constituted from very simple plain old Java objects (POJOs)
with only getters and setters
This can be a bad practice depending on the complexity of your business domain.
Generally speaking, DDD practices require additional efforts to isolate the domain
from the application logic.
Architecture is always a tradeoff. It is good to note that typical ways of designing a
Spring application can lead to complicated maintenance somewhere along the road.
[ 35 ]
Your domain should always be in a valid state. Leave validation inside the
form objects using validators or JSR-303's validation annotations.
Think of your data layer in term of repositories with domain queries (refer to
Spring Data Specification, for example)
Use real objects as much as possible. For instance, manipulate the FirstName
class rather than a string.
There is much more to DDD than these simple rules: Entities, value types,
Ubiquitous Language, Bounded Context, Onion Architecture, and anti corruption
layers. I strongly encourage you to study these principles on your own. As far as we
are concerned, with this book we will try to keep in mind the guidelines listed earlier
as we craft our web application. These concerns will become more familiar to you as
we advance through this book.
Security integration
Github integration
Elasticsearch integration
The GitHub wiki associated with the project is really detailed and will help you get
started easily with the project.
[ 36 ]
Chapter 2
Using Thymeleaf
Thymeleaf is a templating engine that gets particular attention from the Spring
community.
Its success is due mostly to its friendly syntax (it almost looks like HTML) and the
ease with which it can be extended.
Various extensions are available and integrated with Spring Boot:
Support
Dependency
Layouts
nz.net.ultraq.thymeleaf:thymeleaf-layoutdialect
com.github.mxab.thymeleaf.
HTML5 data-* attributes
extras:thymeleaf-extras-data-attribute
Internet Explorer conditional org.thymeleaf.extras:thymeleaf-extrasconditionalcomments
comments
Support for spring security
org.thymeleaf.extras:thymeleaf-extrasspringsecurity3
A very good tutorial on Thymeleaf's integration with Spring can be found at http://
www.thymeleaf.org/doc/tutorials/2.1/thymeleafspring.html.
[ 38 ]
Chapter 2
apply
apply
apply
apply
apply
plugin:
plugin:
plugin:
plugin:
plugin:
'java'
'eclipse'
'idea'
'spring-boot'
'io.spring.dependency-management'
jar {
baseName = 'masterSpringMvc'
version = '0.0.1-SNAPSHOT'
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
compile 'org.springframework.boot:spring-boot-starter-web'
compile 'org.springframework.boot:spring-boot-starter-thymeleaf'
testCompile 'org.springframework.boot:spring-boot-starter-test'
}
eclipse {
classpath {
containers.remove('org.eclipse.jdt.launching.JRE_CONTAINER')
containers 'org.eclipse.jdt.launching.JRE_CONTAINER/org.
eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8'
}
}
task wrapper(type: Wrapper) {
gradleVersion = '2.3'
}
[ 39 ]
We can see from the very start that Thymeleaf integrates perfectly with html and its
syntax almost feels natural.
The th:text value is put between pipes. It means that all the values inside the text
will be concatenated.
It might seem a bit awkward at first, but in practice, text will rarely be hardcoded in
our pages; so, Thymeleaf makes an opinionated design decision here.
Thymeleaf has a big advantage for web designers: everything that is dynamic inside
the templates can fall back to a default value in the case where they are opened
without the server running. Resource URLs can be specified relatively and every
markup can contain placeholders. In our previous example, the text "Hello html"
would not be displayed when the view is rendered in the context of our application,
but it will if the file is opened directly with a web browser.
To speed up development, add this property to your application.properties file:
spring.thymeleaf.cache=false
This will disable the view cache and cause templates to reload every time they are
accessed.
Of course, this setting will need to be disabled when we go into production. We will
see that in Chapter 8, Optimizing Your Requests.
[ 40 ]
Chapter 2
In this example, the controller will redirect the user to the view name resultPage.
The ViewResolver interface will then associate this name with our page.
Let's launch our application again and go to http://localhost:8080.
You will see the following page:
[ 41 ]
DispatcherServlet
The entry point of every Spring web application is the DispatcherServlet. The
following figure illustrates the Dispatcher Servlet architecture:
[ 42 ]
Chapter 2
[ 43 ]
Then, let's modify our controller so it puts this message inside this model:
@Controller
public class HelloController {
@RequestMapping("/")
public String hello(Model model) {
model.addAttribute("message", "Hello from the controller");
return "resultPage";
}
}
I know, the suspense is killing you! Let's see what http://localhost:8080 looks
like.
The first thing to note is that we passed a new argument to the controller's method
and that the DispatcherServlet provided the correct object for us. There are,
in fact, many objects that can be injected into the controller's methods such as
HttpRequest or HttpResponse, the Locale, the TimeZone, and the Principal,
which represent an authenticated user. The full list of such objects is available in the
documentation, which can be found at http://docs.spring.io/spring/docs/
current/spring-framework-reference/html/mvc.html#mvc-ann-arguments.
[ 44 ]
Chapter 2
Syntax
list[0]
map[key]
Ternary operator
Elvis operator
condition ? 'yes' :
'no'
person ?: default
Safe navigation
person?.name
Templating
'Your name is
#{person.name}'
${persons.![name]}
Selection
persons.?[name ==
'Bob']'
Function call
person.sayHello()
Projections
Explanation
The SpEl usage is not limited to views. You can also use it in various places inside
the Spring framework, for instance, when injecting properties inside beans with the
@Value annotation.
[ 45 ]
We can leverage this technique to ask our user for their name. Let's modify our
HelloController class again:
@Controller
public class HelloController {
@RequestMapping("/")
public String hello(@RequestParam("name") String userName, Model
model) {
model.addAttribute("message", "Hello, " + userName);
return "resultPage";
}
}
[ 46 ]
Chapter 2
[ 47 ]
Now, navigate to the keys, access the token, and copy the Consumer Key and
the Consumer Secret. We will use this in a moment. Take a look at the following
screenshot:
By default, our application has read only permissions. This will be enough for our
application, but you can tweak it if you wish.
These are the keys associated with the application we just created.
[ 48 ]
Chapter 2
You will learn more about OAuth in Chapter 6, Securing Your Application. For now,
we will just use those credentials to issue requests to Twitter's API on behalf of our
application.
Accessing Twitter
We can now use Twitter in our controller. Let's change its name to TweetController
as a variable to reflect its new responsibility in a better manner:
@Controller
public class HelloController {
@Autowired
private Twitter twitter;
@RequestMapping("/")
public String hello(@RequestParam(defaultValue =
"masterSpringMVC4") String search, Model model) {
SearchResults searchResults = twitter.searchOperations().
search(search);
String text = searchResults.getTweets().get(0).getText();
model.addAttribute("message", text);
return "resultPage";
}
}
As you can see, the code searches for tweets matching the request parameter. If it all
goes well, you will see the text of the first one being displayed on your screen:
Of course, if the search doesn't yield any result, our clumsy code will fail with an
ArrayOutOfBoundException. So, do not hesitate to tweet to solve the problem!
What if we wanted to display a list of tweets? Let's modify the resultPage.html file:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head lang="en">
<meta charset="UTF-8"/>
<title>Hello twitter</title>
[ 49 ]
Note that we are using Java 8 streams to collect only the messages from the tweets.
The Tweet class contains many other attributes such as the sender, the retweet count,
and so on. However, we will keep it simple for now, as shown in the following
screenshot:
[ 50 ]
Chapter 2
map: This applies a method to every element of a list and returns the list of
reduce: This projects a list into a single value using an operation and an
results
accumulator
Lambdas are shorthand syntax for function expressions. They can be coerced into a
Single Abstract Method, an interface with only one function.
For instance, you can implement the Comparator interface as follows:
Comparator<Integer> c = (e1, e2) -> e1 - e2;
[ 51 ]
The collect method allows us to call a terminal operation. The Collectors class
is a set of terminal operations that will put results into lists, sets, or maps, allowing
grouping, joining, and so on.
Calling the collect(Collectors.toList()) method will produce a list with every
element within the stream; in our case, the tweet names.
[ 52 ]
Chapter 2
The way a WebJar is organized is completely standardized. You will find the JS and
CSS files of any library in /webjars/{lib}/{version}/*.js.
For instance, to add jQuery to our page, the following to a web page:
<script src="/webjars/jquery/2.1.4/jquery.js"></script>
Let's modify our controller so that it gives us a list of all tweet objects instead of
simple text:
package masterSpringMvc.controller;
import
import
import
import
import
import
import
import
org.springframework.beans.factory.annotation.Autowired;
org.springframework.social.twitter.api.SearchResults;
org.springframework.social.twitter.api.Tweet;
org.springframework.social.twitter.api.Twitter;
org.springframework.stereotype.Controller;
org.springframework.ui.Model;
org.springframework.web.bind.annotation.RequestMapping;
org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
@Controller
public class TweetController {
@Autowired
private Twitter twitter;
@RequestMapping("/")
public String hello(@RequestParam(defaultValue =
"masterSpringMVC4") String search, Model model) {
SearchResults searchResults = twitter.searchOperations().
search(search);
List<Tweet> tweets = searchResults.getTweets();
model.addAttribute("tweets", tweets);
model.addAttribute("search", search);
return "resultPage";
}
}
[ 53 ]
[ 54 ]
Chapter 2
Using layouts
The last thing we want to do is to put the reusable chunks of our UI into templates.
To do this, we will use the thymeleaf-layout-dialect dependency, which is
included in the spring-boot-starter-thymeleaf dependency of our project.
We will create a new file called default.html in src/main/resources/templates/
layout. It will contain the code we will repeat from page to page:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
<meta http-equiv="Content-Type" content="text/html;
charset=UTF-8"/>
<meta name="viewport" content="width=device-width, initialscale=1, maximum-scale=1.0, user-scalable=no"/>
<title>Default title</title>
<link href="/webjars/materializecss/0.96.0/css/materialize.css"
type="text/css" rel="stylesheet" media="screen,projection"/>
</head>
<body>
[ 55 ]
We will now modify the resultPage.html file so it uses the layout, which will
simplify its contents:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorator="layout/default">
<head lang="en">
<title>Hello twitter</title>
</head>
<body>
<div class="row" layout:fragment="content">
<h2 class="indigo-text center" th:text="|Tweet results for
${search}|">Tweets</h2>
<ul class="collection">
<li class="collection-item avatar" th:each="tweet :
${tweets}">
<img th:src="${tweet.user.profileImageUrl}" alt=""
class="circle"/>
<span class="title" th:text="${tweet.user.
name}">Username</span>
<p th:text="${tweet.text}">Tweet message</p>
</li>
</ul>
</div>
</body>
</html>
Chapter 2
Navigation
We have a nice little tweet display application, but how are our users supposed to
figure out that they need to supply a "search" request parameter?
It would be nice if we added a little form to our application.
Let's do something like this:
We will add another page to the templates folder called the searchPage.html file.
It will contain a simple form, which will pass the search term to the result page via
the get method:
<!DOCTYPE html>
<html xmlns:th="http://www.w3.org/1999/xhtml"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorator="layout/default">
<head lang="en">
<title>Search</title>
</head>
<body>
<div class="row" layout:fragment="content">
<h4 class="indigo-text center">Please enter a search term</h4>
<form action="/result" method="get" class="col s12">
<div class="row center">
<div class="input-field col s6 offset-s3">
<i class="mdi-action-search prefix"></i>
<input id="search" name="search" type="text"
class="validate"/>
<label for="search">Search</label>
</div>
</div>
</form>
</div>
</body>
</html>
This is very simple HTML and it works perfectly. You can try it now.
What if we wanted to disallow some search result? Let's say we want to display an
error message if the user types in struts.
The best way to achieve this would be to modify the form to post the data. In the
controller, we can then intercept what is posted and implement this business rule
accordingly.
First, we need to change the form in the searchPage, which is as follows:
<form action="/result" method="get" class="col s12">
[ 58 ]
Chapter 2
We also need to handle this post on the server. Add this method to the
TweetController:
@RequestMapping(value = "/postSearch", method = RequestMethod.POST)
public String postSearch(HttpServletRequest request,
RedirectAttributes redirectAttributes) {
String search = request.getParameter("search");
redirectAttributes.addAttribute("search", search);
return "redirect:result";
}
We inject two attributes directly as method parameters. They are the request
and RedirectAttributes.
We retrieve the value posted on the request and pass it on to the next view.
The preceding example is a bit contrived, and we will see smarter form handling in
the next chapter. If you put a breakpoint in the postSearch method, you will see
that it will be called right after a post in our form.
So what about the error message?
[ 59 ]
If the user's search terms contain "struts", we redirect them to the searchPage and
add a little error message using flash attributes.
These special kinds of attributes live only for the time of a request and will disappear
when the page is refreshed. This is very useful when we use the POST-REDIRECT-GET
pattern, as we just did.
We will need to display this message in the searchPage result:
<!DOCTYPE html>
<html xmlns:th="http://www.w3.org/1999/xhtml"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorator="layout/default">
<head lang="en">
<title>Search</title>
</head>
<body>
<div class="row" layout:fragment="content">
<h4 class="indigo-text center">Please enter a search term</h4>
<div class="col s6 offset-s3">
<div id="errorMessage" class="card-panel red lighten-2"
th:if="${error}">
<span class="card-title" th:text="${error}"></span>
</div>
[ 60 ]
Chapter 2
<form action="/postSearch" method="post" class="col s12">
<div class="row center">
<div class="input-field">
<i class="mdi-action-search prefix"></i>
<input id="search" name="search" type="text"
class="validate"/>
<label for="search">Search</label>
</div>
</div>
</form>
</div>
</div>
</body>
</html>
Now, if users try to search for "struts2" tweets, they will get a useful and appropriate
answer:
[ 61 ]
In the src/main/resources directory, you should have one default layout and two
pages using it.
In the application.properties file, we added the Twitter application credentials
as well as a property telling Spring not to cache the templates to ease development:
Summary
In this chapter, you learned what it takes to make a good MVC architecture. We
saw some of the inner workings of Spring MVC and used Spring Social Twitter with
very little configuration. We can now design a beautiful web application, thanks to
WebJars.
In the next chapter, we will ask the user to fill in their profile, so that we can fetch
tweets they might like automatically. This will give you the opportunity to learn
more about forms, formatting, validation, and internationalization.
[ 62 ]
www.PacktPub.com
Stay Connected: