filter&listener

过滤器 & 监听器

第1章 过滤器Filter

1.1 Filter简介

Filter称为过滤器,它是Servlet技术中最实用的技术,web开发人员通过Filter技术,对web服务器所管理的资源(JSP,Servlet,静态图片或静态html文件)进行拦截,从而实现一些特殊的功能。

Filter就是过滤从客户端向服务器发送的请求。对客户端访问的资源过滤,符合条件放行,不符合条件不放行

1.2 快速入门

通过API描述可知:

  1. 我们创建一个过滤器的话需要实现Filter这个接口

  2. doFilter方法执行过滤器的功能

编写步骤

  1. 编写一个类实现Filter接口
  2. 实现接口中尚未实现的方法(着重实现doFilter方法)
  3. 在web.xml中进行配置或注解方式配置 (目的是配置要对哪些资源进行过滤)
  4. 在doFilter方法中书写过滤任务代码,调用FilterChain.doFilter方法放行,访问servlet
  5. 浏览器访问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请求处理乱码

案例分析图解:

实现步骤

  1. 创建一个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>
  1. 创建一个用于接收表单请求的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);
    }
}
  1. 创建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 {

    }
}
  1. 浏览器访问form.jsp页面,表单提交方式为post,表单输入中文
  2. 点击提交按钮,查看控制台,post乱码问题得到解决

[外链图片转存失败(img-YZvQd7z3-1567136081078)(img/19.png)]

  1. 将表单的提交方式换成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配置,配置虚拟路径的语法都是一致的,如下:

  1. 完全匹配

    语法:/sertvle1
    

    只有访问的地址是servlet1时,才执行该过滤器,比如/aaa /aaa/bbb

  2. 目录匹配

    语法:/aaa/bbb/* ,经常使用
    

    当访问的目标资源 的地址是/aaa/bbb/任何资源 都执行该过滤器,/* /aaa/* /aaa/bbb/*

  3. 扩展名匹配

    语法:*.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才会执行

代码演示:

  1. 创建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);
    }
}

  1. 在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 {

    }

}
  1. 浏览器再次访问请求: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:

方法返回值描述
contextInitializedvoid监听ServletContext对象的创建
contextDestroyedvoid监听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操作。

代码实现

  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) {

	}

}
  1. 创建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);
	}

}
  1. 配置监听器 web.xml
<listener>
    <listener-class>com.itheima.listener.onlinecount.OnLineCountServletContextListener
    </listener-class>
</listener>
<listener>
    <listener-class>com.itheima.listener.onlinecount.OnLineCountHttpSessionListener
    </listener-class>
</listener>
  1. 创建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>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值