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:

  1. Model: Contains the application data and business logic
  2. View: Renders the model data, generating the user interface
  3. 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:

  1. The client sends an HTTP request to the server
  2. The DispatcherServlet receives the request
  3. The DispatcherServlet consults the handler mapping to find the appropriate controller method
  4. The controller processes the request, updates the model, and returns a view name
  5. The ViewResolver resolves the view name to an actual view template
  6. The view is rendered with the model data
  7. 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:

  1. Natural templates: Templates look like regular HTML and can be opened in browsers
  2. Integration with Spring: Thymeleaf works seamlessly with Spring MVC
  3. Dialect extensibility: You can define custom dialects with your own attributes and elements
  4. Fragment inclusion: Allows for reusable components (headers, footers, etc.)
  5. 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:

  1. Add the validation starter dependency:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>
  1. 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
}
  1. 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";
}
  1. 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:

  1. Default error page: Spring Boot automatically maps errors to /error
  2. Custom error pages: Create templates in /error directory with names matching error codes (e.g., 404.html)
  3. @ExceptionHandler: Handle specific exceptions in a controller
  4. @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:

  1. Configure the MessageSource and LocaleResolver:
@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());
    }
}
  1. 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
  1. 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

  1. 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
  1. Which annotation is used to map HTTP GET requests to a specific controller method?
  • A) @RequestGet
  • B) @GetMapping
  • C) @GetRequest
  • D) @HttpGet
  1. 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)
  1. Which Thymeleaf attribute is used to iterate over a collection?
  • A) th:loop
  • B) th:for
  • C) th:iterate
  • D) th:each
  1. 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
  1. Which annotation is used to handle form validation errors in a controller?
  • A) @ValidationResult
  • B) @ErrorResult
  • C) @BindingResult
  • D) @FormErrors
  1. How do you display validation error messages in a Thymeleaf template?
  • A) th:errors
  • B) th:validationErrors
  • C) th:formErrors
  • D) th:showErrors
  1. 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
  1. 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
  1. 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

  1. B) Acting as the front controller that processes all HTTP requests
  2. B) @GetMapping
  3. C) model.addAttribute(“key”, value)
  4. D) th:each
  5. B) src/main/resources/static
  6. C) @BindingResult
  7. A) th:errors
  8. B) th:if
  9. A) It automatically detects the Thymeleaf dependency
  10. B) To bind a method parameter or return value to a model attribute

By Wahid Hamdi