一、Listener監聽器 Javaweb開發中的監聽器,是用於監聽web常見對象 HttpServletRequest HttpSession ServletContext 監聽它們的創建與銷毀、屬性變化 以及session綁定javaBean 1、監聽機制 事件 就是一個事情 事件源 產生這個事 ...
一、Listener監聽器
Javaweb開發中的監聽器,是用於監聽web常見對象 HttpServletRequest HttpSession ServletContext
監聽它們的創建與銷毀、屬性變化 以及session綁定javaBean
1、監聽機制
- 事件 就是一個事情
- 事件源 產生這個事情的源頭
- 監聽器 用於監聽指定的事件的對象
- 註冊監聽 要想讓監聽器可以監聽到事件產生,必須對其進行註冊。
2、Javaweb開發中常見監聽器
2.1、監聽域對象的創建與銷毀
- 監聽ServletContext創建與銷毀 ServletContextListener
- 監聽HttpSession創建與銷毀 HttpSessionListener
- 監聽HttpServletRequest創建與銷毀 ServletRequestListener
2.2、監聽域對象的屬性變化
- 監聽ServletContext屬性變化 ServletContextAttributeListener
- 監聽HttpSession屬性變化 HttpSessionAttributeListener
- 監聽HttpServletRequest屬性變化 ServletRequestAttributeListener
2.3、監聽session綁定javaBean
它是用於監聽javaBean對象是否綁定到了session域 HttpSessionBindingListener
它是用於監聽javaBean對象的活化與鈍化 HttpSessionActivationListener
3、監聽器的快速入門
關於創建一個監聽器的步驟
- 創建一個類,實現指定的監聽器介面
- 重寫介面中的方法
- 在web.xml文件中對監聽器進行註冊。
- ServletContext對象的創建與銷毀
3.1、關於域對象創建與銷毀的演示
這個對象是在伺服器啟動時創建的,在伺服器關閉時銷毀的。
HttpSession對象的創建與銷毀
HttpSession session=request.getSession();
Session銷毀 的方法
- 預設超時 30分鐘
- 關閉伺服器
- invalidate()方法
- setMaxInactiveInterval(int interval) 可以設置超時時間
問題:直接訪問一個jsp頁面時,是否會創建session?
會創建,因為我們預設情況下是可以在jsp頁面中直接使用session內置對象的。
HttpServletRequest創建與銷毀
Request對象是發送請求伺服器就會創建它,當響應產生時,request對象就會銷毀。
3.2、演示了Request域對象中屬性變化
在java的監聽機制中,它的監聽器中的方法都是有參數的,參數就是事件對象,而我們可以通過事件對象直接獲取事件源。
3.3、演示session綁定javaBean
1、javaBean對象自動感知被綁定到session中.
HttpSessionBindingListener 這個介面是由javaBean實現的,並且不需要在web.xml文件中註冊.
2、javabean對象可以活化或鈍化到session中。
HttpSessionActivationListener如果javaBean實現了這個介面,那麼當我們正常關閉伺服器時,session中的javaBean對象就會被鈍化到我們指定的文件中。
當下一次在啟動伺服器,因為我們已經將對象寫入到文件中,這時就會自動將javaBean對象活化到session中。
我們還需要個context.xml文件來配置鈍化時存儲的文件
在meta-inf目錄下創建一個context.xml文件
<Context> <Manager className="org.apache.catalina.session.PersistentManager" maxIdleSwap="1"> <Store className="org.apache.catalina.session.FileStore" directory="it315"/> </Manager> </Context>
案例-定時銷毀session
1、怎樣可以將每一個創建的session全都保存起來?
我們可以做一個HttpSessionListener,當session對象創建時,就將這個session對象裝入到一個集合中.
將集合List<HttpSession>保存到ServletContext域中。
2、怎樣可以判斷session過期了?
在HttpSession中有一個方法public long getLastAccessedTime()
它可以得到session對象最後使用的時間,可以使用invalidate方法銷毀。
ps(我們上面的操作需要使用任務調度功能.)在java中有一個Timer定時器類
package com.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()); } }, 2000, 1000); } }
關於三個域對象獲取
如果在Servlet中要獲取request,在方法上就有,request.getSession() getServletContext();
如果我們有request對象了, request.getSession() request.getSession().getServletCotnext();
public class MySessionListener implements HttpSessionListener { public void sessionCreated(HttpSessionEvent arg0) { HttpSession session = arg0.getSession(); //得到application中的list集合 ServletContext application = session.getServletContext(); //得到session對象,並放入list集合 List<HttpSession> list =(List<HttpSession>) application.getAttribute("sessions"); list.add(session); System.out.println("添加了"+session.getId()); } public void sessionDestroyed(HttpSessionEvent arg0) { // TODO Auto-generated method stub } }
程式在使用時,需要考慮併發問題,因為我們在web中,它一定是一個多線程的,那麼我們的程式對集合進行了添加,還有移除操作。具體在MyServletContextListener的方法中如下
public class MyServletContextListener implements ServletContextListener { public void contextDestroyed(ServletContextEvent arg0) { } public void contextInitialized(ServletContextEvent arg0) { //通過事件原對象得到事件源 ServletContext application = arg0.getServletContext(); //創建一個集合 存儲所有session對象 final List<HttpSession> list = Collections.synchronizedList(new ArrayList<HttpSession>()); 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){ System.out.println("session移除了"+session.getId()); session.invalidate(); iterator.remove(); } } } }, 2000, 5000); } }
二、Filter
二、Filter過濾器(重要)
Javaweb中的過濾器可以攔截所有訪問web資源的請求或響應操作。
1.1、步驟:
- 創建一個類實現Filter介面
- 重寫介面中方法 doFilter方法是真正過濾的。
- 在web.xml文件中配置
註意:在Filter的doFilter方法內如果沒有執行chain.doFilter(request,response),那麼資源是不會被訪問到的。
1.2、FilterChain
FilterChain 是 servlet 容器為開發人員提供的對象,它提供了對某一資源的已過濾請求調用鏈的視圖。過濾器使用 FilterChain 調用鏈中的下一個過濾器,如果調用的過濾器是鏈中的最後一個過濾器,則調用鏈末尾的資源。
問題:怎樣可以形成一個Filter鏈?
只要多個Filter對同一個資源進行攔截就可以形成Filter鏈
問題:怎樣確定Filter的執行順序?
由<filter-mapping>來確定
1.3、Filter生命周期
Servlet生命周期:
實例化 --》 初始化 --》 服務 --》 銷毀
- 1 當伺服器啟動,會創建Filter對象,並調用init方法,只調用一次.
- 2 當訪問資源時,路徑與Filter的攔截路徑匹配,會執行Filter中的doFilter方法,這個方法是真正攔截操作的方法.
- 3 當伺服器關閉時,會調用Filter的destroy方法來進行銷毀操作.
1.4、FilterConfig
在Filter的init方法上有一個參數,類型就是FilterConfig.
FilterConfig它是Filter的配置對象,它可以完成下列功能
- 獲取Filtr名稱
- 獲取Filter初始化參數
- 獲取ServletContext對象。
問題:怎樣在Filter中獲取一個FIlterConfig對象?
1 package com.itheima.filter; 2 3 import java.io.IOException; 4 5 import javax.servlet.Filter; 6 import javax.servlet.FilterChain; 7 import javax.servlet.FilterConfig; 8 import javax.servlet.ServletException; 9 import javax.servlet.ServletRequest; 10 import javax.servlet.ServletResponse; 11 12 public class MyFilterConfigTest implements Filter{ 13 14 private FilterConfig filterConfig; 15 16 public void init(FilterConfig filterConfig) throws ServletException { 17 this.filterConfig = filterConfig; 18 } 19 20 public void doFilter(ServletRequest request, ServletResponse response, 21 FilterChain chain) throws IOException, ServletException { 22 //通過FilterConfig對象獲取到配置文件中的初始化信息 23 String encoding = filterConfig.getInitParameter("encoding"); 24 System.out.println(encoding); 25 request.setCharacterEncoding(encoding); 26 //放行 27 chain.doFilter(request, response); 28 } 29 30 public void destroy() { 31 // TODO Auto-generated method stub 32 } 33 }
如下 web.xml配置
<filter>
<filter-name>MyFilterConfigTest</filter-name>
<filter-class>com.itheima.filter.MyFilterConfigTest</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>MyFilterConfigTest</filter-name>
<servlet-name>ServletDemo2</servlet-name>
</filter-mapping>
1.5、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()方法訪問時,那麼該過濾器將被調用。除此之外,該過濾器不會被調用
三、自動登錄 (PS bean的屬性名和資料庫里的欄位名千萬要一樣哦 不然會emmm 很慘)
- 當用戶登陸成功後,判斷是否勾選了自動登陸,如果勾選了,就將用戶名與密碼持久化存儲到cookie
- 做一個Filter,對需要自動登陸的資源進行攔截
首寫要在登錄Servlet中登錄成功時把登錄的信息保存在cookie里,並設置存活時間
if(null != user){ String autologin = request.getParameter("autologin"); Cookie cookie = new Cookie("user", user.getUsername()+"&"+user.getPassword()); cookie.setPath("/"); if(autologin != null){// on / null cookie.setMaxAge(60*60*24*7); }else{ cookie.setMaxAge(0); } response.addCookie(cookie); request.getSession().setAttribute("user", user); request.getRequestDispatcher("/home.jsp").forward(request, response); }
然後設置好filter類 記住要在web.xml裡面配置 如下 為 doFilter代碼
public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2) throws IOException, ServletException { //1、轉換對象 HttpServletRequest req = (HttpServletRequest) arg0; String uri = req.getRequestURI(); String path = req.getContextPath(); path = uri.substring(path.length()); if(!("/login.jsp".equals(path)||"/servlet/loginServlet".equals(path))){ User user = (User) req.getSession().getAttribute("user"); if(user == null){ //2、處理業務 Cookie[] cookies = req.getCookies(); String username = ""; String password = ""; for (int i = 0;cookies!=null && i < cookies.length; i++) { if("user".equals(cookies[i].getName())){ String value = cookies[i].getValue(); String[] values = value.split("&"); username = values[0]; password = values[1]; } } UserService us = new UserService(); User u = us.findUser(username, password); if(u != null){ req.getSession().setAttribute("user", u); } } } //3、放行 arg2.doFilter(arg0, arg1); }
ps 當訪問別的頁面時通過判斷session時減少訪問資料庫的操作
四、MD5加密
在mysql中可以對數據進行md5加密 Md5(欄位)
UPDATE USER SET PASSWORD=MD5(PASSWORD);
需要一個MD5Utils工具類
package com.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; } }
這樣在loginservlet調用一下方法轉化一下就ok了。
五、全局的編碼過濾器
之前做的操作,只能對post請求是ok
怎樣可以做成一個通用的,可以處理post,get所有的請求的?
在java中怎樣可以對一個方法進行功能增強?
- 繼承
- 裝飾設
- 創建一個類讓它與被裝飾類實現同一個介面或繼承同一個父類
- 在裝飾類中持有一個被裝飾類的引用
- 重寫要增強的方法
我們可以用getparameterMap這個方法來實現getparameter和getparametervalues兩個方法 即MyFilter 最終版本
package com.filter; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; 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 destroy() { // TODO Auto-generated method stub } public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) arg0; req = new MyRequest(req); arg2.doFilter(req, arg1); } public void init(FilterConfig arg0) throws ServletException { // TODO Auto-generated method stub } } class MyRequest extends HttpServletRequestWrapper{ private 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) { 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; //防止重覆 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; } }