- Spring MVC Cookbook
- Alex Bretet
- 1250字
- 2021-07-16 13:03:21
Defining a common WebContentInterceptor
In this recipe, we will highlight how we have implemented a WebContentInterceptor
superclass for Controllers.
Getting ready
We are about to present a Controller superclass having the specificity of being registered as a WebContentInterceptor
. This superclass allows us to globally control sessions and to manage caching options.
It will help us understanding the request lifecycle throughout the Framework and through other potential interceptors.
How to do it...
- Registering a default
WebContentInterceptor
with its specific configuration can be done entirely with the configuration approach:<mvc:interceptors> <bean id="webContentInterceptor" class="org.sfw.web.servlet.mvc.WebContentInterc eptor"> <property name="cacheSeconds" value="0"/> <property name="requireSession" value="false"/> ... </bean> <mvc:interceptors>
Tip
In our application, we have registered custom
WebContentInterceptors
to override the behaviors of the default one. - In the codebase, still from the previously checked-out
v2.x.x
branch, a newcloudstreetApiWCI
class can be found incloudstreetmarket-api
:public class CloudstreetApiWCI extends WebContentInterceptor { public CloudstreetApiWCI(){ setRequireSession(false); setCacheSeconds(0); } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException { super.preHandle(request, response, handler); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } }
- A similar
CloudstreetWebAppWCI
is also present in cloudstreetmarket-webapp:public class CloudstreetWebAppWCI extends WebContentInterceptor { public CloudstreetWebAppWCI(){ setRequireSession(false); setCacheSeconds(120); setSupportedMethods("GET","POST", "OPTIONS", "HEAD"); } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException { super.preHandle(request, response, handler); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } }
- In cloudstreetmarket-webapp,
DefaultController
andInfoTagController
now both inheritCloudstreetWebAppWCI
:public class InfoTagController extends CloudstreetWebAppWCI { ... } public class DefaultController extends CloudstreetWebAppWCI { ... }
- In cloudstreetmarket-webapp the
dispatcher-context.xml
context file registers the interceptor:<mvc:interceptors> <bean class="edu.zc...controllers.CloudstreetWebAppWCI"> <property name="cacheMappings"> <props> <prop key="/**/*.js">86400</prop> <prop key="/**/*.css">86400</prop> <prop key="/**/*.png">86400</prop> <prop key="/**/*.jpg">86400</prop> </props> </property> </bean> </mvc:interceptors>
- In the cloudstreetmarket-api,
dispatcher-context.xml
, the other interceptor has also been registered:<mvc:interceptors> <bean class="edu.zc...controllers.CloudstreetApiWCI"/> </mvc:interceptors>
- Finally, in both
dispatcher-context.xml
, theRequestMappingHandlerAdapter
bean has been given thesynchronizeOnSession
property:<bean class="org.sfw...annotation.RequestMappingHandlerAdapter"> <property name="synchronizeOnSession" value="true"/> </bean>
How it works...
In each web module, we have created a superclass for Controllers. In the cloudstreetmarket-webapp module for example, both InfoTagController
and DefaultController
now inherit the CloudstreetWebAppWCI
superclass.
Common behaviors for Controllers
Beyond the WebContentInterceptor
capabilities, it is more than a good practice to share common logic and attributes between controllers if they relate to configuration (application or business); the idea is to avoid creating another service layer. We will see with further implementations that it is a good place for defining user contexts.
A WebContentInterceptor
through its WebContentGenerator
superclass offers useful request and session management tools that we are going to present now. As an interceptor, it must be registered declaratively. This is the reason why we have added two <mvc:interceptors>
entries in our context files.
Global session control
A WebContentInterceptor
, handling requests provides the ability to control how the application should react with HTTP sessions.
Requiring sessions
The WebContentInterceptor
through WebContentGenerator
offers the setRequireSession(boolean)
method. This allows defining whether or not a session should be required when handling a request.
If there is no session bound to the request (if the session has expired for example), the controller will throw a SessionRequiredException
method. In such cases, it is good to have a global ExceptionHandler
defined. We will set up a global exception mapper when we will build the REST API. By default, the sessions are not required.
Synchronizing sessions
Another interesting feature comes with the synchronizeOnSession
property that we have set to true in the RequestMappingHandlerAdapter
definition. When set it to true, the session object is serialized and access to it is made in a synchronized block. This allows concurrent access to identical sessions and avoids issues that sometimes occur when using multiple browser windows or tabs.
Cache-header management
With the setCacheSeconds(int)
method that we have used in the constructors of CloudstreetWebAppWCI
and CloudstreetApiWCI
; the WebContentInterceptor
with WebContentGenerator
can manage a couple of HTTP response headers related to caching.
Set to zero, it adds the extra headers in the response such as Pragma, Expires, Cache-control, and so on.
We have also defined custom caching for static files at the configuration level:
<props> <prop key="/**/*.js">86400</prop> <prop key="/**/*.css">86400</prop> <prop key="/**/*.png">86400</prop> <prop key="/**/*.jpg">86400</prop> </props>
All our static resources are cached in this way for 24 hours, thanks to the native WebContentInterceptor.preHandle
method.
HTTP method support
We have also defined a high-level restriction for HTTP methods. It can be narrowed down by the @RequestMapping
method attribute at the Controller level. Accessing a disallowed method will result in 405 HTTP error: Method not supported
.
A high-level interceptor
In the Interceptor registration in dispatcher-context.xml
, we haven't defined a path mapping for the interceptor to operate on. It is because by default Spring applies the double wildcard operator /**
on such standalone interceptor definitions.
It is not because we have made DefaultController
, extending an interceptor, that the interceptor is acting on the Controller @RequestMapping
path. The interceptor's registration is only made through configuration. If the covered path mapping needs to be modified, we could override our registration in the following way:
<mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**"/> <bean class="edu.zc.csm.portal...CloudstreetWebAppWCI"> <property name="cacheMappings"> <props> <prop key="/**/*.js">86400</prop> <prop key="/**/*.css">86400</prop> <prop key="/**/*.png">86400</prop> <prop key="/**/*.jpg">86400</prop> </props> </property> </bean> </mvc:interceptor> </mvc:interceptors>
We have also overridden the WebContentInterceptor
method's preHandle
, postHandle
, and afterCompletion
. It will allow us later to define common business related operations before and after the Controller request handling.
Request lifecycle
Throughout the interceptor(s), each request is processed according to the following lifecycle:
- Prepare the request's context
- Locate the Controller's handler
- Execute interceptor's preHandle methods
- Invoke the Controller's handler
- Execute interceptor's
postHandle
methods - Handle the Exceptions
- Process the View
- Execute interceptor's
afterCompletion
methods
To better understand the sequence, especially when Exceptions occur, the following workflow is very useful:

