Understanding View Resolution, Forwarding, Redirection, and Static Resource Handling in Spring MVC
Core Mechanism of View Resolution in Spring MVC
Spring MVC supports customizable view components. The framework allows developers to configure different view technologies through XML configuration. For instance, when using Thymeleaf as the template engine, the following setup is typically defined in springmvc.xml:
<bean id="thymeleafViewResolver" class="org.thymeleaf.spring6.view.ThymeleafViewResolver">
<property name="characterEncoding" value="UTF-8" />
<property name="order" value="1" />
<property name="templateEngine">
<bean class="org.thymeleaf.spring6.SpringTemplateEngine">
<property name="templateResolver">
<bean class="org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver">
<property name="prefix" value="/WEB-INF/templates/" />
<property name="suffix" value=".html" />
<property name="templateMode" value="HTML" />
<property name="characterEncoding" value="UTF-8" />
</bean>
</property>
</bean>
</property>
</bean>
This configuration tells Spring MVC that Thymeleaf should be used for rendering views. Switching to another view technology only requires modifying this configuration—no code changes are needed. This design adheres to the Open-Closed Principle (OCP), enabling easy extension without modifying existing code.
Supported View Types in Spring MVC
Spring MVC natively supports multiple view types:
InternalResourceView: Built-in support for JSP templates.RedirectView: Used for HTTP redirection.ThymeleafView: Third-party integration for Thymeleaf templates.FreeMarkerView: For FreeMarker-based templates.VelocityView: For Velocity templates.PDFView,ExcelView: Specialized views for generating PDFs and Excel files.
These can be easily swapped by adjusting the view resolver configuration.
Key Interfaces in View Handling
-
DispatcherServlet: The central front controller responsible for request dispatching.
- Main method:
doDispatch()
- Main method:
-
ViewResolver Interface: Translates logical view names into physical view paths.
- Method:
View resolveViewName(String viewName, Locale locale)
- Method:
-
View Interface: Renders model data into a final output (e.g., HTML).
- Method:
void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)
- Method:
-
ViewResolverRegistry: Registers view resolvers during application startup. If multiple resolvers exist, they are ordered by their
orderproperty.
To implement a custom view component:
- Create a class implementing
ViewResolver, and overrideresolveViewName()to map logical names to physical resources. - Create a class implementing
View, and overriderender()to transform template content into HTML output.
When using Thymeleaf, the actual classes involved are ThymeleafViewResolver and ThymeleafView. With JSP, it's InternalResourceViewResolver and InternalResourceView.
Flow of View Resolution
Scenario: Using Thymeleaf
- Client sends an HTTP request.
DispatcherServletreceives the request.- Request is mapped to a controller method.
- Controller returns a logical view name (e.g.,
index). DispatcherServletinvokesThymeleafViewResolver.resolveViewName()to convertindex→/WEB-INF/templates/index.html.- A
ThymeleafViewinstance is created and returned. ThymeleafView.render()processes the template, converts it to HTML, and sends it back to the client.
Scenario: Using JSP
- Same initial steps.
InternalResourceViewResolverresolvesindex→/WEB-INF/jsp/index.jsp.InternalResourceView.render()forwards the request internally and renders the JSP.
Logical to Physical View Mapping
The mapping depends on the configured view resolver. For example:
-
With Thymeleaf config:
- Logical:
index - Physical:
/WEB-INF/templates/index.html
- Logical:
-
With JSP config:
- Logical:
index - Physical:
/WEB-INF/jsp/index.jsp
- Logical:
Debugging View Creation
By setting breakpoints in DispatcherServlet.doDispatch() and tracing through render() methods, you can observe which view objects are instantiated:
- When returning
"index", the system usesThymeleafView. - When using
"forward:/b", the system createsInternalResourceView. - When using
"redirect:/b", the system createsRedirectView.
Note: forward: and redirect: are not logical view names—they are direct instructions to the dispatcher. Thus, no view resolver is invoked for these cases.
Forwarding vs. Redirection in Spring MVC
Forwarding
- One HTTP request.
- Browser URL remains unchanged.
- Server-side internal jump.
- Can access protected resources like those under
/WEB-INF. - Syntax:
return "forward:/target-path";
Example:
@RequestMapping("/a")
public String toA() {
return "forward:/b";
}
This triggers a server-side forward to /b, creating an InternalResourceView.
Redirection
- Two HTTP requests.
- Browser URL updates.
- Client-side redirect initiated by the server.
- Can cross domains or applications.
- Cannot access
/WEB-INFdirectly via browser. - Syntax:
return "redirect:/target-path";
Example:
@RequestMapping("/a")
public String toA() {
return "redirect:/b";
}
This results in a 302 response, prompting the client to make a new request to /b, where RedirectView is created.
Cross-application redirect:
return "redirect:http://localhost:8080/springmvc2/b";
Configuring View Controllers
The <mvc:view-controller> element maps a URL directly to a view without requiring a controller.
<mvc:view-controller path="/" view-name="index" />
This enables direct access to the index view at the root path. However, this feature requires <mvc:annotation-driven /> to be present in the configuration—otherwise, controllers won't be recognized, leading to 404 errors.
Serving Static Resources
Due to DispatcherServlet's url-pattern being set to /, all requests—including static assets—are routed through it, causing 404s unless handled properly.
Solution 1: Enable Default Servlet
Enable Tomcat’s built-in DefaultServlet to handle static files:
<mvc:annotation-driven />
<mvc:default-servlet-handler />
Tomcat already includes the DefaultServlet in its default web.xml. This setup ensures that if DispatcherServlet cannot find a handler, the request falls back to the default servlet, which serves static content from the web application root.
Solution 2: Use mvc:resources
Define explicit mappings for static resources:
<mvc:annotation-driven />
<mvc:resources mapping="/static/**" location="/static/" />
This rule means any request starting with /static/ will be served from the /static/ directory within the web application. This approach requires <mvc:annotation-driven /> to be active.
Both solutions work, but mvc:resources gives more control over what gets served and how.