Filter & Listener
一 监听器的概述
监听器就是一个实现了特定接口的Java类,用于监听另一个Java类的方法调用或属性的改变。当被监听对象发生上述事件后,监听器某个方法将会立即被执行。
即用来监听其他对象的变化,主要应用在图形化界面开发上。
事件源 专门产生事件的组件
事件 由事件源所产生的动作或者事情
监听器 专门处理事件源所产生的事件
注册/绑定监听器 让监听器时刻监听事件源是否有指定事件产生,如果产生指定事件,则调用监听器处理
在Servlet中定义了多种类型的监听器,用于监听ServletContext、HttpSession和ServletRequest这三个域对象。
分类
一类:监听三个域对象的创建和销毁(三个)
二类:监听三个域对象的属性变更(属性添加、移除、替换)(三个)
三类:监听HttpSession中JavaBean的状态改变(钝化、活化、绑定、解除绑定)(两个)
二 简单监听器使用演示
public class MyFrame extends JFrame { public static void main(String args[]){ //1 创建事件源 MyFrame myFrame = new MyFrame(); myFrame.setBounds(300, 200, 500, 200); myFrame.setVisible(true); //3 绑定监听器 myFrame.addWindowListener(new MyWindowListener()); } } //2 创建监听器 class MyWindowListener implements WindowListener{ @Override public void windowOpened(WindowEvent e) { System.out.println("窗口打开..."); } @Override public void windowClosing(WindowEvent e) { System.out.println("窗口关闭..."); //关闭虚拟机 System.exit(0); } @Override public void windowClosed(WindowEvent e) { } @Override public void windowIconified(WindowEvent e) { } @Override public void windowDeiconified(WindowEvent e) { } @Override public void windowActivated(WindowEvent e) { } @Override public void windowDeactivated(WindowEvent e) { } }
三 ServletContextListener监听器的使用
1 在web项目中编写事件源
public class MyServletContextListener implements ServletContextListener{ @Override public void contextDestroyed(ServletContextEvent arg0) { System.out.println("ServletContext销毁了"); } @Override public void contextInitialized(ServletContextEvent arg0) { System.out.println("ServletContext创建了"); } }
2 在web.xml中配置监听器
<listener> <listener-class>com.my.listener.MyServletContextListener</listener-class> </listener>
ServletContextListener企业用途
1 加载框架的配置文件:Spring框架提供了一个核心监听器ContextLoaderListener。
2 定时任务调度
四 HttpSessionListener监听器的使用
HttpSession创建和销毁
创建:服务器端第一次调用getSession()方法时候
销毁:
非正常关闭服务器(正常关闭服务器session会被序列化)
Session过期(默认过期时间30分钟)
手动调用session.invalidate()方法
访问HTML是否创建Session :不会
访问JSP是否创建Session :会
访问Servlet是否创建Session:不会(默认没有调用getSession方法)
public class MyHttpSessionListener implements HttpSessionListener{ @Override public void sessionCreated(HttpSessionEvent arg0) { System.out.println("HttpSession被创建了..."); } @Override public void sessionDestroyed(HttpSessionEvent arg0) { System.out.println("HttpSession被销毁了..."); } }
<listener> <listener-class>com.my.listener.MyHttpSessionListener</listener-class> </listener>
五 ServletRequestListener监听器的使用
ServletRequest对象的创建和销毁
创建:客户端向服务器发送一次请求,服务器就会创建request对象
销毁:服务器对这次请求作出了响应后,request对象就销毁了
访问HTML页面是否创建请求对象 :会
访问JSP页面是否创建请求对象 :会
访问Servlet是否创建请求对象 :会
public class MyServletRequestListener implements ServletRequestListener{ @Override public void requestDestroyed(ServletRequestEvent arg0) { System.out.println("ServletRequest创建了..."); } @Override public void requestInitialized(ServletRequestEvent arg0) { System.out.println("ServletRequest销毁了..."); } }
监听器的web.xml配置:略,参考上面
六 监听器的应用案例:统计当前在线人数
创建事件源,web.xml中配置监听器,制作jsp页面,开启服务器,访问jsp页面
public class MyServletContextListener implements ServletContextListener{ @Override public void contextDestroyed(ServletContextEvent context) { System.out.println("ServletContext销毁了"); } @Override public void contextInitialized(ServletContextEvent context) { System.out.println("ServletContext创建了"); //服务器启动时初始化一个count存入ServletContext context.getServletContext().setAttribute("count", 0); } }
public class MyHttpSessionListener implements HttpSessionListener{ @Override public void sessionCreated(HttpSessionEvent newSession) { //上线人数+1 HttpSession httpSession = newSession.getSession(); System.out.println(httpSession.getId() + "上线了..."); Integer count = (Integer) httpSession.getServletContext().getAttribute("count"); count++; httpSession.getServletContext().setAttribute("count", count); } @Override public void sessionDestroyed(HttpSessionEvent newSession) { //下1线人数-1 HttpSession httpSession = newSession.getSession(); System.out.println(httpSession.getId() + "下线了..."); Integer count = (Integer) httpSession.getServletContext().getAttribute("count"); count--; httpSession.getServletContext().setAttribute("count", count); } }
<listener> <listener-class>com.my.listener.MyServletContextListener</listener-class> </listener> <listener> <listener-class>com.my.listener.MyHttpSessionListener</listener-class> </listener> <session-config> <session-timeout>1</session-timeout> </session-config>
session-timeout单位:分钟
<body> 在线人数:${count} </body>
七 监听三个域对象属性变更的监听器
(Attribute:属性)
ServletContextAttributeListener 监听ServletContext对象中的属性变更(属性添加,替换,移除)
HttpSessionAttributeListener 监听HttpSession对象中的属性变更(属性添加,替换,移除)
ServletRequestAttributeListener 监听ServletRequest对象中的属性变更(属性添加,替换,移除)
监听器的编写:
public class MyHttpSessionAttributeLitener implements HttpSessionAttributeListener{ @Override public void attributeAdded(HttpSessionBindingEvent arg0) { System.out.println("向session中添加了属性..."); } @Override public void attributeRemoved(HttpSessionBindingEvent arg0) { System.out.println("从session中移除了属性..."); } @Override public void attributeReplaced(HttpSessionBindingEvent arg0) { System.out.println("从session中替换了属性..."); } }
web.xml中监听器的配置:略
jsp测试页面:
<body> <% session.setAttribute("name", "user1");//添加属性 session.setAttribute("name", "user2");//替换属性 session.removeAttribute("name");//移除属性 %> </body>
八 监听HttpSession中Java类状态改变的监听器
保存在Session域中的Java类可以有多种状态:
1 绑定到session中
2 从session中解除绑定
3 随session对象持久化到一个存储设备中(钝化)
4 随session对象从一个存储设备中恢复(活化)
Servlet对方中定义了两个特殊的监听的接口来帮助Java类了解自己在Session域中的状态:
HttpSessionBindingListener接口 监听Java类在HttpSession中绑定和解除绑定的状态
HttpSessionActivationListener接口 监听HttpSession中Java类的钝化和活化
实现这两个接口的类不需要在web.xml中进行配置
用途:当访问页面的用户过多,创建的session对象过多,占用内存资源,此时可以设置session活跃时间,超过时间无活动的session可以持久化到硬盘中,节省内存资源
使用演示:
1 创建bean对象,提供set/get方法,实现HttpSessionBindingListener/HttpSessionActivationListener接口
2 在jsp中<% new一个bean对象,setName,再将bean对象set到session中%>,访问页面,即完成了bean对象的绑定
3 让bean对象实现序列化接口Serializable,此时访问jsp页面后,关闭服务器,session对象即被序列化到tomcat本地文件夹下,再次启动服务器,即被反序列化到session中
配置session的序列化和反序列化:context.xml
配置位置:
tomcat/conf/context.xml 所有tomcat下虚拟主机和虚拟目录下的工程都会序列化session
tomcat/conf/Catalina/localhost/context.xml localhost虚拟主机下的所有项目会序列化session
工程/META-INF/context.xml 当前工程才会序列化session
<Context> <Manager className="org.apache.catalina.session.PersistentManager" maxIdleSwap="1"> <Store className="org.apache.catalina.session.FileStore" directory="路径"/> </Manager> </Context>
maxIdleSwap:Session不活动的最长时间,超过该时间,Session Manager 将会把该Session对象转移到Session Store中,该Session将不在内存中。1 代表1分钟
九 Filter概述
Filter即过滤器,过滤客户端向服务器发送的请求,是Servlet技术中最实用的技术,通过Filter技术,对web服务器所管理的资源(JSP,Servlet,静态图片或静态html文件)进行拦截,从而实现一些特殊的功能。
使用:
1 编写一个类实现Filter接口
2 在web.xml中对过滤器进行配置
public class FilterDemo1 implements Filter{ @Override public void destroy() { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("过滤器执行了..."); //放行 chain.doFilter(request, response); } @Override public void init(FilterConfig arg0) throws ServletException { } }
<filter> <filter-name>FilterDemo1</filter-name> <filter-class>com.my.filter.FilterDemo1</filter-class> </filter> <filter-mapping> <filter-name>FilterDemo1</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
/* 代表全部拦截
十 FilterChain对象概述
FilterChain过滤器链:在一个web应用中,可以编写多个Filter,这些Filter组合起来称为是一个过滤器链
Web服务器根据Filter在web.xml文件中的注册顺序(mapping的配置顺序)依次调用过滤器
十一 Filter的生命周期
Filter的创建和销毁由web服务器负责。Web应用程序启动的时候,web服务器创建Filter的实例对象,并调用其init方法进行初始化(filter对象只会创建一次,init方法也只会执行一次)。
每次filter进行拦截的时候,都会执行doFilter的方法。
当服务器关闭,或应用从服务器中移除时,服务器会销毁Filter对象。
十二 FilterConfig对象概述
在Filter的init方法中有一个参数FilterConfig,FilterConfig对象作用是获取Filter的相关配置信息:
1.获取初始化参数
String getInitparameter(String name);
Enumeration EnumerngetInitParameterNames();
2.获取Filter的名称: getFilterName();
3.获取ServletContext对象:getServletContext();
十三 过滤器的相关配置
<url-pattern>的配置
完全路径匹配 :以/开始 比如/aaa /aaa/bbb
目录匹配 : 以/开始 以*结束 比如/* /aaa/* /aaa/bbb/*
扩展名匹配 : 以*开始 比如*.jsp *.do *.action
<servlet-name>的配置
根据Servlet的配置名称拦截Servlet。
<dispatcher>的配置
默认的情况下过滤器会拦截请求。如果进行转发(需要拦截这次转发)。
dispatcher的取值
REQUEST :默认值。过滤器默认拦截的就是请求。
FORWARD:转发。
INCLUDE :页面包含的时候进行拦截
ERROR :页面出现全局错误页面跳转的时候进行拦截
<filter-mapping> <filter-name>FilterDemo1</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
此时过滤器将会拦截全部页面访问请求。因为这里没有制定任何的< dispatcher >元素,默认值是REQUEST。
<filter-mapping> <filter-name>FilterDemo1</filter-name> <filter-class>com.my.filter.FilterDemo1</filter-class> <dispatcher>INCLUDE</dispatcher> </filter-mapping>
此时只要是通过<jsp:include page="xxx.jsp" />,嵌入进来的页面,每嵌入的一个页面,都会走一次指定的过滤器
<filter-mapping> <filter-name>FilterDemo1</filter-name> <filter-class>com.my.filter.FilterDemo1</filter-class> <dispatcher>FOWARD</dispatcher> </filter-mapping>
此时只有从别的页面转发到当前页面,才会走指定的过滤器
十四 过滤器应用一:权限验证过滤器
需求:登录成功后,重定向到成功页面。未登录直接访问台页面则进行拦截,并跳转到登录页面
<form action="${pageContext.request.contextPath}/userServlet" method="post"> <table border="1" width="500"> <tr> <td>用户名</td> <td><input type="text" name="username" value="${ cookie.remember.value }"/></td> </tr> <tr> <td>密码</td> <td><input type="password" name="password"/></td> </tr> <tr> <td colspan="2"><input type="submit" value="登录"/></td> </tr> </table> </form>
public class UserModel { public User login(User user) throws SQLException { // 连接数据库:通过传入的用户名和密码去数据库中进行查询 QueryRunner queryRunner = new QueryRunner(JDBCUtils.getDataSource()); User existUser = queryRunner.query("select * from user where username = ? and password = ?", new BeanHandler<User>(User.class), user.getUsername(), user.getPassword()); return existUser; } }
public class UserServlet extends HttpServlet{ protected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException,IOException{ try{ //接收数据 String userName = request.getParameter("userName"); String password = request.getParameter("password"); //封装数据 User user = new User(); user.setUserName(userName); user.setPassword(password); //处理数据 UserModel userModel = new UserModel(); User existUser = userModel.login(user); //根据处理结果跳转 if(existUser == null){ request.setAttribute("msg", "用户名或密码错误!"); request.getRequestDispatcher("/login.jsp").forward(request, response); }else{ } }catch (Exception e) { e.printStackTrace(); } } }
public class LoginFilter implements Filter{ @Override public void destroy() { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { //判断用户是否登录 HttpServletRequest req = (HttpServletRequest) request; User existUser = req.getSession().getAttribute("existUser"); if(existUser == null){ req.setAttribute("msg", "你还没有登录,没有权限!"); req.getRequestDispatcher("/login.jsp").forward(req, response); }else{ chain.doFilter(req, response); } } @Override public void init(FilterConfig arg0) throws ServletException { } }
十五 过滤器应用二:通用字符集编码过滤器
网站向后台提交中文数据(GET/POST),需要调用request.getParameter();方法接收数据,此时无论是get还是post接收的数据都存在乱码。
所以需要增强request的getParameter()方法,增强的过程要写在过滤器中。
如何增强一个类中的方法?
继承:必须要能够控制这个类的构造。
装饰者:被增强的类和增强的类需要实现相同的接口,在增强的类中获得被增强的类的引用。
缺点:接口中的方法过多,重写很多其他的方法。
动态代理:类需要实现接口
过滤器代码:
public class GenericEncodingFilter implements Filter{ @Override public void destroy() { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { //在过滤器中增强request对象,并将增强后的request对象传递给Servlet HttpServletRequest req = (HttpServletRequest) request; //增强req MyHttpServletRequest myReq = new MyHttpServletRequest(req); chain.doFilter(myReq, response); } @Override public void init(FilterConfig arg0) throws ServletException { } }
增强类代码:
public class MyHttpServletRequest extends HttpServletRequestWrapper{ private HttpServletRequest request; public MyHttpServletRequest(HttpServletRequest request) { super(request); this.request = request; } //增强request.getParameter();方法 public String getParameter(String name){ //获得请求方式 String method = request.getMethod(); //根据psot/get请求方式进行相应的乱码处理 if("GET".equalsIgnoreCase(method)){ //get的请求方式 String value = super.getParameter(name); try { value = new String(value.getBytes("ISO-8859-1"),"UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return value; }else if("PSOT".equalsIgnoreCase(method)){ //post的请求方式 try { request.setCharacterEncoding("UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } return super.getParameter(name); } }
GenericEncodingFilter.java
/** * 解决get和post请求 全部乱码 * */ public class GenericEncodingFilter implements Filter { @Override public void destroy() { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // 转型为与协议相关对象 HttpServletRequest httpServletRequest = (HttpServletRequest) request; // 对request包装增强 HttpServletRequest myrequest = new MyRequest(httpServletRequest); chain.doFilter(myrequest, response); } @Override public void init(FilterConfig filterConfig) throws ServletException { } } // 自定义request对象 class MyRequest extends HttpServletRequestWrapper { private HttpServletRequest request; private boolean hasEncode; public MyRequest(HttpServletRequest request) { super(request);// super必须写 this.request = request; } // 对需要增强方法 进行覆盖 @Override public Map getParameterMap() { // 先获得请求方式 String method = request.getMethod(); if (method.equalsIgnoreCase("post")) { // post请求 try { // 处理post乱码 request.setCharacterEncoding("utf-8"); return request.getParameterMap(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } else if (method.equalsIgnoreCase("get")) { // get请求 Map<String, String[]> parameterMap = request.getParameterMap(); if (!hasEncode) { // 确保get手动编码逻辑只运行一次 for (String parameterName : parameterMap.keySet()) { String[] values = parameterMap.get(parameterName); if (values != null) { for (int i = 0; i < values.length; i++) { try { // 处理get乱码 values[i] = new String(values[i] .getBytes("ISO-8859-1"), "utf-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } } } hasEncode = true; } return parameterMap; } return super.getParameterMap(); } @Override public String getParameter(String name) { Map<String, String[]> parameterMap = getParameterMap(); String[] values = parameterMap.get(name); if (values == null) { return null; } return values[0]; // 取回参数的第一个值 } @Override public String[] getParameterValues(String name) { Map<String, String[]> parameterMap = getParameterMap(); String[] values = parameterMap.get(name); return values; } }
Filter & Listener的更多相关文章
- JavaWeb三大组件(Servlet,Filter,Listener 自己整理,初学者可以借鉴一下)
JavaWeb三大组件(Servlet,Filter,Listener 自己整理,初学者可以借鉴一下) Reference
- servelt filter listener 的生命周期
1. servlet 当第一次请求一个servlet资源时,servlet容器创建这个servlet实例,并调用他的 init(ServletConfig config)做一些初始化的工作,然后 ...
- Filter&Listener
Filter&Listener 内容待补充... ...
- SpringBoot学习笔记(6)----SpringBoot中使用Servlet,Filter,Listener的三种方式
在一般的运用开发中Controller已经大部分都能够实现了,但是也不排除需要自己实现Servlet,Filter,Listener的方式,SpringBoot提供了三种实现方式. 1. 使用Bean ...
- ServletContextInitializer添加 servlet filter listener
ServletContextInitializer添加 servlet filter listener https://www.cnblogs.com/pomer-huang/p/9639322.ht ...
- SpringBoot---注册Servlet,Filter,Listener
1.概述 1.1.当使用 内嵌的Servlet容器(Tomcat.Jetty等)时,将Servlet,Filter,Listener 注册到Servlet容器的方法: 1.1.1.直接注册Bean ...
- SpringBoot整合WEB开发--(九)整合Servlet,Filter,Listener
简介: 如果需要整合第三方框架时,可能还是不得不使用Servlet,Filter,Listener,Springboot中也有提供支持. @WebServlet("/my") pu ...
- servlet filter listener interceptor 知识点
这篇文章主要介绍 servlet filter listener interceptor 之 知识点.博文主要从 概念,生命周期,使命介绍其区别.详情如下: 概念 生命周期 使命 servlet ...
- springboot之filter/listener/servlet
简介 SpringBoot可以简化开发流程,但是在其中如何使用传统的J2EE servlet/listener/filter呢 @Bean配置 在Configuration类中加入filter和ser ...
- servlet/filter/listener/interceptor区别与联系
转自:http://www.cnblogs.com/doit8791/p/4209442.html servlet.filter.listener是配置到web.xml中(web.xml 的加载顺序是 ...
随机推荐
- java-信息安全(八)-迪菲-赫尔曼(DH)密钥交换
概述 信息安全基本概念: DH(Diffie–Hellman key exchange,迪菲-赫尔曼密钥交换) DH 是一种安全协议,,一种确保共享KEY安全穿越不安全网络的方法,它是OAKLEY的一 ...
- 【转帖】oracle数据类型和对应的java类型
原文地址:http://otndnld.oracle.co.jp/document/products/oracle10g/102/doc_cd/java.102/B19275-03/datacc.ht ...
- 【12月21日】A股滚动市盈率PE历史新低排名
2010年01月01日 到 2018年12月21日 之间,滚动市盈率历史新低排名.上市三年以上的公司,2018年12月21日市盈率在300以下的公司. 1 - 厦门象屿(SH600057) - 历史新 ...
- python对象池模式
class QueueObject(): def __init__(self, queue, auto_get=False): self._queue = queue self.object = se ...
- [Artoolkit] Can I Use LGPL code for commercial application
这是一个比较普遍但又容易被忽略的问题. From: http://answers.google.com/answers/threadview/id/439136.html 假设背景: - want t ...
- akka pubsub example
测了一个小时的 Pubsub 模式,发现这个模式和自己预期的不太一样,具体表现在: 1. 当 subscriber 订阅了某个 topic 并附带 groupName 时,如果 publish 发布的 ...
- Python scipy 计算短时傅里叶变换(Short-time Fourier transforms)
计算短时傅里叶变换(STFT) scipy.signal.stft(x,fs = 1.0,window ='hann',nperseg = 256,noverlap = None,nfft = Non ...
- day3 三、基本数据类型和运算符
一.多行注释和单行注释 """ 多行注释 多行注释 多行注释 """ # 单行注释 # print('hello world') # pri ...
- 线段树 || BZOJ 1112: [POI2008]砖块Klo
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1112 题解: 希望有连续K柱的高度是一样的,就先把1~K的数扔进线段树(线段树的下标就是数值 ...
- GMM-实现聚类的代码示例
Matlab 代码: % GMM code function varargout = gmm(X, K_or_centroids) % input X:N-by-D data matrix % inp ...