1. Filter:过滤器

  • 概念:当访问服务器的资源时,过滤器可以将请求拦截下来,完成一些特殊的功能。
  • 作用:一般用于完成通用的操作。如:登录验证、统一编码处理、敏感字符过滤等。
  • Servlet、Filter、Listener 为 Javaweb 三大组件。
  • 使用步骤:
    1. 定义一个类,实现接口 Filter
    2. 实现方法
    3. 配置拦截路径(web.xml 和 注解两种方式)
  • 过滤器中的路径为所要拦截的路径。
  • 过滤器拦截两次,去和回都拦截。
  • 被过滤器拦截的路径需要在 doFilter 方法中放行。
  • 快速入门:
    import javax.servlet.*;
    import java.io.IOException;
    
    @WebFilter("/*")//访问所有资源之前,都会执行该过滤器
    public class FilterDemo1 implements Filter {
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
        }
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            System.out.println("filterDemo1被执行了....");
            //放行
            filterChain.doFilter(servletRequest,servletResponse);
        }
        @Override
        public void destroy() {
        }
    }
    
  • web.xml 配置过滤器:
    <filter>
        <filter-name>demo1</filter-name>
        <filter-class>cn.itcast.web.filter.FilterDemo1</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>demo1</filter-name>
        <!-- 拦截路径 -->
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
  • 过滤器执行流程:
    1. 执行过滤器
    2. 执行放行后的资源
    3. 回来以后执行过滤器放行代码下边的代码
  • 过滤器默认的拦截方式是拦截请求,即每次请求都对应一个拦截的来回,即请求的时候进入,请求结束回来。
    protected void doPost() {
        System.out.println("Servlet");
        /*Filter来
          Servlet
          jsp
          Filter回*/
        //request.getRequestDispatcher("/index.jsp").forward(request, response);
    
        /*Filter来
          Servlet
          Filter回
          Filter来
          jsp
          Filter回*/
        response.sendRedirect(request.getContextPath()+"/index.jsp");
    }
    
  • 过滤器生命周期方法:
    • init:在服务器启动后,会创建 Filter 对象,然后调用 init 方法。只执行一次。用于加载资源。
    • doFilter:每一次请求被拦截资源时,会执行。执行多次。
    • destroy:在服务器关闭后,Filter 对象被销毁。如果服务器是正常关闭,则会执行 destroy 方法。只执行一次。用于释放资源。
  • 拦截路径配置:
    • 具体资源路径:/index.jsp 只有访问 index.jsp 资源时,过滤器才会被执行
    • 拦截目录:/user/* 访问 /user 下的所有资源时,过滤器都会被执行,例如配置 Servlet1 的虚拟路径为 /user/servlet1,配置 Servlet2 的虚拟路径为 /user/servlet2,则两个 Servlet 会被拦截。
    • 后缀名拦截:*.jsp 访问所有后缀名为 JSP 资源时,过滤器都会被执行。
    • 拦截所有资源:/* 访问所有资源时,过滤器都会被执行。
  • 拦截方式的配置:资源被访问的方式,注解需要设置 dispatcherTypes 属性。
    • REQUEST:默认值。路径被浏览器请求的时候拦截。
    • FORWARD:路径被转发访问的时候拦截。
    • INCLUDE:包含访问资源
    • ERROR:错误跳转资源
    • ASYNC:异步访问资源
  • web.xml 配置拦截方式:设置 <dispatcher></dispatcher> 标签即可。
  • dispatcherTypes 属性的值是一个数组,若想让路径在请求和转发的时候都能访问,可以配置两个值,@WebFilter(value = "/*", dispatcherTypes = {DispatcherType.FORWARD, DispatcherType.REQUEST})
  • 请求中套一个转发,且都设置拦截的时候,转发拦截是被包含在请求拦截里面的。
    //拦截器
    @WebFilter(value = "/*", dispatcherTypes = {DispatcherType.FORWARD, DispatcherType.REQUEST})
    public class TestFilter implements Filter {
        public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
            System.out.println("Filter来");
            chain.doFilter(req, resp);
            System.out.println("Filter回");
        }
    }
    //Servlet
    @WebServlet("/testServlet")
    public class TestServlet extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            System.out.println("Servlet");
            request.getRequestDispatcher("/index.jsp").forward(request, response);
        }
    }
    //jsp
    <%
    System.out.println("jsp");
    %>
    //执行结果:
    //Filter来
    //Servlet
    //Filter来
    //jsp
    //Filter回
    //Filter回
    
  • 过滤器先后顺序问题:
    • 注解配置:按照类名的字符串比较规则比较,值小的先执行
      • 如:AFilter 和 BFilter,AFilter 就先执行了。
    • web.xml 配置:谁的 <filter-mapping> 定义在上边,谁就先执行。
  • 过滤器执行顺序:如果有两个过滤器,过滤器 1 和过滤器 2,则执行顺序为 过滤器 1 → 过滤器 2 →资源执行 → 过滤器 2 → 过滤器 1

2. 登录验证案例

  • 需求:
    • 访问案例的资源。验证其是否登录。
    • 如果登录了,则直接放行。
    • 如果没有登录,则跳转到登录页面,提示"您尚未登录,请先登录"。
  • 实现代码:
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import javax.servlet.http.HttpServletRequest;
    import java.io.IOException;
    @WebFilter("/*")
    public class LoginFilter implements Filter {
        public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
            //0.强制转换,一般都是Http请求,HttpServletRequest中有getRequestURI方法
            HttpServletRequest request = (HttpServletRequest) req;
            //1.获取资源请求路径
            String uri = request.getRequestURI();
            //2.判断是否包含登录相关资源路径,要注意排除掉 css/js/图片/验证码等资源
            if (uri.contains("/login.jsp") || uri.contains("/loginServlet") || uri.contains("/css/") || uri.contains("/js/") || uri.contains("/fonts/") || uri.contains("/checkCodeServlet")) {
                //包含,用户就是想登录。放行
                chain.doFilter(req, resp);
            } else {
                //不包含,需要验证用户是否登录
                //3.从获取session中获取user
                Object user = request.getSession().getAttribute("user");
                if (user != null) {
                    //登录了。放行
                    chain.doFilter(req, resp);
                } else {
                    //没有登录。跳转登录页面
                    request.setAttribute("login_msg", "您尚未登录,请登录");
                    request.getRequestDispatcher("/login.jsp").forward(request, resp);
                }
            }
        }
        public void init(FilterConfig config) throws ServletException { }
        public void destroy() { }
    }
    
  • 注意事项
    • HttpServletRequest 中有 getRequestURI 方法,注意强转。
    • CSS、js、HTML 等资源文件也是一次请求,需要放行。

3. Listener:监听器

  • 事件监听机制:
    • 事件:一件事情
    • 事件源 :事件发生的地方
    • 监听器 :一个对象
    • 注册监听:将事件、事件源、监听器绑定在一起。 当事件源上发生某个事件后,执行监听器代码
  • ServletContextListener:监听 ServletContext 对象的创建和销毁。
    • void contextDestroyed(ServletContextEvent sce):ServletContext 对象被销毁之前会调用该方法
    • void contextInitialized(ServletContextEvent sce):ServletContext 对象创建后会调用该方法
  • 使用步骤:
    • 定义一个类,实现 ServletContextListener 接口
    • 复写方法
    • 配置
      • web.xml 配置
        <listener>
            <listener-class>packageNmae.clssName</listener-class>
        </listener>
        
      • 注解:@WebListener
  • web.xml 配置参数:
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/classes/applicationContext.xml</param-value>
    </context-param>
    
  • 获取 web.xml 中的参数:servletContext.getInitParameter("contextConfigLocation");
  • 监听器可用作获取资源,在框架的 XML 中配置资源文件的项目地址,在监听器中加载资源文件。
    package pers.ylq.firstcg.web.listener;
    
    import javax.servlet.ServletContextEvent;
    import javax.servlet.ServletContextListener;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    
    public class ContextLoaderListener implements ServletContextListener {
        @Override
        public void contextInitialized(ServletContextEvent servletContextEvent) {
            System.out.println("ServletContext对象被创建了...");
    
            //获取参数,值存的是文件在项目中的位置
            String contextConfigLocation = servletContextEvent.getServletContext().getInitParameter("contextConfigLocation");
            //获取资源的真实地址
            String path = servletContextEvent.getServletContext().getRealPath(contextConfigLocation);
            try {
                //加载资源(配置文件)
                FileInputStream fis = new FileInputStream(path);
                System.out.println(fis);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }
    
        @Override
        public void contextDestroyed(ServletContextEvent servletContextEvent) {
            System.out.println("ServletContext对象被销毁了...");
        }
    }