Interceptor Describes the function of the Interceptor. You can customize the Interceptor to run the program and test the effect. Application performance monitoring Login Detection Reference
Interceptor is introduced
Interceptors, like filters, are both aspect oriented programming — a concrete implementation of AOP (AOP aspect programming is just a programming idea).
You can use Interceptor to perform certain tasks, such as writing logs before the Controller processes requests, adding or updating configurations…
In Spring, when a request is sent to the Controller, it must pass through Interceptors (0 or more) before it can be processed by the Controller.
The Spring Interceptor is a concept very similar to a Servlet Filter.
Interceptor role
- Log recording: Logs of request information are recorded for information monitoring, information statistics, and Page View (PV) calculation.
- Permission check: such as login check, enter the processor to check whether the login;
- Performance monitoring: The processing time of the request is obtained by recording the start time before the interceptor enters the processor and the end time after processing. (Reverse proxies such as Apache can also record automatically)
- Common behavior: Read the Cookie to get user information and put the user object into the request, which is easy to use in the subsequent process, and extract Locale, Theme information, etc., as long as multiple processors need to use interceptors.
Custom Interceptor
If you need a custom Interceptor must implement org. Springframework. Web. Servlet. HandlerInterceptor interface or inheritance Org. Springframework. Web. Servlet. Handler. HandlerInterceptorAdapter class, and the need to rewrite the following the following three methods:
preHandler(HttpServletRequest request, HttpServletResponse response, Object handler)
Method is called before the request is processed. This method executes first in the Interceptor class, and is used to perform some pre-initialization or preprocessing of the current request, as well as some judgment to determine whether the request should proceed. The return value of this method is Boolean. When it returns false, the request is complete, and subsequent Interceptors and controllers will not execute. When it returns true, it calls the preHandle method of the next Interceptor, or the Controller method of the current request if it is the last Interceptor.postHandler(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
Method is executed after the current request is processed, after the Controller method is called, but it is called before the DispatcherServlet does the view return rendering, So we can operate on the ModelAndView object after the Controller has processed it in this method.afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex)
Methods need to be executed only if the preHandle method of the current Interceptor class returns true. As the name implies, this method is executed after the entire request is completed, after the DispatcherServlet has rendered the corresponding view. This method is mainly used for resource cleaning.
The next step is to study the actual code.
LogInterceptor class:
public class LogInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
long startTime = System.currentTimeMillis();
System.out.println("\n-------- LogInterception.preHandle --- ");
System.out.println("Request URL: " + request.getRequestURL());
System.out.println("Start Time: " + System.currentTimeMillis());
request.setAttribute("startTime", startTime);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("\n-------- LogInterception.postHandle --- ");
System.out.println("Request URL: " + request.getRequestURL());
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("\n-------- LogInterception.afterCompletion --- ");
long startTime = (Long) request.getAttribute("startTime");
long endTime = System.currentTimeMillis();
System.out.println("Request URL: " + request.getRequestURL());
System.out.println("End Time: " + endTime);
System.out.println("Time Taken: " + (endTime - startTime));
}
}
Copy the code
OldLoginInterceptor class:
public class OldLoginInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("\n-------- OldLoginInterceptor.preHandle --- ");
System.out.println("Request URL: " + request.getRequestURL());
System.out.println("Sorry! This URL is no longer used, Redirect to /admin/login");
response.sendRedirect(request.getContextPath()+ "/admin/login");
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("\n-------- OldLoginInterceptor.postHandle --- ");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("\n-------- OldLoginInterceptor.afterCompletion --- ");
}
}
Copy the code
Configuring interceptors:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LogInterceptor());
registry.addInterceptor(new OldLoginInterceptor()).addPathPatterns("/admin/oldLogin");
registry.addInterceptor(new AdminInterceptor()).addPathPatterns("/admin/*").excludePathPatterns("/admin/oldLogin");
}
}
Copy the code
The LogInterceptor interceptor is used to intercept all requests; OldLoginInterceptor intercepts the link “/ admin/oldLogin”, which redirects to the new “/ admin/login”. ; The AdminInterceptor intercepts the link /admin/*, except for the link /admin/ oldLogin.
Custom Controller validates interceptors
@Controller
public class LoginController {
@RequestMapping("/index")
public String index(Model model){
return "index";
}
@RequestMapping(value = "/admin/login")
public String login(Model model){
return "login";
}
}
Copy the code
Build both pages using the Thymeleaf template.
index.html
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Spring Boot Mvc Interceptor example</title>
</head>
<body>
<div style="border: 1px solid #ccc; padding: 5px; margin-bottom:10px;">
<a th:href="@ {} /">Home</a>
|
<a th:href="@{/admin/oldLogin}">/admin/oldLogin (OLD URL)</a>
</div>
<h3>Spring Boot Mvc Interceptor</h3>
<span style="color:blue;">Testing LogInterceptor</span>
<br/><br/>
See Log in Console..
</body>
</html>
Copy the code
login.html
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Spring Boot Mvc Interceptor example</title>
</head>
<body>
<div style="border: 1px solid #ccc; padding: 5px; margin-bottom:10px;">
<a th:href="@ {} /">Home</a>
|
<a th:href="@{/admin/oldLogin}">/admin/oldLogin (OLD URL)</a>
</div>
<h3>This is Login Page</h3>
<span style="color:blue">Testing OldLoginInterceptor & AdminInterceptor</span>
<br/><br/>
See more info in the Console.
</body>
</html>
Copy the code
Run the program and test the results
When everything is ready, start the project. Open the web site: http://localhost:8080/index
The process of executing the request in the background is illustrated:
If the click/admin/oldLogin (OLD URL) or in the address bar: http://localhost:8080/admin/oldLogin
Console print result:
-------- LogInterception.preHandle ---
Request URL: http://localhost:8080/admin/oldLogin
Start Time: 1576329730709
-------- OldLoginInterceptor.preHandle ---
Request URL: http://localhost:8080/admin/oldLogin
Sorry! This URL is no longer used, Redirect to /admin/login
-------- LogInterception.afterCompletion ---
Request URL: http://localhost:8080/admin/oldLogin
End Time: 1576329730709
Time Taken: 0
-------- LogInterception.preHandle ---
Request URL: http://localhost:8080/admin/login
Start Time: 1576329730716
-------- AdminInterceptor.preHandle ---
-------- AdminInterceptor.postHandle ---
-------- LogInterception.postHandle ---
Request URL: http://localhost:8080/admin/login
-------- AdminInterceptor.afterCompletion ---
-------- LogInterception.afterCompletion ---
Request URL: http://localhost:8080/admin/login
End Time: 1576329730718
Time Taken: 2
Copy the code
Similarly, we use the graphical form to analyze:
application
Performance monitoring
For example, you can record the processing time of a request and get some slow requests (such as more than 500 milliseconds) to improve performance. Common reverse proxy servers like Apache have this capability, but here we demonstrate how to do it with an interceptor.
Implementation analysis:
1. Record the start time before entering the processor, that is, record the start time at the interceptor’s preHandle;
2. Record the end time after the completion of the request processing, i.e. the completion implementation of the interceptor’s afterCompletion record, and use the end time-start time to get the processing time of the request.
Question:
Our interceptors are singletons, so there is only one interceptor implementation no matter how many times the user requests it, that is, thread unsafe, so how should we record the time?
The solution is to use A ThreadLocal, which is A thread-bound variable that provides thread-local variables (one ThreadLocal per thread, A ThreadLocal can only see A ThreadLocal, not B ThreadLocal).
Code implementation:
public class StopWatchHandlerInterceptor extends HandlerInterceptorAdapter {
private NamedThreadLocal<Long> startTimeThreadLocal = new NamedThreadLocal<>("StopWatch-StartTime");
private Logger logger = LoggerFactory.getLogger(StopWatchHandlerInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
long beginTime = System.currentTimeMillis();//1. Start time
startTimeThreadLocal.set(beginTime);// Thread binding variable (this data is only visible to the currently requesting thread)
return true;// Continue the process
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
long endTime = System.currentTimeMillis();//2. End time
long beginTime = startTimeThreadLocal.get();// Get the thread bound local variable (start time)
long consumeTime = endTime - beginTime;//3
if(consumeTime > 500) {// A request that takes more than 500 milliseconds to process is considered slow
//TODO to log file
logger.info(String.format("%s consume %d millis", request.getRequestURI(), consumeTime));
}
// Enable this code for testing because the request time did not exceed 500
// logger.info(String.format("%s consume %d millis", request.getRequestURI(), consumeTime));
}
}
Copy the code
NamedThreadLocal: a NamedThreadLocal implementation provided by Spring.
Need to put stopWatchHandlerInterceptor during testing of the interceptor chain first, so the time is more accurate.
Interceptor configuration class
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new StopWatchHandlerInterceptor());
registry.addInterceptor(new OldLoginInterceptor()).addPathPatterns("/admin/oldLogin");
}
}
Copy the code
The output of the console is as follows:
2019-12-14 21:51:43.881 INFO 4616 --- [nio-8080-exec-3] c.e.i.StopWatchHandlerInterceptor : /index consume 14 millis
-------- OldLoginInterceptor.preHandle ---
Request URL: http://localhost:8080/admin/oldLogin
Sorry! This URL is no longer used, Redirect to /admin/login
2019-12-14 21:51:54.055 INFO 4616 --- [nio-8080-exec-5] c.e.i.StopWatchHandlerInterceptor : /admin/oldLogin consume 1 millis
2019-12-14 21:51:54.070 INFO 4616 --- [nio-8080-exec-6] c.e.i.StopWatchHandlerInterceptor : /admin/login consume 9 millis
Copy the code
Login to detect
When accessing some resources (such as order pages), users need to log in to view them, so login detection is required.
Process:
1. When accessing resources that need to be logged in, the interceptor redirects to the login page;
2, if you visit the login page, the interceptor should not intercept;
3. After successful login, add the login success identifier (such as user ID) to the cookie/session;
4. On the next request, the interceptor decides whether to continue the process or go to the login page by judging whether the cookie/session has the identifier;
5. Here the interceptor should also allow visitors to access the resource.
The interceptor code looks like this:
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
boolean flag = true;
String ip = request.getRemoteAddr();
long startTime = System.currentTimeMillis();
request.setAttribute("requestStartTime", startTime);
if (handler instanceof ResourceHttpRequestHandler) {
System.out.println("PreHandle this is a static resource method!");
} else if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
System.out.println("Users." + ip + ", access target :" + method.getDeclaringClass().getName() + "." + method.getName());
}
// If the user is not logged in
User user = (User) request.getSession().getAttribute("user");
if (null == user) {
// Redirect to the login page
response.sendRedirect("toLogin");
flag = false;
}
return flag;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
if (handler instanceof ResourceHttpRequestHandler) {
System.out.println("PostHandle this is a static resource method!");
} else if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
long startTime = (long) request.getAttribute("requestStartTime");
long endTime = System.currentTimeMillis();
long executeTime = endTime - startTime;
int time = 1000;
// Print the method execution time
if (executeTime > time) {
System.out.println("[" + method.getDeclaringClass().getName() + "." + method.getName() + "] Execution time:"
+ executeTime + "ms");
} else {
System.out.println("[" + method.getDeclaringClass().getSimpleName() + "." + method.getName() + "] Execution time:"
+ executeTime + "ms");
}
}
}
}
Copy the code
The resources
https://snailclimb.gitee.io/springboot-guide/#/./docs/basis/springboot-interceptor
https://www.cnblogs.com/junzi2099/p/8260137.html#_label3_0