Spring interceptors
describe
As the name implies, the interceptor tool can be used in daily development to intercept requests, verify login, verify permissions, or set up some data beforehand. Or intercept a method before it gets called and do something about it.
Below is a small example of the time taken to calculate a request and a source code analysis.
There are two main types of interceptors in Spring
-
HandlerInterceptor
An interceptor in SpringMVC whose goal is to intercept qualified requests
Implement the HandlerIntercepot interface and implement the methods inside it, making it very simple to use.
-
MethodInterceptor
This is for method interception
code
Spring version 5.0 is used here
HandlerInterceptor
The interceptor implemented below is an interceptor that calculates the request time. Start by writing a configuration class for the interceptor.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/ * * *@authorThe WebMvcConfigurerAdapter should be used to implement the configuration. The WebMvcConfigurerAdapter should be used to implement the configuration
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// Multiple interceptors form an interceptor chain
AddPathPatterns is used to add interception rules
AddInterceptor is used to add interceptors to the interceptor chain
// Only requests that go through the DispatcherServlet go through the interceptor chain
/* Intercepts all requests
registry.addInterceptor(interceptorCalculateRequestTime()).addPathPatterns("/ *");
}
@Bean
public InterceptorCalculateRequestTime interceptorCalculateRequestTime(a) {
return newInterceptorCalculateRequestTime(); }}Copy the code
Next, write the concrete interception
import com.sun.management.OperatingSystemMXBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.management.ManagementFactory;
import java.sql.Timestamp;
/ * * *@authorXigua * custom interceptor that implements the HandlerInterceptor method * to calculate the request time */
public class InterceptorCalculateRequestTime implements HandlerInterceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(HandlerInterceptor.class);
/** * All qualified requests are entered into this method, and return true to indicate that they have passed the test and are allowed to continue
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
setParameter(request);
printMemory(request);
return true;
}
/ * * *@param request
* @param response
* @param handler
* @paramModelAndView does not use this method if an error occurs during execution, but only if the call completes successfully. * Here we also calculate the time to see how long the rendering takes *@throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
computeConsumeTime(request, "When processing is complete");
printMemory(request);
}
/ * * *@param request
* @param response
* @param handler
* @paramEx will use this method regardless of whether an error is reported. The time is subject to here. After the foreground page is rendered, this method is generally used to clean up some resources occupied *@throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
computeConsumeTime(request, "At the end of the request");
printMemory(request);
}
private void computeConsumeTime(HttpServletRequest request, String processName) {
String requestId = (String) request.getAttribute("requestId");
long startTime = (long) request.getAttribute("requestStartTime");
System.out.println(Request Id "\ t" + requestId + "\t" + processName + Co-expenses + (System.currentTimeMillis() - startTime) / 1000 + "Seconds");
}
private void printMemory(HttpServletRequest request) {
long vmFree;
long vmUse;
long vmTotal;
long vmMax;
int byteToMb = 1024 * 1024;
// Displays the vm memory information
Runtime runtime = Runtime.getRuntime();
vmTotal = runtime.totalMemory() / byteToMb;
vmFree = runtime.freeMemory() / byteToMb;
vmMax = runtime.maxMemory() / byteToMb;
vmUse = vmTotal - vmFree;
// Query the memory status of the operating system
OperatingSystemMXBean operatingSystemMXBean = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
long physicalFree = operatingSystemMXBean.getFreePhysicalMemorySize() / byteToMb;
long physicalTotal = operatingSystemMXBean.getTotalPhysicalMemorySize() / byteToMb;
long physicalUse = physicalTotal - physicalFree;
System.out.println("Time" + new Timestamp(System.currentTimeMillis())
+ "Request Id:" + request.getAttribute("requestId") + "\t"
+ "Requested address:" + request.getRequestURI() + "\t"
+ "JVM used space" + vmUse + "MB\t"
+ "JVM free space" + vmFree + "MB\t"
+ "Total JVM space" + vmTotal + "MB\t"
+ "Maximum available space for JVM" + vmMax + "MB\t"
+ "The space used by the operating system physical memory is:" + physicalFree + " MB\t"
+ "The free space of the operating system's physical memory is:" + physicalUse + " MB\t"
+ "Total physical memory of operating system:" + physicalTotal + " MB\t");
}
/ * * *@paramRequest Sets the request parameters */
private void setParameter(HttpServletRequest request) {
// Rule: request address + timestamp to determine unique
long Sc = System.currentTimeMillis();
String requestId = request.getRequestURL() + String.valueOf(Sc);
// Set a request ID and a start timestamp
request.setAttribute("requestStartTime", Sc);
request.setAttribute("requestId", requestId); }}Copy the code
MethodInterceptor [to be added]
Copy the code
Source code analysis
HandlerInterceptor
The main focus here is the order in which methods in the interface are called after they are implemented.
Here we refer mainly to the doDispatch method of DispatcherServlet.
The following is the source code, individual key methods with Chinese annotations.
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try{ processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest ! = request);// Determine the handler of the current request, including interceptors and Controller methods
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
/ / determine the handler adapter, usually for RequestMappingHandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return; }}// Call preHandle (); // Call preHandle ()
if(! mappedHandler.applyPreHandle(processedRequest, response)) {return;
}
// After preprocessing, the actual handler is called, also known as the call to the controller
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
Call the postHandler method in the interceptor
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
After the call is complete, the result of the call is processed, which also contains the triggerAfterCompletion method, which is used to call the afterCompletion method in the interceptor
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
// No matter what error is reported, this method is used finally
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if(mappedHandler ! =null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); }}else {
// Clean up any resources used by a multipart request.
if(multipartRequestParsed) { cleanupMultipart(processedRequest); }}}}Copy the code