listener
简述
最初EE规范里面其实只有Servlet,后面发展到某一版本时,又引入了两个组件:一个叫做Listener(监听器);一个叫做Filter(过滤器、拦截器,黑名单)。
监听器。即监听某个对象的创建和销毁等。
现实生活
监听者:朝阳人民群众
被监听者:明星艺人
触发事件:吸毒嫖娼
回调函数:报警
web中的监听器
监听者:自己去编写一个监听器
被监听者:ServletContext对象
触发事件:创建、销毁
回调函数:运行我们监听器里面对应的方法
简单使用
1.编写一个监听器(类实现ServletContextListener接口)
2.在服务器里面注册该监听器(注解、web.xml)
package com.simon;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
@WebListener
public class MyServletContextListener implements ServletContextListener {
//当context对象被创建的时候,那么会调用我们这个方法
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.println("servlet init...");
}
//当context对象被销毁的时候,会调用这个方法
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.println("servlet destory...");
}
}
使用场景
比如之前我们写在某个servlet中,init设置为自启动里面的代码其实可以放置在listener中。JavaWeb发展过程中是先有servlet————>listner、filter。Servlet之前在初始化逻辑写在某个servlet中,终究是不恰当的,为什么不写在其他servlet中?别人去找这个逻辑,他也不一定知道要在哪个servlet去找。如果把这个初始化的逻辑写在listener里面,比较恰当的,比如说应用的启动的时候获取配置文件
JDBC,listener来读取usename、password、url、driverClassName。
原理(监听者模式)
假定宝宝哭了,爸爸妈妈和爷爷奶奶分别在周末和工作日照顾宝宝。实现如下
package com.simon.listenermodel;
public interface Family {
public void action();
}
package com.simon.listenermodel;
public class Dad implements Family {
@Override
public void action() {
System.out.println("Dad take care...");
}
}
package com.simon.listenermodel;
public class Mon implements Family {
@Override
public void action() {
System.out.println("Mon take care...");
}
}
package com.simon.listenermodel;
public class Grandma implements Family {
@Override
public void action() {
System.out.println("Grandma take care...");
}
}
package com.simon.listenermodel;
public class GrandPa implements Family {
@Override
public void action() {
System.out.println("Grandpa take care...");
}
}
package com.simon.listenermodel;
public class Baby {
public void babyCry(Family family){
family.action();
}
}
package com.simon.listenermodel;
import org.junit.Test;
//事实上,这就是监听者模式
//几个对应:
//Family对应ServletContextListener
//Dad对应MyServletContextListener
//Baby对应ServletContext
public class BabyTest {
@Test
public void testCry(){
Baby baby = new Baby();
baby.babyCry(new Dad());
baby.babyCry(new Mon());
baby.babyCry(new Grandma());
}
}
//Dad take care...
//Mon take care...
//Grandma take care...
Filter
简述
Filter就是请求进入Servlet之前必须经过的一层过滤器,也称拦截器。Filter位于服务器内,是一个web组件,位于Servlet之前;请求在到达servlet之前,如果能够经过filter,那么肯定会先经过filter,再进入servlet。Filter定为和城门一样的:请求到达Servlet之前,会先经过filter处理一步,处理一下request
请求返回给客户端之前,会再次经过filer一次,filter进行进一步处理response。
使用
1.编写一个类实现Filter接口
2.声明该Filter
package com.simon;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter("/servlet1")
public class FirstFilter implements Filter {
//会随着应用的启动而执行(和servlet是不一样,默认情况下第一次访问之前)
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("init ...");
}
//类似于servlet的service方法
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
System.out.println("doFilter...");
//通行令牌
filterChain.doFilter(servletRequest,servletResponse);
}
//应用被卸载、服务器关闭
@Override
public void destroy() {
System.out.println("destory...");
}
}
package com.simon;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter("/servlet1")
public class SecondFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("second init ...");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
System.out.println("second doFilter...");
}
@Override
public void destroy() {
System.out.println("second destory...");
}
}
package com.simon;
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("/servlet1")
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("servlet...");
}
}
几点注意
- 如何将filter和servlet产生关联呢?
如果filter和servlet产生的关联,那么应该是调用servlet之前会先调用filter
其实是通过url-patterne来关联的,最简单的关联方式其实就是将servlet的url-patteren直接赋值给filter即可。注意一点:
①filter设置和servlet相同的url-patteren是没有任何问题的。
②servlet和servlet之间不可以设置相同的url-pattern。
- filter url-pattern的特殊之处
①filter和filter之间可以设置相同的url-pattern
②filter可以设置/
③filter还可以设置和serlvet相同的url-patternfilter url-pattern的写法和servlet是相同的, /xxxx 和 .xxxx两种
为什么会有这种filter url-pattern行为呢?
filter主要的功能用途是用来当做拦截器、过滤器;servlet主要功能出发 开发动态web资源—— 主要是做出响应的多个filter同时执行时先后顺序
1.如果是web.xml,调用的先后顺序和filter-mapping的先后顺序有关
2.如果是注解,那么调用的先后顺序和类名的首字母ASCII先后顺序有关
- 为什么doFilter方法里面的日志只打印了一遍?
应用场景
1.filter 设置 / * ,filter和servlet的一对多:编码格式,request、response
2.过滤器、拦截器。 没有登录的情况下,访问我的订单页面,直接将你跳转到登录页面引入Filter之后完整的请求处理流程
以访问http://localhost/app/servlet1http://localhost/app/form.html为例:
1、浏览器生成一个HTTP请求报文,传到目的机器之后
2、被监听着80端口号的Connector接收到,将请求报文解析成为Request对象,同时还提供一个response对象
3、这两个对象被传给engine,engine挑选合适的host将对象进行进一步传递
4、host去挑选一个叫做app的 Context,将这两个对象传给Context
5、首先根据请求的有效路径 /servlet1,找有没有对应的filter可以处理该请求,如果有多个,那么按照他们的先后顺序形成一个链表,接下来看有没有servlet可以处理该请求,(如果没有,则会交给缺省servlet),将这个servlet也加入到链表中,在filter后面。
6、依次去调用链表中的每一个组件,调用对应的方法,如果调用的是filter,则调用doFilter(request,response),如果调用的是servlet,调用的是service(request,response),全程传递的就是我们之前提供的request、response对象。
7、整个过程全部执行完毕,response里面写入了数据,Connector读取response里面的数据,然后生成响应报文
8、响应报文再传回给客户端,客户端进行解析,渲染