在Java Web中,有三大组件,分别是Servlet,Filter.、Filter。对于,Servlet这个内容,在这一篇文章中,进行了很详细的讲解,http://blog.csdn.net/cs_hnu_scw/article/details/74080739 所以,本篇文章主要是讲解关于监听器和过滤器中的内容。
一:Listener监听器
知识点1:什么是监听器?
做过Swing或者AWT图像界面Java程序开发的话,应该对Listener与Event非常熟悉。Swing或者AWT中通过Listener与Event来处理事件,如鼠标事件、键盘事件等。先实现一个Listener接口,然后通过addListener()方法把Listener绑定到某个控件上,单机按钮时就会调用Listener的相应方法,并传回一个Event对象。
Java Web程序也一样,使用Listener与Event完成相应事件的处理。使用Listener不需要关注该类事件是怎样触发的或者怎么调用相应的Listener,只要记住该类事件触发时一定会调用相应的Listener。遵循Servlet规范的服务器完成了相应的工作。开发者只要在Listener里编写相关代码就OK了。
知识点2:八大监听器分别是哪些?
* Servlet的三大域对象:
* Request对象 - 一次请求
* Session对象 - 一次会话
* ServletContext对象 - Tomcat从启动到关闭
* JSP的四大域对象:
* page对象 - 当前页面
* Request对象
* Session对象
* ServletContext对象
* 第一组:用于监听Servlet三个域对象的创建与销毁
* ServletRequestListner
* HttpSessionListener
* ServletContextListener
* 第二组:用于监听Servlet三个域对象的属性变化(设置、删除、修改)
* ServletRequestAttributeListener
* HttpSessionAttributeListener
* ServletContextAttributeListener
* 第三组:用于通知被绑定到Session对象的属性.
* HttpSessionBindingListener
* 第四组:用于监听Session的钝化与活化
* HttpSessionActivationListener
它们各自的作用及其适用范围:
* 第一组:* 作用:用于监听Servlet三个域对象的创建与销毁.
* 三个域对象的创建与销毁:
* Request对象:
* 创建:发生请求时.
* 销毁:请求完成时.
* Session对象:
* 创建:发生请求时,并且执行getSession()语句.
* 销毁:执行Session的销毁方法invalidate().
* ServletContext对象:
* 创建:Tomcat启动时.
* 销毁:Tomcat关闭时.
*第二组:
*作用:用于监听Servlet三个域对象的修改,删除,保存
如何自定义监听器:(针对第一组和第二组,因为它们两者非常类似)
* 第一步:* 创建Java类,实现上述监听器接口之一.
* 重写该接口提供的所有方法.
* 第二步:
* 在web.xml文件中配置Listener.
<listener>
<listener-class>自定义Listener的完整路径</listener-class>
</listener>
* 第三组:HttpSessionBindingListener
* 注意:
* 该监听器应该由JavaBean类实现.(可以将JavaBean绑定到Session上)
* 谁被绑定到Session上,谁就实现该监听器.
* 该监听器的作用:通知被绑定到Session的JavaBean对象.
* 使用该监听器时,不需要在web.xml文件中进行配置的.
* 第四组:HttpSessionActivationListener
* Session的序列化
* 当Tomcat服务器正常关闭后,Session会自动被保存到本地硬盘中.* 在Tomcat服务器的安装目录:%Catalina_HOME%\work\Catalina\localhost\Web工程名目录中.
* 当Tomcat服务器正常启动后,Session会自动从本地硬盘中读取到内存中.
* 保存在本地硬盘中的序列化文件,自动销毁.
* 问题:
* Tomcat服务器为什么提供这么一个机制?
* Session的钝化与活化
* 实现步骤:* 编写Servlet设置Session.
* 在Tomcat安装目录%Catalina_HOME%\conf\Catalina\localhost目录中,创建一个名为Web工程名的xml文件.
<?xml version="1.0" encoding="UTF-8"?> <Context> <!-- maxIdleSwap:指定多长时间后Session会被钝化.(单位为分钟) --> <Manager className="org.apache.catalina.session.PersistentManager" maxIdleSwap="1" > <!-- directory:指定钝化文件的保存目录. 钝化文件保存路径:${CATALINA_HOME}\work\Catalina\localhost\Web工程名\${sessionid}.session --> <Store className="org.apache.catalina.session.FileStore" directory="mysession" /> </Manager> </Context>* 创建JavaBean,用于绑定到Session中.
* 该JavaBean实现了HttpSessionActivationListener接口,并且重写该接口提供的所有方法.
* 该JavaBean实现了Serializable(序列化)接口(Session的钝化与活化是基于Session的序列化实现的)
* Session钝化与活化和序列化的区别:
* Session序列化:
* Tomcat提供的自动机制.
* Session的序列化文件在重新被使用时,消失.
* Session钝化与活化:
* 手动实现.
* Session的钝化文件并不消失.
知识点3:实际案例之适用监听器----------完成在线人数的统计
第一步:编写web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>OnlineUserNumberComputer</display-name> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list> <listener> <listener-class>com.hnu.scw.listener.MyContextServletListener</listener-class> </listener> <listener> <listener-class>com.hnu.scw.listener.MySessionListener</listener-class> </listener> <servlet> <description></description> <display-name>OnLineUserNumberDemo</display-name> <servlet-name>OnLineUserNumberDemo</servlet-name> <servlet-class>com.hnu.scw.demo.OnLineUserNumberDemo</servlet-class> </servlet> <servlet-mapping> <servlet-name>OnLineUserNumberDemo</servlet-name> <url-pattern>/OnLineUserNumberDemo</url-pattern> </servlet-mapping> </web-app>
第二步:编写监听器(这里用了servletContextListener和SessionListener两个监听器来完成)
监听器一:
package com.hnu.scw.listener; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; /** * 用于监听当servlet容器进行初始化的时候,将在线人数置为0 * @author scw * 2018-2-3 */ public class MyContextServletListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent servletContextEvent) { //1:拿到contextservlet对象 ServletContext servletContext = servletContextEvent.getServletContext(); //2:初始化在线人数 servletContext.setAttribute("online", 0); } @Override public void contextDestroyed(ServletContextEvent arg0) { // TODO Auto-generated method stub } }
监听器二:
package com.hnu.scw.listener; import javax.servlet.ServletContext; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; /** * 用于当用户登陆之后,在保存用户信息进入session的时候,将人数+1 * 当用户退出之后,将人数-1 * @author scw * */ public class MySessionListener implements HttpSessionListener { /** * 当用户登陆之后,将用户信息保存进入session,那么就相当于在线人数+1 */ @Override public void sessionCreated(HttpSessionEvent httpSessionEvent) { ServletContext servletContext = httpSessionEvent.getSession().getServletContext(); int number = (int) servletContext.getAttribute("online"); number = number + 1 ; servletContext.setAttribute("online", number); } /** * 当用户退出的时候,即session会被销毁,所以在线人数要-1 */ @Override public void sessionDestroyed(HttpSessionEvent httpSessionEvent) { ServletContext servletContext = httpSessionEvent.getSession().getServletContext(); int number = (int) servletContext.getAttribute("online"); number = number - 1 ; servletContext.setAttribute("online", number); } }
第三步:编写模拟用户成功登陆之后的Demo
package com.hnu.scw.demo; import java.io.IOException; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; /** * 用于模拟用户登陆之后进行的操作(这里只是模拟,实际开发当然不只下面这样处理了) * scw * 2018-2-3 */ public class OnLineUserNumberDemo extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1:当进入的时候,就保存一个当前登陆用户的信息进入session(这是模拟登陆成功之后) HttpSession session = request.getSession(); session.setAttribute("user", "我是用户"); //2:取出当前的登陆人数(因为之前保存在servletcontext中,所以从这里面取) ServletContext servletContext = getServletContext(); int number = (int) servletContext.getAttribute("online"); //3:将当前的人数显示出在页面 response.setContentType("text/html;charset=utf-8"); response.getWriter().println("当前在线人数为:" + number); } }
二:Filter过滤器
知识点1:什么是过滤器?
Filter译为过滤,是JavaEE的三大组件之一,用于在Servlet之外对Request或者Response进行修改。例如,污水净化设备可以看做现实中的过滤器,它负责将污水中的杂质过滤,从而使进入的污水变成净水。而对于Web应用程序来说,过滤器是一个驻留在服务器端的Web组件,它可以截取客户端和服务器端之间的请求与响应信息。
知识点2:过滤器使用的时机?
Filter的作用:起到过滤的作用.
Filter执行的时机:
(1)在执行对应的Servlet之前.(过滤Request对象中的内容)
(2)在执行对应的Servlet之后.(过滤Respons对象中的内容)
过滤器的发展:
* Filter最早在Servlet 2.3版本提供.
* Filter在Servlet 2.5版本完善.
知识点3:如何使用过滤器
Filter是JavaEE提供的一个接口.(自定义Filter需要实现该接口,并重写所有方法)
* Filter提供的方法:
* init()
* doFilter()
* destroy()
使用步骤:
(1)创建Java类,实现Filter接口,并且重写所有方法.
(2) 在web.xml文件中进行配置.
<filter> <filter-name>实现接口的类名</filter-name> <filter-class>实现接口的路径</filter-class> </filter> <filter-mapping> <filter-name>类名</filter-name> <url-pattern>/*</url-pattern> //需要进行过滤的url </filter-mapping>
知识点4:过滤器的生命周期
* 出生:
* init()方法
* 活着:
* doFilter()方法
* 死去:
* destroy()方法
* 扩展知识:
* 面向对象:这个人就是对象,年龄、性别等是属性,出生、上学、结婚等方法.
* 类与对象的区别:
* 类:比作女生.
* 对象:就是范冰冰.
* 实现、继承、多态、封装等概念.
* 面向过程:这个人从出生,长大、上学、工作、...、去世.
知识点5:过滤器链
* 问题:
* 如何定义过滤器被执行的先后顺序?
* Filter的doFilter()方法具有一个参数FilterChain,通过调用chain.doFilter()方法可以放行.
* 在过滤器链中,执行chain.doFilter()方法,是否还是放行的作用?
* 如果是,应该被放行到哪里去了?(单个Filter时,直接被放行到对应的Web资源[Servlet、JSP])
* 解决以上问题:
* chain.doFilter()方法依旧是放行方法.
* 如果执行的不是过滤器链中最后一个过滤器的话,执行chain.doFilter()方法,会被放行到下一个过滤器里.
* 如果执行的是过滤器链中最后一个过滤器的话,chain.doFilter()方法,才会被放行到对应Web资源中.
* 过滤器链中的过滤器执行的先后顺序由web.xml文件中的<filter-mapping>标签定义的先后顺序决定.
* 实际开发的意义:
* 单个Filter完成单个任务.
知识点6:过滤器中init()方法中FilterConfig对象-----功能就类似servletConfig对象
* 读取web.xml文件中的初始化参数.
* 在web.xml文件中是如何配置的:
<filter> <filter-name>实现接口的类名</filter-name> <filter-class>实现接口的路径</filter-class> <init-param> <param-name>自定义需要初始化的参数名字key</param-name> <param-value>自定义需要初始化的参数名字value</param-value> </init-param> </filter>* FilterConfig的用法与ServletConfig一致.
* 在web.xml文件中配置全局初始化参数<context-param>,通过ServletContext对象读取.
知识点7:过滤器中的web.xml文件中的映射配置
* 完全匹配:/xxxx
* 目录匹配:/aaaa/
* 扩展名匹配:*.do
* 优先级别:完全匹配 -> 目录匹配 -> 扩展名匹配
* 如果当前Filter拦截对应Servlet的话:
* 还可以使用<servlet-name>标签
* Filter拦截Servlet默认情况是拦截直接请求.也就是说默认对于请求转发的是不会进行过滤拦截的(通过下面就可以进行拦截)
* 在web.xml文件中配置<filter-mapping>标签中具有<dispatcher>
* <dispatcher>标签在同一个Filter的配置中,可以配置多个.
* <dispatcher>标签的值:
* REQUEST:是默认值,表示一次请求.
* FORWARD:表示请求转发到.
* INCLUDE:表示包含(例如JSP包含另一个JSP等)
* ERROR:表示JSP的<%@ page errorPage=""%>
知识点8:实际案例之过滤器-----------完成servlet中的全文乱码处理
描述:当进行servlet开发的时候,接受前台传送过来的中文,很多情况都碰到乱码,那么处理乱码就是一个问题,虽然有处理的方式,但是都是只针对一个servlet进行,那么一个项目中,肯定有很多个,那么如何进行处理呢?这时候就可以通过过滤器来进行实现,并且,这个开发好了,以后可以直接使用,或者直接打个Jar包,既方便又实用。
扩展:Servlet处理Get和Post的乱码问题
针对Get:
String value = request.getParameter(""); value = new String(value.getBytes("ISO8859-1"),"utf-8"); response.setContentType("text/html;charset=utf-8");
针对Post:-------post方式的较容易处理
request.setCharacterEncoding("utf-8"); response.setContentType("text/html;charset=utf-8");
开发步骤:
第一步:编写自定义的request对象
原因:因为对于Get方式的提交,那么处理中文会比较麻烦,就是因为要把所有的参数,都逐一进行编码处理,而常规的httprequest对象,则无法满足条件,所以需要自定义request对象。
package com.hnu.scw.filter.demo1; import java.io.UnsupportedEncodingException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; /** * 自定义的Request对象 * @author scw */ public class MyRequest extends HttpServletRequestWrapper { public MyRequest(HttpServletRequest request) { super(request); } @Override public String getParameter(String name) { String value = super.getParameter(name); if(super.getMethod().equalsIgnoreCase("GET")){ try { value = new String(value.getBytes("ISO-8859-1"),"utf-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } return value; } }
第二步:编写过滤器
package com.hnu.scw.filter.demo1; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * 当前Filter用于解决全站中文乱码问题. * @author scw */ public class EncodingFilter implements Filter { public void destroy() { } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { //1 进行Request与Response对象的类型强转 HttpServletRequest request = (HttpServletRequest)req; HttpServletResponse response = (HttpServletResponse)resp; //2 解决中文乱码问题:1)请求的中文乱码(GET\POST);2)响应的中文乱码. request.setCharacterEncoding("utf-8"); response.setContentType("text/html;charset=utf-8"); //3 创建自定义的Request对象 MyRequest myRequest = new MyRequest(request); //3 放行 chain.doFilter(myRequest, response); } public void init(FilterConfig arg0) throws ServletException { } }
第三步:配置web.xml文件(就贴关键的过滤器配置,servlet的就不贴了)
<filter> <filter-name>EncodingFilter</filter-name> <filter-class>com.hnu.scw.filter.demo1.EncodingFilter</filter-class> </filter> <filter-mapping> <filter-name>EncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
第四步:进行测试
package com.hnu.scw.filter.demo1; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class EncodeServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username = request.getParameter("username");//接受传送过来的中文参数 System.out.println(username);//打印传送过来的get中文参数是否还乱码 } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username = request.getParameter("username"); //模拟post传送的参数 System.out.println(username); //打印接受过来的中文参数是否乱码 } }
知识点9:实例之过滤器的使用-------------自动登陆的功能
描述:有时候,我们会碰到,有些项目需要自动登陆功能,即当用户是之前登陆的,并且,用户选择了保存自动登陆,那么当下次再访问的时候,就不需要填写用户名和密码,而是直接能够正常访问,这个功能有时候还是很有用的。其实,这个通过过滤器就能够进行实现。
主要就写一下关于过滤器如何进行编写了,逻辑还是很清晰的。
过滤器类代码:
package com.hnu.scw.java.filter.demo2; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; /** * 当前Filter用于完成用户自动登录功能. * @author scw */ public class AutoLoginFilter implements Filter { public void destroy() { } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest)req; Cookie[] cookies = request.getCookies(); if(cookies != null){ for (Cookie cookie : cookies) { if(cookie != null){ String name = cookie.getName(); if("user".equals(name)){ // 曾经登录过 String value = cookie.getValue(); //下面进行分割,主要是因为cookie保存只能是字符串,那么用户的用户名和密码保存中间用了#字符来分割,所以这里就需要进行拆分 String username = value.split("#")[0]; String password = value.split("#")[1]; //将用户信息继续保存到session中,方便后面业务需要 request.getSession().setAttribute("user", username); chain.doFilter(request, resp); }else{ // 没有登陆过,就放行到登陆页面去处理即可。(模拟) chain.doFilter(request, resp); } } } }else{ // 没有登陆过,就放行到登陆页面去处理(模拟) chain.doFilter(request, resp); } } public void init(FilterConfig arg0) throws ServletException { } }
注意:当进行登陆成功之后,再进行判断用户时候选择了自动登陆功能,然后将该用户的信息保存到cookie中,这样以后进行访问页面的时候,就从cookie中进行获取,判断是否有符合要求的cookie内容即可。这就是关键的登陆成功之后的操作。
好了,这就是对于J2EE中的两个关键组件的详细分析,是不是挺简单的呢?但是作用却是非常强大的,因为SpringMvc框架中,很多用的都是这部分的知识点,这对于以后学习框架有很大的帮助,所以,还是对于基础要好好的掌握。