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

  1. Log recording: Logs of request information are recorded for information monitoring, information statistics, and Page View (PV) calculation.
  2. Permission check: such as login check, enter the processor to check whether the login;
  3. 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)
  4. 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:

  1. 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.
  2. 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.
  3. 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>

&nbsp; &nbsp; | &nbsp; &nbsp;

    <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>

&nbsp; &nbsp; | &nbsp; &nbsp;

    <a th:href="@{/admin/oldLogin}">/admin/oldLogin (OLD URL)</a>

</div>



<h3>This is Login Page</h3>



<span style="color:blue">Testing OldLoginInterceptor &amp; 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 Time1576329730709



-------- OldLoginInterceptor.preHandle ---

Request URLhttp://localhost:8080/admin/oldLogin

Sorry! This URL is no longer used, Redirect to /admin/login



-------- LogInterception.afterCompletion ---

Request URLhttp://localhost:8080/admin/oldLogin

End Time1576329730709

Time Taken: 0



-------- LogInterception.preHandle ---

Request URLhttp://localhost:8080/admin/login

Start Time1576329730716



-------- AdminInterceptor.preHandle ---



-------- AdminInterceptor.postHandle ---



-------- LogInterception.postHandle ---

Request URLhttp://localhost:8080/admin/login



-------- AdminInterceptor.afterCompletion ---



-------- LogInterception.afterCompletion ---

Request URLhttp://localhost:8080/admin/login

End Time1576329730718

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