从Spring-Boot开始深入理解Spring系列——Spring-Boot使用servletsfilterlistenerinterceptor
Posted 独孤文彬
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从Spring-Boot开始深入理解Spring系列——Spring-Boot使用servletsfilterlistenerinterceptor相关的知识,希望对你有一定的参考价值。
文章目录
基础概念理解
servlet
Servlet 是一种运行 服务器端 的 java 应用程序,具有 独立于平台和协议 的特性,并且可以动态的生成 web 页面,它工作在 客户端请求 与 服务器响应 的中间层。
扩展认知:同时servelet也是一种JavaEE的技术规范和标准
filter
Filter 又称为过滤器,对 用户请求 进行 预处理,接着将请求交给 Servlet 进行 处理 并 生成响应,最后 Filter 再对 服务器响应 进行 后处理。Filter 是可以复用的代码片段,常用来转换 HTTP 请求、响应 和 头信息。Filter 不像 Servlet,它不能产生 响应,而是只 修改 对某一资源的 请求 或者 响应。
listener
Listener 可以监听 web 服务器中某一个 事件操作,并触发注册的 回调函数。通俗的语言就是在 application,session,request 三个对象 创建/消亡 或者 增删改 属性时,自动执行代码的功能组件。
interceptor
Java 里的拦截器是动态拦截 action 调用的对象。它提供了一种机制可以使开发者可以定义在一个 action 执行的前后执行的代码,也可以在一个 action 执行前阻止其执行,同时也提供了一种可以提取 action 中可重用部分的方式。
类似 面向切面编程 中的 切面 和 通知,我们通过 动态代理 对一个 service() 方法添加 通知 进行功能增强。比如说在方法执行前进行 初始化处理,在方法执行后进行 后置处理。
对比
servlet和filter的关系
servlet技术规范中,定义了filter技术,并提供了接口
Servlet API中提供了一个Filter接口,开发web应用时,如果编写的Java类实现了这个接口,则把这个java类称之为过滤器Filter。通过Filter技术,开发人员可以实现用户在访问某个目标资源之前,对访问的请求和响应进行拦截。简单说,就是可以实现web容器对某资源的访问前截获进行相关的处理,还可以在某资源向web容器返回响应前进行截获进行处理。
filter和listener和interceptor的对比
interceptor和aop的关系
aop是一种编程思想和理念,java中的拦截器是AOP思想的一种简单应用,相当于阉割版的AOP。
aop框架实现了aop的思想,例如SpringAOP、jbossAOP、aspectJ等,并且都用到了反射、JDK的动态代理/静态代理技术/cglib字节码动态生成,最终通过代理的方式通过反射调用目标类的方法
而各大主流框架所实现的拦截器,相当于是一个简化版的AOP框架,例如:mybatis的插件、structs2的拦截器、SpringMVC的拦截器。(当然,你也可以定义和实现自己的拦截器。并且很容易基于这些框架的基础之上,定义一个自己的拦截器。甚至,你可以完全自己手工实现一个简版的拦截器。)
interceptor 和 filter的关系
从本质上理解:
- 都是一种拦截方式,都能够做预处理和后处理。但,相比于Filter, 只是框架中的Interceptor的产生作用的时间和位置、以及拦截的范围不一样
- 都是AOP思想的一种简单实现
从使用上来讲,filter能对所有的web请求,起作用,功能更加强大。而interceptor只能对action起作用。
深入理解:
原理详解
servlet
一、Servlet生命周期分为三个阶段:
1、初始化阶段,调用init()方法
2、响应客户请求阶段,调用service()方法
3、终止阶段,调用destroy()方法
二、Servlet初始化阶段,在下列时刻Servlet容器装载Servlet:
1、Servlet容器启动时自动装载某些Servlet
2、在Servlet容器启动后,客户首次向Servlet发送请求
3、Servlet类文件被更新后,重新装载Servlet。
三、Servlet接收和响应客户请求的过程
1、首先客户发送一个请求。
2、Servlet容器会创建特定于这个请求的ServletRequest对象和ServletResponse对象,然后调用Servlet的service()方法对请求进行响应。
3、service()方法中,对请求的方式进行了匹配,选择调用doGet,doPost等这些方法
4、在doGet、doPost等方法中调用逻辑层的方法,实现对客户的响应
注:
在Servlet接口和GenericServlet类中是没有doGet、doPost等等这些方法的,HttpServlet中定义了这些方法,但是都是返回error信息,所以我们每次定义一个Servlet的时候,都必须实现doGet或doPost等这些方法。
Servlet接口和GenericServlet是不特定于任何协议的,而HttpServlet是特定于HTTP协议的类,所以HttpServlet中实现了service()方法,并将请求ServletRequest,ServletResponse强转为HttpRequest和HttpResponse。
filter
本质:
FilterChain是回调接口, doFilter(request,response)是回调方法,ApplicationFilterChain是实现类,里面能得到实现了Filter接口的实现类xxxFilter,在doFilter(request,response)中执行中了某个Filter实现类的doFilter(request, response, this)(this指的当前ApplicationFilterChain类)方法,在这个方法执行某些处理后需要回调ApplicationFilterChain.doFilter(request,response),这个回调会执行filter链中的下一个循环到结束 。这也解释了为什么在Filter中没有调用chain.doFilter()方法,客户请求不会到达所访问的Web组件。
原理:
HttpRequest ----> Filter ----> Servlet ----> Controller/Action/… ----> Filter ----> HttpResponse
生命周期:
Filter的创建和销毁由WEB服务器负责。 web 应用程序启动时,web 服务器将创建Filter 的实例对象,并调用其init方法,读取web.xml配置,完成对象的初始化功能,从而为后续的用户请求作好拦截的准备工作(filter对象只会创建一次,init方法也只会执行一次)。开发人员通过init方法的参数,可获得代表当前filter配置信息的FilterConfig对象。
listener
监听器就是一个实现特定接口的普通java程序,这个程序专门用于监听一个java对象的方法调用或属性改变,当被监听对象发生上述事件后,监听器某个方法将立即被执行。
简单的说,被监听对象A中,关联着B对象。事件源A类对外提供一个方法,用于设置监听器对象B到A类的某一实例变量中。在需要监听事件源的方法中,方法体的某一处先构造创建一个Event对象,将this即B与相关的动作封装进Event对象中,然后调用监听器B对象的doXXXX(event)方法,将事件对象传入方法实参中。
扩展知识点:
- 在Servlet规范中定义了多种类型的监听器,它们用于监听的事件源分别为SerlvetConext,HttpSession和ServletRequest这三个域对象。
- Servlet规范针对这三个对象上的操作,又把这多种类型的监听器划分为三种类型:
- 1,监听三个域对象创建和销毁的事件监听器.
a、如三个域对象的创建与销毁方法签名:
- ServletRequestListener
- HttpSessionListener
- ServletContextListener
- 2,监听域对象中属性的增加和删除的事件监听器
b、三个类型对象域中增、删、改的监听器(3个)
- ServletContextAttributeListener,
- HttpSessionAttributeListener,
- ServletRequestAttributeListener
- 3,监听绑定到HttpSession域中的某个对象的状态的时间监听器。
-c、感知型监听器(2个):监听自己何时被帮到session上,何时解绑了;何时被钝化了,何时被活化了(序列化到某个存储设置中)。
- HttpSessionBindingListener:实现该接口的类,能检测自己何时被Httpsession绑定,和解绑
- HttpSessionActivationListener:实现该接口的类(要求些javabean必须是实现了Serializable接口的),能监测自己何时随着HttpSession一起激活和钝化。
注意:这种监听器不需要注册。某个javabean实现这些接口后就可以监听何时被绑定、解绑或被激活或钝化。
interceptor
原理技术点
- 反射技术
- 代理技术
- 职责链模式
主流框架中,对拦截器的应用
- webMVC框架:structs2/ springMVC
- 持久化框架:mybatis中的插件(拦截器)
执行顺序,以springmvc为例
HttpRequest ----> DispactherServlet ----> HandlerInterceptor ---->Controller----> HandlerInterceptor ----> HttpResponse
生命周期、执行顺序的理解
总结:
Filter起作用的时机是在请求到达Servlet之前,HandlerInterceptor其作用的时机是在DispactherServlet接收到用户请求完成请求到相应的Handler映射之后。
虽然都先于在具体的业务逻辑执行,但是还是存在一些差异,Filter面对的是所有的请求,而HandlerInterceptor是面对具体的Controller。Filter总是先于HandlerInterceptor发挥作用,在Filter中甚至可以中断请求,从而使它无法到达相应的Servlet。而且两者的配置也不一样,Filter是在web.xml中进行配置,HandlerInterceptor是在具体的applicationContext.xml中进行配置。
实战:
示例应用
自定义filter示例
@WebFilter(filterName = "myFilter", urlPatterns = "/*")
public class MyFilter implements Filter
private static Logger LOGGER = LoggerFactory.getLogger(MyFilter.class);
@Override
public void init(FilterConfig filterConfig) throws ServletException
LOGGER.info("my filter just init……");
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String url = httpRequest.getRequestURI();
LOGGER.info("请求的地址: ", url);
LOGGER.info("请求的session id: ",httpRequest.getSession().getId().toString());
LOGGER.info("before do_filter action , write your code here , before do_filter!");
//example:setMaxInactiveInterval,session will destroy after 1 min
httpRequest.getSession().setMaxInactiveInterval(1*60);
httpRequest.setAttribute("customArg","bill cindy alice ! they are family ! wonderful!");
chain.doFilter(httpRequest, httpResponse);
LOGGER.info("filter response action , write your code here, after do filter!");
//example: addCookie and set a invalid status code to demonstrate
// httpResponse.getOutputStream().flush();
@Override
public void destroy()
LOGGER.info("my filter was destroy!!");
自定义servlet:
@WebServlet(name = "myServlet", urlPatterns = "/myServlet")
public class MyServlet extends HttpServlet
private static Logger logger = LoggerFactory.getLogger(MyServlet.class);
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
doPost(req, resp);
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
logger.info("发起servlet请求:",req.getPathInfo());
ServletOutputStream writer = resp.getOutputStream();
// writer.write("hello world!");
String text = "Now is the winter of our discontent. How Now Brown Cow. The quick brown fox jumped over the lazy dog.\\n";
// for (int i = 0; i < 10; i++)
// text = text + text;
//
// resp.addCookie(new Cookie("username","bill"));
// resp.setStatus(403);
byte[] data = text.getBytes(StandardCharsets.UTF_8);
writer.write(data);
writer.flush();
自定义interceptor:
@Component
public class MyInterceptor extends HandlerInterceptorAdapter
private static final Logger LOGGER = LoggerFactory.getLogger(MyInterceptor.class);
public MyInterceptor()
super();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
LOGGER.info("===>>>preHandle i got the first interceptor handler , yes i love spring-boot now ! just enjoy coding with spring - spring-mvc -and spring-boot !");
return super.preHandle(request, response, handler);
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception
LOGGER.info("<<<<===postHandle i got the first interceptor handler , yes i love spring-boot now ! just enjoy coding with spring - spring-mvc -and spring-boot !");
super.postHandle(request, response, handler, modelAndView);
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception
LOGGER.info(" afterCompletion =====》》》》 i got the first interceptor handler , yes i love spring-boot now ! just enjoy coding with spring - spring-mvc -and spring-boot !");
super.afterCompletion(request, response, handler, ex);
自定义listener 之 HttpSessionListener :
@WebListener("myHttpSessionListener")
public class MyHttpSessionListener implements HttpSessionListener
private static Logger logger = LoggerFactory.getLogger(MyHttpSessionListener.class);
@Override
public void sessionCreated(HttpSessionEvent se)
logger.info("session创建");
@Override
public void sessionDestroyed(HttpSessionEvent se)
logger.info("session销毁");
*自定义listener 之ServletContextListener :
@WebListener("MyListener")
public class MyServletListener implements ServletContextListener
private static Logger LOGGER = LoggerFactory.getLogger(MyServletListener.class);
@Override
public void contextInitialized(ServletContextEvent servletContextEvent)
LOGGER.info("监听器启动");
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent)
LOGGER.info("监听器销毁");
自定义listener 之 ServletRequestListener :
@WebListener("MyServletRequestListener")
public class MyServletRequestListener implements ServletRequestListener
private static final String REQUEST_ATTRIBUTES_ATTRIBUTE = RequestContextListener.class.getName() + ".REQUEST_ATTRIBUTES";
private static final Logger LOGGER = LoggerFactory.getLogger(MyServletRequestListener.class);
@Override
public void requestDestroyed(ServletRequestEvent requestEvent)
LOGGER.info("ServletRequestListener destroyed !!!!");
@Override
public void requestInitialized(ServletRequestEvent requestEvent)
LOGGER.info("ServletRequestListener init !!!");
if (!(requestEvent.getServletRequest() instanceof HttpServletRequest))
throw new IllegalArgumentException("Request is not an HttpServletRequest: " + requestEvent.getServletRequest());
else
HttpServletRequest request = (HttpServletRequest)requestEvent.getServletRequest();
ServletRequestAttributes attributes = new ServletRequestAttributes(request);
request.setAttribute(REQUEST_ATTRIBUTES_ATTRIBUTE, attributes);
LocaleContextHolder.setLocale(request.getLocale());
RequestContextHolder.setRequestAttributes(attributes);
项目实战应用场景
filter:
- 执行目标资源之前做预处理工作,例如设置编码,这种试通常都会放行,只是在目标资源执行之前做一些准备工作;
- 通过条件判断是否放行,例如校验当前用户是否已经登录,或者用户IP是否已经被禁用;
- 在目标资源执行后,做一些后续的特殊处理工作,例如把目标资源输出的数据进行处理
具体场景如
- 统一POST请求中文字符编码的过滤器
- 控制浏览器缓存页面中的静态资源的过滤器
- 实现URL级别的权限认证:在实际开发中我们经常把一些执行敏感操作的servlet映射到一些特殊目录中,并用filter把这些特殊目录保护起来,限制只能拥有相应访问权限的用户才能访问这些目录下的资源。从而在我们系统中实现一种URL级别的权限功能。
- 实现用户自动登陆:首先,在用户登陆成功后,发送一个名称为user的cookie给客户端,cookie的值为用户名和md5加密后的密码。编写一个AutoLoginFilter,这个filter检查用户是否带有名称为user的cookie,如果有,则调用dao查询cookie的用户名和密码是否和数据库匹配,匹配则向session中存入user对象(即用户登陆标记),以实现程序完成自动登陆。
listener
- 在ServletContextLintener监听器的contextInitialized方法中,进行应用级的资源初始化以便提高效率,在contextDestroyed方法中对应用级的资源进行释放。
- web应用中,会存在会话,通常的作法是将当前登录的用户存放在session会话中。那么如何统计在线人数话,如何显示出当前登录的用户呢。如何踢出某些已登录的用户呢。就可以通过HttpSessionAttributeListener监听器的attributeAdded方法。
- 待补充……
interceptor:
- 1、日志记录:记录请求信息的日志,以便进行信息监控、信息统计、计算PV(Page View)等。
- 2、权限检查:如登录检测,进入处理器检测检测是否登录,如果没有直接返回到登录页面;
- 3、性能监控:有时候系统在某段时间莫名其妙的慢,可以通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间(如果有反向代理,如apache可以自动记录);
- 4、通用行为:读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取Locale、Theme信息等,只要是多个处理器都需要的即可使用拦截器实现。
5、OpenSessionInView:如hibernate,在进入处理器打开Session,在完成后关闭Session。
6、实现SQL查询的分页,如mybatis的分页插件
附
源码下载
https://github.com/bill4j/spring-boot-course/tree/develop/spring-boot-filter
参考博客
java实现拦截器
理解java中的filter和interceptor
SpringMVC中的拦截器和原理
拦截器的基本原理
mybatis的拦截器
mybatis拦截器的使用和设计原理
AOP的实现机制
java中的三大器
Springboot入门servlet、filter、listener、interceptor
以上是关于从Spring-Boot开始深入理解Spring系列——Spring-Boot使用servletsfilterlistenerinterceptor的主要内容,如果未能解决你的问题,请参考以下文章
让你的spring-boot应用日志随心所欲--spring boot日志深入分析
点燃不会从 spring-boot 2.0.5 开始 - h2 属性 NESTED_JOINS 不存在