1. 程式人生 > >過濾器 & 裝飾者模式

過濾器 & 裝飾者模式

自動 設計模式 加載 null type nbsp list 正文 多個

一.過濾器概述
------------------------------------------------
1.1.什麽是過濾器?
Servlet技術規範中, 定義了Servlet、Filter、Listener三門技術, 其中Filter也叫做過濾器,通過過濾器技術,開發人員可以實現用戶在訪問某個資源之前或之後,對訪問的請求和響應進行攔截,從而做一些相關的處理。

過濾器:
◇ 所謂的過濾器, 就是攔截用戶對資源的訪問
◇ 一個過濾器可以攔截多個資源, 一個資源也可以配置多個過濾器進行攔截
◇ 其實所謂的攔截, 就是將代表請求的request對象和代表響應的response對象攔截下來, 攔截下來後:
◇ 控制是否允許訪問 -- 用戶登陸之後才能查看自己的訂單頁面
◇ 在訪問資源之前或之後做一些處理 比如: 全站亂碼解決
...

===================================================
二.開發過濾器
------------------------------------------------
2.1.開發過濾器的步驟
Servlet API中提供了一個Filter接口, 開發web應用時, 如果編寫一個類實現了這個接口, 則這個類就是一個過濾器
(1) 寫一個類實現Filter接口, 並實現其中的方法
(2) 在web應用的web.xml中配置過濾器

------------------------------------------------
~~2.2.Filter生命周期:
當服務器啟動時, web應用加載後會立即創建出當前web應用中的所有的Filter對象, 創建出來後, 立即調用init方法進行初始化出操作. 從此以後這個Filter對象一直駐留在內存中為後續所攔截的請求服務, 每次過濾到對資源的訪問時, 都會執行doFilter這個方法進行攔截處理, 直到服務器關閉或者web應用移出容器為止, 隨著web應用的銷毀, 過濾器也跟著銷毀, 在銷毀之前會調用destroy方法執行善後的處理.

------------------------------------------------
2.3.配置過濾器
<filter> -- 配置一個過濾器
<filter-name>FilterDemo1</filter-name>
-- 過濾器的名字
<filter-class>cn.tedu.FilterDemo1</filter-class> -- 過濾器處理類的全路徑名
</filter>

