微服务时代,java依附SpringBoot又再度晋升热度。本来认为php, python之类的会继续鲸吞Java的领地,熟知微服务又复原了Java来日的位置。SpringBoot依赖Spring生态圈满满圈粉,热度更胜当年。
SpringBoot的实质就是实现了主动拆卸,解决了Spring研发的配置地区问题。然而它的根底仍然是Spring, 对于web研发的根底仍然是SpringMVC。因而有必要深刻理解Spring, SpringMVC。
对于Spring来说,拦截器和过滤器是十分外围和重要的两个概念。因而本文针对这两个概念进行深入分析,让咱们彻底了解拦截器和过滤器。
对于拦截器和过滤器是什么,有大量文章曾经做了阐明,咱们这里不具体论述。本文分为五个局部:
- 拦截器和过滤器的区别
- 拦截器应用
- 过滤器应用
- 拦截器和过滤器的利用场景
- 拦截器和过滤器的执行过程及流程
1. 拦截器和过滤器的区别
过滤器(Filter) | 拦截器(Interceptor) | 总结 |
---|---|---|
Filter接口定义在javax.servlet包中 | HandlerInterceptor接口定义在org.springframework.web.servlet包中 | |
Filter定义在web.xml中 | HandlerInterceptor是在应用程序上下文配置的。 | |
Filter只在Servlet前后起作用。Filter通常将申请和响应当做黑盒子,Filter通常不思考Servlet的实现。 | 拦截器能深刻到办法前后、异样抛出前后等,因而拦截器的应用具备更大的弹性。容许用户接入(hook into)申请的生命周期,在申请过程中获取信息,Interceptor通常和申请更加耦合。 | 在Spring架构的程序中,要无限应用拦截器。简直所有Filter能做的事件,Interceptor都可能轻松的实现。 |
Filter是Servlet标准规定的。 | 拦截器既能够用于Web程序,也能够用于Application、Swing程序中。 | 应用范畴不同 |
Filter是在Servlet标准中定义的,是Servlet容器反对的。 | 拦截器是在Spring容器内的,是Spring框架反对的。 | 标准不同 |
Filter不可能应用Spring容器资源。 | 拦截器是一个Spring的组件,归Spring治理,配置在Spring文件中,因而能应用Spring里的任何资源、对象,例如Service对象、数据源、事务管理等,通过IoC注入到拦截器即可。 | Spring中应用拦截器更容易 |
Filter是被Servlet(就像Tomcat)调用的。 | 拦截器(Interceptor)是被Spring调用的。 | 因而Filter总是优于Interceptor执行。 |
上面援用他人的一张图阐明一下,在tomcat容器下,拦截器、过滤器、Servlet以及Spring Controller之间的关系。
在申请解决和响应过程中过滤器和拦截器等组件的执行流程及关系:
- 申请首先达到Servlet容器,将申请转给web容器。
- web容器调用Servlet过滤器,而后持续将申请转交给Spring的DispatcherServlet, 申请就转交给Spring上下文。
- DispatcherServlet就能够调用Spring的拦截器对申请进行拦挡解决,最初交给资源控制器。
- 最终有资源控制器联合业务模型进行业务解决,而后沿着4到1一层层朝外对相应进行解决,最终返回给客户端。
2. 拦截器的应用
Interceptor的执行程序大抵为:
- 申请达到DispatcherServlet
- DispatcherServlet发送至Interceptor, 执行preHandle
- 申请达到Controller
- 申请完结后,postHandle执行
Spring中次要通过HandlerInterceptor接口来实现申请的拦挡,实现HandlerInterceptor接口须要实现上面三个办法:
- preHandle(): 在handle执行之前,返回boolean值,true示意继续执行,false为进行执行并返回。
- postHandle(): 在handle执行之后,能够在返回之前对返回的后果进行批改。
- afterCompletion(): 在申请实现,视图生成后调用。
2.1 利用拦截器进行申请耗时统计
申请耗时统计能够通过在申请解决之前记录一个工夫,申请解决实现后再记录一个工夫,打印出两个工夫的差就是申请耗时。
2.1.1 定义一个拦截器
public class ExecuteTimeInterceptor extends HandlerInterceptorAdapter { // private static final Logger log = LoggerFactory.getLogger(ExecuteTimeInterceptor.class); /** * 在handler执行之前执行 */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { long startTime = System.currentTimeMillis(); request.setAttribute("startTime", startTime); return super.preHandle(request, response, handler); } /** * 在handler执行之后执行 */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { long startTime = (long) request.getAttribute("startTime"); long endTime = System.currentTimeMillis(); long executeTime = endTime - startTime; System.out.println("[" + handler + "] executeTime : " + executeTime + "ms"); super.postHandle(request, response, handler, modelAndView); }}
2.1.2 配置拦截器
后面介绍咱们晓得拦截器是Spring上下文的货色,因而咱们须要再Spring的上下文进行配置,应用springmvc-servlet.xml配置的形式如下:
<mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**"/> <bean class="com.xxx.interceptor.ExecuteTimeInterceptor" /> </mvc:interceptor></mvc:interceptors>
2.1.3 运行测试
运行起来,而后拜访一个接口,就会看到控制台有输入如下:
2.2 应用拦截器实现利用安全检查
通常咱们的利用分为前端利用和治理后盾利用,它们的安全检查策略是不同的。咱们就能够通过拦截器实现不同安全策略的查看。具体的拦截器逻辑实现咱们就不列举了,仅简略形容一下:
- 对于所有前端利用的申请都进行个别的安全检查,咱们暂且让FrontSecurityInterceptor拦截器来实现具体的业务。
- 对于所有后端利用的申请都进行较严格的安全检查,因为后盾数据很重要。咱们权且用BackendSecurityInterceptor拦截器来实现这个艰巨的工作。
- 另外,咱们前端利用和治理端利用都部署在同一个域名www.example.com下,前台利用间接拜访这个链接即可,后盾治理咱们通过增加前缀/admin来拜访。
那么咱们就能够配置拦截器如下所示:
<mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**"/> <mvc:exclude-mapping path="/admin/**" /> <bean class="com.yang.interceptor.FrontSecurityInterceptor" /> </mvc:interceptor> <mvc:interceptor> <mvc:mapping path="/admin/**"/> <bean class="com.yang.interceptor.BackendSecurityInterceptor" /> </mvc:interceptor></mvc:interceptors>
2.3 Spring拦截器总结
咱们能够利用mvc:interceptors标签申明一系列的拦截器,而后它们就能够造成一个拦截器链,拦截器的执行程序是按申明的先后顺序执行的,先申明的拦截器中的preHandle办法会先执行,然而它的postHandle办法和afterCompletion办法却会后执行。
在mvc:interceptors标签下申明interceptor次要有两种形式:
- 间接定义一个Interceptor实现类的bean对象。应用这种形式申明的Interceptor拦截器将会对所有的申请进行拦挡。
- 应用mvc:interceptor标签进行申明。应用这种形式进行申明的Interceptor能够通过mvc:mapping子标签来定义须要进行拦挡的申请门路。
3. Filter的应用
Servlet 的 Filter 接口须要实现如下办法:
void init(FilterConfig paramFilterConfig)
– 当容器初始化 Filter 时调用,该办法在 Filter 的生命周期只会被调用一次,个别在该办法中初始化一些资源,FilterConfig 是容器提供给 Filter 的初始化参数,在该办法中能够抛出 ServletException 。init 办法必须执行胜利,否则 Filter 可能不起作用,呈现以下两种状况时,web 容器中 Filter 可能有效:- 抛出 ServletException
- 超过 web 容器定义的执行工夫。
doFilter(ServletRequest paramServletRequest, ServletResponse paramServletResponse, FilterChain paramFilterChain)
– Web 容器每一次申请都会调用该办法。该办法将容器的申请和响应作为参数传递进来, FilterChain 用来调用下一个 Filter。void destroy()
– 当容器销毁 Filter 实例时调用该办法,能够在办法中销毁资源,该办法在 Filter 的生命周期只会被调用一次。
Filter和Interceptor的一些用处
以下是Filter接口源代码中列举出的过滤器的一些用处
- Authentication Filters
- Logging and Auditing Filters
- Image conversion Filters
- Data compression Filters
- Encryption Filters
- Tokenizing Filters
- Filters that trigger resource access events
- XSL/T filters
- Mime-type chain Filter
4.1 申请过滤器利用场景
- 执行安全检查(perform security checks)
- 格式化申请header和body(reformat request headers or bodies)
- 审查或记录日志(audit or log requests)
- 依据申请内容受权或者限度用户拜访(Authentication-Blocking requests based on user identity)
- 依据申请频率限度用户拜访
4.2 响应过滤器利用场景
- 压缩响应内容,比方让下载的内容更小(Compress the response stream)
- 追加或者批改响应(append or alter the response stream)
- 创立或者整体批改响应(create a different response altogether)
- 依据中央不同批改响应内容(Localization-Targeting the request and response to a particular locale)
5. 验证过滤器和拦截器执行过程
5.1 定义两个过滤器和拦截器
上面咱们验证一下过滤器和拦截器的执行流程和程序:
- 定义两个filter, 在web.xml中进行配置: 别离是FirstFilter和SecondFilter, 对所有申请进行过滤。
- 定义两个拦截器,在spring上下文中进行配置,这里在springmvc-servlet.xml中进行配置: 别离是FirstInterceptor和SecondInterceptor。
两个Filter实现根本相似:
// Filterpublic class FirstFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println(this.getClass().getName() + ": init"); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println(this.getClass().getName() + ": doFilter"); chain.doFilter(request, response); } @Override public void destroy() { System.out.println(this.getClass().getName() + ": destroy"); }}
两个Interceptor的实现也根本类似:
public class FirstInterceptor extends HandlerInterceptorAdapter { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println(this.getClass().getName() + ": preHandle"); return super.preHandle(request, response, handler); } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println(this.getClass().getName() + ": postHandle"); super.postHandle(request, response, handler, modelAndView); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println(this.getClass().getName() + ": afterCompletion"); super.afterCompletion(request, response, handler, ex); }}
5.2 配置过滤器和拦截器
在web.xml配置过滤器:
<!-- 定义两个filter --><filter> <filter-name>firstFilter</filter-name> <filter-class>com.yang.filter.FirstFilter</filter-class></filter><filter-mapping> <filter-name>firstFilter</filter-name> <url-pattern>/*</url-pattern></filter-mapping><filter> <filter-name>secondFilter</filter-name> <filter-class>com.yang.filter.SecondFilter</filter-class></filter><filter-mapping> <filter-name>secondFilter</filter-name> <url-pattern>/*</url-pattern></filter-mapping>
在springmvc-servlet.xml中配置拦截器:
<mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**"/> <bean class="com.yang.interceptor.ExecuteTimeInterceptor" /> </mvc:interceptor> <mvc:interceptor> <mvc:mapping path="/**"/> <bean class="com.yang.interceptor.FirstInterceptor" /> </mvc:interceptor> <mvc:interceptor> <mvc:mapping path="/**"/> <bean class="com.yang.interceptor.SecondInterceptor" /> </mvc:interceptor></mvc:interceptors>
5.3 验证后果
而后运行Tomcat, 能够看到FirstFilter, SecondFilter的init()办法在启动的时候就被执行了。
咱们启动tomcat后主动关上一个jsp页面: 咱们看到两个filter的doFilter也执行了:
而后咱们拜访一个API接口: GET /demo/hello, 咱们看到控制台输入:
5.4 总结
- 过滤器是被容器调用的,容器启动会调用过滤器的init()办法。
- 过滤器是针对所有申请的,包含一般的jsp页面,比方首页。
- 多个过滤器执行程序是依照在web.xml中配置的程序执行的。
- 拦截器是Spring上下文执行的,是在容器启动之后,申请被转给Spring的时候。
- 多个拦截器preHandle执行程序依照在容器中配置的程序进行执行。
- 多个拦截器afterHandle执行程序依照在容器中配置的倒序进行执行。
- 多个拦截器afterCompletion执行程序依照在容器中配置的倒序进行执行,并且在所有afterHandle执行实现之后执行。
码字不易,如果对大家有用,请关注 “老丐说码”。
老丐说码: 专一Java研发、SpringBoot、Spring Cloud,前面继续退出一系列相干文章。
另外举荐大家一个收费好用的本地markdown编辑器: Typora
参考文章:
- Servlet Filter and Handler Interceptor- Spring boot Implementation
- Introduction To Servlet Filter and Handler Interceptor
- Spring Interceptor vs Filter 拦截器和过滤器区别