openplanning

Hướng dẫn và ví dụ Spring Boot Interceptor

  1. Spring Interceptor là gì?
  2. Tạo dự án Spring Boot
  3. Interceptor classes
  4. Cấu hình Interceptor
  5. Controllers & Views
  6. Chạy ứng dụng

1. Spring Interceptor là gì?

Khi bạn tới công ty và muốn gặp sếp của công ty đó. Bạn cần phải đi qua các chốt chặn (Interceptor), các chốt chặn ở đây có thể là người bảo vệ cổng, nhân viên lễ tân,..

Trong Spring, khi một request được gửi đến controller, trước khi request được xử lý bởi Controller, nó phải vượt qua các Interceptor (0 hoặc nhiều).
Spring Interceptor là một khái niệm khá giống với Servlet Filter.

Spring Interceptor chỉ áp dụng đối với các request đang được gửi đến một Controller.
Bạn có thể sử dụng Interceptor để làm một số việc, chẳng hạn như ghi lại Log, thêm hoặc cập nhập các cấu hình trước khi request được xử lý bởi Controller, ...
Một trong các ứng dụng Spring Boot Mvc có sử dụng Interceptor là "Ứng dụng web đa ngôn ngữ". Bạn có thể xem thêm về ứng dụng này tại đây:
Lớp Interceptor của bạn cần phải thực hiện interface org.springframework.web.servlet.HandlerInterceptor hoặc mở rộng từ lớp org.springframework.web.servlet.handler.HandlerInterceptorAdapter.
Bạn cần phải triển khai 3 phương thức trừu tượng:
public boolean preHandle(HttpServletRequest request,
                         HttpServletResponse response,
                         Object handler)


public void postHandle(HttpServletRequest request,
                       HttpServletResponse response,
                       Object handler,
                       ModelAndView modelAndView)


public void afterCompletion(HttpServletRequest request,
                            HttpServletResponse response,
                            Object handler,
                            Exception ex)
Chú ý: phương thức preHandle trả về true hoặc false. Nếu trả về true nghĩa là request sẽ tiếp tục đi tới đích của nó (Là một Controller) .
Một request có thể phải vượt qua nhiều Interceptor. Hình dưới đây minh họa điều đó.

2. Tạo dự án Spring Boot

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.o7planning</groupId>
    <artifactId>SpringBootInterceptor</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>SpringBootInterceptor</name>
    <description>Spring Boot + Interceptor</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>
SpringBootInterceptorApplication.java
package org.o7planning.sbinterceptor;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringBootInterceptorApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootInterceptorApplication.class, args);
    }
    
}

3. Interceptor classes

LogInterceptor được áp dụng cho mọi request đang tiến đến một Controller. (Xem cấu hình trong WebMvcConfig).
LogInterceptor.java
package org.o7planning.sbinterceptor.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

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());

		// Ở đây, bạn có thể add các attribute vào modelAndView
		// Và sử dụng nó trong các View (jsp,..)
	}

	@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));
	}

}
OldLoginInterceptor là một chốt chặn (interceptor), nếu người dùng nhập vào đường dẫn /admin/oldLogin nó sẽ chuyển hướng tới đường dẫn mới là /admin/login.
OldLoginInterceptor.java
package org.o7planning.sbinterceptor.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

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 {

		// Đoạn code này sẽ không được chạy.
		System.out.println("\n-------- OldLoginInterceptor.postHandle --- ");
	}

	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, //
			Object handler, Exception ex) throws Exception {
		
		// Đoạn code này sẽ không được chạy.
		System.out.println("\n-------- QueryStringInterceptor.afterCompletion --- ");
	}

}
AdminInterceptor.java
package org.o7planning.sbinterceptor.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

public class AdminInterceptor extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {

        System.out.println("\n-------- AdminInterceptor.preHandle --- ");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, //
            Object handler, ModelAndView modelAndView) throws Exception {
        
        System.out.println("\n-------- AdminInterceptor.postHandle --- ");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, //
            Object handler, Exception ex) throws Exception {

        System.out.println("\n-------- AdminInterceptor.afterCompletion --- ");
    }

}

4. Cấu hình Interceptor

WebMvcConfig.java
package org.o7planning.sbinterceptor.config;

import org.o7planning.sbinterceptor.interceptor.AdminInterceptor;
import org.o7planning.sbinterceptor.interceptor.LogInterceptor;
import org.o7planning.sbinterceptor.interceptor.OldLoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {

	// 
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		// LogInterceptor áp dụng cho mọi URL.
		registry.addInterceptor(new LogInterceptor());

		// Đường dẫn login cũ, không còn sử dụng nữa.
		// Sử dụng OldURLInterceptor để điều hướng tới một URL mới.
		registry.addInterceptor(new OldLoginInterceptor())//
				.addPathPatterns("/admin/oldLogin");

		// Interceptor này áp dụng cho các URL có dạng /admin/*
		// Loại đi trường hợp /admin/oldLogin
		registry.addInterceptor(new AdminInterceptor())//
				.addPathPatterns("/admin/*")//
				.excludePathPatterns("/admin/oldLogin");
	}

}

5. Controllers & Views

MainController.java
package org.o7planning.sbinterceptor.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class MainController {

	@RequestMapping(value = { "/", "/test" })
	public String test(Model model) {

		System.out.println("\n-------- MainController.test --- ");

		System.out.println(" ** You are in Controller ** ");

		return "test";
	}

	// Đường dẫn này không còn sử dụng nữa.
	// Nó sẽ bị chuyển hướng bởi OldLoginInterceptor
	@Deprecated
	@RequestMapping(value = { "/admin/oldLogin" })
	public String oldLogin(Model model) {

		// Code ở đây không bao giờ được chạy.
		return "oldLogin";
	}

	@RequestMapping(value = { "/admin/login" })
	public String login(Model model) {

		System.out.println("\n-------- MainController.login --- ");

		System.out.println(" ** You are in Controller ** ");

		return "login";
	}

}
test.html
<!DOCTYPE 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>
login.html
<!DOCTYPE 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>

6. Chạy ứng dụng

Test trường hợp khi người dùng truy cập vào một trang, LogInterceptor sẽ ghi lại các thông tin liên quan (Địa chỉ trang, thời điểm truy cập), và tính khoảng thời gian Web Server đã phục vụ cho yêu cầu đó.
-------- LogInterception.preHandle ---
Request URL: http://localhost:8080/
Start Time: 1512231713663

-------- MainController.test ---
 ** You are in Controller **

-------- LogInterception.postHandle ---
Request URL: http://localhost:8080/

-------- LogInterception.afterCompletion ---
Request URL: http://localhost:8080/
End Time: 1512231713665
Time Taken: 2
Test trường hợp khi người dùng truy cập vào một trang đăng nhập cũ (Không còn sử dụng nữa), OldLoginInterceptor sẽ chuyển hướng yêu cầu tới trang đăng nhập mới.
-------- LogInterception.preHandle ---
Request URL: http://localhost:8080/admin/oldLogin
Start Time: 1512231812219

-------- 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: 1512231812219
Time Taken: 1

-------- LogInterception.preHandle ---
Request URL: http://localhost:8080/admin/login
Start Time: 1512231812222

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

-------- MainController.login ---
 ** You are in Controller **

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

-------- LogInterception.postHandle ---
Request URL: http://localhost:8080/admin/login

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

-------- LogInterception.afterCompletion ---
Request URL: http://localhost:8080/admin/login
End Time: 1512231812227
Time Taken: 5

Các hướng dẫn Spring Boot

Show More