目录
介绍一下
1、过滤器和拦截器(只针对Servlet的Filter接口和SpringMVC的HandlerInterceptor接口,不针对其它实现方式,手撸服务器的大神再见)
过滤器和拦截器都是将客户端的请求进行拦截处理,然后将请求转交给下一资源。其处理方式如下图所示。
拦截器和过滤都能实现相同的功能,那么具体差别在哪里呢?
(现在网上很多都是相互摘抄,错误一大堆,感觉没有营养,所以谈谈自己的使用看法,不谈实现)
a、过滤器依赖web容器实现,拦截器依赖mvc框架实现,也就导致其作用顺序是不同的,另外就是过滤器的作用域应该是包裹拦截器的(即过滤器在拦截器之前执行(亲自验证))。另外Filter接口提供了init()方法和destroy()方法,相对来说想在web容器初始化和销毁的时候实现一些业务逻辑,过滤器相对简单一些。
它们之间体现的一个包裹关系如下图所示(图是复制的,谢谢原图作者)。
b、在现在Spring大行其道的情况下,过滤器和拦截器都可以交给IoC管理,从而使用依赖注入(很多文章说不行,是错误的,但是在过滤器的init()和destroy()方法中避免使用依赖注入,因为IoC的生命周期和web容器的生命周期是不一样的,总之,避免在IoC没有初始化之前去使用IoC)。在SpringMVC中封装的拦截器灵活性更强,使用更加容易,优先考虑拦截器。
注意:
Filter接口中使用的是javax.servlet.ServletRequest和javax.servlet.ServletResponse,HandlerInterceptor接口中使用的是javax.servlet.http.HttpServletRequest和javax.servlet.http.HttpServletResponse。
HttpServletRequest继承自ServletRequest,HttpServletResponse继承自ServletResponse。当然HttpServletRequest和HttpServletResponse功能更加强大。
在Filter中是可以将ServletRequest和ServletResponse强转成HttpServletRequest和HttpServletResponse的。
都知道,向上转型是绝对安全的,而向下转型不一定安全。这里能这么做,是因为它们都是接口,要获取一个接口的实例,只能实例化它们的实现类。在tomcat中,只有一个实现类,分别是由org.apache.catalina.connector.RequestFacade和org.apache.catalina.connector.ResponseFacade去实现。
2、监听器(这里主指Servlet监听器)
在Servlet中已经定义好了一些事件,监听器主要作用于监听Servlet的有效事件。作用是监听由于Web应用中状态改变而引起的Servlet容器产生的相应事件,然后接受并处理这些事件。
过滤器实现
为了方便,在SpringBoot项目中实现。
1、实现Filter接口
//@Order(value=1) 用于多个Filter指定过滤器顺序
@WebFilter(filterName="myFilter1", urlPatterns= {"/*"})//标记过滤器,拦截所有请求
public class MyFilter1 implements Filter {
/**
* 过滤器初始化方法,该方法在过滤器初始化时调用(即容器加载时)
* @param filterConfig FilterConfig也是接口对象 由Servlet容器实现,主要
* 用于获取过滤器中的配置信息,其常用方法声明如下:
* String getFilterName 获取过滤器名字
* ServletContext getServletContext() 获取Servlet上下文
* String getInitParameter(String name) 获取过滤器的初始化参数值
* Enumeration getInitParameterNames() 获取过滤器的所有初始化参数
* */
@Override
public void init(FilterConfig filterConfig) throws ServletException {
//TODO 没有业务逻辑则不写相应的实现
}
/**
* 对请求进行过滤处理(也就是主要实现过滤功能的方法)
* @param chain FilterChain接口也是由Servlet容器实现。
* 其只有一个方法,如下:
* void doFilter(ServletRequest request, ServletResponse response)
* 该方法用于将过滤后的请求传递给下一个过滤器。如果是最后一个过滤器,则将请求转交给目标资源。
* @param request ServletRequest可强转为HttpServletRequest ,其都由Servlet实现
* @param response ServletResponse可强转为HttpServletResponse,因为其都由Servlet实现
* */
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
//TODO 具体的过滤逻辑实现--比如登录权限验证、字符编码过滤等
/*
* 将请求转交给下一过滤器或其它目标资源
* 注:如果没有调用此方法,则请求会在此处终止(也不会响应客户端)。
* 可以直接使用response直接响应客户端(比如在权限验证不通过的时候)。
* */
chain.doFilter(request, response);
}
/**
* 过滤器销毁方法,以便释放资源(即关闭容器时调用)
* */
@Override
public void destroy() {
//TODO 没有业务逻辑则不写相应的实现
}
}
2、在启动类上加上Servlet注解扫描
@SpringBootApplication
@ServletComponentScan//Servlet注解扫描
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
拦截器实现
为了方便,在SpringBoot项目中实现。
1、实现HandlerInterceptor接口
/**
* 可将其标记为Spring的组件
* 或在注册拦截器的时候利用构造方法传递其它Spring的组件(如其它Service层或Dao层)
* */
//@Order(value=1)//用于多个拦截器指定执行顺序
public class MyHandlerInterceptor1 implements HandlerInterceptor {
/**
* <p>该方法就是进行拦截处理的,将在Controller方法处理之前调用
* <p>该方法返回True时拦截器才会继续往下执行,返回false则会结束整个请求
* <p>可以直接使用response或全局异常拦截直接响应客户端
* */
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// TODO 具体的拦截逻辑 --比如登录权限校验、非法敏感字符校验等
//根据不同的逻辑返回boolean类型即可 也可直接使用response或全局异常拦截直接响应客户端
return HandlerInterceptor.super.preHandle(request, response, handler);
}
/**
* <p>该方法将在处理请求完成后(即Controller方法调用之后)视图渲染之前的处理操作
* <p>该方法只能在当前类的preHandle()方法返回true时才会执行
* */
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// TODO 没有相关业务逻辑 则不用重写此方法
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
/**
* <p>该方法将在整个请求和相应动作完成之后执行(即视图渲染之后)
* <p>该方法只能在当前类的preHandle()方法返回true时才会执行
* */
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
// TODO 没有相关业务逻辑 则不用重写此方法
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
2、注册拦截器,实现WebMvcConfigurer接口。
@Configuration
public class InterceptConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//注册拦截器 可利用构造方法传递一些参数
registry.addInterceptor(new MyHandlerInterceptor1())
//拦截所有请求
.addPathPatterns("/**");
//还可排除一些静态资源请求拦截
//.excludePathPatterns(null);
}
Servlet监听器实现
1、实现Servlet中定义的监听器接口,如ServletContextListener(Servlet还定义了其它监听器)
/**
* <p>Servlet监听器
* <p>@WebListener注解只有一个属性 用于描述这个监听器 可以默认不要
* */
@WebListener(value = "监听Servlet上下文的创建和删除")
public class MyListener implements ServletContextListener {
/**
* <p>通知正在收听的对象,应用程序已经被加载及初始化
* */
@Override
public void contextInitialized(ServletContextEvent sce) {
//TODO 没有相应的业务逻辑 则不写实现
}
/**
* <p>通知正在收听的对象,应用程序已经被销毁(即关闭)
* */
@Override
public void contextDestroyed(ServletContextEvent sce) {
//TODO 没有相应的业务逻辑 则不写实现
}
}
2、在启动类上加上Servlet注解扫描
@SpringBootApplication
@ServletComponentScan//Servlet注解扫描
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
3、Servlet其它监听器
Servlet监听器的功能就是监听由于Web应用中状态改变而引起的Servlet容器产生的相应事件,
比如利用HttpSessionBindingListener统计在线人数。
除了上述的ServletContextListener,还有以下监听器(共8个)。如下表所示:
Servlet上下文监听 | ServletContextListener | 主要监听Servlet上下文的创建和删除 |
ServletContextAttributeListener | 主要监听Servlet上下文属性的增加、删除和修改 | |
HTTP会话监听 | HttpSessionListener | 主要监听HTTP会话创建和销毁 |
HttpSessionActivationListener | 实现监听HTTP会话active和passivate | |
HttpSessionAttributeListener | 实现监听HTTP会话中属性的设置请求 | |
HttpSessionBindingListener | 实现监听HTTP会话中对象的绑定信息 | |
Servlet请求监听 | ServletRequestListener | 实现监听客户端的请求 |
ServletRequestAttributeListener | 实现讲台客户端请求属性变化 |
ServletContextAttributeListener
public class MyListener2 implements ServletContextAttributeListener {
/**
* <p>当有对象加入Application范围时,通知正在收听的对象
* */
@Override
public void attributeAdded(ServletContextAttributeEvent scae) {
// TODO Auto-generated method stub
}
/**
* <p>当有对象从Application范围中移除,通知正在收听的对象
* */
@Override
public void attributeRemoved(ServletContextAttributeEvent scae) {
// TODO Auto-generated method stub
}
/**
* <p>挡在Application的范围有对象取代另一个对象时,通知正在收听的对象
* */
@Override
public void attributeReplaced(ServletContextAttributeEvent scae) {
// TODO Auto-generated method stub
}
}
HttpSessionListener
public class MyListener2 implements HttpSessionListener {
/**
* <p>通知正在收听的对象,session已经被加载及初始化
* */
@Override
public void sessionCreated(HttpSessionEvent se) {
// TODO Auto-generated method stub
}
/**
* <p>通知正在收听的对象,session已经被载出销毁
* @param se HttpSessionEvent的主要方法是getSession(),可以使用
* 该方法回传一个session对象
* */
@Override
public void sessionDestroyed(HttpSessionEvent se) {
// TODO Auto-generated method stub
}
}
HttpSessionActivationListener
public class MyListener2 implements HttpSessionActivationListener {
/**
* <p>通知正在收听的对象,它的session已经被变为无效状态
* */
@Override
public void sessionWillPassivate(HttpSessionEvent se) {
// TODO Auto-generated method stub
}
/**
* <p>通知正在收听的对象,它的session已经变为有效状态
* */
@Override
public void sessionDidActivate(HttpSessionEvent se) {
// TODO Auto-generated method stub
}
}
HttpSessionAttributeListener
public class MyListener2 implements HttpSessionAttributeListener {
/**
* <p>当有对象加入session范围时,通知正在收听的对象
* */
@Override
public void attributeAdded(HttpSessionBindingEvent se) {
// TODO Auto-generated method stub
}
/**
* <p>当有对象从session范围移除时,通知正在收听的对象
* @param se HttpSessionBindingEvent类主要有3个方法
* getName()、getSession()、getValues()
* */
@Override
public void attributeRemoved(HttpSessionBindingEvent se) {
// TODO Auto-generated method stub
}
/**
* <p>当在session的范围有对象取代另一个对象时,通知正在收听的对象
* */
@Override
public void attributeReplaced(HttpSessionBindingEvent se) {
// TODO Auto-generated method stub
}
}
HttpSessionBindingListener
public class MyListener2 implements HttpSessionBindingListener {
/**
* <p>当有对象加入session范围时会被自动调用
* */
@Override
public void valueBound(HttpSessionBindingEvent event) {
// TODO Auto-generated method stub
}
/**
* <p>当有对象从session的范围内移除时会被自动调用
* */
@Override
public void valueUnbound(HttpSessionBindingEvent event) {
// TODO Auto-generated method stub
}
}
ServletRequestListener
public class MyListener2 implements ServletRequestListener {
/**
* <p>通知正在收听的对象,ServletRequest已经被加载及初始化
* */
@Override
public void requestInitialized(ServletRequestEvent sre) {
// TODO Auto-generated method stub
}
/**
* <p>通知正在收听的对象,ServletRequest已经被载出销毁
* */
@Override
public void requestDestroyed(ServletRequestEvent sre) {
// TODO Auto-generated method stub
}
}
ServletRequestAttributeListener
public class MyListener2 implements ServletRequestAttributeListener {
/**
* <p>当有对象加入request范围时,通知正在收听的对象
* */
@Override
public void attributeAdded(ServletRequestAttributeEvent srae) {
// TODO Auto-generated method stub
}
/**
* <p>当有对象从request范围移除时,通知正在收听的对象
* */
@Override
public void attributeRemoved(ServletRequestAttributeEvent srae) {
// TODO Auto-generated method stub
}
/**
* <p>当在request范围内有对象取代另一个对象时,通知正在收听的对象
* */
@Override
public void attributeReplaced(ServletRequestAttributeEvent srae) {
// TODO Auto-generated method stub
}
}