<filter-mapping> -- 為指定的過濾器配置要攔截的路徑, 一個過濾器可以配置多個<filter-mapping>
<filter-name>FilterDemo1</filter-name> -- 過濾器的名字
<servlet-name>ServletDemo1</servlet-name> -- 攔截哪個名字的Servlet, 可以配置多個
<url-pattern>/servlet/*</url-pattern> -- 要攔截的路徑, 路徑的寫法和Servlet的<url-pattern>寫法一致, 可以配置多個
<dispatcher>REQUEST</dispatcher> -- 配置攔截哪種方式的對資源的訪問, 可以取值為REQUEST/FORWARD/INCLUDE/ERROR
REQUEST:默認,普通請求,最常用
FORWARD:所攔截的資源是通過請求轉發訪問的
INCLUDE:所攔截的資源是通過頁面包含訪問的
ERROR:所攔截的資源通過異常機制訪問的
</filter-mapping>

------------------------------------------------
2.4.Filter中的方法介紹
--------------------------------------------
init(FilterConfig filterConfig)
FilterConfig -- 代表當前Filter在web.xml中配置信息的對象
通過這一對象可以獲取當前過濾器在web.xml配置的初始化參數
通過這一對象可以獲取代表當前web應用的ServletContext對象
獲取初始化參數:
getInitParameter(String name);
getInitParameterNames()
獲取ServletContext對象
getServletContext();

--------------------------------------------
doFilter(request, response, FilterChian filterChian)
FilterChian -- 過濾器鏈
一個web資源可以被多個過濾器所攔截, 多個過濾器攔截的順序是按照Filter在web.xml中配置的<filter-mapping>的順序執行的.這多個過濾器按照攔截的順序就組成了一個攔截器鏈, 用FilterChian表示.

如果一個過濾器處理完所攔截的請求後, 想要執行後面的攔截器, 則可以調用FilterChian上doFilter方法, 表示放行過濾器, 接著執行下一個節點
如果下一個節點仍然是過濾器, 則接著進行過濾, 執行的過程同上
如果沒有後續的過濾器, 則執行真正的資源處理這次請求

--------------------------------------------
destroy()


===================================================
三.過濾器的應用
------------------------------------------------
3.1.全站亂碼解決過濾器
(1).創建EncodingFilter過濾器類, 實現過濾器接口(Filter)
詳細代碼參考: EncodingFilter.java

(2).在web.xml中配置過濾器
<!-- 配置全站亂碼解決過濾器 -->
<filter>
<filter-name>EncodingFilter</filter-name>
<filter-class>cn.tedu.filter.EncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>EncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

------------------------------------------------
3.2.自動登陸過濾器實現
(1).創建AutoLoginFilter過濾器類, 實現過濾器接口(Filter)
詳細代碼參考: AutoLoginFilter.java

(2).在web.xml中配置過濾器
<!-- 配置自動登陸過濾器 -->
<filter>
<filter-name>AutoLoginFilter</filter-name>
<filter-class>cn.tedu.filter.AutoLoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>AutoLoginFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

(3).在LoginServlet中, 實現30天自動登陸, 將用戶名和密碼保存進Cookie
if("true".equals(request.getParameter("autologin"))){
//實現30天自動登陸
Cookie cookie = new Cookie("autologin", username+":"+password);
cookie.setPath(request.getContextPath()+"/");
cookie.setMaxAge(3600*24*30);
response.addCookie(cookie);
}
===================================================
四.//案例--裝飾者模式在亂碼處理中的應用

public class EncodingFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("全站亂碼解決過濾器初始化成功...");
}

public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
//1.解決響應正文亂碼
response.setContentType("text/html;charset=utf-8");

//2.解決請求參數亂碼 -- (利用裝飾設計模式對request對象進行包裝)
HttpServletRequest myReq = new MyHttpServletRequest((HttpServletRequest)request);

//3.放行過濾器
chain.doFilter(myReq, response);
}

public void destroy() {

}
}

class MyHttpServletRequest extends HttpServletRequestWrapper{
//將request對象保存在類的內部
private HttpServletRequest request;

//定義flag, 控制getParameterMap()方法中map的遍歷次數
private boolean flag = true;

public MyHttpServletRequest(HttpServletRequest request) {
super(request);//這行代碼千萬不要省寫!!!
this.request = request;
}

public String getParameter(String name) {
return getParameterValues(name) == null ? null : getParameterValues(name)[0];
}

public String[] getParameterValues(String name) {
return (String[]) getParameterMap().get(name);
}

public Map getParameterMap() {
try {
String method = request.getMethod();
if("POST".equals(method)){//--POST提交
request.setCharacterEncoding("utf-8");
return request.getParameterMap();
}else if("GET".equals(method)){
//手動編解碼解決亂碼問題!
Map<String, String[]> map = request.getParameterMap();
if(flag){
for(Map.Entry<String, String[]> entry : map.entrySet()){
String[] vs = entry.getValue();
for(int i=0; i<vs.length; i++){
vs[i] = new String(vs[i].getBytes("iso8859-1"), "utf-8");
}
}
flag = false;
}
return map;
}else{
return request.getParameterMap();
}

} catch (UnsupportedEncodingException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}

=================================================

五.裝飾類詳解

1. request繼承結構

ServletRequest(接口)

|-- HttpServletRequest(接口)

|-- 匿名實現類(xxx) 實例: request對象

2. ServletRequestWrapper裝飾類

request對象 --> 被裝飾者

ServletRequestWrapper--> 裝飾類

1.ServletRequestWrapper裝飾類 和 被裝飾者(request對象)所屬的類(xxx)實現了同一個接口(ServletRequest)

2.提供了構造方法允許將被裝飾者傳入並保存在了類的內部

3.對於不想改造的方法直接調用已有對象上的方法, 對於想要改造的方法直接進行改造(沒有對任何方法進行改造), 如:

3. HttpServletRequestWrapper裝飾類

request對象 --> 被裝飾者

HttpServletRequestWrapper -- 裝飾類

HttpServletRequestWrapper類繼承了ServletRequestWrapper裝飾類類, 所以HttpServletRequestWrapper也是一個裝飾類!!

HttpServletRequestWrapper類沒有直接去包裝request對象, 而是先將當前構造方法中的request對象傳給父類(ServletRequestWrapper), 讓父類進行包裝, 再繼承父類中包裝後的方法。

而對於自身獨有的方法, 自己再進行包裝: 通過父類提供的方法(super.getRequest()) 獲取 包裝後的request對象, 並強制轉型為 HttpServletRequest, 並通過提供 _getHttpServletRequest 方法, 方便在當前類的內部使用, 代碼如下:

對於HttpServletRequestWrapper類中所有的方法, 直接調 super.getRequest() 對象 -- 即被父類包裝後的request對象上的方法

也就是說, 對於HttpServletRequestWrapper裝飾類, 是向將自己構造方法中的request對象傳給父類(方便父類進行包裝), 再通過super.getRequest(); 獲取父類中包裝的request對象(目的是保證自己和父類包裝的是同一個request)

接下來對內部的方法進行包裝, 即HttpServletRequestWrapper類中的方法分為兩類: 第一類是通過父類繼承過來的(父類對於這行方法已經進行包裝), 第二類是自己獨有的方法, 在自身類的內部進行包裝!!























過濾器 & 裝飾者模式