过滤器 & 监听器
第1章 过滤器Filter
1.1 Filter简介
Filter称为过滤器,它是Servlet技术中最实用的技术,web开发人员通过Filter技术,对web服务器所管理的资源(JSP,Servlet,静态图片或静态html文件)进行拦截,从而实现一些特殊的功能。
Filter就是过滤从客户端向服务器发送的请求。对客户端访问的资源过滤,符合条件放行,不符合条件不放行
1.2 快速入门
通过API描述可知:
-
我们创建一个过滤器的话需要实现Filter这个接口
-
doFilter方法执行过滤器的功能
编写步骤
- 编写一个类实现Filter接口
- 实现接口中尚未实现的方法(着重实现doFilter方法)
- 在web.xml中进行配置或注解方式配置 (目的是配置要对哪些资源进行过滤)
- 在doFilter方法中书写过滤任务代码,调用FilterChain.doFilter方法放行,访问servlet
- 浏览器访问index.jsp页面,查看过滤器的执行效果
注意事项
过滤器doFilter方法默认拦截请求,如果需要经过过滤器之后,可以继续访问资源,要使用filterChain放行。
代码实现
public class FilterDemo1 implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("每次请求的路径被过滤器拦截的时候,都会执行过滤器的doFilter方法");
//下行代码用来实现过滤放行,去执行后面的Filter或目标资源Servlet、JSP
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
}
web.xml配置
<filter>
<filter-name>MyFilter1</filter-name>
<filter-class>com.itheima.MyFilter1</filter-class>
</filter>
<filter-mapping>
<filter-name>MyFilter1</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
注解方式,不在web.xml中进行配置,使用@webFilter进行配置,如下:
@WebFilter(urlPatterns = "/*")
public class MyFilter1 implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("执行MyFilter1的filter......");
//下行代码用来实现过滤放行,去执行后面的Filter或目标资源Servlet、JSP
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
}
1.3 过滤器的执行流程图解
1.4 Filter的API详解
Filter对象的生命周期:
- Filter何时创建:服务器启动时,就创建该Filter对象
- Filter何时执行过滤:每当一个请求的路径是满足过滤器的配置路径,那么就会执行一次过滤器的doFilter方法
- Filter何时销毁:服务器关闭时,Filter销毁
Filter接口有三个方法,并且这个三个都是与Filter的生命相关的方法
方法 | 描述 |
---|---|
init(Filterconfig) | 代表filter对象初始化方法 filter对象创建时执行 |
doFilter(ServletRequest,ServletResponse,FilterChain) | 代表filter执行过滤的核心方法,如果某资源在已经被配置到这个filter进行过滤的话,那么每次访问这个资源都会执行doFilter方法 |
destory() | 代表是filter销毁方法 当filter对象销毁时执行该方法 |
Filter的API详解:
init(FilterConfig):
- 描述:
* Filter对象创建候,立即执行init方法
- 参数:
* FilterConfig是Filter的配置对象,该对象可以获得ServletContext对象
destory():
- 描述:
* Filter对象销毁前执行destory方法
doFilter(ServletRequest, ServletResponse, FilterChain):
- 描述:
* filter执行过滤的核心方法,如果某资源在已经被配置到这个filter进行过滤的话,那么每次访问这个资源都会执行doFilter方法
- 参数:
* ServletRequest / ServletResponse:每次在执行doFilter方法时 web容器负责创建一个request和一个response对象作为doFilter的参数传递进来。该request与该response就是在访问目标资源的service方法时的request和response。
* FilterChain:过滤器链对象,通过该对象的doFilter方法可以放行该请求
代码演示
过滤器doFilter代码如下:
@WebFilter(filterName = "FilterDemo",urlPatterns = "/*")
public class FilterDemo1 implements Filter{
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.print("过滤器初始化了");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.print("每次请求的路径被过滤器拦截的时候,都会执行过滤器的doFilter方法");
//放行当前请求
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
System.out.println("服务器停止的时候销毁过滤器,执行过滤器的desotory方法");
}
}
servlet资源代码如下:
@WebServlet(name = "DemoServlet",urlPatterns = "/DemoServlet")
public class DemoServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request,response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
response.getWriter().write("DemoServlet给出响应");
}
}
运行结果
在浏览器多次输入http://localhost:8080/day05/DemoServlet
访问servlet资源
或者多次输入http://localhost:8080/day05
访问首页index.jsp资源
浏览器能够访问到目标资源。
1.5 过滤器的应用场景
场景一:处理全站中文乱码:
浏览器发出的任何请求,通过过滤器统一处理中文乱码。
场景二:登录权限检查:
当客户端浏览器发出一个请求,这个请求在访问到正真的目标资源之前,我们需要进行登录权限的检查。如果已经登录,我们就让这个请求通过,访问资源;如果没有登录,我们不能让请求访问目标资源。这样的操作需要在访问具体资源进行条件的过滤,我们可以使用过滤器来完成。
1.6 案例:处理全站中文乱码
案例需求
浏览器发出的任何请求,通过过滤器统一处理中文乱码。
案例效果
分别以get方式和post方式提交中文,servlet中不做中文乱码处理,直接获取参数,得到的参数不存在中文乱码问题。
案例分析
1.创建一个过滤器。
2.因为对所有的请求进行乱码的过滤,所以过滤器的过滤路径配置为/*
3.针对post请求处理乱码
案例分析图解:
实现步骤
- 创建一个form.jsp表单,用于测试过滤器解决乱码的功能 :
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="${pageContext.request.contextPath }/encoding" method="post">
<input type="text" name="name"/>
<input type="submit" value="提交"/>
</form>
</body>
</html>
- 创建一个用于接收表单请求的EncodingServlet:
@WebServlet(name = "EncodingServlet" , urlPatterns = "/encoding")
public class EncodingServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request,response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取表单提交的参数
String name = request.getParameter("name");
System.out.println(name);
}
}
- 创建EncodingFilter用于乱码处理的过滤器
@WebFilter(filterName = "EncodingFilter",urlPatterns = "/*")
public class EncodingFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//处理响应的中文乱码问题
HttpServletRequest request = (HttpServletRequest)req;
resp.setContentType("text/html;charset=utf-8");
//获取请求的方式
String method = request.getMethod();
if("post".equalsIgnoreCase(method)){
//如果是post请求,处理请求的中文乱码问题
request.setCharacterEncoding("utf-8");
chain.doFilter(request, resp);
//结束当前方法
return;
}
chain.doFilter(req, resp);
}
public void init(FilterConfig config) throws ServletException {
}
}
- 浏览器访问form.jsp页面,表单提交方式为post,表单输入中文
- 点击提交按钮,查看控制台,post乱码问题得到解决
[外链图片转存失败(img-YZvQd7z3-1567136081078)(img/19.png)]
- 将表单的提交方式换成get,提交表单,查看控制台没有乱码问题。
1.7 过滤器链FilterChain
什么是过滤器链
FilterChain过滤器链:在一个web应用中,可以开发编写多个Filter,这些Filter组合起来称为是一个过滤器链。
Web服务器根据Filter在web.xml文件中的注册顺序(mapping的配置顺序)决定先调用那个Filter。依次调用后面的过滤器,如果没有下一个过滤器,调用目标资源
[外链图片转存失败(img-i4yA9jwb-1567136081078)(img/过滤器链.png)]
过滤器链的执行流程
[外链图片转存失败(img-oIIjZHig-1567136081079)(img/25.png)]
提示:
- web.xml配置的Filter的执行顺序取决于filter-mapping的顺序
- 注解配置的filter的执行顺序取决于filter的类名称的字母排序
代码演示 过滤器链,访问demo1.jsp
demo1.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>Demo1.jsp</h1>
<%
System.out.println("Demo1.jsp执行了...");
%>
</body>
</html>
第一个Filter过滤器
package com.itheima.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
//@WebFilter(urlPatterns = "/*")
public class FilterDemo1 implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("FilterDemo1执行了...");
//下行代码用来实现过滤放行,去执行后面的Filter或目标资源Servlet、JSP
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("FilterDemo1执行结束了...");
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
}
第二个Filter过滤器
package com.itheima.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
//@WebFilter(urlPatterns = "/*")
public class FilterDemo2 implements Filter {
public void destroy() {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("FilterDemo2执行了...");
//下行代码用来实现过滤放行,去执行后面的Filter或目标资源Servlet、JSP
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("FilterDemo2执行结束了...");
}
public void init(FilterConfig config) throws ServletException {
}
}
第三个Filter过滤器
package com.itheima.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
//@WebFilter(urlPatterns = "/*")
public class FilterDemo3 implements Filter {
public void destroy() {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("FilterDemo3执行了...");
//下行代码用来实现过滤放行,去执行后面的Filter或目标资源Servlet、JSP
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("FilterDemo3执行结束了...");
}
public void init(FilterConfig config) throws ServletException {
}
}
web.xml 配置过滤器
<filter>
<filter-name>FilterDemo1</filter-name>
<filter-class>com.itheima.filter.FilterDemo1</filter-class>
</filter>
<filter>
<filter-name>FilterDemo2</filter-name>
<filter-class>com.itheima.filter.FilterDemo2</filter-class>
</filter>
<filter>
<filter-name>FilterDemo3</filter-name>
<filter-class>com.itheima.filter.FilterDemo3</filter-class>
</filter>
<filter-mapping>
<filter-name>FilterDemo3</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>FilterDemo2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>FilterDemo1</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
1.8 Filter配置
1.8.1 映射路径url-pattern的配置
- xml方式配置:
<filter>
<filter-name>MyFilter1</filter-name>
<filter-class>com.itheima.filter.MyFilter1</filter-class>
</filter>
<filter-mapping>
<filter-name>MyFilter1</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
- 注解配置:
@WebFilter(urlPatterns = "/*")
public class MyFilter1 implements Filter{
//...
}
不管是注解配置还是xml配置,配置虚拟路径的语法都是一致的,如下:
-
完全匹配
语法:/sertvle1
只有访问的地址是servlet1时,才执行该过滤器,比如/aaa /aaa/bbb
-
目录匹配
语法:/aaa/bbb/* ,经常使用
当访问的目标资源 的地址是/aaa/bbb/任何资源 都执行该过滤器,/* /aaa/* /aaa/bbb/*
-
扩展名匹配
语法:*.abc *.jsp
当访问的目标资源的扩展名是 abc、jsp时 才执行该过滤器,比如*.jsp *.do *.action
代码演示 映射路径,讲义代码略
1.8.2 拦截方式dispatcher的配置
有了上面学习的映射路径,我们可以控制过滤器过滤指定的内容,但是我们在访问资源的时候,并不是每次都是直接访问,有时是以转发的方式访问的,这就需要我们要让过滤器可以区分不同的访问资源的方式,有不同的拦截方式。
- dispatcher:访问的方式
- REQUEST:默认值,代表直接访问某个资源时执行filter
- FORWARD:转发时才执行filter
格式:
@WebFilter(urlPatterns = "/*",dispatcherTypes = {DispatcherType.REQUEST,DispatcherType.FORWARD})
public class MyFilter1 implements Filter {
//...
}
注意:只有访问的路径 与访问的方式 都匹配时 那么该filter才会执行
代码演示:
- 创建ForwardServlet,转发到index.jsp去,代码如下
@WebFilter(filterName = "MethodFilter",urlPatterns = "/index.jsp",dispatcherTypes = DispatcherType.FORWARD)
public class ForwardServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request,response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("ForwardServlet执行了,下面开始转发请求到index.jsp去");
request.getRequestDispatcher("/index.jsp").forward(request,response);
}
}
- 在MethodFilter的dispatcherTypes中以数组的形式添加两个拦截方式
@WebFilter(filterName = "MethodFilter",dispatcherTypes = {DispatcherType.FORWARD,DispatcherType.REQUEST},urlPatterns = "/*")
public class MethodFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
System.out.println("+++++++++MethodFilter过滤器执行了++++++++++");
chain.doFilter(req, resp);
}
public void init(FilterConfig config) throws ServletException {
}
}
- 浏览器再次访问请求:
http://localhost:8080/day05/ForwardServlet
,在转发前后都执行了过滤。
1.9 案例:登录权限检查
案例需求
现在一个网站上需要有登录的功能,在登录成功后,重定向到后台的成功页面(后台的页面有很多)。如果现在没有登录直接在地址栏上输入后台页面地址。
编写一个过滤器:可以对没有登录权限的用户进行拦截。(没有登录权限,回到登录页面。如果已经有登录权限,放行。)
代码实现
提供工具类、实体类、页面
- c3p0-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql:///web05</property>
<property name="user">root</property>
<property name="password">root</property>
<property name="initialPoolSize">5</property>
<property name="minPoolSize">5</property>
<property name="maxPoolSize">20</property>
</default-config>
</c3p0-config>
- 工具类:JdbcUtils
package com.itheima.utils;
public class JDBCUtils {
// 创建一个连接池:但是这个连接池只需要创建一次即可。
private static final ComboPooledDataSource dataSource = new ComboPooledDataSource();
/**
* 获得连接的方法
*/
public static Connection getConnection() throws SQLException{
return dataSource.getConnection();
}
/**
* 获得连接池:
*/
public static DataSource getDataSource(){
return dataSource;
}
}
- 用户实体类:User
package com.itheima.domain;
public class User {
private Integer id;
private String username;
private String password;
//省略set、get
}
- 页面
登录页面 login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登录页面</title>
</head>
<body>
<h1>登录页面</h1>
<h3><font color="red">${ msg }</font></h3>
<form action="${ pageContext.request.contextPath }/UserServlet" method="post">
<table border="1" width="400">
<tr>
<td>用户名</td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td>密码</td>
<td><input type="password" name="password"></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="登录"></td>
</tr>
</table>
</form>
</body>
</html>
提交页面 sub.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>提交页面</title>
</head>
<body>
<h1>get提交方式</h1>
<form action="${ pageContext.request.contextPath }/ServletDemo1" method="get">
姓名:<input type="text" name="name"/><br>
<input type="submit" value="提交">
</form>
<h1>post提交方式</h1>
<form action="${ pageContext.request.contextPath }/ServletDemo1" method="post">
姓名:<input type="text" name="name"/><br>
<input type="submit" value="提交">
</form>
</body>
</html>
成功页面 success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>成功页面</title>
</head>
<body>
<h1>您已经登录成功!欢迎:${ existUser.username }</h1>
<a href="${ pageContext.request.contextPath }/jsp/sub.jsp">提交数据</a>
</body>
</html>
Servlet
- 用户登录的Servlet
package com.itheima.controller;
public class UserServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try{
// 接收数据:
String username = request.getParameter("username");
String password = request.getParameter("password");
// 封装数据:
User user = new User();
user.setUsername(username);
user.setPassword(password);
// 处理数据:
UserService userService = new UserService();
User existUser = userService.login(user);
// 根据处理结果页面跳转:
if(existUser == null){
// 登录失败
request.setAttribute("msg", "用户名或密码错误!");
request.getRequestDispatcher("/login.jsp").forward(request, response);
}else{
// 登录成功
request.getSession().setAttribute("existUser", existUser);
response.sendRedirect(request.getContextPath()+"/jsp/success.jsp");
}
}catch(Exception e){
e.printStackTrace();
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
doGet(request, response);
}
}
- 接收数据的Servlet
package com.itheima.controller;
public class ServletDemo1 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String name = request.getParameter("name");
System.out.println("GET方式接收的名称:"+name);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
String name = request.getParameter("name");
System.out.println("POST方式接收的名称:"+name);
}
}
Filter
- 权限验证的过滤器:PrivilegeFilter
package com.itheima.filter;
public class PrivilegeFilter implements Filter{
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 判断:判断用户是否已经登录了。如果已经登录,放行。如果没有登录,回到登录页面!
HttpServletRequest req = (HttpServletRequest) request;
User existUser = (User) req.getSession().getAttribute("existUser");
// 判断:
if(existUser == null){
// 没有登录:
req.setAttribute("msg", "您还没有登录!没有权限访问!");
req.getRequestDispatcher("/login.jsp").forward(req, response);
}else{
// 已经登录:
chain.doFilter(req, response);
}
}
@Override
public void destroy() {
}
}
Service
package com.itheima.dao;
public class UserService {
public User login(User user) throws SQLException {
UserDao userDao = new UserDao();
User existUser = userDao.login(user);
return existUser;
}
}
Dao
package com.itheima.dao;
public class UserDao {
public User login(User user) throws SQLException {
QueryRunner queryRunner = new QueryRunner(JDBCUtils.getDataSource());
String sql = "select * from user where username = ? and password = ?";
User existUser = queryRunner.query(sql, new BeanHandler<User>(User.class), user.getUsername(),user.getPassword());
return existUser;
}
}
web.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!-- servlet配置-->
<servlet>
<servlet-name>UserServlet</servlet-name>
<servlet-class>com.itheima.controller.UserServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UserServlet</servlet-name>
<url-pattern>/UserServlet</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>ServletDemo1</servlet-name>
<servlet-class>com.itheima.controller.ServletDemo1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ServletDemo1</servlet-name>
<url-pattern>/ServletDemo1</url-pattern>
</servlet-mapping>
<!-- Filter配置-->
<filter>
<filter-name>PrivilegeFilter</filter-name>
<filter-class>com.itheima.filter.PrivilegeFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>PrivilegeFilter</filter-name>
<url-pattern>/jsp/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>GenericEncodingFilter</filter-name>
<filter-class>com.itheima.filter.GenericEncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>GenericEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
第2章 监听器Listener
2.1 监听器概述
什么是监听器
监听器就是一个实现了特定接口的Java类,这个Java类用于监听另一个Java类的方法调用或者属性的改变。当被监听对象发生上述事件后,监听器某个方法将会立即被执行。
监听器的用途
用来监听其他对象的变化的。主要应用在图形化界面开发上。
- Java中GUI,Android
2.2 监听器入门
java中的GUI入门程序演示
/**
* GUI的小窗口演示监听器
*/
public class MyFrame extends JFrame{
public static void main(String[] args) {
// 1.创建小窗口对象:(被监听的对象)
MyFrame myFrame = new MyFrame();
// 设置窗口名称:
myFrame.setName("小窗口");
// 设置窗口的宽高:
myFrame.setBounds(0, 0, 200, 180);
// 2.设置窗口显示:
myFrame.setVisible(true);
// 在事件源上绑定监听器:
myFrame.addWindowListener(new MyWindowListener());
}
}
/**
* 监听器对象
*/
class MyWindowListener implements WindowListener{
@Override
public void windowClosing(WindowEvent e) {
MyFrame myFrame = (MyFrame) e.getSource();
System.out.println(myFrame.getName());
System.out.println("窗口关闭...");
System.exit(0);
}
//其他方法进行默认实现
}
监听器的执行过程
监听器的术语
- 事件源:指的是被监听对象。
- 例如,(汽车)
- 监听器:指的是监听的对象。
- 例如,(报警器)
- 事件源和监听器绑定:在事件源上绑定监听器。
- 例如,(在汽车上安装报警器)
- 事件:指的是事件源对象的改变
- 例如,(踹了汽车一脚)----主要功能获得事件源对象。
2.3 监听器有哪些
在Servlet中定义了多种类型的监听器,它们用于监听的事件源分别是ServletContext、HttpSession和ServletRequest这三个域对象。
-
一类:监听三个域对象的创建和销毁的监听器(三个)
-
二类:监听三个域对象的属性变更(属性添加、移除、替换)的监听器(三个)
-
三类:监听HttpSession中JavaBean的状态改变(钝化、活化、绑定、解除绑定)的监听(两个)
2.4 ServletContextListener监听器
ServletContextListener监听器作用:
- 用于监听ServletContext域对象的创建与销毁的监听器
ServletContext域的生命周期:
-
何时创建:在服务器启动的时候,为每个web应用创建单独的ServletContext对象。
-
何时销毁:在服务器关闭的时候,或者项目从web服务器中移除的时候。
-
作用范围:整个web应用
ServletContextListener监听器企业开发用途:
-
加载框架的配置文件:
- Spring框架提供了一个核心监听器ContextLoaderListener。
-
定时任务调度:
ServletContextListener监听器的API:
方法 | 返回值 | 描述 |
---|---|---|
contextInitialized | void | 监听ServletContext对象的创建 |
contextDestroyed | void | 监听contextDestroyed对象的销毁 |
代码演示:
/**
ServletContext监听器
事件源:ServletContext
监听器:MyMyServletContextListener
事件源和监听器绑定:通过web.xml配置方式绑定
*/
public class MyServletContextListener implements ServletContextListener{
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("ServletContext对象创建");
ServletContext context = sce.getServletContext();
System.out.println(context);
Object source = sce.getSource();
System.out.println(source);
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("ServletContext对象销毁");
}
}
web.xml配置
<listener>
<listener-class>com.itheima.listener.MyServletContextListener
</listener-class>
</listener>
注解方式:
/**
ServletContext监听器
事件源:ServletContext
监听器:MyMyServletContextListener
事件源和监听器绑定:通过注解配置方式绑定
*/
@WebListener
public class MyServletContextListener implements ServletContextListener{
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.println("ServletContext对象创建的了.........");
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.println("ServletContext对象销毁的了.........");
}
}
2.5 HttpSessionListener监听器
HttpSessionListener监听器作用:
- 用来监听HttpSession域对象的创建和销毁。
HttpSessionListener域的生命周期:
- 何时创建:
- 服务器端第一次调用getSession()方法时候。
- 何时销毁:
- 非正常关闭服务器(正常关闭服务器session会被序列化)。
- Session过期(默认过期时间30分钟)。
- 手动调用session.invalidate()方法。
思考:
访问HTML是否创建Session:不会
访问JSP是否创建Session:会
访问Servlet是否创建Session:不会(默认没有调用getSession方法)
HttpSessionListener监听器的API:
方法 | 返回值 | 描述 |
---|---|---|
sessionCreated(HttpSessionEvent se) | void | 监听HttpSession对象的创建 |
sessionDestoryed(HttpSessionEvent se) | void | 监听HttpSession对象的销毁 |
代码演示:
/**
* 监听HttpSession的创建和销毁的监听器
*/
public class MyHttpSessionListener implements HttpSessionListener{
@Override
// 监听HttpSession的创建
public void sessionCreated(HttpSessionEvent se) {
System.out.println("HttpSession对象被创建了...");
}
@Override
// 监听HttpSession的销毁
public void sessionDestroyed(HttpSessionEvent se) {
System.out.println("HttpSession对象被销毁了...");
}
}
web.xml配置
<listener>
<listener-class>com.itheima.listener.MyHttpSessionListener
</listener-class>
</listener>
2.6 案例 统计当前在线人数
案例需求
统计当前在线人数
案例分析
在服务器启动的时候需要有一个初始化为零。
当浏览器访问服务器上的某个JSP时,创建session,获得初始值,进行+1操作。
如果session销毁了,获得该值进行-1操作。
代码实现
- 创建ServletContextListener进行初始化
package com.itheima.listener.onlinecount;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class OnLineCountServletContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
// 在服务器启动的时候初始化一个值为0
// 还需要将这个值存入到ServletContext中。
sce.getServletContext().setAttribute("count", 0);
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
}
}
- 创建HttpSessionListener
package com.itheima.listener.onlinecount;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
public class OnLineCountHttpSessionListener implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent se) {
// 在线了
HttpSession session = se.getSession();
System.out.println(session.getId()+"上线了...");
// 获得ServletContext中的值
Integer count = (Integer) session.getServletContext().getAttribute("count");
count++;
session.getServletContext().setAttribute("count", count);
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
// 离线了
HttpSession session = se.getSession();
System.out.println(session.getId()+"离线了...");
// 获得ServletContext中的值
Integer count = (Integer) session.getServletContext().getAttribute("count");
count--;
session.getServletContext().setAttribute("count", count);
}
}
- 配置监听器 web.xml
<listener>
<listener-class>com.itheima.listener.onlinecount.OnLineCountServletContextListener
</listener-class>
</listener>
<listener>
<listener-class>com.itheima.listener.onlinecount.OnLineCountHttpSessionListener
</listener-class>
</listener>
- 创建JSP页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>在线人数:${ count }</h1>
</body>
</html>