JavaWeb详解(第三篇)之Servlet基础简介-过滤器Filter&Listener监听器

161 阅读6分钟

JavaWeb的三大组件分别是:Servlet 程序、Filter 过滤器、Listener 监听器。前面我们学习了Servlet ,接下来我们在了解一下Filter 过滤器和Listener 监听器。

1、Filter 过滤器

1.1、什么是Filter 过滤器

Filter 过滤器它是 JavaEE 的规范。也就是接口。可以对指定URL请求进行拦截,拦截之后在过滤器中进行相应的处理/判断。

Filter 过滤器它的作用是:拦截请求,过滤响应。

拦截请求常见的应用场景有: 1)权限检查;2)日记操作 ;3)事务管理...等等。

1.2、Filter 入门

案例描述:在首页,访问个人信息页面,如果没有登陆则不允许访问。

1)编写一个类去实现Filter 接口

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
​
public class FilterDemo implements Filter{
​
    /**
     * 用于拦截请求
     */
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        // TODO Auto-generated method stub
        HttpServletRequest httpServletRequest = (HttpServletRequest) request; 
        HttpSession session = httpServletRequest.getSession(); 
        Object user = session.getAttribute("user"); 
        // 如果等于 null,说明还没有登录:则不允许访问私人页面
        if (user == null) { 
            request.getRequestDispatcher("/login.jsp").forward(request, response);
            return;
        } else { 
            // 让程序继续往下访问用户的目标资源:如果没有其他的过滤器,将访问请求的资源;如果有过滤器则继续进入其他过滤器
            chain.doFilter(request,response);
        }
    }
}

2)编写一个InfoServlet

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
​
public class InfoServlet extends HttpServlet{
​
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
          response.setContentType("text/html;charset=gbk");
          PrintWriter out = response.getWriter();
          out.println("私人空间,请谨慎查看!");
    }
}

3)修改index.jsp主页

<body>
    <div>我是页头</div>
        <a href="/persion/infoServlet">查看我的信息</a>
    <div>我是页尾</div>
  </body>

4)配置web.xml

<!--filter 标签用于配置一个 Filter 过滤器--> 
<filter> 
    <filter-name>FilterDemo</filter-name> 
    <filter-class>com.cn.filterAndListener.FilterDemo</filter-class> 
</filter>
<filter-mapping>
    <filter-name>FilterDemo</filter-name>
    <!-- 匹配规则同servlet,支持/*也支持*.do 
        .../persion/infoServlet 访问此链接会被拦截
    -->
    <url-pattern>/persion/infoServlet</url-pattern>
</filter-mapping>

案例效果:

props_test10121.gif 成功拦截了,但是我们发现有一个问题。我之前的登陆页面转发过去,怎么验证码不显示了呢?一查,发现路径好像不对:

image-20211027152133887.png

扩展:补充说明base标签的作用。
public class FilterDemo implements Filter{
​
    /**
     * 用于拦截请求
     */
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        // TODO Auto-generated method stub
        HttpServletRequest httpServletRequest = (HttpServletRequest) request; 
        HttpSession session = httpServletRequest.getSession(); 
        Object user = session.getAttribute("user"); 
        // 如果等于 null,说明还没有登录:则不允许访问私人页面
        if (user == null) {
            //注:"/" 跟目录 request.getRequestDispatcher("/"),表示当前工程http://localhost:8080/
            //当我使用/login.jsp,确实可以转发到登陆页面,但是此时的登陆页面的url:http://localhost:8080/persion/infoServlet
            request.getRequestDispatcher("/login.jsp").forward(request, response);
            return;
        } else { 
            // 让程序继续往下访问用户的目标资源:如果没有其他的过滤器,将访问请求的资源;如果有过滤器则继续进入其他过滤器
            chain.doFilter(request,response);
        }
    }
}
<!--把src目录复制下来-->
<img id="codeimg" style="width: 60px;height:20px;" src="ImageServlet?0.14947579772812647">
跳转的链接是:
http://localhost:8080/persion/ImageServlet?0.14947579772812647
我们没有在login.jsp中声明<base>这里的base路径默认是http://localhost:8080/persion/
​
<!--base 标签设置页面相对路径工作时参照地址的地址 href 属性就是参数的地址值 -->
所以我们得到的是上诉地址,导致验证码无法显示
把js中的代码改成如下:发现正常了,但是这样做并不好,业务我们根本不知道从第几层,这种做法不通用
window.document.getElementById("codeimg").src="../"+url+"?"+Math.random();
​
我们在login.jsp页面中加入base设置页面相对路径工作时参照地址
<base href="/">

最终我们看到:

image-20211027155949967.png

1.3、Filter 应用

1.3.1、过滤器解决中文乱码问题:
public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {
   //全拦截先设置一波字符编码
   request.setCharacterEncoding("UTF-8");
   chain.doFilter(request, response);
}
​

2、Listener 监听器

2.1、什么是监听器

如果你属性JAVA的GUI图形界面,应该接触过很多监听器。比如:监听键盘事件、按钮的点击事件,然后触发某种动作。这就是监听器的作用:无需主动调用,自动触发。

监听器就是一个实现特定接口的普通java程序,这个程序专门用于监听另一个java对象的方法调用或属性改变,当被监听对象发生上述事件后,监听器某个方法将立即被执行。

在Servlet规范中定义了多种类型的监听器:

1)监听对象的创建和销毁:

a)ServletRequestListener: 可以在请求时获得通知

b)HttpSessionListener: 可以用来收集在线者信息,或者更改会话列表时获得通知

c)ServletContextListener: 可以获取web.xml里面的参数配置

2)监听对象属性变化:

