Spring Boot 2.2.4.RELEASE
准备
新建 Spring Boot 项目,引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
新建控制器类:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class IndexController {
@GetMapping({"/"})
public String index() {
return "index";
}
@GetMapping({"/hello"})
public String hello() {
return "hello";
}
}
过滤器
基于注解
新建过滤器类,并在类上使用 @WebFilter
注解:
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
@WebFilter(urlPatterns = {"/hello"})
public class IpFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("过滤器初始化...");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("TimeFilter.doFilter() 过滤器开始执行...");
String ip = request.getRemoteAddr();
System.out.println(ip);
chain.doFilter(request, response);
System.out.println("TimeFilter.doFilter() 过滤器结束执行...");
}
@Override
public void destroy() {
System.out.println("过滤器被销毁...");
}
}
在启动类上添加 @ServletComponentScan
注解:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
@ServletComponentScan
@SpringBootApplication
public class SpringBootFilterApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootFilterApplication.class, args);
}
}
启动项目,可以看到控制台输出 过滤器初始化...
。
因为配置 IpFilter
过滤器时,只对 /hello
进行过滤。所以,访问 http://localhost:8080/,是不会经过 IpFilter.doFilter
方法的。
当我们访问 http://localhost:8080/hello,控制台输出:
TimeFilter.doFilter() 过滤器开始执行...
0:0:0:0:0:0:0:1
TimeFilter.doFilter() 过滤器结束执行...
注意:如果直接在过滤器类上使用 @Component
注解,而不是使用 @WebFilter
注解,则默认会过滤所有请求。
基于编程式配置
新建配置类:
import javax.servlet.Filter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.mk.filter.IpFilter;
@Configuration
public class WebConfig {
@Bean
public FilterRegistrationBean<Filter> ipFilter() {
FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<Filter>();
IpFilter ipFilter = new IpFilter();
filterRegistrationBean.setFilter(ipFilter);
filterRegistrationBean.addUrlPatterns("/hello");
return filterRegistrationBean;
}
}
取消 IpFilter
过滤器类上的 @WebFilter
注解,以及启动类上的 @ServletComponentScan
注解。重启项目,访问 http://localhost:8080/ 或 http://localhost:8080/hello,可以在控制台看到和上面一样的效果。
拦截器
新建一个拦截器类,实现 HandlerInterceptor
接口:
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
@Component
public class HelloInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("HelloInterceptor.preHandle() 开始...");
System.out.println("方法:" + ((HandlerMethod) handler).getBean().getClass().getName()
+ "." + ((HandlerMethod) handler).getMethod().getName());
System.out.println("HelloInterceptor.preHandle() 结束...");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("HelloInterceptor.postHandle() 开始...");
System.out.println("HelloInterceptor.postHandle() 结束...");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("HelloInterceptor.afterCompletion() 开始...");
System.out.println("异常信息:" + ex);
System.out.println("HelloInterceptor.afterCompletion() 结束...");
}
}
HelloInterceptor
拦截器实现了 HandlerInterceptor
接口的三个方法。preHandle
方法在处理拦截之前执行,postHandle
方法只有当被拦截的方法没有抛出异常才会执行,afterCompletion
方法无论被拦截的方法抛出异常与否都会执行。
修改 WebConfig
配置类,实现 WebMvcConfigurer
接口,重写 addInterceptors
方法,添加拦截器(第 26 ~ 29 行):
package com.mk.config;
import javax.servlet.Filter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
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;
import com.mk.filter.IpFilter;
import com.mk.interceptor.HelloInterceptor;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private HelloInterceptor helloInterceptor;
@Bean
public FilterRegistrationBean<Filter> ipFilter() {
...
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(this.helloInterceptor);
}
}
修改 IndexController
控制器类,新建 exception
方法,手动抛出异常:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class IndexController {
@GetMapping({"/"})
public String index() {
return "index";
}
@GetMapping({"/hello"})
public String hello() {
System.out.println("IndexController.hello()");
return "hello";
}
@GetMapping("/ex")
public void exception() {
throw new RuntimeException("异常!");
}
}
重启项目,访问 http://localhost:8080/hello,控制台输出:
TimeFilter.doFilter() 过滤器开始执行...
127.0.0.1
HelloInterceptor.preHandle() 开始...
方法:com.mk.controller.IndexController.hello
HelloInterceptor.preHandle() 结束...
IndexController.hello()
HelloInterceptor.postHandle() 开始...
HelloInterceptor.postHandle() 结束...
HelloInterceptor.afterCompletion() 开始...
异常信息:null
HelloInterceptor.afterCompletion() 结束...
TimeFilter.doFilter() 过滤器结束执行...
从输出中我们可以了解到三个方法的执行顺序,并且三个方法都被执行了。并且,过滤器要先于拦截器执行,晚于拦截器结束。
访问 http://127.0.0.1:8080/ex,控制台输出:
HelloInterceptor.preHandle() 开始...
方法:com.mk.controller.IndexController.exception
HelloInterceptor.preHandle() 结束...
HelloInterceptor.afterCompletion() 开始...
异常信息:java.lang.RuntimeException: 异常!
HelloInterceptor.afterCompletion() 结束...
可看到,postHandle
方法并没有被执行。