Spring MVC 复盘录前言


前言

在还没有使用 Spring MVC 之前,我们 WEB 应用可能是这样操作的:

  • 先创建一个 maven web 项目:

  • 导入必要的依赖后,在 java 目录下新建两个 Servlet :

      package org.example;
    
      import javax.servlet.ServletException;
      import javax.servlet.http.HttpServlet;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      import java.io.IOException;
    
      public class TestForwardServlet extends HttpServlet {
    
          @Override
          protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
              System.out.println("自定义的重定向 Servlet 运行了");
    
              // 将这个重定向给另一个 Servlet
              resp.sendRedirect("/redirect");
          }
      }
    

    上面这个是重定向 Servlet ,下面那个是请求转发 Servlet:

      package org.example;
    
      import javax.servlet.RequestDispatcher;
      import javax.servlet.ServletContext;
      import javax.servlet.ServletException;
      import javax.servlet.http.HttpServlet;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      import java.io.IOException;
    
      public class TestRedirectServlet extends HttpServlet {
    
          @Override
          protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
              System.out.println("自定义的请求转发 Servlet 运行了");
    
              // 获取请求转发器
              ServletContext servletContext = req.getServletContext();
              RequestDispatcher dispatcher = servletContext.getRequestDispatcher(servletContext.getContextPath() + "/jsp/login.jsp");
              
              // 转发请求
              dispatcher.forward(req, resp);
          }
      }
    

    Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。

  • 打开 web.xml ,配置这个 Servlet:

      
    
      
        Archetype Created Web Application
    
        
          forwardServlet
          org.example.TestForwardServlet
        
        
          forwardServlet
          /test
        
    
        
          redirectServlet
          org.example.TestRedirectServlet
        
        
          redirectServlet
          /redirect
        
    
      
    

    启动一个WEB项目的时候,WEB容器会去读取它的配置文件 web.xml。

  • 再在 WEB-INF/jsp (无则创建) 目录下创建一个简单的 login.jsp 页面:

      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      
      
          登录
      
      
      登陆页面
      
      
    

    上述步骤完成后,即可运行,在地址栏输入 http://localhost:8080/test 进入后,运行结果:

      后台打印结果:
      自定义的重定向 Servlet 运行了
      自定义的请求转发 Servlet 运行了
    
  • 添加两个监听器,一个是请求监听器:

     package org.example.listener;
    
     import javax.servlet.ServletRequestEvent;
     import javax.servlet.ServletRequestListener;
     import javax.servlet.http.HttpServletRequest;
    
     public class WebRequestListener implements ServletRequestListener {
         @Override
         public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
    
         }
    
         @Override
         public void requestInitialized(ServletRequestEvent servletRequestEvent) {
             HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest();
             System.out.println("监听到一条请求: " + request.getRequestURI());
         }
     }
    

    另一个是Servlet 上下文监听器:

     package org.example.listener;
    
     import javax.servlet.ServletContextEvent;
     import javax.servlet.ServletContextListener;
    
     // Servlet 上下文监听器
     public class WebServletListener implements ServletContextListener {
         @Override
         public void contextInitialized(ServletContextEvent servletContextEvent) {
             System.out.println("Servlet 上下文已初始化");
         }
    
         @Override
         public void contextDestroyed(ServletContextEvent servletContextEvent) {
    
             System.out.println("Servlet 上下文已销毁了");
         }
     }
    
  • 再添加两个过滤器:

      package org.example.filter;
    
      import javax.servlet.*;
      import javax.servlet.http.HttpServletRequest;
      import java.io.IOException;
    
      public class MyFilter2 implements Filter {
    
          private String param;
    
          // 初始化方法在 WEB 应用启动时会被调用
          @Override
          public void init(FilterConfig filterConfig) throws ServletException {
    
              // 可以获取到在 web.xml 中配置的初始化参数,注意名字的一致性
              param = filterConfig.getInitParameter("test");
              System.out.println("配置在 web.xml/filter/init-param 下的初始化参数" + param);
          }
    
          // 在访问 filter-mapping 中指定的 url 时才会被调用
          @Override
          public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
              System.out.println("filter-1 过滤到了一条请求: " + ((HttpServletRequest)servletRequest).getRequestURI());
    
              // 如果初始化参数 test 允许离开,就放行
              if (param.equals("exit")){
                  filterChain.doFilter(servletRequest, servletResponse);
              }
          }
    
          @Override
          public void destroy() {
    
          }
      }
    

    第二个过滤器用来测试是否放行的问题:

      package org.example.filter;
    
      import javax.servlet.*;
      import javax.servlet.http.HttpServletRequest;
      import java.io.IOException;
    
      public class MyFilter2 implements Filter {
    
          private String param;
    
          // 初始化方法在 WEB 应用启动时会被调用
          @Override
          public void init(FilterConfig filterConfig) throws ServletException {
    
              // 可以获取到在 web.xml 中配置的初始化参数,注意名字的一致性
              param = filterConfig.getInitParameter("test");
              System.out.println("配置在 web.xml/filter/init-param 下的初始化参数" + param);
          }
    
          // 在访问 filter-mapping 中指定的 url 时才会被调用
          @Override
          public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
              System.out.println("filter-2 过滤到了一条请求: " + ((HttpServletRequest)servletRequest).getRequestURI());
    
              // 如果初始化参数 test 允许离开,就放行
              if (param.equals("exit")){
                  filterChain.doFilter(servletRequest, servletResponse);
              }
          }
    
          @Override
          public void destroy() {
    
          }
      }
    

    在请求和响应对象在Servlet处理之前和之后,可以通过过滤器对这两个对象进行处理。

  • 最后在 web.xml webapp 标签内中配置这两个监听器和过滤器:

    
      org.example.listener.WebRequestListener
    
    
    
      org.example.listener.WebServletListener
    
    
    
      filter-1
      org.example.filter.MyFilter
      
        test
        exit1
      
    
    
      filter-1
      forwardServlet
    
    
    
      filter-2
      org.example.filter.MyFilter2
      
        test
        exit
      
    
    
      filter-2
      forwardServlet
    
    

    从运行结果中可以发现,listener 和 filter 在 WEB 容器启动时就被加载了,重点是过滤器的问题,多个过滤器会组合成一条过滤链,一旦哪里出现中断,对应的 Servlet 就不会被执行。