a)ServletContextAttributeListener

b)HttpSessionAttributeListener

c)ServletRequestAttributeListener

3)监听Session对象

a)HttpSessionBindingListener

b)HttpSessionActivationListener

2.2、监听器入门

1)新建一个ListenerDemo

import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
​
public class ListenerDemo implements  HttpSessionListener,HttpSessionAttributeListener{
​
     // 新建一个session时触发此操作  
    public void sessionCreated(HttpSessionEvent se) {  
        System.out.println("新建一个session");
    }  
    
    // 添加session时触发此操作
    public void attributeAdded(HttpSessionBindingEvent e) {
           HttpSession session = e.getSession();
           String name = (String) session.getAttribute(session.getAttributeNames().nextElement());
           System.out.println("我可以把session添加到对象中,值为:"+name);
    }
    
    public void attributeRemoved(HttpSessionBindingEvent e) {
       System.out.println("我可以把session从对象中移除");
    }
    
    public void attributeReplaced(HttpSessionBindingEvent e) {  
        System.out.println("替换");
    }
}

2)修改xml

<listener>
    <!-- <display-name>ListenerDemo</display-name> -->
    <listener-class>com.cn.filterAndListener.ListenerDemo</listener-class>
</listener>

3)使用我们之前的案例:SessionDemo

案例效果:

props_test10122.gif

  • 注:三者的执行顺序:listener->Filter->servlet。简单记为:理(Listener)发(Filter)师(servlet)

3、其他知识补充

3.1、错误页面

1)新建ExceptionServletDemo

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
​
public class ExceptionServletDemo extends HttpServlet {
​
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
​
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        int i = 9/0;
        System.out.println("大兄弟,代码出错了啊!!!");
    }
}

2)web.xml

<servlet>
    <servlet-name>ExceptionServletDemo</servlet-name>
    <servlet-class>com.cn.filterAndListener.ExceptionServletDemo</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>ExceptionServletDemo</servlet-name>
    <url-pattern>/ExceptionServletDemo</url-pattern>
</servlet-mapping><!--配置捕错误时进入的页面-->
<error-page>
    <exception-type>java.lang.ArithmeticException</exception-type>
    <location>/error.jsp</location>
</error-page>
<!--也可以配置-->
<error-page>
     <error-code>404</error-code>
     <location>/error.jsp</location>
</error-page>

案例效果:

image-20211027174339836.png

3.2、上传下载

1)上传

需要:(SmartUpload.jar)包

import java.io.IOException;
import java.util.ArrayList;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.jspsmart.upload.File;
import com.jspsmart.upload.Files;
import com.jspsmart.upload.Request;
import com.jspsmart.upload.SmartUpload;
import com.jspsmart.upload.SmartUploadException;
​
public class UploadServlet extends HttpServlet {
​
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        this.doPost(request, response);
    }
​
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        try {
            String dirPath = this.getServletContext().getRealPath("/images");
            // 接收客户端上传的文件
            // 创建 SmartUpload 对象用于接收文件的上传
            SmartUpload su = new SmartUpload();
            // 通过 su 对象初始化 request 中的数据
            su.initialize(this.getServletConfig(), request, response);
            // 设置上床文件的限制信息
            su.setAllowedFilesList("jpg,png,bmp,gif");
            // 设置单个上传文件为2M
            su.setMaxFileSize(2 * 1024 * 1024);
            // 执行上传:上传之后,页面提交的数据都在 su 对象中
            su.upload();
            // 从 su 对象中获取数据(文件和字段)
            // 获取到 su 中上传的文件
            Files files = su.getFiles();
            // 获取到包含所有文本字段的 request 对象
            Request r = su.getRequest();
            // 遍历files获取到所有上传的文件(允许批量上传)
            for (int i = 0; i < files.getCount(); i++) {
                File file = files.getFile(i);
                file.saveAs(dirPath + "/" + file.getFileName());
            }
            // 从 r 中获取到需要的字段
            String name = r.getParameter("userName");
            String pass = r.getParameter("userPass");
            String[] fns = new java.io.File(dirPath).list();
            ArrayList<String> fileNames = new ArrayList<String>();
            for (String f : fns) {
                fileNames.add(f);
            }
            request.setAttribute("fileNames", fileNames);
            request.getRequestDispatcher("list.jsp").forward(request, response);
        } catch (SmartUploadException e) {
            e.printStackTrace();
        }
    }
}

2)下载

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
​
public class DownloadServlet extends HttpServlet {
   public void doGet(HttpServletRequest request, HttpServletResponse response)
         throws ServletException, IOException {
      this.doPost(request, response);
   }
​
   public void doPost(HttpServletRequest request, HttpServletResponse response)
         throws ServletException, IOException {
      // 接受客户端需要下载的文件名
      String fileName = request.getParameter("fname");
      // 获取到文件存放的目录
      String dirPath = this.getServletContext().getRealPath("/images");
      // 要下载的文件是
      File f = new File(dirPath + "/" + fileName);
      // 再将图片信息发送给客户端之前,设置响应信息的格式
      // 设置相应数据的格式为浏览器无法解析的格式
      response.setContentType("application/pdf");
      // 设置文件的头信息
      response.setHeader("Content-Disposition", "attachment;filename="
            + fileName);
      // 设置响应数据的大小
      response.setContentLength((int) f.length());
      // 开始下载
      FileInputStream fis = new FileInputStream(f);
      OutputStream out = response.getOutputStream();
      byte[] buff = new byte[102400];
      int len = -1;
      while ((len = fis.read(buff)) != -1) {
         out.write(buff, 0, len);
         out.flush();
      }
      out.close();
   }
}