JAVAWEB过滤器、监听器的作用及使用>从零开始学JAVA系列
JAVAWEB过滤器、拦截器的作用及使用
过滤器Filter
什么是过滤器
- 过滤器是运行在服务端的程序
- 过滤器是在达到目标资源前的预处理操作(servlet、jsp、html等)
- 过滤器是在请求即将离开服务器之前的处理程序
- 多个过滤器可以组合使用,形成一个过滤链
为什么要使用过滤器(过滤器所能解决的问题)
- 为了解决大量重复代码的出现
- 例如配置项目的编码
- 例如登录的拦截(如果每个servlet中都写一个是否登录判断,代码量将非常多)
配置一个过滤器完成编码的过滤
编写一个EncodingFilter(名称自定义)
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class EncodingFilter implements Filter {
private String charset;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 从配置中读取编码
charset = filterConfig.getInitParameter("charset");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// 将req和resp对象转换为带Http协议的
HttpServletRequest req = (HttpServletRequest)servletRequest;
HttpServletResponse resp = (HttpServletResponse)servletResponse;
// 配置请求post编码过滤
req.setCharacterEncoding(charset);
// 配置默认相应类型及编码
resp.setContentType("text/html; charset=" + charset);
// 执行下一个过滤器或者目标资源,理解为放行
filterChain.doFilter(req, resp);
}
@Override
public void destroy() {
System.out.println("EncodingFilter 过滤器已销毁");
}
}
在web.xml中配置过滤器
<filter>
<filter-name>myEncodingFilter</filter-name>
<filter-class>com.oa.filter.EncodingFilter</filter-class>
<init-param>
<param-name>charset</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>myEncodingFilter</filter-name>
<!-- 意思是拦截/abc下的所有资源,可以为Servlet指定前缀(例如abc),因为编码过滤只需要涉及到Servlet -->
<url-pattern>/abc/*</url-pattern>
</filter-mapping>
配置一个测试的Servlet
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/abc/encoding/demo")
public class EncodingServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 未配置过滤器前是null,配置后是 UTF-8
System.out.println(req.getCharacterEncoding());
}
}
配置项目的登录控制(如果未登录不让访问资源)
配置一个Filter并使用注解的方式注册Filter
import com.oa.entity.Employee;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
// 表示拦截所有/abc路径下的资源、以及所有的jsp和html
@WebFilter(urlPatterns = {"/abc/*","*.jsp","*.html"})
public class LoginFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// 将req和resp对象转换为带Http协议的
HttpServletRequest req = (HttpServletRequest)servletRequest;
HttpServletResponse resp = (HttpServletResponse)servletResponse;
// 判断当前请求的路径是否为登录页面或登录提交的Servlet
String requestUri = req.getRequestURI();
String loginPage = req.getContextPath() + "/login.jsp";
String loginServlet = req.getContextPath() + "/sys/login";
if(loginPage.equals(requestUri)|| loginServlet.equals(requestUri)){
filterChain.doFilter(req, resp);
}else {
// 获取到Session对象,并通过session从session域中获取用户
HttpSession session = req.getSession();
Employee employee = (Employee)session.getAttribute("user");
// 处理结果
if(employee == null) {
resp.sendRedirect(req.getContextPath() + "/login.jsp");
}else {
// 放行
filterChain.doFilter(req, resp);
}
}
}
@Override
public void destroy() {
}
}
关于过滤器的几个小问题
多个过滤器执行的顺序如何确定
答:通过filter-mapping配置的顺序执行每个请求或响应都需要经过过滤器吗
答:通过url-pattern决定请求和响应是否将过滤器代码从头到尾执行
答:不是,会先执行预处理操作,然后放行到下一个资源或过滤器,最后再执行后处理操作在过滤器中是否可以跳转到任意资源
答:可以,因为重定向可以写绝对路径重定向和转发是否经过过滤器
答:重定向经过过滤器,而转发默认不经过过滤器
可以通过配置过滤器的filter-mapping中的dispatcher属性来让转发也经过过滤器(不推荐,基本不会这么做)
<filter-mapping>
<filter-name>myEncodingFilter</filter-name>
<url-pattern>/abc/*</url-pattern>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
Filter的执行顺序
- Filter会按照配置的filter-mapping的先后顺序执行相同的过滤
- Filter过滤链在执行时会先进后出(在执行到filterChain.doFilter(req, resp)时)
// 执行到该行时,会先执行完下一个过滤器的filterChain.doFilter(req, resp);后的语句,才会执行到该过滤器的System.out.println("我是一个过滤器");
filterChain.doFilter(req, resp);
System.out.println("我是一个过滤器");
监听器Listener
什么是监听器?监听器的作用是什么?
- 什么是监听器
监听器是以观察者设计模式实现的,相当于一个监视者,监视着你的动作并做出响应 - 监听器的作用
javaweb为我们提供的监听器一共有8个,可以分别用来监听request、session、application作用域的创建、销毁、内容的创建
javax中所提供的监听器(都是接口)
ServletRequestListener
监听request请求的建立和销毁(2个方法)ServletRequestAttributeListener
监听Request的作用域中属性的存入、删除、替换(当request销毁时也会进入删除的方法)ServletContextListener
监听application的建立和销毁(2个方法)ServletContextAttributeListener
监听Application的作用域中属性的存入、删除、替换(当application销毁时也会进入删除的方法)HttpSessionListener
监听Session的建立和销毁HttpSessionAttributeListener
监听Session的作用域中属性的存入、删除、替换(当session销毁时也会进入删除的方法)HttpSessionActivationListener(无需在web.xml中配置)
监听Session作用域中监听了该对象的钝化和活化(序列化、反序列化)HttpSessionBindingListener(无需在web.xml中配置)
监听Session作用域中监听对象的绑定与解绑(存入session作用域、在session作用域中删除),这个跟HttpSessionAttributeListener很像,不过这个是针对单个对象的
使用监听器实现在线人数的统计
分析:
- 每次用户登录成功后就有了一位在线人数(session存入了user一个对象)
可以使用HttpSessionAttributeListener监听存入 - 每当用户点击注销时(session销毁时)
可以使用HttpSessionListener监听销毁 - 在线人数存放在哪??
在线人数存在Appliction作用域,也就是ServletContext对象中
编写统计在线人数的监听器
import javax.servlet.ServletContext;
import javax.servlet.http.*;
public class OnLineListener implements HttpSessionAttributeListener, HttpSessionListener {
@Override
public void attributeAdded(HttpSessionBindingEvent event) {
// 当前存入到session作用域中的key值
String name = event.getName();
// 判断是否是user
if ("user".equals(name)) {
// 获得容器对象
ServletContext servletContext = event.getSession().getServletContext();
// 获取在线统计人数
Integer online = (Integer) servletContext.getAttribute("online");
if (online == null) {
online = 1;
} else {
online ++;
}
servletContext.setAttribute("online", online);
}
}
@Override
public void attributeRemoved(HttpSessionBindingEvent httpSessionBindingEvent) {
}
@Override
public void attributeReplaced(HttpSessionBindingEvent httpSessionBindingEvent) {
}
@Override
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
}
// session容器销毁时
@Override
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
// 判断当前用户是否已经登录
HttpSession session = httpSessionEvent.getSession();
Object user = session.getAttribute("user");
// 不等于null说明登录了
if(user != null) {
// 获得容器对象
ServletContext servletContext = session.getServletContext();
// 获取在线统计人数
Integer online = (Integer) servletContext.getAttribute("online");
if (online == null) {
online = 0;
} else {
online --;
}
servletContext.setAttribute("online", online);
}
}
}
在web.xml中配置监听器
<listener>
<listener-class>com.oa.listener.OnLineListener</listener-class>
</listener>
在jsp页面中获取在线人数
<span>当前在线人数为:${applicationScope.online}</span>
HttpSessionBindingListener监听器的使用(监听单个Class,无需配置web.xml)
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;
import java.io.Serializable;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class People implements Serializable, HttpSessionBindingListener {
private String name;
private int age;
@Override
public void valueBound(HttpSessionBindingEvent httpSessionBindingEvent) {
// 当该类被实例化绑定到Seesion中时执行(session.setAttribute)
System.out.println("People被存到了session作用域中");
}
@Override
public void valueUnbound(HttpSessionBindingEvent httpSessionBindingEvent) {
// 当该类的对象从Session中解绑时执行(session.removeAttribute)
System.out.println("People从session作用域中被移除");
}
}
使用HttpSessionActivationListener监听Session作用域中该JavaBean的钝化与活化
解析:
钝化:序列化到硬盘中
活化:反序列化到服务器中
所以需要钝化和活化的实体需要实现Serializable接口
配置tomcat的conf文件夹中的context.xml来开启钝化与活化功能
- 该功能开启后,服务器一旦关闭,那么在关闭时会将所有存入到了Session中的值钝化(序列化)到服务器上
- 并在服务器启动时将数据活化(反序列化)到服务器中。
- 钝化后可以在相应位置看到以 .sessioin为后缀的文件
<Manager className="org.apache.catalina.session.PersistentManager" saveOnRestart="true">
<!-- 这里的directory实质上跑在服务器中的时候应该写相对路径,
我这里由于使用idea演示,每次idea启动时都会复制一份新的tomcat
这会导致钝化的session数据都消失掉了。
-->
<Store className="org.apache.catalina.session.FileStore" directory="D:\my_dev_tools\apache-tomcat-8.5.66-windows-x64\apache-tomcat-8.5.66\conf"/>
</Manager>
配置一个JavaBean作为测试继承HTTP
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionEvent;
import java.io.Serializable;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class People implements Serializable,HttpSessionActivationListener {
private String name;
private int age;
@Override
public void sessionWillPassivate(HttpSessionEvent httpSessionEvent) {
// 钝化(序列化)
System.out.println("钝化");
}
@Override
public void sessionDidActivate(HttpSessionEvent httpSessionEvent) {
// 活化(反序列化)
System.out.println("活化");
}
}
这里给个小建议,开发Servlet时,使用统一的前缀目录,这样更方便使用Filter拦截
例如:
- /abc/employee/findOne
- /abc/dept/findAll
JAVAWEB过滤器、监听器的作用及使用>从零开始学JAVA系列的更多相关文章
- JAVAWEB的基本入门(JSP、Tomcat)>从零开始学JAVA系列
目录 JAVAWEB的基本入门(JSP.Tomcat) 使用idea创建web项目的两种方式 1.直接创建一个web项目(这样创建好的项目可以直接运行) 2.创建一个普通的java项目并配置web模块 ...
- JAVAWEB - Servlet原理及其使用>从零开始学JAVA系列
目录 Servlet原理及其使用 什么是Servlet Servlet的使用 编写一个Servlet,使用继承HttpServlet的方式 配置web.xml 很简单的几个JSP文件 小提示,如果继承 ...
- spring框架的学习->从零开始学JAVA系列
目录 Spring框架的学习 框架的概念 框架的使用 Spring框架的引入 概念 作用 内容 SpringIOC的学习 概念 作用 基本使用流程 SpringIOC创建对象的三种方式 通过构造器方式 ...
- JAVA虚拟机的组成>从零开始学java系列
目录 JAVA虚拟机的组成 什么是虚拟机? JAVA虚拟机的组成部分 堆区(堆内存) 方法区 虚拟机栈 本地方法栈 程序计数器 字符串常量池 JAVA虚拟机的组成 什么是虚拟机? 虚拟机是运行在隔离环 ...
- JSP的执行原理、JSP的内置对象、四大作用域解析、MVC模式理解>从零开始学JAVA系列
目录 JSP的执行原理.JSP的内置对象.四大作用域解析.MVC模式理解 JSP的执行原理 这里拿一个小例子来解析JSP是如何被访问到的 首先将该项目部署到tomcat,并且通过tomcat启动 通过 ...
- Session与Cookie的原理以及使用小案例>从零开始学JAVA系列
目录 Session与Cookie的原理以及使用小案例 Cookie和Session所解决的问题 Session与Cookie的原理 Cookie的原理 Cookie的失效时机 小提示 Session ...
- JAVA数组的基础入门>从零开始学java系列
目录 JAVA数组的基础入门 什么是数组,什么情况下使用数组 数组的创建方式 获取数组的数据 数组的内存模型 为什么数组查询修改快,而增删慢? 查询快的原因 增删慢的原因 数组的两种遍历方式以及区别 ...
- IDEA使用Tomcat时控制台乱码的解决方案>从零开始学JAVA系列
IDEA使用Tomcat时控制台乱码的解决方案 解决方案1,修改启动时虚拟机参数 解决方案2,修改idea的设置 解决方案3,修改idea配置文件 在最后添加一行 '-Dfile.encoding=U ...
- 数据库建模、面向对象建模>从零开始学java系列
目录 数据库建模 前置知识 使用PowerDesigner数据库建模设计 一对多CDM概念数据模型设计 多对多的PDM物理数据模型设计(针对mysql) PowerDesigner将不同的模型进行转换 ...
随机推荐
- 示例讲解PostgreSQL表分区的三种方式
我最新最全的文章都在南瓜慢说 www.pkslow.com,欢迎大家来喝茶! 1 简介 表分区是解决一些因单表过大引用的性能问题的方式,比如某张表过大就会造成查询变慢,可能分区是一种解决方案.一般建议 ...
- 8、负载均衡HAproxy部署
8.1.基本环境说明: 服务器名(centos7) ip地址 安装软件 slave-node1 172.16.1.91 haproxy1.8.15,tomcat8.5.37(8080.8081实例), ...
- 关于PHP导出数据超时的优化
一般情况下,导出超时可能都是以下三种情况: 一.sql语句复杂,查询时间过长: 二.处理查询后数据逻辑冗余: 三.数据量过大导致响应超时. 接下来分别给出这三种情况的优化建议. 一.sql语句复杂,查 ...
- SpringBoot Cache 深入
这上一篇文章中我们熟悉了SpringBoot Cache的基本使用,接下来我们看下它的执行流程 CacheAutoConfiguration 自动装配类 根据图中标注,看到它引用了CachingCon ...
- 【重学Java】Set集合
Set集合 Set集合概述和特点[应用] 无序不可重复 没有索引,不能使用普通for循环遍历.可以使用迭代器或者增强foreach语句遍历 TreeSet集合 TreeSet集合概述和特点[应用] 无 ...
- Linux:监测收集linux服务器性能数据工具Sysstat的使用与安装
Sysstat是一个工具集,包括sar.pidstat.iostat.mpstat.sadf.sadc.其中sar是其中最强大,也是最能符合我们测试要求的工具,同时pidstat也是非常有用的东东,因 ...
- 23 shell 进程替换
0.shell进程替换的用法 1.使用进程替换的必要性 2.进程替换的本质 进程替换和命令替换非常相似.命令替换是把一个命令的输出结果赋值给另一个变量,例如dir_files=`ls -l`或date ...
- ctf常见编码形式(罗师傅)
https://zhuanlan.zhihu.com/p/30323085 这是原链接 ASCII编码 •ASCII编码大致可以分作三部分组成: •第一部分是:ASCII非打印控制字符(参详ASCII ...
- 备战-Java 容器
备战-Java 容器 玉阶生白露,夜久侵罗袜. 简介:备战-Java 容器 一.概述 容器主要包括 Collection 和 Map 两种,Collection 存储着对象的集合,而 Map 存储着k ...
- C语言:char总结
char字符型数据1.用单引号限制的1字节的字符称为字符型数据,字符型常量2.字符型常量实质保存的是对应字符的ASCII码值,是一个整数3.字符型常量表示范围:0-2554.声明字符型变量 char ...