filter&listener
一、Filter
1.Filter概述
1.1什么是filter
Filter:一个实现了特殊接口(Filter)的Java类. 实现对请求资源(jsp,servlet,html,)的过滤的功能.
过滤器是一个运行在服务器的程序, 优先于请求资源(Servlet或者jsp,html)之前执行. 过滤器是javaweb技术中最为实用的技术之一.
1.2 过滤器的作用
对目标资源(Servlet,jsp)进行过滤.
应用场景:登录权限检查,解决网站乱码,过滤敏感字符 …
2.Filter入门(重点)
2.1.步骤
2.1.1配置文件方式
- 创建一个类实现Filter接口
- 在web.xml配置FIlter的拦截路径
2.1.2注解方式
- 创建一个类实现Filter接口
- 在这个类上面添加@WebFilter(“拦截的路径”)
建议:可以直接new Filter
2.2.代码
2.2.1 通过xml配置方式
- 创建一个类实现Filter接口
/**
* - 创建一个类实现Filter接口
* - 在web.xml对过滤器进行配置
*/
public class FilterDemo01 implements Filter {
@Override
//过滤的方法
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("FilterDemo01收到了请求...");
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
}
- 在web.xml对过滤器进行配置
<!--注册Filter-->
<filter>
<filter-name>FilterDemo01</filter-name>
<filter-class>com.itheima.web.filter.FilterDemo01</filter-class>
</filter>
<!--配置Filter过滤路径-->
<filter-mapping>
<filter-name>FilterDemo01</filter-name>
<url-pattern>/demo01</url-pattern>
</filter-mapping>
2.2.2通过注解方式
- 创建一个类实现Filter接口
- 直接在这个类上面添加注解进行配置
@WebFilter("/demo02")
public class FilterDemo02 implements Filter{
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("FilterDemo02... 收到了请求");
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
}
3.Filter的生命周期
3.1Filter生命周期介绍
过滤器从创建到销毁的过程
3.2生命周期方法
init(FilterConfig):初始化
doFilter(ServletReqeust req,ServletResponse resp,FilterChain chain):执行过滤的方法
destroy():销毁
3.3Filter生命周期描述
- 服务器启动的时候, 会调用init()方法进行初始化【调用一次】
- 任何一次请求都会调用doFilter()方法进行过滤【路径相匹配】
- 服务器正常关闭或者项目从服务器移除, 调用destory()方法进行销毁【调用一次】
注意: 默认情况下,Servlet是来了第一次请求的时候 调用init()方法进行初始化.我们可以在Servlet里面设置启动项.
3.4 FilterConfig【了解】
获得过滤器的初始化参数
- 配置初始化参数
<filter>
<filter-name>myFilter01</filter-name>
<filter-class>com.itheima.filter.MyFilter01</filter-class>
<!--添加初始化参数-->
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>myFilter01</filter-name>
<!--
servlet的映射路径的目的是: 指定由哪个servlet去处理对应的请求
过滤器的过滤路径的目的是: 指定过滤哪个或者哪些请求
-->
<url-pattern>/*</url-pattern>
</filter-mapping>
- 在Filter的init()方法里面获得了
@Override
public void init(FilterConfig filterConfig) throws ServletException {
//在filter对象初始化的时候,使用filterConfig对象获取web.xml配置文件中的初始化参数
String encoding = filterConfig.getInitParameter("encoding");
System.out.println("MyFilter01创建了..."+encoding);
}
4.映射路径
假设有一个管理员权限的过滤器,它应该对用户发出的管理员功能的请求进行条件的过滤。但是当用户发出登录、注册等请求的时候,不应该进行过滤。所以我们过滤器,应该有选择的过滤器请求。这就需要学习配置过滤器不同的映射路径,从而让过滤器过滤希望过滤器的请求。
4.1完全路径匹配
以"/"开始
/demo01 ---> 过滤器只能拦截路径/demo01;
4.2目录匹配
以"/"开始 以 *结束 .
/* --->当前项目下的所有的路径都可以拦截; /aa/* ---> 可以拦截 /aa/bb, /aa/bb/cc
4.3扩展名匹配
以"*"开始 例如: *.jsp *.do
*.do--->可以拦截路径的后缀是 do的 ; *.jsp--->拦截所有JSP
4.4 缺省匹配
/ 除了jsp以外的资源都匹配
当前Filter里面不支持, Servlet里面可行的. 后面在Servlet里面遇到
5.拦截方式
有了上面学习的映射路径,我们可以控制过滤器过滤指定的内容,但是我们在访问资源的时候,并不是每次都是之间访问,有时是以转发的方式访问的,这就需要我们要让过滤器可以区分不同的访问资源的方式,有不同的拦截方式。 是通过 DispatcherType 来指定的.
-
DispatcherType.REQUEST,默认值,过滤从浏览器发送过来的请求和重定向 不过滤转发
-
DispatcherType.FORWARD,只过滤转发过来的请求
-
通过dispatcherTypes配置拦截方式
-
DispatcherType.FORWARD: 【只】过滤转发
-
DispatcherType.REQUEST: 除了转发以为其它的都过滤(1.浏览器的直接请求 2.重定向)【默认值】
-
-
拦截方式的这个值,我们可以配置多个
@WebFilter(value = {"/demo06"},dispatcherTypes={DispatcherType.FORWARD,DispatcherType.REQUEST})
一般情况下, 转发我们不会过滤的. 转发属于服务器内部的行为. 直接使用默认值的情况偏多
6.过滤器链(filterChain)
过滤器链作用:一个请求可能被多个过滤器所过滤,只有当所有过滤器都放行,请求才能到达目标资源,如果有某一个过滤器没有放行,那么请求则无法到达后续过滤器以及目标资源
- 过滤器链执行顺序
- 配置文件: 谁先配置
filter-mapping
谁先执行 - 注解方式: 按照Filter的首字母顺序 eg: AFilter BFilter A在B的前面, AFilter先执行
- 如果既有注解方式配置的过滤器,又有配置文件方式配置的过滤器,那么配置文件方式配置的过滤器先进行过滤
- 配置文件: 谁先配置
案例一: 统一全网站请求的中文乱码的处理
1,需求分析
在整个网站中,可能会有get请求或post请求向服务器提交参数.参数中往往有中文信息.在后台每个Servlet中都需要去处理乱码.
我们想做的是:请求到达Servlet中.就可以直接调用getParameter方法获得请求参数,请求参数已经没有乱码了.
2,思路分析
3,代码实现
package com.itheima.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
/**
* 包名:${PACKAGE_NAME}
*
* @author Leevi
* 日期2020-07-17 10:41
*/
@WebFilter("/*")
public class EncodingFilter implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//统一设置请求的编码方式为UTF-8
req.setCharacterEncoding("UTF-8");
chain.doFilter(req, resp);
}
@Override
public void init(FilterConfig config) throws ServletException {
}
}
案例二: 非法字符过滤
1,需求分析
当用户发出非法言论的时候,提示用户言论非法。
效果:
第一个版本
思路分析
代码实现
- IllegalFilter
package com.itheima.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
/**
* @author Leevi
* 日期2020-10-18 10:09
*/
@WebFilter("/*")
public class IllegalCharFilter implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//1. 获取客户端输入的内容
String content = req.getParameter("content");
if (content != null) {
//2. 判断评论内容中是否有非法字符
if (content.contains("你大爷")) {
resp.setContentType("text/html;charset=UTF-8");
//评论内容中有非法字符
resp.getWriter().write("评论中包含非法字符,请重新评论");
return;
}
}
//如果请求参数中没有content,或者content中没有非法字符,都放行
chain.doFilter(req, resp);
}
@Override
public void init(FilterConfig config) throws ServletException {
}
}
第二个版本
思路分析
代码实现
package com.itheima.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
/**
* @author Leevi
* 日期2020-07-17 11:01
*/
@WebFilter("/*")
public class IllegalCharFilter implements Filter {
private List<String> strList = new ArrayList<>();
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//从strList中遍历出每一个字符串,然后进行比较
//判断请求参数中是否包含非法字符
String content = req.getParameter("content");
if (content != null) {
for (String str : strList) {
if (content.contains(str)) {
//表示请求参数中有非法字符,则拦截
resp.getWriter().write("评论中包含非法字符,请重新评论!!!");
return;
}
}
}
chain.doFilter(req, resp);
}
@Override
public void init(FilterConfig config) throws ServletException {
//读取IllegalWords.txt里面的数据
//1. 将IllegalWords.txt转换成字节输入流
InputStream is = IllegalCharFilter.class.getClassLoader().getResourceAsStream("IllegalWords.txt");
//2. 将字节输入流,包装成BufferReader
try {
BufferedReader bfr = new BufferedReader(new InputStreamReader(is,"UTF-8"));
String str = null;
while (( str = bfr.readLine()) != null) {
//每读到一个字符串,就将它存储到strList中
strList.add(str);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
第三个版本
思路分析
- 动态代理的作用是:
- 在不修改类的源码的基础上,增强类的方法
- 可以减少重复代码,在执行方法之前添加前置操作、执行方法之后添加后置操作(Spring的AOP思想)
- 我们可以在不写接口的实现类的情况下,创建接口的对象(mybatis框架)
- 代理模式的分类:
- 静态代理
- 动态代理
- 静态代理和动态代理的区别:
- 静态代理中必须存在代理类
- 静态代理的代理关系是在编译的时候确定的,而动态代理的代理关系是在程序运行的时候才确定的
代码实现
package com.itheima.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
/**
* 包名:${PACKAGE_NAME}
*
* @author Leevi
* 日期2020-07-17 11:01
*/
@WebFilter("/*")
public class IllegalCharFilter implements Filter {
private List<String> strList = new ArrayList<>();
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//放行之前,使用动态代理技术,增强req对象的方法
//将req强转成HttpServletRequest
HttpServletRequest request = (HttpServletRequest) req;
//类加载器
ClassLoader classLoader = req.getClass().getClassLoader();
//被代理的接口: HttpServletRequest
HttpServletRequest requestProxy = (HttpServletRequest) Proxy.newProxyInstance(classLoader, new Class[]{HttpServletRequest.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//增强getParameter()方法,其它方法还是调用被代理者原本的方法
if (method.getName().equals("getParameter")) {
//要增强getParameter()方法,其实就是将请求参数值中的非法字符替换成*
//1. 获取请求参数值
String value = (String) method.invoke(request, args);
//2. 判断请求参数值中是否包含非法字符
for (String str : strList) {
if (value.contains(str)) {
//2.1 包含非法字符,就要将value中的非法字符替换成*
String start = "";
for (int i=0;i<str.length();i++){
start += "*";
}
value = value.replace(str,start);
}
}
return value;
}
//不用增强的方法,要调用被代理者原本的方法
return method.invoke(request,args);
}
});
//最后肯定要放行
chain.doFilter(requestProxy, resp);
}
@Override
public void init(FilterConfig config) throws ServletException {
//读取IllegalWords.txt里面的数据
//1. 将IllegalWords.txt转换成字节输入流
InputStream is = IllegalCharFilter.class.getClassLoader().getResourceAsStream("IllegalWords.txt");
//2. 将字节输入流,包装成BufferReader
try {
BufferedReader bfr = new BufferedReader(new InputStreamReader(is,"UTF-8"));
String str = null;
while (( str = bfr.readLine()) != null) {
//每读到一个字符串,就将它存储到strList中
strList.add(str);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
二、监听器Listener
1.Listener概述
1.1什么是Listener
监听器就是一个Java类,用来监听其他的JavaBean对象的变化
在javaweb中监听器就是监听三个域对象的状态的。request,session,servletContext(application)
1.2监听器的应用
主要在Swing编程
在Android/ios大量应用
JS里面的事件
2.javaweb中的监听器
2.1 javaweb的监听器
javaweb的监听器:监听ServletContext,HttpSession,ServletRequest三个域对象状态
事件源和监听器绑定的过程:通过配置web.xml完成
2.2 JavaWeb中的监听器类型
- 三类8个
只讲解监听ServletContext的创建和销毁.
ServletContext是什么时候创建: 服务器启动的时候
ServletContext是什么时候销毁: 服务器关闭的时候
所以监听ServletContext创建和销毁,就能监听服务器的启动和关闭,就可以在服务器启动和关闭的时候执行一些代码(比如在服务器启动的时候读取Spring的配置文件,创建Spring的核心容器)
2.3JavaWeb的监听器使用步骤
- 创建一个类实现监听器接口
- 在web.xml进行配置(绑定)
3.监听ServletContext的创建和销毁的监听器
说明: 监听ServletContext对象的创建和销毁
-
方法
-
问题
ServletContext对象何时创建和销毁:应用在,它就在
创建:服务器启动时候(前提是这个项目在这个服务器里面). 服务器为每个WEB应用创建一个单独的ServletContext. 销毁:服务器关闭的时候,或者项目从服务器中移除.
-
企业中应用
初始化工作.
加载配置文件:Spring框架,ContextLoaderListener
步骤:
- 创建一个类实现ServletContextListener
- 在web.xml配置
代码:
-
JAVA代码
package com.work.listener; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; /** * 包名:com.work.listener * * @author Leevi * 日期2020-07-17 14:38 * 一、写一个类实现ServletContextListener接口,并重写方法 * 方法1: contextInitialized()会在ServletContext对象创建的时候执行,也就是在服务器启动的时候 * 方法2: contextDestroyed()会在ServletContext对象销毁的时候执行,也就是在服务器关闭的时候 * * 二、在web.xml中配置监听器 */ public class MyContextListener implements ServletContextListener{ @Override public void contextInitialized(ServletContextEvent sce) { System.out.println("监听到了服务器的启动....创建spring的核心容器"); } @Override public void contextDestroyed(ServletContextEvent sce) { System.out.println("监听到了服务器的关闭....销毁spring的核心容器"); } }
-
配置(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"> <listener> <listener-class>com.work.listener.MyContextListener</listener-class> </listener> </web-app>