1.spring应用中存在两种过滤用法,一种是拦截器,另一种是过滤器,两者的作用其实相差并不大
2.filter功能:它使用户可以改变一个request和修改一个response.Filter不是一个servlet,它不能产生一个response,它能够在
一个request到达servlet之前预处理request,也可以在离开servlet时处理response.
3.过滤器与拦截器的区别:
Filter是依赖于Servlet容器,属于Servlet规范的一部分,而拦截器则是独立存在的,可以在任何情况下使用。
Filter的执行由Servlet容器回调完成,而拦截器通常通过动态代理的方式来执行。
Filter的生命周期由Servlet容器管理,而拦截器则可以通过IoC容器来管理,因此可以通过注入等方式来获取其他Bean的实例,因此使用会更方便。
这里我们就对SpringBoot的过滤器进行实战讲解:
一.SpringBoot自带的Filter以及加载顺序
SpringBoot启动默认会加载下面几个filter,我们可以在启动日志里面看到:
characterEncodingFilter
hiddenHttpMethodFilter
httpPutFormContentFilter
requestContextFilter
这几个过滤器都是SpringBoot启动之后进行加载的,我们自己添加的Filter也会跟在这几个后面。过滤器加载是有优先级的,毕竟他有那么多Filter,到底哪个排在前面呢,SpringBoot是根据order这个字段进行判断过滤器优先级的,我们可以查看源码:
那么过滤器的一个优先级范围就是在 -2147483648与 2147483647之间,低位值意味着更高的优先级:Higher values are interpreted as lower priority。
二.自定义Filter
自定义实现filter有两种方式,一个是实现FilterRegistrationBean,另外一个是使用Servlet3.0
新增的注解实现过滤器
1.使用FilterRegistrationBean实现过滤器
首先新建一个过滤器类Filter1实现Filter接口,这里我们就打印了一下请求的路径:
package com.qzsun.springbootdemo.filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
public class Filter1 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
String requestURI = request.getRequestURI();
System.out.println("-----------------"+requestURI+"----------------");
filterChain.doFilter(request, servletResponse);
}
@Override
public void destroy() {
}
}
然后在启动类里面配置一个bean,把刚刚新建的一个过滤器加进去并设置优先级,这样一个自定义filter就实现了
package com.qzsun.springbootdemo;
import com.qzsun.springbootdemo.filter.Filter1;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class SpringbootdemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootdemoApplication.class, args);
}
@Bean
public FilterRegistrationBean testFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
//当过滤器有注入其他bean类时,可直接通过@bean的方式进行实体类过滤器,这样不可自动注入过滤器使用的其他bean类。
//当然,若无其他bean需要获取时,可直接new Filter1(),也可使用getBean的方式。
registration.setFilter(new Filter1());
//拦截路径
registration.addUrlPatterns("/*");
registration.addInitParameter("paramName", "paramValue");
//过滤器名称
registration.setName("filter1");
//设置顺序
registration.setOrder(10);
return registration;
}
}
在indexController中加一个访问方法:
@RequestMapping("/index")
public String index(){
System.out.println("热部署1");
System.out.println("热部署2");
System.out.println("热部署3");
System.out.println("测试配置文件的值"+webTest);
return "index";
}
我们启动并访问发现过滤器生效,如果我们需要配置多个过滤器,就注册多个FilterRegistrationBean就行了。
2.使用Servlet3.0的注解进行配置 (推荐)
2.1新建一个Filter2类实现Filter接口,实现三个方法init(),doFilter(),destroy(),进行登录的验证,这里要加上@WebFilter注解,目的是将该FIlter加入Spring容器的管理,这里可以通过urlPatterns 进行路径的拦截,filterName配置filter的名字,这样的话就比上面一种方法方便多了,不需要配置bean了,@Order注解就是设置过滤器的优先级
package com.qzsun.springbootdemo.filter;
import org.springframework.core.annotation.Order;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Order(9)
@WebFilter(urlPatterns = "/api/*",filterName = "Filter2")
public class Filter2 implements Filter {
/**
* 容器加载的时候调用
*/
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("-------------过滤器初始化-------------");
}
/**
* 请求被拦截时调用
*/
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse)servletResponse;
String requestURI = request.getRequestURI();
String username = request.getParameter("username");
int status = response.getStatus();
System.out.println("-----------请求URL"+requestURI+"用户名"+username);
System.out.println("-----------返回状态"+status);
if (username!=null){
filterChain.doFilter(request, servletResponse);
}else {
return;
}
}
/**
* 容器被销毁时调用
*/
@Override
public void destroy() {
System.out.println("-------------过滤器销毁-------------");
}
}
2.1.1:init()是filter初始化,在容器启动的时候会调用这个方法,
2.1.2:doFilter()是一个最重要的方法,当请求经过filter的时候进行业务,比如判断一个人时候登录,登陆的话就调用filterChain.doFilter(request, servletResponse)放行,没有登录就重定向或者跳转,在这里我们可以拿到请求过来的request,也可以拿到返回数据时候的response,还是非常有用的
2.1.3:destroy()是在容器销毁的时候进行回调,一般关掉容器的时候是看不到这个方法的打印的,只有热部署的时候能看到,因为热部署的时候要销毁这个filter进行重新创建(下面会进行验证)
2.2在启动类上加上@ServletComponentScan注解,这个主要作用是Servlet、Filter、Listener 可以直接通过 @WebServlet、@WebFilter、@WebListener 注解自动注册,无需其他代码。
2.3在indexController中加上一个方法进行测试:
@RequestMapping("/api/index")
public String apiIndex(){
return "index";
}
启动发现过滤器已经初始化了:
访问http://localhost:8081/api/index,不加username,过滤器也是起作用的,走了return跳出,什么都没有返回
下面我们修改刚刚的apiIndex()方法,返回一个json数据,然后在访问链接上加上?username=zhangsan,然后我们改动trigger.txt的版本号进行热部署,发现走了destroy()方法,销毁了Filter,并且filter也放行了,正常返回数据!
@RequestMapping("/api/index")
@ResponseBody
public Map<String, Object> apiIndex(){
Map<String,Object> map = new HashMap<>();
map.put("name","zhangsan");
map.put("age","111");
return map;
}
filter场景:权限控制、用户登录等,主要适用于非前后端分离的场景