本文共 21057 字,大约阅读时间需要 70 分钟。
Javaweb开发中的监听器,是用于
例如web的常见对象有:ServletContext、HttpServletRequest、HttpSession 监听它们的监听web常见对象
的。创建与销毁
、属性变化
、session绑定javaBean
。
事件:就是一个事情。
事件源:产生这个事情的源头。 监听器:用于监听指定的事件的对象。(关联事件和事件源) 注册监听:要想让监听器可以监听到事件产生,必须对其进行注册。若要监听ServletContext对象的创建与销毁 则需要写一个监听器类实现 ServletContextListener 接口
若要监听HttpSession对象的创建与销毁 则需要写一个监听器类实现 HttpSessionListener 接口 若要监听HttpServletRequest对象的创建与销毁 则需要写一个监听器类实现 ServletRequestListener 接口若要监听ServletContext对象的属性变化 则需要写一个监听器类实现 ServletContextAttributeListener 接口
若要监听HttpSession对象的属性变化 则需要写一个监听器类实现 HttpSessionAttributeListener 接口 若要监听HttpServletRequest对象的属性变化 则需要写一个监听器类实现 ServletRequestAttributeListener 接口若要监听javaBean对象是否绑定到了session域 则需要写一个javaBean实现 HttpSessionBindingListener 接口
若要监听javaBean对象的活化与钝化 则需要写一个javaBean实现 HttpSessionActivationListener 接口关于创建一个监听器的步骤:
1. 创建一个监听器类,实现指定的监听器接口。 2. 重写接口中的方法。 3. 在web.xml文件中对监听器进行注册
。 1. ServletContext对象的创建与销毁
ServletContext对象
是在服务器启动时创建的,在服务器关闭时销毁的。 package com.itheima.listener; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; public class MyServletContextListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { System.out.println("ServletContext对象被创建了"); // 服务器一启动,ServletContext对象就被创建了 } @Override public void contextDestroyed(ServletContextEvent sce) { System.out.println("ServletContext对象被销毁了"); // 服务器一关闭,ServletContext对象就被销毁了 } }
2. HttpSession对象的创建与销毁
HttpSession session = request.getSession(); Session的销毁方式 1. 默认超时30分钟后销毁 2. 关闭服务器时销毁 3. 调用invalidate()方法 4. setMaxInactiveInterval(int interval) 可以设置超时时间问题
:直接访问一个jsp页面时,是否会创建Session
? 答:会创建,因为我们默认情况下是在jsp页面中直接使用Session内置对象的。 package com.itheima.listener; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; public class MyHttpSessionListener implements HttpSessionListener { @Override public void sessionCreated(HttpSessionEvent se) { System.out.println("HttpSession对象被创建了"); } @Override public void sessionDestroyed(HttpSessionEvent se) { System.out.println("HttpSession对象被销毁了"); } }
index.jsp
<% session.invalidate(); // 手动使session销毁 %>
3. HttpServletRequest对象的创建与销毁
Request对象
是发送请求时服务器就会去创建它,当响应产生时,request对象就会被销毁。 package com.itheima.listener; import javax.servlet.ServletRequestEvent; import javax.servlet.ServletRequestListener; public class MyServletRequestListener implements ServletRequestListener { @Override public void requestDestroyed(ServletRequestEvent sre) { System.out.println("ServletRequest被销毁了"); } @Override public void requestInitialized(ServletRequestEvent sre) { System.out.println("ServletRequest被创建 了"); } }
在java的监听机制中,它的监听器中的方法都是有参数的
,参数就是事件对象
,而我们可以通过事件对象直接获取事件源。
package com.itheima.attributelistener; import javax.servlet.ServletRequestAttributeEvent; import javax.servlet.ServletRequestAttributeListener; public class MyServletRequestListener implements ServletRequestAttributeListener { @Override public void attributeAdded(ServletRequestAttributeEvent srae) { System.out.println("ServletRequest添加属性了"); } @Override public void attributeRemoved(ServletRequestAttributeEvent srae) { System.out.println("ServletRequest移除属性了"); } @Override public void attributeReplaced(ServletRequestAttributeEvent srae) { // 参数代表事件对象 System.out.println("ServletRequest替换属性了"); // srae.getServletRequest(); // 通过事件对象直接获取事件源 // System.out.println(srae.getName() + "\t" + srae.getValue()); // org.apache.catalina.ASYNC_SUPPORTED true } }
index.jsp
<% // session.invalidate(); // 手动使session销毁 request.setAttribute("name", "tom"); request.setAttribute("name", "luna"); request.removeAttribute("name"); %>
1.javaBean对象自动感知
被绑定到session中
HttpSessionBindingListener 这个接口是由javaBean实现的
,并且不需要在web.xml文件中进行注册。 package com.itheima.domain; import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionBindingListener; public class User implements HttpSessionBindingListener { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public void valueBound(HttpSessionBindingEvent event) { System.out.println("向session中绑定了一个user对象"); } @Override public void valueUnbound(HttpSessionBindingEvent event) { System.out.println("从session中将user对象移除"); } }
index.jsp
<% // session.invalidate(); // 手动使session销毁 // request.setAttribute("name", "tom"); // request.setAttribute("name", "luna"); // request.removeAttribute("name"); User user = new User(); user.setName("luna"); user.setAge(25); // 绑定到session session.setAttribute("u", user); // 解除绑定 session.removeAttribute("u"); %>
2.javabean对象可以活化或钝化到session中
HttpSessionActivationListener,如果javaBean实现了这个接口,那么当我们正常关闭服务器
时,session中的javaBean对象就会被钝化到我们指定的文件中
。 当下一次再启动服务器
,因为我们已经将对象写入到文件中,这时就会自动将javaBean对象活化到session中。 package com.itheima.domain; import java.io.Serializable; import javax.servlet.http.HttpSessionActivationListener; import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionBindingListener; import javax.servlet.http.HttpSessionEvent; public class User implements Serializable, HttpSessionBindingListener, HttpSessionActivationListener { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public void valueBound(HttpSessionBindingEvent event) { System.out.println("向session中绑定了一个user对象"); } @Override public void valueUnbound(HttpSessionBindingEvent event) { System.out.println("从session中将user对象移除"); } @Override public void sessionDidActivate(HttpSessionEvent se) { System.out.println("钝化"); } @Override public void sessionWillPassivate(HttpSessionEvent se) { System.out.println("活化"); } }
我们还需要个context.xml
文件来配置钝化时存储的文件,在META-INF目录下
创建一个context.xml文件,输入以下代码:
HttpSessionListener
,当session对象创建时,就将这个session对象装入到一个集合中,然后将集合List<HttpSession>
保存到ServletContext域
中。public long getLastAccessedTime()
,它可以得到session对象最后使用的时
间。然后可以使用invalidate方法销毁
。 我们上面的操作需要使用任务调度功能
。
在java中有一个Timer定时器类,定时器测试代码:
TestTimer.javapackage com.itheima.timer; import java.util.Date; import java.util.Timer; import java.util.TimerTask; public class TestTimer { public static void main(String[] args) { // 创建一个计时器对象 Timer t = new Timer(); // 调度任务功能 t.schedule(new TimerTask() { // 要执行的任务 @Override public void run() { System.out.println(new Date().toLocaleString()); } }, 5000, 1000); // 5秒后开始,每隔1秒执行一次 } }
监听ServletContext代码:
package com.itheima.test; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Timer; import java.util.TimerTask; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.http.HttpSession; public class MyServletContextListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { // 通过事件对象得到事件源(ServletContext) ServletContext application = sce.getServletContext(); // 创建一个集合用于存储所有的session对象(需要考虑并发问题,因为我们在web中,它一定是一个多线程的) final Listlist = Collections.synchronizedList(new ArrayList ()); // 把集合放到application域中 application.setAttribute("sessions", list); // 创建一个计时器对象 Timer t = new Timer(); // 调度任务 t.schedule(new TimerTask() { @Override public void run() { System.out.println("开始扫描了..."); for (Iterator iterator = list.iterator(); iterator.hasNext();) { HttpSession session = (HttpSession) iterator.next(); long l = System.currentTimeMillis() - session.getLastAccessedTime(); if (l > 5000) { // 如果时间大于5秒,就把session销毁 System.out.println("session移除了" + session.getId()); session.invalidate(); // 把session销毁 iterator.remove(); } } /* for (HttpSession session : list) { long l = System.currentTimeMillis() - session.getLastAccessedTime(); if (l > 5000) { // 如果时间大于5秒,就把session销毁 session.invalidate(); // 把session销毁 list.remove(session); // 从集合中移除 } } */ } }, 2000, 5000); // 延迟2秒后执行,每间隔5秒执行一次 } @Override public void contextDestroyed(ServletContextEvent sce) { } }
监听Session代码:
package com.itheima.test; import java.util.List; import javax.servlet.ServletContext; import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; public class MySessionListener implements HttpSessionListener { @Override public void sessionCreated(HttpSessionEvent se) { // 通过事件源得到HttpSession对象 HttpSession session = se.getSession(); // 得到application对象(ServletContext对象) ServletContext application = session.getServletContext(); // 得到session对象,并放入到list集合中 Listlist = (List ) application.getAttribute("sessions"); list.add(session); System.out.println("添加了" + session.getId()); } @Override public void sessionDestroyed(HttpSessionEvent se) { } }
配置注册监听代码:
com.itheima.test.MyServletContextListener com.itheima.test.MySessionListener
Javaweb中的
过滤器
可以拦截
所有访问web资源的请求
或响应
操作。
步骤:
1. 创建一个类实现Filter接口。 2. 重写接口中方法,其中doFilter方法是真正过滤用的。 3. 在web.xml文件中进行配置。注意
:在实现Filter接口的类中重写的doFilter方法内
如果没有执行这句代码chain.doFilter(request, response);
那么浏览器访问服务器的资源是不会被访问到的。 示例代码如下: package com.itheima.filter; 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; public class MyFilter1 implements Filter{ @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("MyFilter1拦截开始了"); // 对浏览器发送的请求进行过滤 // 放行 chain.doFilter(request, response); System.out.println("MyFilter1拦截结束了"); // 对服务器返回的响应进行过滤 } @Override public void destroy() { } }
在web.xml文件中进行配置。
day17_01_filter MyFilter1 com.itheima.filter.MyFilter1 MyFilter1 /* ServletDemo1 com.itheima.servlet.ServletDemo1 ServletDemo1 /servlet/demo1 index.html index.htm index.jsp default.html default.htm default.jsp
测试代码
测试代码一:http://localhost:8080/day17_01_filter/index.jsp 测试代码二:http://localhost:8080/day17_01_filter/servlet/demo1package com.itheima.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class ServletDemo1 extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("ServletDemo1"); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
FilterChain 是 servlet 容器(即服务器)为开发人员提供的对象,它提供了对某一资源的已过滤请求调用链
的视图。
问题
:怎样可以形成一个Filter链? 答:只要多个Filter对同一个资源
进行拦截就可以形成Filter链。 问题
:怎样确定Filter的执行顺序? 答:由web.xml中的<filter-mapping>
的先后顺序来确定。 示例代码如下图所示: Servlet的生命周期
:
第一次访问服务器资源的时候
,Servlet就进行实例化和初始化,只调用一次。(二者不同的地方) 只要应用在(即应用没有卸载),service服务就一直在。 只要应用卸载了或者服务器停止了,Servlet就销毁了。 Filter的生命周期
:
服务器启动时
,会创建Filter对象(即调用构造方法进行实例化),并调用init方法,只调用一次。(二者不同的地方) 当浏览器访问服务器资源时,路径与Filter的拦截路径匹配后,会执行Filter中的doFilter方法,这个方法是真正拦截操作的方法。 当服务器关闭或应用卸载时,会调用Filter中的destroy方法来进行销毁操作。 在Filter的init方法
上有一个参数,类型就是FilterConfig。
问题
:怎样在Filter中获取一个FIlterConfig对象呢? 答:如下图所示: 基本配置
Filter名称 Filter类的包名.类名 Filter名称 路径
关于其它配置
1.完全匹配 以"/demo1"开始,不包含通配符*。 目录匹配 以”/”开始 ,以*结束。 扩展名匹配 *.xxx 不能写成 /*.xxx。
2.它是对指定的servlet名称的servlet进行拦截的。
3.可以取的值有 REQUEST FORWARD ERROR INCLUDE 它的作用是:当以什么方式去访问web资源时,进行拦截操作。 1.REQUEST 当从浏览器直接访问资源,或是重定向到某个资源时进行拦截方式配置,它也是默认值。 2.FORWARD 它描述的是请求转发的拦截方式配置。 3.ERROR 如果目标资源是通过声明式异常处理机制调用时,那么该过滤器将被调用。除此之外,过滤器不会被调用。(了解即可) 4.INCLUDE 如果目标资源是通过RequestDispatcher的include()方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用。跟FORWORD差不多。(了解即可)
1、创建数据库与数据表
CREATE DATABASE day17; USE day17; CREATE TABLE USER( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(100), PASSWORD VARCHAR(100) ) INSERT INTO USER VALUES(NULL, "tom", "123");
2、自动登陆功能实现步骤
自动登录思路图:
在mysql中可以对数据进行md5加密,是不可逆(单向)加密。
Md5(字段) UPDATE USER SET PASSWORD=MD5(PASSWORD) WHERE id=1;
在java中也提供了md5加密。md5工具类(MD5Utils类)示例代码如下:
package com.itheima.util; import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; public class MD5Utils { /** * 使用md5的算法给某数据进行加密 */ public static String md5(String plainText) { byte[] secretBytes = null; try { secretBytes = MessageDigest.getInstance("md5").digest( plainText.getBytes()); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("没有md5这个算法!"); } String md5code = new BigInteger(1, secretBytes).toString(16); for (int i = 0; i < 32 - md5code.length(); i++) { md5code = "0" + md5code; } return md5code; } }
分析
:
怎样可以做成一个通用的,可以处理post、get所有的请求的?
在java中怎样可以对一个方法进行功能增强
?
问题
:我们获取请求参数有以下方法:
getParameter
与 getParameterValues
方法可以依赖于 getParamterMap
方法来实现。 login.jsp LoginServlet.java
package com.itheima.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class LoginServlet extends HttpServlet { @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // request.setCharacterEncoding("UTF-8"); // 每一个servlet都写这句代码,不好 String name = request.getParameterValues("username")[0]; String name1 = request.getParameter("username1"); System.out.println(name); System.out.println(name1); } @Override public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
web.xml
day17_03_Globalfilter MyFilter com.itheima.filter.MyFilter MyFilter /* LoginServlet com.itheima.servlet.LoginServlet LoginServlet /servlet/loginServlet index.html index.htm index.jsp default.html default.htm default.jsp
MyFilter.java
package com.itheima.filter; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.Map; 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.HttpServletRequestWrapper; // import javax.servlet.http.HttpServletResponse; public class MyFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; // HttpServletResponse resp = (HttpServletResponse) response; // req.setCharacterEncoding("UTF-8"); // 只能解决post的提交方式 // resp.setContentType("text/html; charset=utf-8"); req = new MyRequest(req); // 装饰一把(把处理乱码的动作放在装饰类代码里) chain.doFilter(req, response); } @Override public void destroy() { } } // 装饰设计模式 + 适配器 // 实现与被包装对象相同的接口 // 定义一个与被包装类相同对象的引用(字段) // 定义一个构造方法,把被包装的对象传过来 // 对于不需要改写方法,直接调用 // 对于需要改写方法,写自己的方法体 class MyRequest extends HttpServletRequestWrapper { HttpServletRequest request; public MyRequest(HttpServletRequest request) { super(request); // 是因为父类没有无参数构造 this.request = request; } // 一个一个的乱码处理,不好 // @Override public String getParameter(String name) { // name = request.getParameter(name); // 可能会出现乱码 // try { // return new String(name.getBytes("iso-8859-1"), "UTF-8"); // 处理乱码 // } catch (UnsupportedEncodingException e) { // e.printStackTrace(); // } // return null; // // } @Override public String getParameter(String name) { Mapmap = getParameterMap(); return map.get(name)[0]; } @Override public String[] getParameterValues(String name) { Map map = getParameterMap(); return map.get(name); } private boolean flag = true; @Override public Map getParameterMap() { // 得到原始的map集合 Map map = request.getParameterMap(); // 可能会出现乱码 if (flag) { // 处理乱码 // 将map集合中的String[]得到,解决每一个元素的乱码问题 for (Map.Entry m : map.entrySet()) { String[] values = m.getValue(); for (int i = 0; i < values.length; i++) { try { values[i] = new String(values[i].getBytes("iso-8859-1"), "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } } flag = false; } return map; } }