昨天和大家介紹了一下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; } }


