Understanding Spring MVC Request Handling and Data Sharing
MVC Architectural Pattern
MVC divides application logic into three interconnected components:
- Model (M): Represents JavaBeans handling data. Two primary types exist:
- Entity Beans: Store business data (e.g.,
Customer,Product). - Businses Logic Beans: Handle service and data access operations (e.g.,
Service,Repository).
- Entity Beans: Store business data (e.g.,
- View (V): Represents user interface components like HTML or JSP pages responsible for data presentation and user interaction.
- Controller (C): Manages application flow, typically implemented as servlets, to receive requests and send responses.
Note: MVC's presentation layer aligns with the three-tier architecture's presentation layer, which includes UI components and backend servlets. Spring MVC provides abstraction over servlet handling.
Regarding url-pattern configurations in web.xml:
/*matches any request sent to the server./does not match requests ending with.jspas JSP pages are compiled into servlets.
Core Concepts and Configuration
View Resolution: Physical view path = View prefix + Logical view name + View suffix.
DispatcherServlet Configuration in web.xml:
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<!-- Specifies the location of the Spring MVC configuration file -->
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-web-config.xml</param-value>
</init-param>
<!-- Loads the servlet during server startup; lower numbers indicate higher priority -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
Request Flow Summary:
The DispatcherServlet, configured as the front controller, intercepts requests matching its url-pattern. It reads the Spring MVC configuration to locate controllers. The request URL is matched against the @RequestMapping annotation's value attribute on controller methods. Upon a successful match, the corresponding method executes, returning a logical view name. This name is resolved by the ViewResolver by adding configured prefix and suffix to locate the physical view template (e.g., JSP, Thymeleaf), which is then rendered and forwarded to the client.
The @RequestMapping Annotation
Placement
- On a class: Defines a base request path for all handler methods within that controller.
- On a method: Specifies the specific request path for that handler.
@Controller
@RequestMapping("/api/users")
public class UserController {
// Handles requests to /api/users/profile
@RequestMapping("/profile")
public String showUserProfile() {
return "user-profile-view";
}
}
The value Attribute
Maps requests to handler methods based on the URL path. It accepts an array of Strings, allowing a single method to handle multiple paths.
@RequestMapping({"/details", "/info"})
public String getDetails() { ... }
The method Attribute
Filters requests based on HTTP methods (GET, POST, etc.). It uses the RequestMethod enum.
@RequestMapping(value = "/create", method = RequestMethod.POST)
public String createItem() { ... }
Derived annotations offer shorthand for common methods: @GetMapping, @PostMapping, @PutMapping, @DeleteMapping.
Mismatch Consequence: A path match with a method mismatch results in HTTP 405 (Method Not Supported).
The params Attribute
Filters requests based on the presence and values of request parameters.
"paramName": Request must includeparamName."!paramName": Request must not includeparamName."paramName=expectedValue": Request must includeparamNamewith the specified value."paramName!=excludedValue": Request may omitparamName, but if present, its value must not equalexcludedValue.
Mismatch Consequence: Results in HTTP 400 (Bad Request).
The headers Attirbute
Filters requests based on HTTP header values, using syntax identical to the params attribute.
Mismatch Consequence: Results in HTTP 404 (Not Found) if the path and method match but headers do not. Note: Header keys are case-insensitive, while values are case-sensitive.
Advanced Path Mapping
Ant-style Patterns
?: Matches any single character.*: Matches zero or more characters within a path segment.**: Matches zero or more directories across the path. Must be used as/**within the pattern.
URI Template Variables (Path Placeholders)
Enables RESTful URL patterns.
// Handles GET /products/delete/123
@GetMapping("/products/delete/{productId}")
public String removeProduct(@PathVariable("productId") Long id) {
// `id` will be 123
return "confirmation-view";
}
Accessing Request Parameters
1. Using Servlet API
Inject HttpServletRequest as a method parameter.
@PostMapping("/login")
public String processLogin(HttpServletRequest req) {
String user = req.getParameter("username");
String pass = req.getParameter("password");
// ... process data
return "result-view";
}
2. Direct Method Parameter Binding
Method parameter names must match request parameter names.
@PostMapping("/login")
public String directBinding(String username, String password) {
System.out.println("User: " + username);
return "result-view";
}
3. Using @RequestParam Annotation
Provides control over parameter binding.
value/name: The name of the request parameter.required: Whether the parameter is mandatory (default:true).defaultValue: A default value if the parameter is not supplied.
@GetMapping("/search")
public String searchItems(
@RequestParam(value = "q", required = false, defaultValue = "") String query,
@RequestParam("page") int pageNumber) {
// ...
return "search-results-view";
}
4. Binding Headers and Cookies
@RequestHeader: Binds an HTTP header value to a method parameter.@CookieValue: Binds a cookie's value to a method parameter.
5. Binding to a POJO (Command Object)
If request parameter names match the properties of a plain Java object, Spring can automatically populate it.
public class UserForm {
private String username;
private String email;
// getters and setters
}
@PostMapping("/register")
public String handleRegistration(UserForm newUser) {
// `newUser` is populated with request parameters 'username' and 'email'
System.out.println(newUser.getUsername());
return "welcome-view";
}
Handling Character Encoding
To prevent garbled characters from request parameters, configure an encoding filter in web.xml. It must be the first filter in the chain.
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<!-- Forces response encoding as well -->
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Sharing Data Across Scopes
1. Request Scope
- Using
HttpServletRequest:request.setAttribute("dataKey", dataObject); - Using
ModelAndView: Combines model data and view specification.@GetMapping("/data") public ModelAndView getData() { ModelAndView mav = new ModelAndView("data-view"); mav.addObject("message", "Data loaded successfully."); return mav; } - Using
Model,ModelMap, orMap<String, Object>: These types are interchangeable as method parameters for adding request attributes.@GetMapping("/info") public String getInfo(Model model) { // Can also use ModelMap or Map model.addAttribute("serverTime", LocalDateTime.now()); return "info-page-view"; }
2. Session Scope
Use HttpSession to store data across multiple requests from the same client.
@PostMapping("/cart/add")
public String addToCart(@RequestParam Long itemId, HttpSession session) {
ShoppingCart cart = (ShoppingCart) session.getAttribute("userCart");
if (cart == null) {
cart = new ShoppingCart();
session.setAttribute("userCart", cart);
}
cart.addItem(itemId);
return "redirect:/cart/view";
}
3. Application Scope
Use ServletContext (available via HttpServletRequest.getServletContext()) to store global, application-wide data.
@GetMapping("/stats")
public String showStats(HttpServletRequest request) {
ServletContext ctx = request.getServletContext();
Integer visitCount = (Integer) ctx.getAttribute("totalVisits");
// ...
return "statistics-view";
}
Accessing Data in Views (e.g., Thymeleaf)
- Request attribute:
${attributeName} - Session attribute:
${session.attributeName} - Application attribute:
${application.attributeName}