拦截器
简介
- 用于在某个方法被访问之前进行拦截,然后在方法执行之前或之后加入某些操作,其实就是AOP的一种实现策略。
- 它通过动态拦截Action调用的对象,允许开发者定义在一个action执行的前后执行的代码,也可以在一个action执行前阻止其执行。
- 同时它也提供了一种可以提取action中可重用的部分的方式。
- 每个Interceptor的调用会依据它在xml文件中声明顺序依次执行。
- SpringMVC中的Interceptor是链式调用的,每个Interceptor的调用都根据它的声明顺序依次执行,且最先执行其preHandle方法,所以可以在该方法中进行一些前置初始化操作或是预处理。
- 实现一个interceptor拦截器类后,需要在配置中配置使它生效:实现 WebMvcConfigurerAdapter并重写 addInterceptors,同时在这个方法里设置要过滤的URL。
实现
- springMVC 中的Interceptor 拦截请求是通过HandlerInterceptor 来实现。
- 方式一:实现Spring 的HandlerInterceptor 接口,或者继承实现了HandlerInterceptor 接口的类,比如HandlerInterceptorAdapter 。
- 方式二:实现Spring的WebRequestInterceptor接口,或者是继承实现了WebRequestInterceptor的类。
- preHandle方法:在请求处理之前进行调用。可以在这个方法中进行一些前置初始化操作或者是对当前请求的一个预处理,也可以在这个方法中进行一些判断来决定请求是否要继续进行下去。该方法的返回值是布尔类型,如果返回false,表示请求结束,后续的Interceptor和Controller都不会再执行了,如果返回true就执行下一个拦截器的preHandle方法,一直到最后一个拦截器preHandle方法执行完成后调用当前请求的Controller方法。
- postHandle方法:当前所属的Interceptor 的preHandle 方法的返回值为true 时才能被调用,并且在当前请求进行处理之后,也就是Controller 方法调用之后执行,但是它会在DispatcherServlet 进行视图返回渲染之前被调用,所以我们可以在这个方法中对Controller 处理之后的ModelAndView 对象进行操作。
- afterCompletion方法:用于资源清理。当前对应的Interceptor 的preHandle 方法的返回值为true ,并且在整个请求结束之后,也就是在DispatcherServlet 渲染了对应的视图之后执行。
过滤器
简介
- 用于在客户端与Web资源之间的请求和响应进行过滤。
- Servlet中的过滤器Filter是实现了javax.servlet.Filter接口的服务器端程序,主要的用途是设置字符集、控制权限、控制转向、做一些业务逻辑判断等。
- 它主要用于对用户请求进行预处理,也可以对请求响应进行后处理。
- 基本流程:Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。
- Filter可以负责拦截多个请求或响应;一个请求或响应也可以被多个Filter拦截。
用途
- 在HttpServletRequest到达Servlet之前,拦截客户的HttpServletRequest。
- 可以检查HttpServletRequest、修改HttpServletRequest头和数据。
- 在HttpServletResponse到达客户端之前,拦截HttpServletResponse。
- 可以检查HttpServletResponse、修改HttpServletResponse头和数据。
种类
- 用户授权Filter:Filter负责检查用户请求,根据请求过滤用户非法请求。如解析token。
- 日志Filter:详细记录某些特殊的用户请求。
- 解码Filter:对非标准编码的请求解码。
- XSLT Filter:能改变XML内容。
- 跨域过滤器等。
实现
- void init(FilterConfig config):用于完成Filter的初始化。
- void destory():用于Filter销毁前,完成某些资源的回收。
- void doFilter(ServletRequest request,ServletResponse response,FilterChain chain):实现过滤功能,该方法就是对每个请求及响应增加的额外处理。该方法可以实现对用户请求进行预处理(ServletRequest request),也可实现对服务器响应进行后处理(ServletResponse response)—它们的分界线为是否调用了chain.doFilter(),执行该方法之前,即对用户请求进行预处理;执行该方法之后,即对服务器响应进行后处理。
过滤器和拦截器的区别
- Filter是依赖于Servlet容器,属于Servlet规范的一部分,而拦截器则是独立存在的,可以在任何情况下使用。
- Filter的执行由Servlet容器回调完成,而拦截器通常通过动态代理的方式来执行。
- Filter的生命周期由Servlet容器管理,而拦截器则可以通过IoC容器来管理,因此可以通过注入等方式来获取其他Bean的实例,因此使用会更方便。
- 拦截器是基于java反射机制的,而过滤器是基于函数回调。
- 拦截器只能对action请求起作用,而过滤器可以对几乎所以的请求起作用。
- 在Action的生命周期周,拦截器可以被多次调用,而过滤器只能在容器初始化的时候被调用一次。
- Filter是Servlet规范规定的,只能用于Web程序中,而拦截器既可以用于Web程序,也可以用于Application、Swing程序中。
- 拦截器由Spring管理,配置在Spring文件中,因此能使用Spring里的任何资源、对象(各种bean),而Filter不行。
- Filter只在Servlet前后起作用,而拦截器能够深入到方法前后、异常跑出前后等,拦截器的使用有更大的弹性。
拦截器使用
执行顺序
- 请求到达 DispatcherServlet
- DispatcherServlet 发送至 Interceptor ,执行 preHandle
- 请求达到 Controller
- 请求结束后,postHandle 执行
案例
- 创建HandlerInterceptor 实现类
public class LogCosInterceptor implements HandlerInterceptor {
long start = System.currentTimeMillis();
//在handler执行之前,返回 boolean 值,true 表示继续执行,false 为停止执行并返回。
//在执行Handler之前进行,即Controller方法调用之前执行,主要进行初始化操作。
@override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
start = System.currentTimeMillis();
return true;
}
//在handler执行之后, 可以在返回之前对返回的结果进行修改
//在执行Handler之后进行,即Controller 方法调用之后执行,主要对ModelAndView对象进行操作。
@override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,Object o, ModelAndView modelAndView) throws Exception {
System.out.println("Interceptor cost="+(System.currentTimeMillis()-start));
}
//在请求完全结束后调用,常用于清理资源等工作,可以用来统计请求耗时等等
//在整个请求结束之后,即渲染对应的视图之后执行, 主要进行资源清理工作。
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse, Object o, Exception e) throws Exception {
}
}
- 通过配置类向spring加入拦截器
@Configuration
public class InterceptorConfig extends WebMvcConfigurerAdapter {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//指定拦截器和拦截的URL
registry.addInterceptor(new LogCosInterceptor()).addPathPatterns("/**");
super.addInterceptors(registry);
}
}
- 使用mvc:interceptors标签来声明需要加入到SpringMVC拦截器链中的拦截器
<!-- 可以利用mvc:interceptors标签声明一系列的拦截器,然后它们就可以形成一个拦截器链,拦截器的执行顺序是按声明的先后顺序执行的,先声明的拦截器中的preHandle方法会先执行,然而它的postHandle方法和afterCompletion方法却会后执行。-->
<mvc:interceptors>
<!-- 使用bean定义一个Interceptor,直接定义在mvc:interceptors根下面的Interceptor将拦截所有的请求 -->
<bean class="com.company.app.web.interceptor.AllInterceptor"/>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/parent/**"/>
<bean class="com.company.authorization.interceptor.SecurityInterceptor" />
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/parent/**"/>
<bean class="com.company.authorization.interceptor.SecuritySystemInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
过滤器使用
- 创建Filter实现类
public class LogCosFilter implements Filter {
@Override
public init(FilterConfig filterConfig) throws ServletExcpetion {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletReqonse servletReponse, FilterChain filterChain) thows IOException IOException, ServletExcption {
long start = System.currentTimeMillis();
filter.Chain.doFilter(servletRequest,servletResponse);
System.out.println("Execute cost=" + (System.currentTimeMillis()-start));
}
@override
public void destroy() {
}
}
- 创建配置类
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean registFilter() {
FilterRegistrationBean registraton = new FilterRegistrationBean();
registration.setFilter(new LogCosFilter()); //实例化Filter类
registration.addUrlPatterns("/*"); //指定Url匹配模式
registration.setName("LogCostFilter"); //设置过滤器名称
registration.setOrder(1); //指定执行顺序
return registration;
}
}
- XMl方式配置
<filter>
<filter-name>filtertest</filter-name>
<filter-class>LogCosFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>filtertest</filter-name>
<url-pattern>/point/*</url-pattern>
</filter-mapping>
经典案例
- 跨域过滤器
public class CORSFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) servletResponse;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with,Authorization");
response.setHeader("Access-Control-Allow-Credentials", "true");
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}
public class ContextConfig {
@Bean
public FilterRegistrationBean<Filter> headerCrosFilterRegistrationBean() {
FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<Filter>();
registration.setFilter(new CORSFilter());
registration.addUrlPatterns("/*");
registration.setEnabled(true);
registration.setOrder(1);
return registration;
}
}