SpringBoot过滤器
从springboot启动日志中我们可以发现springboot默认加载的过滤器:
2018-07-19 18:43:32.748 INFO 9332 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/*]
2018-07-19 18:43:32.748 INFO 9332 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2018-07-19 18:43:32.748 INFO 9332 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2018-07-19 18:43:32.748 INFO 9332 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'requestContextFilter' to: [/*]
这四个过滤器位于 spring-boot.jar中的 org.springframework.boot.web.servlet.filter包下。
Ordered
他们都实现了 org.springframework.core.Ordered接口,通过其中的 int getOrder()对过滤器的优先级进行排序。
值得注意的是:Higher values are interpreted as lower priority(返回的int值越高意味着优先级越低,处理被拦截请求的顺序越靠后)。不要试图将两个过滤器返回相同的值,那样会导致排序失败。
Servlet3.0配置过滤器
1. 创建自定义过滤器类 MyFilter,实现 Filter接口中的方法
- init()给过滤器初始化一些变量
- doFilter()编写拦截、放行逻辑
- destroy()多用来释放资源
2. 给 MyFilter加上 @WebFilter(serlvet3.0提供)注解以注册到servlet容器中,并通过 urlPatterns属性指定拦截路径
urlPatterns指明拦截的路径
filterName,给过滤器命名,名称随意但最好不要重复
package top.zhenganwen.springbootweb.filter;
import org.apache.catalina.servlet4preview.http.HttpServletRequest;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter(urlPatterns = "/hello/*",filterName = "myFilter")
public class MyFilter implements Filter {
/**
* 在servlet容器启动时被调用
* @param filterConfig
* @throws ServletException
*/
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("MyFilter被加载了");
}
/**
* 请求被拦截时被调用
* @param request
* @param response
* @param chain
* @throws IOException
* @throws ServletException
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
System.out.println("请求的接口是:" + req.getRequestURL());
System.out.println("经过了MyFilter................");
//放行必须调用doFilter,拦截可以通过response重定向到提示页面(非前后端分离场景)
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
}
3. 给启动类加上 @ServletComponentScan注解以扫描 servlet组件
@SpringBootApplication
@ServletComponentScan
public class Application{
public static void main(String[] args){
SpringApplication.run(Application.class, args);
}
}
4. 访问 localhost:8080/hello/hello
@RestController
@RequestMapping("/hello")
public class HelloWorldController {
@RequestMapping("/hello")
public String hello() {
return "Hello World";
}
}
5. 控制台输出
请求的接口是:http://localhost:8080/hello/hello
经过了MyFilter................
Servlet3.0的注解自定义原生Servlet实战
1. 创建自定义Servlet类 如UserServlet,继承 HttpServlet并重写 doGet和 doPost方法
2. 在 UserServlet上添加 @WebServlet注解并通过 urlPatterns属性配置请求路径,该注解在 Servlet3.0之后提供
package top.zhenganwen.springbootweb.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(name = "userServlet",urlPatterns = "/user/hello")
public class UserServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("hello");
resp.getWriter().flush();
resp.getWriter().close();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
3. 在启动类上添加 @ServletComponentScan注解
4. 访问 localhost:8080/user/hello,页面响应 hello
Servlet3.0的注解自定义原生Listener实战
常用的监听器接口有 ServletContextListener、ServletRequestListener、ServletSessionListener,分别监听容器、请求、会话的创建和销毁。还有 ServletContextAttributeListener、ServletRequestAttributeListener、ServletSessionAttributeListener对应监听三者的 addAttribute和 removeAttribute方法。
1. 创建自定义监听器类如 MyListener,按需实现上述6个接口中的一个
2. 在 MyListener上添加 @WebListener
package top.zhenganwen.springbootweb.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(name = "userServlet",urlPatterns = "/user/hello")
public class UserServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("hello");
resp.getWriter().flush();
resp.getWriter().close();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
3. 在启动类上添加 @ServletComponentScan注解
4. 访问 localhost:8080/hello/hello
@RequestMapping("/hello")
public String hello() {
System.out.println("经过HelloWorldController");
return "Hello World";
}
request initialized============
请求的接口是:http://localhost:8080/hello/hello
经过了MyFilter................
经过HelloWorldController
request destroyed===============
ServletContextListener主要用来做资源加载如加载缓存,ServletRequestListener则主要用来对请求进行统计
自定义拦截器及新旧配置对比
自定义拦截器
preHandle、postHandle、afterCompletion的调用时机已在相应的方法体中输出。
package top.zhenganwen.springbootweb.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("在调用controller之前============");
//return true表示放行,将走到下一个拦截器或controller中
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("在调用controller之后、返回逻辑视图之前=======");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("在返回逻辑试图之后,通常用来释放资源=========");
}
}
SpringBoot1.x配置拦截器
SpringBoot2.x与SpringBoot1.x配置拦截器的方式略有不同
1. 创建一个配置类继承 WebMvcConfigurerAdapter并实现其中的 addInterceptors,将自定义的拦截器加入到拦截器列表中
2. 在配置类上添加 @Configuration注解
package top.zhenganwen.springbootweb.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import top.zhenganwen.springbootweb.interceptor.LoginInterceptor;
@Configuration
public class CustomOldWebMvcInterceptor extends WebMvcConfigurerAdapter {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/cart/*/**");
super.addInterceptors(registry);
}
}
SpringBoot2.x配置拦截器
创建一个配置类实现 WebMvcConfigurer接口并重写 addInterceptors方法,在该配置类上添加 @Configuration注解
package top.zhenganwen.springbootweb.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import top.zhenganwen.springbootweb.interceptor.LoginInterceptor;
@Configuration
public class CustomWebMvcConfigure implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/cart/*/**");
WebMvcConfigurer.super.addInterceptors(registry);
}
}
值得注意的是,在 Java8中,接口中的方法是可以有方法体的,并且可以被 defautl修饰作为方法的默认实现。
测试
添加 CartController
package top.zhenganwen.springbootweb.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("cart")
public class CartController {
@RequestMapping("addItem")
public Object addItem() {
System.out.println("添加商品到购物车");
return "success";
}
}
访问 http://localhost:8080/cart/addItem ,控制台输出如下:
在调用controller之前============
添加商品到购物车
在调用controller之后、返回逻辑视图之前=======
在返回逻辑试图之后,通常用来释放资源=========
注意:不要试图启用新旧两种配置方式。
拦截器不生效的常见原因
1. 没有在配置类上添加 @Configuration,这样你的配置类不会生效,配置类中将自定义拦截器添加到拦截器列表的操作自然也不会生效。或者配置类不在启动类扫描范围之内
2. 请求路径与addPathPattern()的拦截规则不匹配。拦截所有目录应该用 /*/,末尾一定要写成 /**。*表示匹配当前目录的子目录,**表示匹配当前目录下的所有目录
拦截器和过滤器的区别
- Filter是基于函数回调: doFilter(),而Interceptor则是基于AOP思想
- Filter在只在Servlet前起作用,而Interceptor够深入到方法前后、异常抛出前后等
- 依赖于Servlet容器即web应用中,而Interceptor不依赖于Servlet容器所以可以运行在多种环境。在接口调用的生命周期里,Interceptor可以被多次调用,而Filter只能在容器初始化时调用一次。
- Filter和Interceptor的执行顺序:过滤前->拦截前->action执行->拦截后->过滤后
更多学习资料请点击白玉搜一搜搜索下载