Module 2 - Building Web Applications and MVC
Lecture: Spring MVC and Thymeleaf
Introduction to Spring MVC
Spring MVC (Model-View-Controller) is a powerful framework for building web applications in Java. It’s part of the core Spring Framework and provides a robust architecture for separating concerns in your application.
The MVC pattern divides an application into three interconnected components:
- Model: Contains the application data and business logic
- View: Renders the model data, generating the user interface
- Controller: Processes user requests, updates the model, and selects the view
Spring MVC implements this pattern with several components working together:
- DispatcherServlet: The front controller that handles all HTTP requests and delegates them to appropriate handlers
- Controllers: Handle user requests, update the model, and return a view name
- ViewResolver: Maps view names to actual view templates
- Models: Hold application data to be displayed in the view
Spring MVC Request Flow
Let’s trace the flow of a typical Spring MVC request:
- The client sends an HTTP request to the server
- The DispatcherServlet receives the request
- The DispatcherServlet consults the handler mapping to find the appropriate controller method
- The controller processes the request, updates the model, and returns a view name
- The ViewResolver resolves the view name to an actual view template
- The view is rendered with the model data
- The response is sent back to the client
Controller Annotations
Spring MVC uses various annotations to define controllers and their methods:
- @Controller: Marks a class as a Spring MVC controller
- @RestController: Combines @Controller and @ResponseBody, used for RESTful services
- @RequestMapping: Maps a URL pattern to a method or controller
- @GetMapping: Shorthand for @RequestMapping(method = RequestMethod.GET)
- @PostMapping: Shorthand for @RequestMapping(method = RequestMethod.POST)
- @PutMapping: Shorthand for @RequestMapping(method = RequestMethod.PUT)
- @DeleteMapping: Shorthand for @RequestMapping(method = RequestMethod.DELETE)
- @PathVariable: Extracts values from the URI path
- @RequestParam: Extracts query parameters from the URL
- @RequestBody: Maps the body of the HTTP request to an object
- @ModelAttribute: Binds a method parameter or method return value to a named model attribute
Understanding Models and ModelAttributes
In Spring MVC, the Model is a container for data that will be rendered by the view. You can populate it several ways:
// Using the Model parameter
@GetMapping("/greeting")
public String greeting(Model model) {
model.addAttribute("message", "Hello, World!");
return "greeting";
}
// Using ModelAndView
@GetMapping("/greeting")
public ModelAndView greeting() {
ModelAndView mav = new ModelAndView("greeting");
mav.addObject("message", "Hello, World!");
return mav;
}
// Using @ModelAttribute for methods that should be called before any request
@ModelAttribute("commonData")
public CommonData getCommonData() {
return commonDataService.getData();
}
Introduction to Thymeleaf
Thymeleaf is a modern server-side Java template engine for web applications. It allows you to create dynamic HTML content by adding special attributes to your HTML tags.
Key features of Thymeleaf:
- Natural templates: Templates look like regular HTML and can be opened in browsers
- Integration with Spring: Thymeleaf works seamlessly with Spring MVC
- Dialect extensibility: You can define custom dialects with your own attributes and elements
- Fragment inclusion: Allows for reusable components (headers, footers, etc.)
- Layout dialects: For creating master layouts with content inclusion
Basic Thymeleaf Syntax
Thymeleaf uses attributes prefixed with th:
to add dynamic behavior to HTML elements.
Here are some common Thymeleaf attributes:
- th:text: Sets the text content of an element
- th:utext: Sets the unescaped text content (allows HTML)
- th:each: Iterates over a collection
- th:if, th:unless: Conditional rendering
- th:href, th:src: Dynamic URLs for links and resources
- th:object, th:field: Form handling
- th:fragment: Define reusable fragments
- th:include, th:insert, th:replace: Include fragments
Examples:
<!-- Display a simple value -->
<p th:text="${message}">This will be replaced</p>
<!-- Iterate over a collection -->
<ul>
<li th:each="student : ${students}" th:text="${student.name}">
Student name
</li>
</ul>
<!-- Conditional rendering -->
<div th:if="${not #lists.isEmpty(students)}">
<p>We have students!</p>
</div>
<div th:unless="${not #lists.isEmpty(students)}">
<p>No students found.</p>
</div>
<!-- URL handling -->
<a th:href="@{/students/{id}(id=${student.id})}">View Student</a>
<!-- Form handling -->
<form th:action="@{/students}" th:object="${student}" method="post">
<input type="text" th:field="*{name}" />
<span th:if="${#fields.hasErrors('name')}" th:errors="*{name}"
>Name Error</span
>
<button type="submit">Submit</button>
</form>
<!-- Fragment inclusion -->
<div th:replace="fragments/header :: header">Header placeholder</div>
Spring Boot + Thymeleaf Configuration
Spring Boot automatically configures Thymeleaf when the spring-boot-starter-thymeleaf
dependency is added to your project. The default configuration includes:
- Template location:
src/main/resources/templates/
- Template suffix:
.html
- Template mode: HTML
- Cache templates: Enabled in production, disabled in development
You can override these settings in your application.properties
or application.yml
file:
# Thymeleaf settings
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.cache=false # Set to true in production
Forms and Validation
Spring MVC integrates with Hibernate Validator (the reference implementation of Bean Validation) to provide robust form validation.
To enable validation:
- Add the validation starter dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
- Annotate your model class with validation constraints:
public class Student {
@NotBlank(message = "Name is required")
private String name;
@Min(value = 18, message = "Age must be at least 18")
private int age;
@Email(message = "Please provide a valid email address")
private String email;
// Getters and setters
}
- Use the
@Valid
annotation in your controller method:
@PostMapping("/students")
public String registerStudent(@Valid @ModelAttribute Student student,
BindingResult result, Model model) {
if (result.hasErrors()) {
return "studentForm";
}
studentService.save(student);
return "redirect:/students";
}
- Display validation errors in your Thymeleaf template:
<form th:action="@{/students}" th:object="${student}" method="post">
<div>
<label>Name:</label>
<input type="text" th:field="*{name}" />
<span th:if="${#fields.hasErrors('name')}" th:errors="*{name}"
>Name Error</span
>
</div>
<div>
<label>Age:</label>
<input type="number" th:field="*{age}" />
<span th:if="${#fields.hasErrors('age')}" th:errors="*{age}"
>Age Error</span
>
</div>
<div>
<label>Email:</label>
<input type="email" th:field="*{email}" />
<span th:if="${#fields.hasErrors('email')}" th:errors="*{email}"
>Email Error</span
>
</div>
<button type="submit">Submit</button>
</form>
Static Resources in Spring Boot
Spring Boot automatically serves static resources located in specific directories:
/static
/public
/resources
/META-INF/resources
All these directories should be placed in src/main/resources/
.
To reference static resources in your Thymeleaf templates:
<!-- Using th:href with @{} syntax -->
<link th:href="@{/css/styles.css}" rel="stylesheet" />
<!-- Using th:src with @{} syntax -->
<img th:src="@{/images/logo.png}" alt="Logo" />
<!-- Including JavaScript -->
<script th:src="@{/js/script.js}"></script>
You can customize static resource handling in your application.properties
:
# Static resources configuration
spring.mvc.static-path-pattern=/resources/**
spring.web.resources.static-locations=classpath:/custom-static/
Error Handling in Spring MVC
Spring Boot provides several ways to handle errors:
- Default error page: Spring Boot automatically maps errors to
/error
- Custom error pages: Create templates in
/error
directory with names matching error codes (e.g.,404.html
) - @ExceptionHandler: Handle specific exceptions in a controller
- @ControllerAdvice: Global exception handling across controllers
Example of a global exception handler:
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ModelAndView handleResourceNotFoundException(ResourceNotFoundException ex) {
ModelAndView model = new ModelAndView("error/notFound");
model.addObject("message", ex.getMessage());
return model;
}
@ExceptionHandler(Exception.class)
public ModelAndView handleGenericException(Exception ex) {
ModelAndView model = new ModelAndView("error/generic");
model.addObject("message", "An unexpected error occurred");
return model;
}
}
Internationalization (i18n)
Spring Boot and Thymeleaf support internationalization out of the box:
- Configure the
MessageSource
andLocaleResolver
:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver slr = new SessionLocaleResolver();
slr.setDefaultLocale(Locale.US);
return slr;
}
@Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
lci.setParamName("lang");
return lci;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeChangeInterceptor());
}
}
- Create message properties files:
# src/main/resources/messages.properties (default)
greeting=Hello
# src/main/resources/messages_fr.properties
greeting=Bonjour
# src/main/resources/messages_es.properties
greeting=Hola
- Use messages in Thymeleaf templates:
<h1 th:text="#{greeting}">Hello</h1>
<a th:href="@{?lang=en}">English</a>
<a th:href="@{?lang=fr}">French</a>
<a th:href="@{?lang=es}">Spanish</a>
Quiz: Spring MVC and Thymeleaf
- What is the primary purpose of the DispatcherServlet in Spring MVC?
- A) Rendering views
- B) Acting as the front controller that processes all HTTP requests
- C) Managing the Model data
- D) Handling database operations
- Which annotation is used to map HTTP GET requests to a specific controller method?
- A) @RequestGet
- B) @GetMapping
- C) @GetRequest
- D) @HttpGet
- How do you add data to the Model in a Spring MVC controller?
- A) model.put(“key”, value)
- B) model.add(“key”, value)
- C) model.addAttribute(“key”, value)
- D) model.setAttribute(“key”, value)
- Which Thymeleaf attribute is used to iterate over a collection?
- A) th:loop
- B) th:for
- C) th:iterate
- D) th:each
- Where should static resources be placed in a Spring Boot application?
- A) src/main/resources/templates
- B) src/main/resources/static
- C) src/main/webapp
- D) src/static
- Which annotation is used to handle form validation errors in a controller?
- A) @ValidationResult
- B) @ErrorResult
- C) @BindingResult
- D) @FormErrors
- How do you display validation error messages in a Thymeleaf template?
- A) th:errors
- B) th:validationErrors
- C) th:formErrors
- D) th:showErrors
- Which Thymeleaf attribute is used for conditional rendering based on a condition being true?
- A) th:when
- B) th:if
- C) th:condition
- D) th:check
- How does Spring Boot know to use Thymeleaf as the template engine?
- A) It automatically detects the Thymeleaf dependency
- B) You need to explicitly configure it in application.properties
- C) You need to add @EnableThymeleaf annotation
- D) By default, Spring Boot uses Thymeleaf
- What is the purpose of the @ModelAttribute annotation?
- A) To mark a class as a model
- B) To bind a method parameter or return value to a model attribute
- C) To validate model attributes
- D) To indicate that a method returns a model
Quiz Answers
- B) Acting as the front controller that processes all HTTP requests
- B) @GetMapping
- C) model.addAttribute(“key”, value)
- D) th:each
- B) src/main/resources/static
- C) @BindingResult
- A) th:errors
- B) th:if
- A) It automatically detects the Thymeleaf dependency
- B) To bind a method parameter or return value to a model attribute
By Wahid Hamdi