filter简单理解:
过滤器实际上就是对web资源进行拦截,做一些处理后再交给下一个过滤器或servlet处理
通常都是用来拦截request进行处理的,也可以对返回的response进行拦截处理。
大概流程图如下:
应用场景:
- 自动登录
- 统一设置编码格式
- 访问权限控制
- 敏感字符过滤等
SpringBoot启动默认加载的Filter
- characterEncodingFilter
- hiddenHttpMethodFilter
- httpPutFormContentFilter
- requestContextFilter
Filter优先级
- Ordered.HIGHEST_PRECEDENCE
- Ordered.LOWEST_PRECEDENCE
低位值意味着更高的优先级 Higher values are interpreted as lower priority
自定义Filter,避免和默认的Filter优先级一样,不然会冲突
注册Filter的bean FilterRegistrationBean
同模块里面有相关默认Filter
web->servlet->filter
自定义Filter
package net.xdclass.demo.Filter;
import net.xdclass.demo.controller.FileController;
import javax.servlet.*;
import javax.servlet.Filter;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
//拦截所有的api请求
@WebFilter(urlPatterns = "/api/*",filterName = "loginFilter")
public class LoginFilter implements Filter {
//容器加载的时候调用
@Override
public void init(FilterConfig filterConfig) throws ServletException{
System.out.println("init loginfilter");
}
//请求拦截的时候调用
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("doFilter loginFilter");
HttpServletRequest request =(HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
/*
获取用户名密码,进行判断是否正确
*/
String username = request.getParameter("username");
if("xdclass".equals(username)){
filterChain.doFilter(servletRequest,servletResponse);
}else {
return;
}
filterChain.doFilter(servletRequest,servletResponse);
}
//容器被销毁的时候被调用
@Override
public void destroy() {
System.out.println("destroy loginFilter");
}
}
- 使用Servlet3.0的注解进行配置
- 启动类里面增加 @ServletComponentScan,进行扫描
- 新建一个Filter类,implements Filter,并实现对应的接口
- @WebFilter 标记一个类为filter,被spring进行扫描
- urlPatterns:拦截规则,支持正则
- 控制chain.doFilter的方法的调用,来实现是否通过放行
不放行,web应用resp.sendRedirect("/index.html");
场景:权限控制、用户登录(非前端后端分离场景)等
官网地址:https://docs.spring.io/spring-boot/docs/2.1.0.BUILD-SNAPSHOT/reference/htmlsingle/#boot-features-embedded-container-servlets-filters-listeners
使用 Servlet3.0的注解自定义原生Servlet和Listener
自定义原生Servlet
@WebServlet(name = "userServlet",urlPatterns = "/test/customs")
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().print("custom sevlet");
/*
清空缓冲区数据,就是说你用读写流的时候,其实数据是先被读到了内存中,
然后用数据写到文件中,当你数据读完的时候不代表你的数据已经写完了,
因为还有一部分有可能会留在内存这个缓冲区中。这时候如果你调用了 close()方法关闭了读写流,
那么这部分数据就会丢失,所以应该在关闭读写流之前先flush(),先清空数据。
*/
resp.getWriter().flush();
resp.getWriter().close();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req,resp);
}
}
Servlet3.0的注解自定义原生Listener监听器
监听器用于监听web应用中某些对象、信息的创建、销毁、增加,修改,删除等动作的发生,然后作出相应的响应处理。当范围对象的状态发生变化的时候,服务器自动调用监听器对象中的方法。常用于统计在线人数和在线用户,系统加载时进行信息初始化,统计网站的访问量等等。
常用的监听器 servletContextListener、httpSessionListener、servletRequestListener
- ServletContext是一个全局的储存信息的空间,其监听器从服务器启动调用contextInitialized方法到服务器停止contextDestoryed方法,对于一个web项目,ServletContext对象只能有一个,而ServletContextListener却可以有多个。ServletContext主要用于定时器和全局属性对象。
- HttpSession监听器在一次会话发生时被调用sessionCreated方法会话结束时调用sessionDestoryed方法。在一个web项目中可以有多个HttpSession对象,1个HttpSession可以注册多个HttpSessionListener。HttpSession常用于记录访问人数和访问日志。
- ServletRequest监听器在客户端请求事件发生时被调用requestInitialized方法事件完成时requestDestoryed方法。一个ServletRequest同样可以注册多个ServletRequestListener,ServletRequest常用于读取参数和记录访问历史。
自定义Listener
@WebListener
public class RequestListener implements ServletRequestListener {
@Override
public void requestDestroyed(ServletRequestEvent sre) {
// TODO Auto-generated method stub
System.out.println("======requestDestroyed========");
}
@Override
public void requestInitialized(ServletRequestEvent sre) {
System.out.println("======requestInitialized========");
}
SpringBoot2.X拦截器使用
1、@Configuration
继承WebMvcConfigurationAdapter(SpringBoot2.X之前旧版本)
SpringBoot2.X 新版本配置拦截器 implements WebMvcConfigurer
2、自定义拦截器 HandlerInterceptor
- preHandle:调用Controller某个方法之前
- postHandle:Controller之后调用,视图渲染之前,如果控制器Controller出现了异常,则不会执行此方法
- afterCompletion:不管有没有异常,这个afterCompletion都会被调用,用于资源清理
package net.xdclass.demo.intecpter;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LoginIntercepter implements HandlerInterceptor {
//进入controller方法之前
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("======preHandle======");
/*
判断用户是否登录,是否拥有权限
*/
// String token = request.getParameter("access_token");
// response.getWriter().print("fail");
return HandlerInterceptor.super.preHandle(request,response,handler);
}
//调用完controller之后,视图渲染之前
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("======postHandle======");
HandlerInterceptor.super.postHandle(request,response,handler,modelAndView);
}
//整个完成之后,通常用于资源清理
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("======afterCompletion======");
HandlerInterceptor.super.afterCompletion(request,response,handler,ex);
}
}
3、按照注册顺序进行拦截,先注册,先被拦截
package net.xdclass.demo.intecpter;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class CustomerWebMvcConfiguer implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginIntercepter()).addPathPatterns("/api2/*/**");
registry.addInterceptor(new TwoIntercepter()).addPathPatterns("/v1/*/**");
WebMvcConfigurer.super.addInterceptors(registry);
//拦截全部/*/**/***
//若拦截test.action类动作则/*.*
}
}
拦截器不生效常见问题:
1)是否有加@Configuration
2)拦截路径是否有问题 ** 和 *
3)拦截器最后路径一定要 “/**”, 如果是目录的话则是 /*/
Filter
是基于函数回调 doFilter(),而Interceptor则是基于AOP思想
Filter在只在Servlet前后起作用,而Interceptor够深入到方法前后、异常抛出前后等
依赖于Servlet容器即web应用中,而Interceptor不依赖于Servlet容器所以可以运行在多种环境。
在接口调用的生命周期里,Interceptor可以被多次调用,而Filter只能在容器初始化时调用一次。
Filter和Interceptor的执行顺序:
过滤前->拦截前->action执行->拦截后->过滤后
过滤器和拦截器的区别:
①拦截器是基于java的反射机制的,而过滤器是基于函数回调。
②拦截器不依赖与servlet容器,过滤器依赖与servlet容器。
③拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
④拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
⑤在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。
⑥拦截器可以获取IOC容器中的各个bean,而过滤器就不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。