Spring Boot, with Spring Boot WebMVC, make it easy to create MVC apps with very clear delineations and interactions. The Model represents formal underlying data constructs that the View uses to present the user with the look and feel of the application. A Controller is like a traffic cop. It receives incoming requests (traffic) and routes that traffic according to your application’s configuration.
This is a huge upgrade from the early days of JSP, when it was not uncommon to have one (or a small number of) files that were each dependent on their baked-in logic. It was basically one giant View with internal logic to deal with inputs and session objects. This was a super bad design AND it didn’t scale well. In practice, you ended up with bloated, monolithic template files that have a heavy mix of Java and template code.
So, how do we use Spring Boot to create web MVC applications? I can’t wait to show you how simple it is! We’ll start with a very simple RESTful API example and then expand the example to use Thymeleaf, a modern templating engine.
The code used throughout this post can be found here. The examples below use HTTPie, a modern curl replacement.
Looking for a deeper dive? In the next post of our Spring Boot Technical Series we’ll dig even deeper into Thymeleaf for form validation and advanced model handling.
Set Up Your pom.xml in Spring Boot WebMVC
Here’s a snippet from the pom.xml file. The only dependency is spring-boot-starter-web (the parent takes care of all versioning):
...
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.0.RELEASE</version>
</parent>
...
<dependencies>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
...
</dependencies>
...
Build a RESTful API in 10 Lines
Let’s take a look at the simplest Spring Boot MVC “Hello World” application:
@SpringBootApplication
@RestController
public class SpringMVCApplication {
public static void main(String[] args) {
SpringApplication.run(SpringMVCApplication.class, args);
}
@RequestMapping("/")
public String helloWorld() { return "Hello World!"; }
}
To tell the truth, we are cheating just a little bit here. We’ve put the entire application in a single class and it’s responsible for the Controller as well the Spring Boot application itself.
In this case, there really isn’t a model or a view. We’ll get to that shortly.
The @SpringBootApplication annotation makes it (not surprisingly) a Spring Boot application. The annotation actually is a shorthand for three other annotations:
@Configuration – Tells Spring Boot to look for bean definitions that should be loaded into the application context in this class (we don’t have any).@EnableAutoConfiguration – Automatically loads beans based on configuration and other bean definitions.@ComponentScan – Tells Spring Boot to look for other components (as well as services and configurations) that are in the same package as this application. This makes it easy to setup external Controllers without additional coding or configuration. We’ll see this next.We also get some additional Spring Boot autoconfiguration magic wherein if it finds spring-webmvc on the classpath, we don’t have to explicitly have the @EnableWebMvc annotation.
So, @SpringBootApplication packs quite a punch!
The @RestController annotation tells Spring Boot that this class will also function as a controller and will return a particular type of response – a Restful one. This annotation is also multiple annotations bundled up into one:
@Controller – Tells Spring Boot that this is a controller component@ResponseBody – Tells Spring Boot to return data, not a viewYou can fire up this application and then run:
http localhost:8080
You’ll see:
HTTP/1.1 200 Content-Length: 12 Content-Type: text/plain;charset=UTF-8 Date: Tue, 06 Sep 2016 19:48:46 GMT Hello World!
Working with Models
The previous example was really only a Controller. Let’s add in some Models. Once again, Spring Boot WebMVC makes this super easy.
First, let’s see what’s going in our Controller:
@RestController
public class MathController {
@RequestMapping(path = "/maths", method = POST)
public MathResponse maths(@RequestBody MathRequest req) {
return compute(req);
}
private MathResponse compute(MathRequest req) {
...
}
}
On line 4, we see that the maths method will only accept POST requests.
On line 5, we see that the maths method returns a model object of type MathResponse. And, the method expects a parameter of type MathRequest.
Let’s see what a request produces:
http -v POST localhost:8080/maths num=5 operation=square
POST /maths HTTP/1.1
...
{
"num": "5",
"operation": "square"
}
HTTP/1.1 200
...
{
"input": 5,
"msg": "Operation 'square' is successful.",
"operation": "square",
"result": 25,
"status": "SUCCESS"
}
Notice that a JSON object is passed in and a JSON object is returned. There’s some great Sprint Boot magic going on here.
Jackson for Java to JSON Mapping
In the old days before Spring Boot and Spring Boot WebMVC (like 2 years ago), you had to manually serialize incoming JSON to Java objects and deserialize Java Objects to be returned as JSON. This was often done with the Jackson JSON mapper library.
Spring Boot includes Jackson by default and attempts to map JSON to Java Objects (and back) automatically. Now, our controller method signature starts to make more sense:
public MathResponse maths(@RequestBody MathRequest req)
Note: Remember from before that using the @RestController annotation automatically ensures that all response are @ResponseBody (that is, data – not a view).
Here’s our MathRequest model object:
public class MathRequest {
private int num;
private String operation;
// getters and setters here
}
Pretty straightforward POJO here. Jackson can easily handle taking in the JSON above, creating a MathRequest object and passing it into the maths method.
Here’s our MathResponse model object:
@JsonInclude(Include.NON_NULL)
public class MathResponse {
public enum Status {
SUCCESS, ERROR
}
private String msg;
private Status status;
private String operation;
private Integer input;
private Integer result;
// getters and setters here
}
Notice that in this case, we’re using the @JsonInclude(Include.NON_NULL) annotation. This provides a hint to Jackson that says any null value in the model object should be ignored in the response.
Return A View For the Full MVC Experience
In the last section, we added Model capabilities to our application to go along with the Controller. To round out our conversation, we will now make use of Views.
To do this, we’ll start by adding in Thymeleaf as a dependency to our application. Thymeleaf is a modern templating engine that’s very easy to use with Spring Boot.
We simply replace:
<artifactId>spring-boot-starter-web</artifactId>
With:
<artifactId>spring-boot-starter-thymeleaf</artifactId>
Let’s take a look at our controller:
@Controller
public class MathController {
@Autowired
MathService mathService;
@RequestMapping(path = "/compute", method = GET)
public String computeForm() {
return "compute-form";
}
@RequestMapping(path = "/compute", method = POST)
public String computeResult(MathRequest req, Model model) {
model.addAttribute("mathResponse", mathService.compute(req));
return "compute-result";
}
}
For both methods, computeForm and computeResult, the path is the same: /compute. That’s where the method attribute comes in. computeForm is only for GET requests and computeResult is only for POST requests.
computeForm simply returns a template called compute-form. Using the default location for templates, we create the file: src/main/resources/templates/compute-form.html. This displays a simple form for input:
The computeResult method, takes a MathRequest and Model objects as parameters. Spring Boot does it’s magic using Jackson as described before to take the form submission and marshall it into a MathRequest object. And, Spring Boot automatically passes in the Model object. Any attribute added to this model object is available to the template ultimately returned by the method.
The line: model.addAttribute("mathResponse", mathService.compute(req)); ensures that the resulting MathResponse object added to the model, which is made available to the returned template. In this case, the template is compute-result.html:
...
<div th:if="${mathResponse.status.name()} == 'ERROR'">
<h1 th:text="'ERROR: ' + ${mathResponse.msg}"/>
</div>
<div th:if="${mathResponse.status.name()} == 'SUCCESS'">
<h1 th:text="${mathResponse.input} + ' ' + ${mathResponse.operation} + 'd is: ' + ${mathResponse.result}"/>
</div>
...
The above snippet is the Thymeleaf syntax for working with the mathResponse object from the model. If there was an error, we show the message. If the operation was successful, we show the result.
Now I Know My MVC, Won’t You Sing Along With Me?
Here’s a partial view of the project structure:
.
├── java
│ └── com
│ └── stormpath
│ └── example
│ ├── controller
│ │ ├── MathController.java
│ │ └── MathRestController.java
│ └── model
│ ├── MathRequest.java
│ └── MathResponse.java
└── resources
└── templates
├── compute-form.html
└── compute-result.html
The Models used in the example are MathRequest and MathResponse. The Views are in the templates folder: compute-form.html and compute-result.html. And the Controllers are MathRestController and MathController.
Having the concerns separated in this way makes for a very clear and easy to follow application.
In the next installment of the Spring Boot series, we will delve deeper into Thymeleaf templates, including form validation and error messaging.
Learn More
Need to catch up on the first two posts from this series, or just can’t wait for the next one? We’ve got you covered:
The post Spring Boot WebMVC – Spring Boot Technical Concepts Series, Part 3 appeared first on Stormpath User Identity API.