1. 程式人生 > >Javaweb學習筆記——(二十一)——————過濾器

Javaweb學習筆記——(二十一)——————過濾器

過濾器     過濾器概述         1.什麼是過濾器:             過濾器javaweb三大元件之一,它與Serlvet很相似,不過它過濾器是用來攔截請求的,而不是處理             請求的。             當用戶請求某個Servlet時,會先執行部署在這個請求上的Filter,如果Filter“放行”,那麼會繼承             執行使用者請求的Servlet;如果Filter不“放行”,那麼就不會執行使用者請求的Servlet。             其實可以這樣理解,當用戶請求某個Servlet時,Tomcat會去執行註冊在這個請求上的Filter,             然後是否“放行”由Filter來決定。可以理解為:Filter來決定是否呼叫Servlet,當執行完成Servlet             的程式碼後,還會執行Filter後面的程式碼。

        2.過濾器之Hello World             其實過濾器與Servlet很相似,我們回憶一下,如果寫的第一個Servlet應用,寫一個類,實現Servlet             介面,沒錯,寫過濾器就是寫一個類,實現Filter介面。             public class HelloFilter implements Filter {                 public void init (FilterConfig filterConfig) throws ServletException {}                 public void doFilter (ServletRequest request, ServletResponse response,                         FilterChain chain) throws IOException, ServletException {                     System.out.println("Hello Filter");                 }                 public void destroy () {}             }

            第二步也與Servlet一樣,在Web.xml檔案中部署Filter              <filter>                   <filter-name>helloFilter</filter-name>                   <filter-class>cn.itcast.filter.HelloFilter</filter-class>               </filter>               <filter-mapping>                   <filter-name>helloFilter</filter-name>                   <url-pattern>/index.jsp </url-pattern>               </filter-mapping>

            當用戶訪問index.jsp頁面時,會執行HelloFilter的doFilter()方法,在我們的例項中,index.jsp             頁面是不會執行的,如果想執行index.jsp頁面,那麼我們需要放行:                     public void doFilter(ServletRequest request, ServletResponse response,                             FilterChain chain) throws IOException, ServletException {                         System.out.println("filter start...");                          chain.doFilter(request, response);                          System.out.println("filter end...");                      }

            錯誤的認為,一個請求在給客戶端輸出之後就算是結束了,這是不對的!其實很多事情都需要在給客戶端響應之後才能完成!

    過濾器詳細         1.過濾器的生命週期             學習過Servlet的生命週期,那麼Filter的生命週期也是類似                 *init(FilterConfig):在伺服器啟動時會建立Filter例項,並且每個型別的Filter只建立一個例項,                 從此不再建立。在建立完Filter例項後,會馬上呼叫init()方法完成初始化工作,這個方法只會                 被執行一次;                 *doFilter(ServletRequest request, ServletResponse response, FilterChain chain):這個方法會在使用者                 沒次訪問“目標資源(<url-pattern>index.jsp</url-pattern>)”時執行,如果需要“放行”,那麼需要呼叫FilterChain的                 doFilter(ServletRequest, ServletResponse)方法,如果不呼叫FilterChain的doFilter()方法,那麼目標資源將無法執行;                 *destroy()伺服器會在建立Filter物件之後,把Filter放到快取中一直使用,通常不會銷燬                 它。一般會在伺服器關閉時銷燬Filter物件,在銷燬Filter物件之前,伺服器會呼叫Filter                 物件的destory()方法。

        2.FilterConfig             Filter介面中的init()方法的引數型別為FilterConfig型別。它的功能與ServletConfig相似,與web.xml檔案中的配置資訊對應。             下面是FilterConfig的功能介紹:                 *ServletContext getServletContext():獲取ServeltContext的方法;                 *String getFilterName():獲取Filter的配置名稱,與<filter-name>元素對應;                 *String getInitParameter(String name):獲取Fitler的初始化配置,與<init-param>元素對應;                 *Enumeration getInitParameterNames():獲取所有初始化引數的名稱。

        3.FilterChain              doFilter()方法的引數中有一個型別為FilterChain的引數,它只有一個方法:             doFilter(ServletRequest, ServletResponse).             前面說到doFilter()方法的放行,讓請求流訪問目標資源。但這麼說不嚴密,其實呼叫該方法             的意思是,“我(當前Fitler)”放行了,但不代表其他人(其他過濾器)也放行。             也就是說,一個目標資源上,可能部署了多個過濾器,就好比你去北京的路上有多個打劫的             土匪(過濾器),而其中第一夥土匪放行了,但不代表第二夥土匪也放行了,所以呼叫FilterChain類的             doFilter()方法表示的是執行了下一個過濾器的doFilter()方法,或者執行目標資源。             如果當前過濾器是最後一個過濾器,那麼呼叫chain.doFilter()方法表示執行目標資源,而不是最後一個             過濾器,那麼chain.doFilter()表示執行下一個過濾器的doFilter()方法。        

        4.多個過濾器執行順序             一個目標資源可以指定多個過濾器,過濾器的執行順序是在web.xml檔案中的部署順序:                   <filter>                       <filter-name>myFilter1 </filter-name>                       <filter-class>cn.itcast.filter.MyFilter1</filter-class>                   </filter>                   <filter-mapping>                       <filter-name>myFilter1</filter-name>                       <url-pattern>/index.jsp</url-pattern>                   </filter-mapping>                   <filter>                       <filter-name>myFilter2</filter-name>                       <filter-class>cn.itcast.filter.MyFilter2</filter-class>                   </filter>                   <filter-mapping>                       <filter-name>myFilter2</filter-name>                       <url-pattern>/index.jsp</url-pattern>                   </filter-mapping>                 public class MyFilter1 extends HttpFilter {                     public void doFilter(HttpServletRequest request, HttpServletResponse response,                             FilterChain chain) throws IOException, ServletException {                         System.out.println("filter1 start...");                         chain.doFilter(request, response);//放行,執行MyFilter2的doFilter()方法                         System.out.println("filter1 end...");                     }                 }                 public class MyFilter2 extends HttpFilter {                     public void doFilter(HttpServletRequest request, HttpServletResponse response,                             FilterChain chain) throws IOException, ServletException {                         System.out.println("filter2 start...");                         chain.doFilter(request, response);//放行,執行目標資源                         System.out.println("filter2 end...");                     }                 }                   <body>                     This is my JSP page. <br>                     <h1>index.jsp</h1>                     <%System.out.println("index.jsp"); %>                   </body>

                當有使用者訪問index.jsp頁面時,輸出結果如下:                 filter1 start...                 filter2 start...                 index.jsp                 filter2 end...                 filter1 end...

        5.四種攔截方式             我們寫一個過濾器,指定過濾資源為b.jsp,然後在瀏覽器中直接訪問b.jsp,過濾器將執行。             但是當在a.jsp中request.getRequestDsipathcer("/b.jsp").forward(request, reponse);時,就不會在執行過濾器,也就是說,             在預設情況下,只能直接訪問目標資源才會執行過濾器,而forward執行目標資源,不會執行過濾器。                 public class MyFilter extends HttpFilter {                     public void doFilter(HttpServletRequest request,                             HttpServletResponse response, FilterChain chain)                             throws IOException, ServletException {                         System.out.println("myfilter...");                         chain.doFilter(request, response);                     }                 }                         <filter>                         <filter-name>myfilter</filter-name>                         <filter-class>cn.itcast.filter.MyFilter</filter-class>                     </filter>                     <filter-mapping>                         <filter-name>myfilter</filter-name>                         <url-pattern>/b.jsp</url-pattern>                     </filter-mapping>                   <body>                    <h1>b.jsp</h1>                   </body>                   <h1>a.jsp</h1>                     <%                         request.getRequestDispatcher("/b.jsp").forward(request, response);                     %>                   </body>

            http://localhost:8080/filtertext/b.jsp--->直接訪問b.jsp時,會執行過濾器內容;             http://localhost:8080/filtertest/a.jsp--->訪問a.jsp,但是a.jsp會forward到b.jsp,這時就不會執行過濾器。

            其實過濾器有四種攔截方式。分別是:request,forward、include、error                 *request:直接訪問目標資源時執行過濾器。包括:在位址列中直接訪問、表單提交、超連結、重定向,只要在位址列中                 可以看到目標資源的路徑,就是request;                 *forward:轉發訪問執行過濾器。包括RequsetDispatcher#forward()方法、<jsp:forward>標籤都是轉發訪問;                 *include:包含訪問執行過濾器。包括RequestDisepatcher#forward()方法、<jsp:include>標籤都是包含訪問;                 *error:當目標資源在web.xml中配置為<error-page>中時,並且真的出現了異常,轉發到目標資源時,會執行過濾器。

            可以在<filter-mapping>中新增0~n個<dispatcher>子元素,來說明當前訪問的攔截方式。                     <filter-mapping>                         <filter-name>myfilter</filter-name>                         <url-pattern>/b.jsp</url-pattern>                         <dispatcher>REQUEST</dispatcher>                         <dispatcher>FORWARD</dispatcher>                     </filter-mapping>                     <filter-mapping>                         <filter-name>myfilter</filter-name>                         <url-pattern>/b.jsp</url-pattern>                     </filter-mapping>

                    <filter-mapping>                         <filter-name>myfilter</filter-name>                         <url-pattern>/b.jsp</url-pattern>                         <dispatcher>FORWARD</dispatcher>                     </filter-mapping>

            其實最為常用的就是request和forward兩種攔截方式,而include和error都比較少用,其中include比較好理解,             這就不再給出程式碼。下面給出error攔截方式例子                     <filter-mapping>                         <filter-name>myfilter</filter-name>                         <url-pattern>/b.jsp</url-pattern>                         <dispatcher>ERROR</dispatcher>                     </filter-mapping>                     <error-page>                         <error-code>500</error-code>                         <location>/b.jsp</location>                     </error-page>                   <body>                   <h1>a.jsp</h1>                    <%                    if(true)                        throw new RuntimeException("嘻嘻~");                    %>                   </body>

        6.過濾器的應用場景             過濾器的應用場景                 *執行目標資源之前做預處理工作,例如設定編碼,這種通常使用放行。只是在目標資源執行之前做一些準備工作;                 *通過條件判斷是否放行,例如校驗當前使用者是否已經登入,或者使用者ip是否已經被禁用;                 *在目標資源執行後,做一些後續的特殊處理工作,例如把目標資源輸出的資料進行處理;

        7.設定目標資源             在web.xml檔案中不是Filter時,可以通過“*”來執行目標資源:                 <filter-mapping>                     <filter-name>myfilter</filter-name>                     <url-pattern>/*</url-pattern>                 </filter-mapping>             這一特性和Servlet完全相同。通過這一特性,可以在使用者訪問敏感資源時,執行過濾器,例如:<url-pattern>/admin/*</url-pattern>,             可以把所有管理員才能訪問的資源放到/admin路徑下,這時可以通過過濾器來校驗使用者身份。             還可以為<Filter-mapping>指定目標資源為某個Servlet,例如:                     <servlet>                         <servlet-name>myservlet</servlet-name>                         <servlet-class>cn.itcast.servlet.MyServlet</servlet-class>                     </servlet>                     <servlet-mapping>                         <servlet-name>myservlet</servlet-name>                         <url-pattern>/abc</url-pattern>                     </servlet-mapping>                     <filter>                         <filter-name>myfilter</filter-name>                         <filter-class>cn.itcast.filter.MyFilter</filter-class>                     </filter>                     <filter-mapping>                         <filter-name>myfilter</filter-name>                         <servlet-name>myservlet</servlet-name>                     </filter-mapping>                 當用戶訪問http://localhost:8080/filtertest/abc時,會執行名字為myservlet的Servlet,這時會執行過濾器。

        8.Fitler小結             Filter的三個方法:                 *void init():在Tomcat啟動時被呼叫;                 *void destroy():在Tomcat關閉時被呼叫;                 *void doFilter(ServletRequest, ServletResponse, FilterChain):每次請求時都呼叫該方法;

            FitlerConfig類:與ServletConfig相似,用來獲取Fitler的初始化引數                 *ServletContext getServletContext():獲取ServletContext的方法;                 *String getFilterName():獲取Filter的配置名稱;                 *String getInitParameter(String name):獲取Filter的初始化配置,與<init-param>元素對應;                 *Enumeration getInitParameterNames():獲取所有初始化引數的名稱。

            FilterChain類:                 *void doFilter(ServletRequest, ServletResponse):放行,表示執行下一個過濾器,或者執行目標資源。可以在呼叫FilterChain的                 doFilter()方法的前後新增語句,在FilterChain的doFilter()方法之前的語句會在目標資源執行之前執行,在FilterChain的doFilter()方法                 之後的語句會在目標資源執行之後執行。

            四種攔截方式:Requset,forward、include、error,預設是request方式。                 *request:攔截直接請求方式                 *forward:攔截請求轉發方式                 *include:攔截請求包含方式                 *error:攔截錯誤轉發方式

    過濾器應用案例         分ip統計網站訪問次數             ip                     count             192.168.1.111        2             192.168.2.112        89

            統計工作需要在所有資源之前都執行,那麼就可以放到Filter中了。             該過濾器不打算做攔截操作,因為只是用來應用統計。             使用Map<String, Integer>             整個網站只需要一個Map即可。             map什麼時候建立(使用ServletContextListener,在伺服器啟動時完成建立,並只在到ServletContext中),map儲存到哪裡?             (map儲存到ServletContext中、)                 *Map需要在Filter中用來儲存資料                 *Map需要在頁面使用,列印Map中的資料

            1.說明:網站統計每個ip地址訪問本網站的次數。                          2.分析:                 因為一個網站可能有多個頁面,無論哪個頁面被訪問,都需要被統計訪問次數,所以使用過濾器最為方便。                 因為需要分ip統計,所以可以在過濾器中建立一個Map,使用ip為key,訪問次數為value。當用使用者訪問時,獲取請求的ip,如果ip在map中存在,說明以前訪問過,                 那麼在訪問次數上加1,即可;ip在map中不存在,那麼次數為1.                 把這個Map存放到ServletContext中。

            3.程式碼                 index.jsp                       <body>                         <h1>分IP統計訪問次數</h1>                         <table align="center" width="50%" border="1">                             <tr>                                 <th>IP地址</th>                                 <th>次數</th>                             </tr>                         <c:forEach items="${applicationScope.ipCountMap }" var="entry">                             <tr>                                 <td>${entry.key }</td>                                 <td>${entry.value }</td>                             </tr>                         </c:forEach>                         </table>                           </body>                 IPFilter                     public class IPFilter implements Filter {                         private ServletContext context;

                        public void init(FilterConfig fConfig) throws ServletException {                             context = fConfig.getServletContext();                             Map<String, Integer> ipCountMap = Collections                                     .synchronizedMap(new LinkedHashMap<String, Integer>());                             context.setAttribute("ipCountMap", ipCountMap);                         }

                        @SuppressWarnings("unchecked")                         public void doFilter(ServletRequest request, ServletResponse response,                                 FilterChain chain) throws IOException, ServletException {                             HttpServletRequest req = (HttpServletRequest) request;                             String ip = req.getRemoteAddr();

                            Map<String, Integer> ipCountMap = (Map<String, Integer>) context                                     .getAttribute("ipCountMap");

                            Integer count = ipCountMap.get(ip);                             if (count == null) {                                 count = 1;                             } else {                                 count += 1;                             }                             ipCountMap.put(ip, count);

                            context.setAttribute("ipCountMap", ipCountMap);                             chain.doFilter(request, response);                         }

                        public void destroy() {}                     }                       <filter>                         <display-name>IPFilter</display-name>                         <filter-name>IPFilter</filter-name>                         <filter-class>cn.itcast.filter.ip.IPFilter</filter-class>                       </filter>                       <filter-mapping>                         <filter-name>IPFilter</filter-name>                         <url-pattern>/*</url-pattern>                       </filter-mapping>

    粗粒度許可權控制(攔截是否登入、攔截使用者名稱admin許可權)         RBAC->基於角色的許可權控制             *tb_user             *tb_role             *tb_userrole             *tb_menu(增、刪、改、查)             *tb_rolemenu

        1.說明             我們給出三個頁面:index.jsp、user.jsp、admin.jsp。                 *index.jsp:誰都可以訪問,沒用限制;                 *user.jsp:只用登入使用者才能訪問;                 *admin.jsp:只有管理員才能訪問。

        2.分析:             設計Uesr類:username、password、prade,其中grade表示使用者等級,1表示普通等級,2表示管理員使用者。             當用戶登入成功後,把user儲存到session中。             建立LoginFilter,它有兩種過濾方式:                 *如果訪問的是user.jsp,檢視session中是否存在user;                 *如果訪問的是admin.jsp,檢視session中是否存在user,並且user的grade等於2。

        3.程式碼             User.java                 public class User {                     private String username;                     private String password;                     private int grade;                 …                 }

            為了方便,這裡就不適用資料庫,所以我們需要在UserService中建立一個Map,用來儲存所有使用者。Map中的key中使用者名稱,             value為User物件。                 UserService.java                     public class UserService {                         private static Map<String,User> users = new HashMap<String,User>();                         static {                             users.put("zhangSan", new User("zhangSan", "123", 1));                             users.put("liSi", new User("liSi", "123", 2));                         }                                                  public User login(String username, String password) {                             User user = users.get(username);                             if(user == null) return null;                             return user.getPassword().equals(password) ? user : null;                         }                     }

                login.jsp                       <body>                           <h1>登入</h1>                               <p style="font-weight: 900; color: red">${msg }</p>                             <form action="<c:url value='/LoginServlet'/>" method="post">                                 使用者名稱:<input type="text" name="username"/><br/>                                 密 碼:<input type="password" name="password"/><br/>                                 <input type="submit" value="登入"/>                             </form>                           </body>

                index.jsp                       <body>                         <h1>主頁</h1>                         <h3>${user.username }</h3>                         <hr/>                         <a href="<c:url value='/login.jsp'/>">登入</a><br/>                         <a href="<c:url value='/user/user.jsp'/>">使用者頁面</a><br/>                         <a href="<c:url value='/admin/admin.jsp'/>">管理員頁面</a>                       </body>

                /user/user.jsp                     <body>                     <h1>使用者頁面</h1>                     <h3>${user.username }</h3>                     <hr/>                     </body>

                /admin/admin.jsp                     <body>                       <h1>管理員頁面</h1>                       <h3>${user.username }</h3>                       <hr/>                     </body>

                LoginServlet                     public class LoginServlet extends HttpServlet {                         public void doPost(HttpServletRequest request, HttpServletResponse response)                                 throws ServletException, IOException {                             request.setCharacterEncoding("utf-8");                             response.setContentType("text/html;charset=utf-8");                                                          String username = request.getParameter("username");                             String password = request.getParameter("password");                             UserService userService = new UserService();                             User user = userService.login(username, password);                             if(user == null) {                                 request.setAttribute("msg", "使用者名稱或密碼錯誤");                                 request.getRequestDispatcher("/login.jsp").forward(request, response);                             } else {                                 request.getSession().setAttribute("user", user);                                 request.getRequestDispatcher("/index.jsp").forward(request, response);                             }                         }                     }

                LoginUserFilter.java                       <filter>                             <display-name>LoginUserFilter</display-name>                             <filter-name>LoginUserFilter</filter-name>                             <filter-class>cn.itcast.filter.LoginUserFilter</filter-class>                           </filter>                           <filter-mapping>                             <filter-name>LoginUserFilter</filter-name>                             <url-pattern>/user/*</url-pattern>                           </filter-mapping>

                        public class LoginUserFilter implements Filter {                             public void destroy() {}                             public void init(FilterConfig fConfig) throws ServletException {}

                            public void doFilter(ServletRequest request, ServletResponse response,                                     FilterChain chain) throws IOException, ServletException {                                 response.setContentType("text/html;charset=utf-8");                                 HttpServletRequest req = (HttpServletRequest) request;                                 User user = (User) req.getSession().getAttribute("user");                                 if(user == null) {                                     response.getWriter().print("您還沒有登入");                                     return;                                 }                                 chain.doFilter(request, response);                             }                         }

                LoginAdminFilter.java                       <filter>                             <display-name>LoginAdminFilter</display-name>                             <filter-name>LoginAdminFilter</filter-name>                             <filter-class>cn.itcast.filter.LoginAdminFilter</filter-class>                           </filter>                           <filter-mapping>                             <filter-name>LoginAdminFilter</filter-name>                             <url-pattern>/admin/*</url-pattern>                           </filter-mapping>                         public class LoginAdminFilter implements Filter {                             public void destroy() {}                             public void init(FilterConfig fConfig) throws ServletException {}

                            public void doFilter(ServletRequest request, ServletResponse response,                                     FilterChain chain) throws IOException, ServletException {                                 response.setContentType("text/html;charset=utf-8");                                 HttpServletRequest req = (HttpServletRequest) request;                                 User user = (User) req.getSession().getAttribute("user");                                 if(user == null) {                                     response.getWriter().print("您還沒有登入!");                                     return;                                 }                                 if(user.getGrade() < 2) {                                     response.getWriter().print("您的等級不夠!");                                     return;                                 }                                 chain.doFilter(request, response);                             }                         }

        禁用資源快取             瀏覽器只是要快取頁面,這對我們在開發時測試很不方便,所以我們可以過濾所有資源,然後新增去除所有快取                 public class NoCacheFilter extends HttpFilter {                     public void doFilter(HttpServletRequest request,                             HttpServletResponse response, FilterChain chain)                             throws IOException, ServletException {                         response.setHeader("cache-control", "no-cache");                         response.setHeader("pragma", "no-cache");                         response.setHeader("expires", "0");                         chain.doFilter(request, response);                     }                 }                          但是要注意,有的瀏覽器可能不會理會你的設定,還是會快取,這時就要在頁面中使用時間戳來處理了。

        解決全站字元亂碼(POST和GET中文編碼問題)

            Serlvet:                 *POST:requset.setCharacterEncodin("utf-8");                 *GET:                     *String username = requset。geteParameter("username");             1.說明:                 亂碼問題:                     *獲取請求引數中的亂碼問題                         **POST請求:request。setCharacterEncoding("utf-8");                         **GET請求:new String(request.getParameter("xxx").getBytes("iso-8859-1"), "utf-8");

                    *響應的亂碼問題:response.setContextType("text/html;charset=utf-8").

                    基本上在每個Servlet中都要處理亂碼問題,所以應該吧這個工作放到過濾器中來完成。

            2.分析                 其實全站亂碼問題的難點就是處理GET請求引數的問題。                 如果只是處理POST請求的編碼問題,已經響應編碼問題,那麼這個過濾器就太簡單了。                     public class EncodingFilter extends HttpFilter {                         public void doFilter(HttpServletRequest request,                                 HttpServletResponse response, FilterChain chain)                                 throws IOException, ServletException {                             String charset = this.getInitParameter("charset");                             if(charset == null || charset.isEmpty()) {                                 charset = "UTF-8";                             }                             request.setCharacterEncoding(charset);                             response.setContentType("text/html;charset=" + charset);                             chain.doFilter(request, response);                         }                     }

                如果是POST請求,當執行目標Servlet時,Servlet中呼叫request.getParameter()方法時,就會根據request.serCharacterEncoding()                 設定的編碼來轉碼,這說明在過濾器中呼叫request.setCharacterEncoding()方法會影響在目標Servlet中的request.getParameter()方法的                 行為。                 但是如果是GET請求,將如何影響request.getParameter()方法的行為呢?這將不好實現,不可能先呼叫request.getParameter()方法獲取引數,                 然後手動轉碼後,再施加在request中,因為request只有getParameter(),而沒有setParameter()方法。

                處理GET請求引數編碼問,需要在Filtter中放行時,把request物件給“調包”了,也就是讓目標Servlet使用我們“調包”之後的request物件。                 這說明我們需要保證“調包”之後的request物件中所有方法都有與“調包”之前一樣可以使用,並且getParameter()方法還要有能力返回轉碼後的引數。

                    這可能讓你想起了“繼承”,但是這裡不能用繼承,而是“裝飾者(Decorator Pattern)”。                     下面三種對a物件機械能增強的手段:                         *繼承:AA類繼承a物件的型別:A類,然後去重寫fun1()方法,其中重寫的fun1()方法就是被增強的方法。但是,繼承必須要知道a物件的真實型別,                         然後才能去繼承。如果我們不知道a物件的確切型別,而只知道a物件的IA介面的實現類物件,那麼就無法使用繼承來增強a物件了;

                        *裝飾者模式:AA類去實現a物件相同的介面:IA介面,還需要給AA類傳遞a物件,然後在AA類中所有的方法實現都是                         通過代理a物件的相同方法完成的,只有fun1()方法在代理a物件相同方法的前後1新增一些內容,這就是對fun1()方法進行了增強。

                        *動態代理:動態代理與裝飾者模式比較類似,而且是通過反射來完成的。

                    對request物件進行增強的條件,剛好符合裝飾者模式的特點。因為我們不知道request物件的具體型別,但是我們知道request是HttpServletRequest介面的實現類。                     這說明我們寫一個類EncodingRequest,去實現HttpServletRequest介面,然後再把原來的request傳遞給EncodingRequest類。在EncodingRequest中對                     HttpServletRequest介面中的所有方法的實現都是通過代理原來的request物件來完成的,只有對getParameter()方法添加了增強程式碼。                     javaEE已經給提供了一個HttpServletRequest的裝飾類,但是它不是用來直接使用的,而是用來讓我們去繼承的,它就是HttpServletRequest類,但它不做任何的增強,                     在這裡,一個裝飾類,不做任何增強,有什麼意義啊?使用這個裝飾類的物件,和使用原有的request有什麼分別呢?                     HttpServletRequestWrapper類雖然是HttpServletRequest的裝飾類,但它不是用來直接使用的,而是用來讓我們去繼承的,當我們想寫一個裝飾類的時候,                     還要對所有不需要增強的方法做一次實現的是很複雜的事情,但是如果你只是去繼承HttpServletRequestWrapper類,那麼只需要重寫需要增強的方法即可了。

            3.程式碼                 EncodingRequest                     public class EncodingRequest extends HttpServletRequestWrapper {                         private String charset;                         public EncodingRequest(HttpServletRequest request, String charset) {                             super(request);                             this.charset = charset;                         }

                        public String getParameter(String name) {                             HttpServletRequest request = (HttpServletRequest) getRequest();                                                          String method = request.getMethod();                             if(method.equalsIgnoreCase("post")) {                                 try {                                     request.setCharacterEncoding(charset);                                 } catch (UnsupportedEncodingException e) {}                             } else if(method.equalsIgnoreCase("get")) {                                 String value = request.getParameter(name);                                 try {                                     value = new String(name.getBytes("ISO-8859-1"), charset);                                 } catch (UnsupportedEncodingException e) {                                 }                                 return value;                             }                             return request.getParameter(name);                         }                     }

                EncodingFilter                     public class EncodingFilter extends HttpFilter {                         public void doFilter(HttpServletRequest request,                                 HttpServletResponse response, FilterChain chain)                                 throws IOException, ServletException {                             String charset = this.getInitParameter("charset");                             if(charset == null || charset.isEmpty()) {                                 charset = "UTF-8";                             }                             response.setCharacterEncoding(charset);                             response.setContentType("text/html;charset=" + charset);                             EncodingRequest res = new EncodingRequest(request, charset);                             chain.doFilter(res, response);                         }                     }

                web.xml                       <filter>                           <filter-name>EncodingFilter</filter-name>                           <filter-class>cn.itcast.filter.EncodingFilter</filter-class>                           <init-param>                               <param-name>charset</param-name>                               <param-value>UTF-8</param-value>                           </init-param>                       </filter>                       <filter-mapping>                           <filter-name>EncodingFilter</filter-name>                           <url-pattern>/*</url-pattern>                       </filter-mapping>

        頁面靜態化             1.說明                 你到“噹噹”搜尋最多的是什麼分類,是的,就是Java分類,你猜猜,當你去搜索Java分類時,“噹噹”會不會查詢資料庫呢?                 當然會。不查詢資料庫怎麼獲取Java分類的下的圖書呢?其實每天都會有很多人去搜索“Java分類”的圖書,每次都去訪問資料庫,                 這就會有效能上的缺失,如果是在訪問靜態頁面“html”那麼就會快很多了,靜態頁面本身就比動態頁面快很多倍,而且動態頁面總是要                 去資料庫查詢,這樣會更加降低訪問速度。                 頁面靜態化是吧動態頁面生成的html儲存到伺服器的檔案上,然後再有相同的請求時,不再去執行動態頁面,而是直接給使用者                 響應上次已經生成的靜態頁面。而且靜態頁面還有助於搜尋引擎找到你。

            2.檢視圖書分類                 例子:                     用來檢視不同分類的圖書,然後去思考如何讓動態頁面靜態化的問題:

                    index.jsp                           <body>                             <a href="<c:url value='/BookServlet'/>">全部圖書</a><br/>                             <a href="<c:url value='/BookServlet?category=1'/>">JavaSE分類</a><br/>                             <a href="<c:url value='/BookServlet?category=2'/>">JavaEE分類</a><br/>                             <a href="<c:url value='/BookServlet?category=3'/>">Java框架分類</a><br/>                               </body>                                                   BookServlet.java                         public class BookServlet extends HttpServlet {                             public void doGet(HttpServletRequest request, HttpServletResponse response)                                     throws ServletException, IOException {                                 BookService bookService = new BookService();                                 List<Book> bookList = null;                                 String param = request.getParameter("category");                                 if(param == null || param.isEmpty()) {                                     bookList = bookService.findAll();                                 } else {                                     int category = Integer.parseInt(param);                                     bookList = bookService.findByCategory(category);                                 }                                                                  request.setAttribute("bookList", bookList);                                 request.getRequestDispatcher("/show.jsp").forward(request, response);                             }                         }

                    show.jsp                         <table border="1" align="center" width="50%">                             <tr>                                 <th>圖書名稱</th>                                 <th>圖書單價</th>                                 <th>圖書分類</th>                             </tr>                                                        <c:forEach items="${bookList }" var="book">                             <tr>                                 <td>${book.bname }</td>                                 <td>${book.price }</td>                                 <td>                                     <c:choose>                                         <c:when test="${book.category eq 1}"><p style="color:red;">JavaSE分類</p></c:when>                                         <c:when test="${book.category eq 2}"><p style="color:blue;">JavaEE分類</p></c:when>                                         <c:when test="${book.category eq 3}"><p style="color:green;">Java框架分類</p></c:when>                                     </c:choose>                                 </td>                             </tr>                           </c:forEach>                         </table>

            3.分析:                     需求:在使用者第一次訪問頁面時生成靜態頁面,然後請求重定向到靜態頁面上。單使用者再次訪問時,直接重定向到                 靜態頁面上去。                 我們需要為不同的請求生成靜態頁面,例如使用者訪問BookServlet?category=1時,我們要生成靜態頁面,當用戶訪問BookServlet?                 category=2時,也要生成靜態頁面,即不同1引數生成不同的靜態頁面                 我們可以使用category為key,靜態頁面的路徑為value,儲存到一個Map中,然再把Map儲存到SerlvetContext中。沒有對應的靜態頁面,                 我們生成靜態頁面,再重定向到靜態頁面,如果存靜態頁面,那麼直接重定向即可。

                StaticResponse.java                     public class StaticResponse extends HttpServletResponseWrapper {                         private PrintWriter pw;

                        public StaticResponse(HttpServletResponse response, String filepath)                                 throws FileNotFoundException, UnsupportedEncodingException {                             super(response);                             pw = new PrintWriter(filepath, "UTF-8");                         }

                        public PrintWriter getWriter() throws IOException {                             return pw;                         }

                        public void close() throws IOException {                             pw.close();                         }                     }

                StaticFilter.java                     public class StaticFilter implements Filter {                         private ServletContext sc;                                                  public void destroy() {                         }

                        public void doFilter(ServletRequest request, ServletResponse response,                                 FilterChain chain) throws IOException, ServletException {                             HttpServletRequest req = (HttpServletRequest) request;                             HttpServletResponse res = (HttpServletResponse) response;

                            String key = "key_" + request.getParameter("category");                                                                  Map<String,String> map = (Map<String, String>) sc.getAttribute("pages");                             if(map == null) {                                 map = new HashMap<String,String>();                                 sc.setAttribute("pages", map);                             }                                                          if(map.containsKey(key)) {                                 res.sendRedirect(req.getContextPath() + "/staticPages/" + map.get(key));                                 return;                             }

                            String html = key + ".html";                             String realPath = sc.getRealPath("/staticPages/" + html);                             StaticResponse sr = new StaticResponse(res, realPath);                             chain.doFilter(request, sr);                             sr.close();

                            res.sendRedirect(req.getContextPath() + "/staticPages/" + html);                             map.put(key, html);                         }

                        public void init(FilterConfig fConfig) throws ServletException {                             this.sc = fConfig.getServletContext();                         }                     }