昨天和大家介绍了一下JSON的用法,其实JSON中主要是用来和数据库交互数据的。今天给大家讲解的是Filter和Listener的用法。
一、Listenner监听器
1.1、定义
Javaweb中的监听器是用于监听web常见对象HttpServletRequest,HttpSession,ServletContext。
1.2、监听器的作用
监听web对象创建与销毁.
监听web对象的属性变化
监听session绑定javaBean操作.
1.3、监听机制中的概念
事件----一件事情
事件源---产生这件事情的源头
注册监听---将监听器与事件绑定,当事件产生时,监听器可以知道,并进行处理。
监听器---对某件事情进行处理监听的一个对象
二、JavaWeb中常见的监听器
2.1、创建一个监听器的步骤
创建一个类,实现需要监听器的接口
重写接口中的方法
在web.xml中配置注册该监听器
2.2、监听域对象的创建与销毁
监听ServletContext创建与销毁 ServletContextListener
监听HttpSession创建与销毁 HttpSessionListener
监听HttpServletRequest创建与销毁 ServletRequestListener
下面为大家展示的实例,首先我们的实例在jsp中进行的话,我们要明确这三个域对象什么时候被jsp创建什么时候又被销毁呢。
在ServletContext中是服务器开启的时候就被创建,当服务器关闭的时候就被销毁
在session中,jsp里的page指令中可以配置默认是ture的,所以当你加载这个jsp页面的时候session就被创建了,而销毁的话就有
四种方法:默认超时 30分钟、关闭服务器、invalidate()方法、setMaxInactiveInterval(int interval) 可以设置超时时间,在servlet中
需要通过request.getSseeion()来创建session。
在request中,发送请求服务器就会创建它,当响应产生时,request对象就会销毁。在jsp页面的话,你加载开始创建,加载完就销毁了。
2.2.1、实例:监听域对象的创建与销毁
这里只演示一个,其他的两个同理都是这样子操作的。
HttpServletListener监听器
a.创建类,实现HttpServletListener接口,并重写方法
package test; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; public class MyHttpSessionListener implements HttpSessionListener { @Override public void sessionCreated(HttpSessionEvent arg0) { System.out.println("session被创建了"); } @Override public void sessionDestroyed(HttpSessionEvent arg0) { System.out.println("session被销毁了"); } }
b.在web.xml中的配置
c.在newFile.jsp中测试
注:在jsp中我添加了invalidate方法来测试销毁
d.结果
2.3、监听域对象的属性变化
监听ServletContext属性变化 ServletContextAttributeListener
监听HttpSession属性变化 HttpSessionAttributeListener
监听HttpServletRequest属性变化 ServletRequestAttributeListener
2.3.1、实例:监听域对象的实例变化
a.创建一个MyServletRequest类继承ServletRequestAttributeListener接口,并实现改接口方法
package test; import javax.servlet.ServletRequestAttributeEvent; import javax.servlet.ServletRequestAttributeListener; public class MyServletRequest implements ServletRequestAttributeListener{ @Override public void attributeAdded(ServletRequestAttributeEvent arg0) { System.out.println("requestAttribute添加了属性"); } @Override public void attributeRemoved(ServletRequestAttributeEvent arg0) { // TODO Auto-generated method stub System.out.println("requestAttribute被移除了"); } @Override public void attributeReplaced(ServletRequestAttributeEvent arg0) { // TODO Auto-generated method stub System.out.println("requestAttribute被替换了"); System.out.println(arg0.getName()+arg0.getValue()); } }
b.在web.xml中注册该监听器
<listener>
<listener-class>test.MyServletRequest</listener-class>
</listener>
c.在jsp测试一下
结果:
当我们在添加一个removeAttribute()方法时
2.4、监听session绑定javabean
2.4.1、HttpSessionBindingListener(这个是用javabean去实现的,所以不需要去注册)
用于监听javaBean对象是否绑定到了session域
创建一个User去实现HttpSessionBindingListener接口
package test; 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 arg0) { System.out.println("User对象被session绑定了!"); } @Override public void valueUnbound(HttpSessionBindingEvent arg0) { System.out.println("User对象解除session绑定了!"); } }
在jsp页面中测试
结果:
注意:不需要再web.xml中去配置该监听器,在执行setAttrubute()方法的时候自动去识别该监听器
2.4.2.HttpSessionActivationListener
用于监听javaBean对象的活化与钝化。
HttpSessionActivationListener如果javaBean实现了这个接口,那么当我们正常关闭服务器时,session中的javaBean对象就会被钝化到我们指定的文件中。
当下一次在启动服务器,因为我们已经将对象写入到文件中,这时就会自动将javaBean对象活化到session中。
首先我们要明白什么是活化与钝化等下单独写一篇文章介绍活化与钝化
我们还需要个context.xml文件来配置钝化时存储的文件
<Context> <Manager className="org.apache.catalina.session.PersistentManager" maxIdleSwap="1"> <Store className="org.apache.catalina.session.FileStore" directory="it315"/> </Manager> </Context>
三、Filter
接下来为大家讲解的是Filter,这个相对于Listener来说就重要的多了,它的应用非常的广泛,主要是起到一个过滤、拦截的作用。
3.1、Filter的定义
Javaweb中的过滤器可以拦截所有访问web资源的请求或响应操作。执行过滤任务的对象,这些任务是针对对某一资源(servlet 或静态内容)的请求或来自某一资源的响应执行的,抑或同时针对这两者执行。
3.2、怎么创建一个过滤器
创建一个类实现Filter接口
重写接口中方法 doFilter方法是真正过滤的
在web.xml文件中配置
注意:在Filter的doFilter方法内如果没有执行chain.doFilter(request,response)那么资源是不会被访问到的。
3.3、实例
首先我创建了一个MyFilter.java实现Filter接口
package com.jxlg.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 MyFilter implements Filter{ @Override public void destroy() { } @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { System.out.println("doFilter执行了"); chain.doFilter(req, res); //放行 System.out.println("执行完返回到客户端"); } @Override public void init(FilterConfig arg0) throws ServletException { } }
在web.xml中配置过滤器
<filter> <filter-name>MyFilter</filter-name> <filter-class>com.jxlg.filter.MyFilter</filter-class> </filter> <filter-mapping> <filter-name>MyFilter</filter-name> <url-pattern>/demo1</url-pattern> </filter-mapping>
写一个servlet让MyFilter进行过滤
结果
3.4、FilterChain
FilterChain 是 servlet 容器为开发人员提供的对象,它提供了对某一资源的已过滤请求调用链的视图。过滤器使用 FilterChain 调用链中的下一个过滤器,如果调用的过滤器是链中的最后一个过滤器,则调用链末尾的资源。
问题:怎样可以形成一个Filter链?
只要多个Filter对同一个资源进行拦截就可以形成Filter链
问题:怎样确定Filter的执行顺序?
由<filter-mapping>来确定,在web.xml文件中哪一个过滤器的<filter-mapping>放到了前面谁就先执行。
3.5、Filter的生命周期
Servlet生命周期:
实例化 --》 初始化 --》 服务 --》 销毁
当服务器启动,会创建Filter对象,并调用init方法,只调用一次.
当访问资源时,路径与Filter的拦截路径匹配,会执行Filter中的doFilter方法,这个方法是真正拦截操作的方法.
当服务器关闭时,会调用Filter的destroy方法来进行销毁操作.
3.6、FilterConfig
在Filter的init方法上有一个参数,类型就是FilterConfig.
FilterConfig它是Filter的配置对象,它可以完成下列功能
1.获取Filtr名称
2.获取Filter初始化参数
3.获取ServletContext对象。
怎么获取到一个filterConfig对象
fiterConfig中的方法
实例:获取web.xml文件中的字符编码,用来过滤,告知服务器请求用的是什么编码。
package com.jxlg.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 MyFilterConfig implements Filter { private FilterConfig filterConfig; @Override public void destroy() { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { //通过filterConfig来获取配置信息中的初始化参数 String encoding = filterConfig.getInitParameter("encoding"); request.setCharacterEncoding(encoding); chain.doFilter(request, response);//放行 } @Override public void init(FilterConfig filterConfig) throws ServletException { this.filterConfig = filterConfig; } }
web.xml中的配置信息
<filter> <filter-name>MyFilterConfig</filter-name> <filter-class>com.jxlg.filter.MyFilterConfig</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>MyFilterConfig</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
3.7、Filter配置
基本配置
<filter>
<filter-name>filter名称</filter-name>
<filter-class>Filter类的包名.类名</filter-class>
</filter>
<filter-mapping>
<filter-name>filter名称</filter-name>
<url-pattern>路径</url-pattern>
</filter-mapping>
关于其它配置
1.<url-pattern>
完全匹配 以”/demo1”开始,不包含通配符*
目录匹配 以”/”开始 以*结束
扩展名匹配 *.xxx 不能写成/*.xxx
2.<servlet-name>
它是对指定的servlet名称的servlet进行拦截的。
3.<dispatcher>
可以取的值有 REQUEST FORWARD ERROR INCLUDE
它的作用是:当以什么方式去访问web资源时,进行拦截操作.
1.REQUEST 当是从浏览器直接访问资源,或是重定向到某个资源时进行拦截方式配置的 它也是默认值
2.FORWARD 它描述的是请求转发的拦截方式配置
3.ERROR 如果目标资源是通过声明式异常处理机制调用时,那么该过滤器将被调用。除此之外,过滤器不会被调用。
4.INCLUDE 如果目标资源是通过RequestDispatcher的include()方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用。
四、使用Fliter实现自动登录
这个例子就是一个简单的登录验证与数据库交互,我们使用Fliter来实现自动登录。
思路分析:
login.jsp
${msg} <form action="${pageContext.request.contextPath }/servlet/findUserServlet" action="post"> 用户名:<input type="text" name="username" /></br> 密码:<input type="password" name="password" /></br> <input type="checkbox" name="autoLogin" />自动登陆</br> <input type="submit" value="提交"/> </form>
FindUserServlet.java
这里获取了表单提交的数据,然后如果按了自动登录,我们就会把用户名和密码保存到cooki中去
package com.jxlg.web.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.jxlg.web.domain.User; import com.jxlg.web.service.UserService; public class FindUserServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获取表单数据 String username = request.getParameter("username"); String password = request.getParameter("password"); //处理业务逻辑 UserService us = new UserService(); User user = us.findUser(username,password); if(user!=null){ String autoLogin = request.getParameter("autoLogin"); Cookie cookie = new Cookie("user",user.getUsername()+"&"+user.getPassword()); cookie.setPath("/"); if(autoLogin!=null){//将用户名和密码保存到cookie中 cookie.setMaxAge(60*60*24*7); }else{//清除cookie cookie.setMaxAge(0); } response.addCookie(cookie); //把cookie保存到客户端 request.getSession().setAttribute("u", user); request.getRequestDispatcher("/home.jsp").forward(request, response); }else{ request.setAttribute("msg", "用户名或密码输入错误,请重新登陆"); request.getRequestDispatcher("/login.jsp").forward(request, response); } } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
UserService
这里是service业务层代码
ackage com.jxlg.web.service; import java.sql.SQLException; import com.jxlg.Dao.UserDao; import com.jxlg.web.domain.User; public class UserService { UserDao ud = new UserDao(); public User findUser(String username, String password) { try { return ud.findUser(username,password); } catch (SQLException e) { e.printStackTrace(); } return null; } }
UserDao
这里是DAO实现类,与数据库交互,我这里是用的是C3P0连接池,和dbUtils与数据库交互
package com.jxlg.Dao; import java.sql.SQLException; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.BeanHandler; import com.jxlg.util.C3P0Util; import com.jxlg.web.domain.User; public class UserDao { public User findUser(String username, String password) throws SQLException { QueryRunner qr = new QueryRunner(C3P0Util.getDateSource()); return qr.query("select * from user where username=? and password=?", new BeanHandler<User>(User.class),username,password); } }
home.jsp
登录成功跳转的界面
<body>
欢迎你:${u.username }
</body>
MyFilter
这是一个过滤器,记住一定要去web.xml配置,如果我们在longin.jsp中选择了自动登录的话,我们可以直接访问home.jsp实现自动登录。
package com.jxlg.web.domain; 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; import javax.servlet.http.HttpServletResponse; import com.jxlg.web.service.UserService; public class MyFilter implements Filter{ @Override public void destroy() { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain Chain) throws IOException, ServletException { //转化两个对象 HttpServletRequest req = (HttpServletRequest)request; HttpServletResponse res = (HttpServletResponse) response; String uri = req.getRequestURI(); // System.out.println(uri); ///StudentLogin/login.jsp String path = req.getContextPath(); path = uri.substring(path.length()); // System.out.println(path); ///login.jsp if("/login.jsp".equals(path)||"/servlet/findUserServlet".equals(path)){ User user = (User) req.getSession().getAttribute("u"); //如果用户没有登录过,我们就执行登录操作。 if(user==null){ //得到cookies数组 Cookie[] cookies = req.getCookies(); String username=""; String password=""; //从cookie中找到想要的user对象 for(int i=0;cookies!=null&&i<cookies.length;i++){ if("user".equals(cookies[i].getName())){ String value = cookies[i].getValue(); //tom&123 String[] values = value.split("&"); username = values[0]; password = values[1]; } } //执行登录操作 UserService us = new UserService(); User u = us.findUser(username, password); //如果登录成功,把user保存到session中 if(user!=null){ req.getSession().setAttribute("u", u); } } } //放行 Chain.doFilter(request, response); }
开发中遇到的问题
1.在写MyFilter中,在遍历保存在客户端的cookie的时候,我没有家伙写cookies!=null,这样会导致空指针异常
2.在使用HttpServletRequest时,记住在Filter中一定要记住,进行转化,因为在Filter中是ServletRequest对象。
3.因为我在Filter中web.xml文件中配置的url-pattern是当前应用的所有url,所以当我们请求的每一个资源都会调用Filter进行过滤,但是我们并不需要在login.jsp等也自动登录,所以就记得要排除掉这些。
提供源码,收好不谢:
链接:http://pan.baidu.com/s/1c1K6UJ2 密码:mewe
五、全局编码的过滤器
前言:
使用过滤器对post请求的乱码进行过滤,测试之后是没有问题的,但是当我们是get请求的时候,我们该怎么去处理呢?
怎样可以做成一个通用的,可以处理post,get所有的请求的?
在java中怎样可以对一个方法进行功能增强?
1.继承
2.装饰设计模式
1.创建一个类让它与被装饰类实现同一个接口或继承同一个父类
2.在装饰类中持有一个被装饰类的引用
3.重写要增强的方法
分析:我们获取请求参数无非就三种方式:getParameter、getParameterValues、getParameterMap.
我们知道getParameter与getParameterValue方法可以依赖于getParamterMap方法来实现。
login.jsp
<body> <form action="${pageContext.request.contextPath }/servlet/loginServlet" method="get"> username:<input type="text" name="username"/><br/> username:<input type="text" name="username1"/><br/> <input type="submit" value="登录" /><br/> </form> </body>
LoginServlet
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String name1 = request.getParameter("username1"); String name = request.getParameterValues("username")[0]; System.out.println(name); System.out.println(name1); }
MyFilter
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; public class MyFilter implements Filter { public void init(FilterConfig filterConfig) throws ServletException { } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; // 解决post方式 // req.setCharacterEncoding("UTF-8"); req = new MyRequest(req); chain.doFilter(req, response); } public void destroy() { } } // 实现与被包装对象相同的接口 // 定义一个与被包装类相对象的引用 // 定义一个构造方法,把被包装对象传过来 // 对于不需要改写方法,直接调用 // 对于无需要改写方法,写自己的方法体 class MyRequest extends HttpServletRequestWrapper { HttpServletRequest request; // 是用于接收外部传递的原始的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) { Map<String, String[]> map = getParameterMap(); return map.get(name)[0]; } @Override public String[] getParameterValues(String name) { Map<String, String[]> map = getParameterMap(); return map.get(name); } private boolean flag = true; @Override public Map<String, String[]> getParameterMap() { Map<String, String[]> map = request.getParameterMap();// 乱码 if (flag) { for (Map.Entry<String, String[]> 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; } }