1 前言
1.1 内容概要
- 掌握ServletContextListener的使用,并且理解其执行时机
- 掌握Filter的使用,并且理解其执行时机
- 能够使用Filter解决一些实际的问题
1.2 前置知识准备
-
Servlet的执行
-
ServletContext的功能和使用
2 Web组件
JavaEE的三大Web组件
- Servlet → 处理请求对应的业务
- Listener → 监听器
- Filter → 过滤器
2.1 Listener监听器
顾名思义就是监听东西的,其实和命名有关系,我们提供的是什么监听器就是监听什么的。
UserListener 就是监听User
监听器在监听到主体做了XX事情,就会触发对应的事件。
2.2 ServletContextListener
监听的主体就是ServletContext,当发现ServletContext做了事情,监听器就会执行该事件特定的方法
- ServletContext如果初始化,则会执行监听器的初始化方法
- ServletContext如果销毁,则会执行监听器的销毁方法
- 如果我们想要在应用程序启动的过程中,实现一些自定义的代码,需要方法ServletContextListener的监听ServletContext初始化的方法对应的执行方法 → 监听器的初始化方法,会在应用程序启动的时候执行,主要做一些资源的初始化
- 如果我们想要在应用程序关闭的过程中,实现一些自定义的代码,放在监听器的销毁方法 → 监听器的销毁方法,会在应用程序关闭(卸载的时候执行) ,主要做资源的释放
2.3 执行过程
当应用程序启动的过程中,逐步加载Web组件
- 首先会加载ServletContext和Listener组件
- ServletContext伴随着应用程序初始化,它开始初始化,然后ServletContextListener监听到ServletContext初始化,会执行Listener的Initialized方法
- 然后初始化loadOnStartup为正数的Servlet
改造之前的业务代码,之前整合MyBatis时,SqlSessionFactory的初始化是通过Servlet的生命周期init方法,当前可以通过ServletContextListener,在应用程序启动的时候,执行contextInitialized方法,在该方法中进行SqlSessionFactory初始化过程,并将其放到ServletContext中
@WebListener
public class CustomServletContextListener implements ServletContextListener {
// 当ServletContext初始化的时候执行
// 应用程序启动的时候向ServletContext中塞入一些数据
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
ServletContext servletContext = servletContextEvent.getServletContext();
SqlSessionFactory sqlSessionFactory = null;
try {
sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis.xml"));
System.out.println("ServletContext初始化");
System.out.println("sqlSessionFactory = " + sqlSessionFactory);
} catch (IOException e) {
e.printStackTrace();
}
servletContext.setAttribute("SqlSessionFactory",sqlSessionFactory);
}
// 当ServletContext销毁的时候执行
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.println("ServletContext销毁");
}
}
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
SqlSessionFactory sqlSessionFactory;
@Override
public void init() throws ServletException {
ServletContext servletContext = getServletContext();
sqlSessionFactory = (SqlSessionFactory) servletContext.getAttribute("SqlSessionFactory");
System.out.println("Servlet初始化");
System.out.println("sqlSessionFactory = " + sqlSessionFactory);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
提供Listener,其实主要就是去初始化这个ServletContext
SpringMVC就是基于这样的特点去实现的
3 Filter
Filter是一个执行过滤任务的一个对象。它既可以作用于Request对象,也可以作用于Response对象,或者两者均作用。
也就是Servlet中获取请求之前,Servlet响应之后
3.1 Filter和Servlet的执行
URL-Pattern和Servlet之间存在着映射关系,URL-Pattern和Filter之间也存在着映射关系。
- 1个URL-Pattern只能对应一个Servlet,但是可以对应多个Filter
- Servlet和URL-Pattern之间是一对多的关系,但是URL-Pattern和Servlet之间是一对一
其实就意味着一件事,当我们发起一个请求的时候,其实就是一个URL-Pattern对应的请求
- 对应1个Servlet
- 对应多个Filter
如果只有一个过滤器那么执行流程如下
多个过滤器,就是就组成了一个过滤器的链,依次执行过滤器
如果增加上对应的方法
有一个问题,是否每一次都会继续执行到下一个拦截器或Servlet?不一定,去界定是否为放行状态
doFilter这个方法中,提供了一个形参,形参叫filterChain,filterChain中提供了一个doFilter方法,如果执行这个方法就是放行,如果不执行,则中断流程
3.2 使用
/**
* localhost:8080/demo5/hello
* localhost:8080/demo5/bye
* URL-Pattern对于上面两个请求都能起作用,那么我们的URL-Pattern可以设置为 /*
*/
@WebFilter("/*")
public class URLPrintFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
String url = request.getRequestURL().toString();
System.out.println("url = " + url);
System.out.println("Filter的前半部分");
//放行
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("Filter的后半部分");
}
@Override
public void destroy() {
}
}
就算没有Servlet,仍然是可以执行到Filter的
Filter能否继续执行,取决于FilterChain的doFilter方法是否执行
3.3 案例
3.3.1 给请求和响应设置字符集
Post请求中文乱码
request.setCharacterEncoding(“utf-8”)
响应的时候,响应的字符中文乱码
response.setContentType(“text/html;charset=utf-8”)
@WebFilter("/*")
public class CharacterEncodingFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
request.setCharacterEncoding("utf-8");
//response.setContentType("text/html;charset=utf-8");
response.setContentType("application/json;charset=utf-8");
filterChain.doFilter(request, response);
}
@Override
public void destroy() {
}
}
3.3.2 登录案例
Http://localhost:8080/demo6/user/login
Http://localhost:8080/demo6/user/info
在Session中是否有存储用户的信息
- login登录实现的什么事情?
- 验证用户传入的用户名和密码,和数据库中存储的信息是否一致
- 验证如果成功,将用户信息存到Session里面
- info查看用户信息实现的是什么事情
- 从Session中取出信息,分析是否取出了信息
- 如果取出了信息,说明登录成功了,根据取出的信息查询用户的具体信息
- 如果没有取出信息,提示未登录的json数据
- order/list 查询当前用户的订单信息
- 从Session中取出信息,分析是否取出了信息
- 如果取出了信息,说明登录成功了,根据取出的信息查询用户的订单信息
- 如果没有取出信息,提示未登录的json数据
- 发现了2a和3a做的是相同的事情,2c和3c做的也实现相同的事情,相同的事情我们可以提取到Filter中
- 如果我提取到了Filter中 2中只有2b ,3中只有3b
4 小结
4.1 Web组件
- 核心是Servlet,处理核心业务
- Listener,用来做资源的初始化
- Filter,在Servlet处理前后增加通用的处理