今日内容
1. Filter:过滤器
2. Listener:监听器
一、Filter:过滤器
1.概念
* 生活中的过滤器:净化器,空气净化器,土匪(筛选出给钱的,而且来回筛选两次)
* web中的过滤器:当访问服务器时过滤器会将请求拦下来,完成一些特殊工功能。
* 过滤器的作用:比如浏览器访问多个服务器时,对每个服务器都有一些通用的功能需要设置。
那么就可以设一个过滤器将这些请求拦下来,统一完成这些通用的操作。
* 通用的操作:如登录操作。统一编码处理。敏感字符过滤。
2.快速入门:
1.步骤:
1.定义一个类,实现接口Filter
2.复写方法
3.配置拦截路径
1.web.xml
2.注解
3.过滤器细节:
1.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">
<filter>
<filter-name>demo1</filter-name>
<filter-class>cn.itcast.web.filter.FilterDemo1</filter-class>
</filter>
<filter-mapping>
<filter-name>demo1</filter-name>
<!--url-pattern:拦截路径-->
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
```
2.过滤器执行流程
浏览器发出请求--->走过滤器--->过滤器放行--->请求相应资源--->响应浏览器--->
再走过滤器--->走的是放行下面的代码。
注意:过滤器的代码分两步才走完。
```
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//对request对象请求消息增强
System.out.println("filterDemo2执行了...");
//放行
chain.doFilter(req, resp);
//对response对象的响应消息增强
System.out.println("filterDemo2又回来了...");
}
```
3.过滤器的生命流程
1. public void init(FilterConfig config){}
* 在服务器启动后,会创建Filter对象,然后调用init方法。
* 用于加载资源,只执行一次
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain){}
* 每一次请求被拦截资源时,会执行,所以可能执行多次
public void destroy()
* 在服务器关闭后,Filter对象被销毁,如果服务器是正常关闭,调用destroy方法
* 用于释放资源,只执行一次
4.过滤器配置详解
* 拦截路径配置:
1. 具体的资源路径:/index.jsp 只有访问index.jsp资源时,过滤器才会被执行。
2. 目录拦截:/user/* 访问/user下的所有资源时,过滤器都会被执行。
3. 后缀名拦截:*.jsp 访问所有后缀名为jsp时,过滤器都会被执行。
4. 拦截所有资源:/* 访问所有资源时,过滤器都会被执行
* 拦截方式配置:资源被访问的方式。
* 注解配置
* 设置disPatcherTypes属性
1. REQUEST:默认值。浏览器直接请求资源
2. FORWARD:只拦截转发访问资源。
3. INCLUDE:包含访问资源
4. ERROR:错误跳转资源
5. ASYNC:异步访问资源
* web.xml
* 设置<dispatcher>REQUEST</dispatcher>标签即可
5.过滤器链(配置多个过滤器)
* 执行顺序:
如果有两个过滤器:过滤器1和过滤器2
过滤器1-->过滤器2-->访问资源-->过滤器2-->过滤器1
filterDemo6执行了...
filterDemo7执行了...
index.jsp...
filterDemo7回来了...
filterDemo6回来了...
* 那个过滤器先拦截,那个后拦截
1.注解配置
按照类名的字符串表规则:值小的先拦截
如:AFilter 和 BFilter,逐个比较每个字符,AFilter先执行。
2.web.xml配置
谁定义在上面,谁先执行。
<filter-mapping>
<filter-name>demo1</filter-name>
<!–url-pattern:拦截路径–>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
哪个<filter-mapping>在上面,哪个先执行。
二、Filter案例
1.案例1_登录案例
*需求:
1. 访问day0508_case案例的资源。验证其是否登录
3. 如果登录了,则直接放行。
4. 如果没有登录,则跳转到登录页面,提示"您尚未登录,请先登录"。
2.案例2_敏感词汇过滤
*需求:
1. 对day17_case案例录入的数据进行敏感词汇过滤
2. 敏感词汇参考《敏感词汇.txt》
3. 如果是敏感词汇,替换为 ***
请求index.jsp--->
request给filter---> 1.打印一次loginFilter:req
经过过滤后发现没有登录,要转发到login.jsp---> 2.打印一次loginFilter返程
```
(为什么会打印loginFilter返程:因为没有放行,只是跳转了,所以没有执行chain.doFilter()
方法。那么就会将loginFilter过滤器doFilter()方法中剩余的代码执行完。不同于放行成功
后,再返回来执行剩余的代码)
```
转发请求login.jsp--->
request再给filter--> 3.打印一次loginFilter:req
经过过滤发现可以直接访问--->
显示登录页面---> 4.打印一次loginFilter返程
登陆验证,跳转到loginServlet-->
又要将request给filter---> 5.打印一次loginFilter:req
经过过滤后发现可以放行--->
访问到loginServlet---> 6.打印:Servlet:req
访问loginServlet成功后---> 7.打印一次loginFilter返程
登录验证成功-->
重定向到到index.jsp--->
request再给filter--> 8.打印一次loginFilter:req
经过过滤后发现已经登陆了,可以放行---> 9.打印一次loginFilter返程
*每次打印的req都是一样的。
说明Servlet和Filter拿到的都是同一个request。
*疑问?
因为默认的拦截配置的REQUEST,转发为什么也被拦截到了?是我上面的分析有问题吗?
是因为拦截路径配置的是拦截所有路径吗?@WebFilter("/*")
分析:
1. 需要对request对象进行增强。
增强获取参数相关的方法。
2. 放行。传递代理对象。
增强对象的功能:
* 设计模式:一些通用的解决固定问题的方式
1.装饰模式:装饰模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个
对象的功能。
2.代理模式:所谓的代理者是指一个类别可以作为其它东西的接口。代理者可以作任何东西的接口。
的接口
* 概念:
1.真实对象:被代理的对象。联想总公司
2.代理对象:西安联想代理商
3.代理模式:代理对象代理真实对象,来达到增强真实对象功能的目的。
* 实现方式:
1.静态代理:生成方式,有一个类文件描述代理模式
2.动态代理:内存中来形成代理类。
* 实现步骤:
1.代理对象和真实对象实现相同的接口(兄弟关系)
2.代理对象:Proxy.newProxyInstance();
3.使用代理对象调用方法。
4.增强方法。
* 增强方式:
1.增强参数列表
2.增强返回值类型
3.增强方法体执行逻辑
* 详见public class ProxyTest
```
public class ProxyTest {
public static void main(String[] args) {
//1.创建真实对象
Lenovo lenovo = new Lenovo();
/**
* 2.动态代理增强lenovo对象
* Proxy.newProxyInstance()三个参数:
* 1.类加载器:真实对象.getClass().getClassLoader()
* 2.接口数组:真实对象.getClass().getInterfaces()-----生成的代理对象要实现的接口字节码数组
* 3.处理器: new InvocationHandler() {}
* 返回一个代理对象: proxy_lenovo
*/
SaleComputer proxy_lenovo = (SaleComputer)Proxy.newProxyInstance(
lenovo.getClass().getClassLoader(), lenovo.getClass().getInterfaces(),
new InvocationHandler() {
/**
* 编写代理逻辑的方法:代理对象调用的所有方法都会触发该方法执行。
* 那么增强代码的逻辑就会写在此处。
* @param proxy:代理对象
* @param method:我在步骤3调用的方法sale(代理对象调用的方法),会被封装成一个对象method,再传参
* @param args:代理对象调用方法时,传递的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws
Throwable {
/*System.out.println("增强方法执行了");
System.out.println(method.getName());
System.out.println(args[0]);*/
//1.增强参数
//判断是否是sale方法
if(method.getName().equals("sale")){
double money = (double)args[0];
money = money * 0.85; //进货价格折扣15%
//3.增强方法提
System.out.println("专车接。");
String obj = (String)method.invoke(lenovo, money);
System.out.println("免费送货。");
//2.增强返回值:不仅仅返回电脑还有其他
return obj + ",鼠标,键盘。";
}else{
//如果不是sale函数:按照真实对象中的方法执行
Object obj = method.invoke(lenovo, args);
return obj;
}
/**
* Object obj = method.invoke(lenovo, args);
* 1.真实对象lenovo执行method,并且参数是args。
* 所以这句话相当于:使用真实对象调用method方法。所以如果参数args改变,那么调用真实对象中的该方法
* 参数就是改变后的args。
* 2.并且此处的返回值就是真实对象的返回值。
* 所以返回值的不同,结果也会不同。
*
* return obj; //3.返回值会传递给步骤3中代理对象调用方法的返回值。
*/
}
});
//3.调用方法:其实不管你调用的什么方法,其实都没有被调用,真正执行的都是增强方法invoke()
//会将调用的方法封装成一个对象传给步骤2中的method。
String computer = proxy_lenovo.sale(8000);
System.out.println(computer);
/*proxy_lenovo.show();*/
}
}
```
三、Listener:监听器
* 概念:web的三大组件之一
* 事件监听机制
* 事件: 一件事情
* 事件器:事件发生的地方。 按钮被点击了,按钮就是事件源。
* 监听器:一个对象
* 注册监听:将事件,事件源,监听器绑定在一起。当事件源发生某个事件后,执行监听代码。
* ServletContextListener:监听ServletContext的创建和销毁
* Interface ServletContextListener :是一个接口,而且没有具体的实现类。
* 两个方法
1.void contextDestroyed(ServletContextEvent sce)
ServletContext对象被销毁之前会调用该方法
2.void contextInitialized(ServletContextEvent sce)
ServletContext对象创建后会调用该方法。
* 步骤:
1.定义一个类,实现ServletContextListener接口
2.复写方法
3.配置:
1.web.xml:<listener>指定监听器的类文件
```
<listener>
<listener-class>cn.itcast.web.listener.ContextLoaderListener</listener-class>
</listener>
```
* 还可以初始化参数
```
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/classes/applicationContext.xml</param-value>
</context-param>
```
2.注解配置
@WebListener //这个注解不需要指定路径。直接注解:注册了监听器。