Reference: Spring And Hibernate by Santosh Kumar K.
From this diagram, you can see that:
- The controller handler is invoked, unless one of the interceptors'
preHandle
methods throws an exception. - An interceptor's
postHandle
method is called when the controller's handler finishes without throwing an exception and if no precedingpostHandler
method has thrown an exception. - An interceptor's
afterCompletion
is always called, unless a precedingafterCompletion
throws an exception.
Obviously, if no Interceptor is registered, the same sequence applies, skipping the interceptors' steps.
There is more...
There is more to say about the WebContentGenerator
class.
More features offered by WebContentGenerator
Again, WebContentGenerator
is a superclass of WebContentInterceptor
. From its JavaDoc page: http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/servlet/support/WebContentGenerator.html you can find the following for example:
- Three constants (String)
METHOD_GET
,METHOD_POST
, andMETHOD_HEAD
refer to the valuesGET
,POST
, andHEAD
- Some caching specific methods such as
setUseExpiresHeader
,setUseCacheControlHeader
,setUseCacheControlNoStore
,setAlwaysMustRevalidate
, andpreventCaching
Also, with WebApplicationObjectSupport
, WebContentGenerator
provides:
- Access to
ServletContext
out of the request or response object throughgetServletContext()
. - Access to the temporary directory for the current web application, as provided by the servlet container through
getTempDir()
. - Access to the
WebApplicationContext
throughgetWebApplicationContext()
. - Also, a couple of tools to set and initialize the
ServletContext
and theWebApplicationContext
, even if these tools are initially intended for use within the Framework itself.
See also...
We quickly passed through web caching. There are a lot of customizations and standards in this domain. Also, a new RequestMappingHandlerAdapter
has been created with Spring MVC 3.1. It will be helpful to understand the change.
Web caching
Find out more about web caching through this very complete caching tutorial:
https://www.mnot.net/cache_docs
New support classes for @RequestMapping since Spring MVC 3.1
We have used the RequestMappingHandlerAdapter
with its bean definition in dispatcher-context.xml
. This bean is a new feature with Spring MVC 3.1 and has replaced the former AnnotationMethodHandlerAdapter
. Also, the support class DefaultAnnotationHandlerMapping
has now been replaced by RequestMappingHandlerMapping
.
We will go deeper into RequestMappingHandlerAdapter
in Chapter 4, Building a REST API for a Stateless Architecture.
In the meantime, you can read the official change note:
- 少兒人工智能趣味入門:Scratch 3.0動(dòng)畫與游戲編程
- 編寫高質(zhì)量代碼:改善Python程序的91個(gè)建議
- Learning Informatica PowerCenter 10.x(Second Edition)
- C語(yǔ)言程序設(shè)計(jì)立體化案例教程
- Mastering C# Concurrency
- 零基礎(chǔ)學(xué)Java程序設(shè)計(jì)
- PhpStorm Cookbook
- HTML5+CSS3網(wǎng)站設(shè)計(jì)基礎(chǔ)教程
- Scala編程實(shí)戰(zhàn)(原書第2版)
- Java EE 7 Performance Tuning and Optimization
- Python圖形化編程(微課版)
- Java Web從入門到精通(第3版)
- Django實(shí)戰(zhàn):Python Web典型模塊與項(xiàng)目開發(fā)
- Practical Microservices
- WordPress Search Engine Optimization(Second Edition)