1.Listener监听器
1.1 监听器的概述
1.1.1 什么是监听器
监听器就是一个实现了特点接口的Java类,这个Java类用于监听另一个Java类的方法调用或者属性的改变。当被监听对象发生上述事件后,监听器某个方法将会立即被执行。
1.1.2 监听器的用途
用来监听其他对象的变化。主要应用在图形化界面开发上。
- Java中GUI,Android
1.1.3 监听器的术语
- 事件源:指的是被监听对象(汽车)
- 监听器:指的是监听的对象(报警器)
- 事件源和监听器绑定:在汽车上安装报警器
- 事件:指的是事件源对象的改变(踹了汽车一脚)–主要功能是获得事件源对象
1.2 监听器的入门
1.2.1 监听器的入门程序
1.2.2 监听器的执行过程
1.3 Servlet中的监听器
1.3.1 Serlvet中的监听器简介
在Servlet中定义了多种类型的监听器,它们用于监听的事件源分别是ServletContext、HttpSession和ServletRequest这三个域对象。
1.3.2 Servlet中的监听器的分类
- 一类:监听三个域对象的创建和销毁的监听器(三个)
- 二类:监听三个域对象的属性变更(属性添加、移除、替换)的监听器(三个)
- 三类:监听HttpSession中JavaBean的状态改变(钝化、活化、绑定、解除绑定)的监听(两个)
1.4 第一类监听器
监听三个域对象的创建和销毁的监听器]
1.4.1 SerlvetContextListener
####1.4.1.1 SerlvetContextListener的作用
- 用来监听SerlvetContext域对象的创建和销毁的监听器
1.4.1.2 ServletContext的创建和销毁
SerlvetContext
- 创建:服务器启动的时候,为每个web应用创建单独的SerlvetContext对象。
- 销毁:服务器关闭的时候,或者项目从web服务器中移除的时候。
1.4.1.3 ServletContextListener监听器的方法
-
监听ServletContext对象的创建
-
监听ServletContext对象的销毁
1.4.1.4 编写监听器
1.4.1.5 绑定监听器和事件源
通过配置方式绑定
在web.xml
1.4.1.6 ServletContextListener企业用途
-
记载框架的配置文件:
Spring框架提供了一个核心监听器ContextLoaderListener。 -
定时任务调度
1.4.2 HttpSessionListener监听器
1.4.2.1 HttpSessionListener监听器的作用
用来监听HttpSession对象的创建和销毁
1.4.2.2 HttpSession创建和销毁
- 创建:服务器端第一次调用getSession()方法的时候
- 销毁:
① 非正常关闭服务器(正常关闭服务器session会被序列化)
② Session过期(默认过期时间30分钟)
③ 手动调用session.invalidate()方法
1.4.2.3 HttpSessionListener监听器的方法
- 监听HttpSession创建
- 监听HttpSession销毁
1.4.2.4 编写监听器监听HttpSession对象创建和销毁
1.4.2.5 监听器代码
1.4.2.6 监听器配置
……
注意:
- 访问HTML是否创建Session :不会
- 访问JSP是否创建Session :会
- 访问Servlet是否创建Session :不会(默认没有调用getSession方法)
1.4.3 ServletRequestListener监听器的
1.4.3.1 ServletRequestListener的作用
- 用户监听ServletRequest对象的创建和销毁
1.4.3.2 ServletRequest对象的创建和销毁
- 创建:从客户端向服务器发送一次请求,服务器就会创建request对象。
- 销毁:服务器对这次请求作出了响应之后,request对象就销毁了。
1.4.3.3 ServletRequestListener监听器的方法
1.4.3.4 编写监听器监听ServletRequest对象创建和销毁
1.4.3.5 监听器代码
1.4.3.6 监听器配置
……
注意
- 访问HTML页面是否创建请求对象 :会
- 访问JSP页面是否创建请求对象 :会
- 访问Servlet是否创建请求对象 :会
1.4.4 案例-统计当前在线人数
1.4.4.1 案例分析
1.4.4.2 代码实现
- 创建MySerlvetContextListener
public class MyServletContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
//在服务器启动的时候初始化一个值为0
//把这个值存入到ServletContext中
servletContextEvent.getServletContext().setAttribute("count",0);
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
}
}
- 创建MyHttpSessionListener
public class MyHttpSessionListener implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
//在线
//获得原来的值,进行加一操作
HttpSession session = httpSessionEvent.getSession();
System.out.println(session.getId()+"上线了");
//获得ServletContext中的值
int count = (int) session.getServletContext().getAttribute("count");
count++;
session.getServletContext().setAttribute("count",count);
}
@Override
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
//下线
//获得原来的值,进行减一操作
HttpSession session = httpSessionEvent.getSession();
System.out.println(session.getId()+"上线了");
//获得ServletContext中的值
int count = (int) session.getServletContext().getAttribute("count");
count--;
session.getServletContext().setAttribute("count",count);
}
}
编辑一个JSP来显示在线人数
<h1>在线人数:${applicationScope.count}</h1>
1.5 第二类监听器
监听三个域对象的属性变更(属性添加、移除、替换)的监听器
1.5.1 ServletContextAttributeListener
- 监听ServletContext对象中的属性变更(属性添加,移除,替换)的监听器
方法
1.5.2HttpSessionAttributeListener
- 监听HttpSession对象中的属性变更的监听器
方法
1.5.3 ServletRequestAttributeListener
- 监听ServletRequest对象中的属性变更的监听器
方法
1.5.4 演示第二类监听器
由于这三个监听器方法都类似,所以选择HttpSessionAttributeListener来进行演示。
1.6 第三类监听器
用来监听HttpSession中Java类状态改变的监听器
1.6.1 第三类监听器概述
保存在Session域中的Java类可以有多种状态:
① 绑定到session中;
② 从session中解除绑定;
③ 随session对象持久化到一个存储设备中(钝化);
④ 随session对象从一个存储设备中恢复(活化)。
Servlet规范中定义了两个特殊的监听接口来帮助Java类来了解自己在Session域中的状态:
- HttpSessionBindingListener 接口
- HttpSessionActivationListener 接口
实现这两个接口的类不需要再web.xml中进行配置
1.6.1.1 HttpSessionBindingListener监听器
监听Java类在HttpSession中的绑定和解除绑定的状态的监听器
测试代码:
1.6.1.2 HttpSessionActivationListener监听器
监听HttpSession中Java类的钝化和活化监听器。
- 配置完成session的序列化和反序列化
Context标签可以配置在:
Tomcat/conf/context.xml:所有tomcat下虚拟主机和虚拟目录下的工程都会序列化session
Tomcat/conf/Catalina/localhost/context.xml:只有localhost这个虚拟主机下的所有项目会序列化session
工程/META-INF/context.xml:只有当前工程才会序列化session
注意:
在idea创建web项目并不会直接创建META-INF文件夹
① 打开Project Structure
② 选择Facets,然后Add Application Server specific descriptor
③ 在打开的选择框中选择Tomcat即可
2.Filter过滤器
2.1 Filter的概述
2.1.1 什么是Filter
Filter称为过滤器,它是Servlet技术中最实用的技术,web开发人员通过Filter技术,对web服务器所管理的资源(JSP,Servlet,静态图片或静态html文件)进行拦截,从而实现一些特殊的功能。
Filter就是过滤从客户端向服务器发送的请求。
2.1.2 为什么学习过滤器
2.2 Filter的入门
2.2.1 第一步:编写一个类实现Filter接口
2.2.2 第二步:对过滤器进行配置
2.3 FilterChain对象的概述
2.3.1 FilterChain对象的概述
FilterChain过滤器链:在一个web应用中,可以开发编写多个Filter,这些Filter组合起来称为是一个过滤器链。
Web服务器根据Filter在web.xml文件中的注册顺序(mapping的配置顺序)决定先调用那个Filter。依次调用后面的过滤器,如果没有下一个过滤器,调用目标资源。
2.4 Filter的生命周期
2.4.1 Filter的生命周期的描述
Filter的创建和销毁是由web服务器负责。Web应用程序启动的时候,web服务器创建Filter的实例对象。并调用其init方法进行初始化(filter对象只会创建一次,init方法也只会执行一次)。
每次filter进行拦截的时候,都会执行doFilter的方法。
当服务器关闭的时候,应用从服务器中移除的时候,服务器会销毁Filter对象。
2.5 FilterConfig对象
2.5.1 FilterConfig的概述
2.5.1.1 FilterConfig的作用
用来获得Filter的相关的配置的对象。
2.5.1.2 FilterConfig对象的API
代码演示
2.6 过滤器的相关配置
2.6.1 <url-pattern>
的配置
- 完全路径匹配 :以/开始 比如/aaa /aaa/bbb
- 目录匹配 :以/开始 以结束 比如/ /aaa/* /aaa/bbb/*
- 扩展名匹配 :不能以/开始 以开始 比如.jsp *.do *.action
2.6.2 <servlet-name>
的配置
专门以Servlet的配置的名称拦截Servlet
2.6.3 <dispatcher>
的配置
- 默认的情况下过滤器会拦截请求。如果进行转发(需要拦截这次转发)。
dispatcher的取值
- REQUEST :默认值。默认过滤器拦截的就是请求。
- FORWARD:转发。
- INCLUDE :页面包含的时候进行拦截
- ERROR :页面出现全局错误页面跳转的时候进行拦截’
2.7 案例:权限验证过滤器
2.7.1 案例需求
现在一个网站上需要有登录的功能,在登录成功后,重定向到后台的成功页面(后台的页面有很多)。如果现在没有登录直接在地址栏上输入后台页面地址。
编写一个过滤器:可以对没有登录的用户进行拦截。(没有登录,回到登录页面。如果已经登录,放行。)
2.7.2 案例登录功能
与之前大同小异,唯一一处不同的就是在登录的Servlet中,为了防止因项目名称变更导致的页面重定向失败,使用了
替代了
在login.jsp页面同样如此
2.7.3 案例权限过滤器
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//判断用户是否已经登录。如果已经登陆,放行。如果没有登陆回到登陆页面
HttpServletRequest req = (HttpServletRequest) servletRequest;
User existUser = (User) req.getSession().getAttribute("existUser");
//判断:
if(existUser == null ){
//没有登录
req.setAttribute("msg","您还没有登录!没有权限访问");
req.getRequestDispatcher("/login.jsp").forward(req,servletResponse);
}else {
//已经登陆:
filterChain.doFilter(req, servletResponse);
}
}
<url-pattern>
设置成了jsp文件夹下的所有文件,这样如果用户不登录就进入到这些修改页面就都会跳转回登录页面
2.8 案例:通用的字符集编码过滤器
2.8.1 案例分析
网站,需要向后台提交中文的数据(有可能是GET也有可能是POST)。中文处理根据不同的请求方式,处理的方式也是不一样的。
需要调用request.getParameter();方法接收数据,但是这个时候无论是get还是post接收的数据都是存在乱码。现在调用request.getParameter()方法无论是get还是post请求提交的中文,都没有乱码。
2.8.2 增强request类的getParameter方法
通过上面的分析,现在增强request的getParameter的方法。增强的过程要写在过滤器中。
如何增强一个类中的方法?
① 继承
- 必须要能够控制这个类的构造。
② 装饰者
- 被增强的类和增强的类需要实现相同的接口。
- 在增强的类中获得被增强的类的引用。
- 缺点:接口中的方法过多,重写很多其他的方法。
③ 动态代理
- 类需要实现接口。
2.8.3 案例代码
新建 submit.jsp
<body>
<form action="${pageContext.request.contextPath}/SubmitServlet" method="post">
姓名:<input type="text" name="username"><br>
<input type="submit" name="提交">
</form>
<form action="${pageContext.request.contextPath}/SubmitServlet" method="get">
姓名:<input type="text" name="username"><br>
<input type="submit" name="提交">
</form>
</body>
新建一个过滤器,记得去web.xml中配置他
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
//在过滤器中增强request对象,并将增强后的request对象传递给Servlet。
MyHttpServletRequest myHttpServletRequest = new MyHttpServletRequest(request);
filterChain.doFilter(myHttpServletRequest,servletResponse);
}
新建一个增强的类,来继承HttpServletRequest的包装类HttpServletRequestWrapper,然后重写增强其中的request.getParameter()方法
public class MyHttpServletRequest extends HttpServletRequestWrapper {
private HttpServletRequest request;
public MyHttpServletRequest(HttpServletRequest request) {
super(request);
this.request = request;
}
//增强request.getParameter()方法
@Override
public String getParameter(String name) {
//获得请求方式
String method = request.getMethod();
//根据是get还是post请求进行不同方式的乱码处理
if("GET".equalsIgnoreCase(method)){
//GET方式的请求
String parameter = super.getParameter(name);
try {
parameter = new String(parameter.getBytes("UTF-8"),"UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return parameter;
}else if("POST".equalsIgnoreCase(method)){
//POST方式的请求
try {
request.setCharacterEncoding("UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
return super.getParameter(name);
}
在写该程序时,由于get方法得到的参数本身就是UTF-8编码的,我按照教程使用ISO-8859-1,反而乱码。