Java Web 开发:Web 组件

1267 字
6 分钟
Java Web 开发:Web 组件

前言#

学习目标#

  1. 掌握ServletContextListener的使用,并且理解其执行时机
  2. 掌握Filter的使用,并且理解其执行时机
  3. 能够使用Filter解决一些实际的问题

前置知识准备#

  • Servlet的执行

  • ServletContext的功能和使用

Web组件#

JavaEE的Web组件 三大Web组件

  1. Servlet → 处理请求对应的业务
  2. Listener → 监听器
  3. Filter → 过滤器

Listener监听器#

顾名思义就是监听东西的,其实和命名有关系,我们提供的是什么监听器就是监听什么的。

监听器在监听到主体做了XX事情,就会触发对应的事件。

ServletContextListener#

监听的主体就是ServletContext,当发现ServletContext做了事情,监听器就会执行该事件特定的方法

  • ServletContext如果初始化,则会执行监听器的初始化方法contextInitialized
    • ServletContext应用程序启动的时候初始化,它初始化,就意味着应用程序启动
  • ServletContext如果销毁,则会执行监听器的销毁方法contextDestroy
    • ServletContext应用程序关闭的时候销毁,它销毁,意味着应用程序关闭

应用程序启动的时候会执行ServletContextListener的contextInitialized方法;应用程序关闭的时候会执行contextDestroy

执行过程#

当应用程序启动的过程中,逐步加载Web组件

  • 首先会加载ServletContext和Listener组件
    • ServletContext伴随着应用程序初始化,它开始初始化,然后ServletContextListener监听到ServletContext初始化,会执行Listener的Initialized方法
  • 然后初始化loadOnStartup为正数的Servlet

image-20230220175957723
image-20230220175957723

改造之前的业务代码,之前整合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);
}
}

image-20230220180052194
image-20230220180052194

提供Listener,其实主要就是去初始化这个ServletContext

后面的话SpringMVC就是基于这样的特点去实现的

Filter过滤器#

Filter是一个执行过滤任务的一个对象。它既可以作用于Request对象,也可以作用于Response对

象,或者两者均作用。

也就是Servlet中获取请求之前,Servlet响应之后

image-20230313175734907

Filter和Servlet的执行#

URL-Pattern和Servlet之间存在着映射关系,URL-Pattern和Filter之间也存在着映射关系。

  • 1个URL-Pattern只能对应一个Servlet,但是可以对应多个Filter
  • Servlet和URL-Pattern之间是一对多的关系,但是URL-Pattern和Servlet之间是一对一

其实就意味着一件事,当我们发起一个请求的时候,其实就是一个URL-Pattern对应的请求

  • 对应1个Servlet
  • 对应多个Filter

image-20230222103201276
image-20230222103201276

如果只有一个过滤器那么执行流程如下

image-20230313220043752
image-20230313220043752

多个过滤器,就是就组成了一个过滤器的链,依次执行过滤器

image-20230222103412350
image-20230222103412350

如果增加上对应的方法

image-20230313222518316
image-20230313222518316

有一个问题,是否每一次都会继续执行到下一个拦截器,或Servlet?不一定,去界定是否 是放行状态

doFilter这个方法中,提供了一个形参,形参叫filterChain,filterChain中提供了一个doFilter方法,如果执行这个方法就是放行,如果不执行,则中断流程

image-20230222103923204
image-20230222103923204

使用#

/**
* localhost:8080/demo5/hello
* localhost:8080/demo5/bye
* URL-Pattern对于上面两个请求都能起作用,那么我们的URL-Pattern可以设置为 /*
* @author stone
* @date 2023/02/22 10:45
*/
@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的

image-20230222113435528
image-20230222113435528

image-20230222113349934
image-20230222113349934

Filter能否继续执行,取决于FilterChain的doFilter方法是否执行

案例#

给请求和响应设置字符集#

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() {
}
}

登录案例#

Http://localhost:8080/demo6/user/login

Http://localhost:8080/demo6/user/info

在Session中是否有存储用户的信息

/user/login

/user/logout

/user/info

/order/list

增加白名单功能

image-20230404175611089
image-20230404175611089

小结#

Web组件#

  • 核心是Servlet,处理核心业务
  • Listener,用来做资源的初始化
  • Filter,在Servlet处理前后增加通用的处理

image-20230222102757909
image-20230222102757909

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

Java Web 开发:Web 组件
https://firefly-mu-weld.vercel.app/posts/10-web组件/
作者
Daisy
发布于
2026-06-10
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
Daisy
Hello, I'm Daisy.
公告
欢迎来到我的博客!这是一则示例公告。
分类
标签

文章目录