1.JavaWeb三大組件都需要在web.xml中進行配置
2.過濾器
它會在一組資源(jsp、servlet、.css、.html等等)的前面執行!
它可以讓請求得到目標資源,也可以不讓請求達到!
*過濾器有攔截請求的能力!
過濾器如何編寫
1.寫一個類實現Filter接口
2.在web.xml中進行配置
Filter接口
void init(FilterConfig)
*創建之后馬上執行;Filter會在服務器啟動時就創建!
void destory()
*銷毀之前執行!在服務器關閉的時候銷毀
void doFilter(ServletRequest , ServletResponse, FilterChain)
*每次過濾時都會執行
Filter是單例的!
web.xml中配置:
<filter> <filter-name>xxx</filter-name> <filter-class>com.xjs.web.filter.AFilter</filter-class> </filter> <filter-mapping> <filter-name>xxx</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
使用:
寫一個過濾器AFilter:
package com.xjs.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 AFilter implements Filter{ /** * 創建之后馬上執行,用來做初始化! */ @Override public void init(FilterConfig arg0) throws ServletException { System.out.println("過濾器出生"); } /** * 每次過濾時都會執行 */ @Override public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2) throws IOException, ServletException { System.out.println("開始攔截"); } /** * 銷毀之前執行,用來做對非內存資源進行釋放 */ @Override public void destroy() { System.out.println("過濾器銷毀"); } }
然后在web.xml中配置Filter,指定過濾哪些路徑(這里是AServlet)
<filter> <filter-name>xx</filter-name> <filter-class>com.xjs.filter.AFilter</filter-class> </filter> <filter-mapping> <filter-name>xx</filter-name> <url-pattern>/AServlet</url-pattern> </filter-mapping>
AServlet:
package com.xjs; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class AServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("AServlet...."); } }
當請求AServlet時被過濾器攔截。
FilterConfig--->與ServletConfig相似
* 獲取初始化參數:getInitParameter()
* 獲取過濾器名稱:getFilterName()
* 獲取application:getServletContext()
FilterChain
* doFilter(ServletRequest, ServletResponse):放行!
放行,就相當於調用了目標Servlet的service()方法。
多過濾器
FilterChain#doFilter()方法:
執行目標資源,或者執行下一個過濾器!
過濾器的四種攔截方式
<dispatcher>REQUEST</dispatcher> 默認的! <dispatcher>FORWARD</dispatcher> <dispatcher>INCLUDE</dispatcher> <dispatcher>ERROR</dispatcher>
<error-page> <error-code>500</error-code> <location>500.jsp</location> </error-page>
在<filter-mapping>中進行配置!
多個過濾器的執行順序
<filter-mapping>的配置順序決定了過濾器的執行順序!
創建的FIlter
過濾器的應用場景
過濾器的應用場景:
*執行目標資源之前做預處理工作,例如設置編碼,這種通常會放行,只是在目標資源執行之前做的一些准備工作;
幾乎所有的Servlet中都需要寫request.setCharacterEndoing()可以把它放到一個Filter中
*通過條件判斷是否放行,例如校驗當前用戶是否已經登錄,或者用戶IP是否已經被禁用;
*在目標資源執行之后,做一些后續的特殊處理工作,例如把目標資源輸出的數據進行處理;
回程攔截!
案例1:分ip統計網站次數
ip | count |
192.168.1.111 | 5 |
192.168.1.112 | 62 |
統計工作需要在所有資源之前都執行,那么就可以放到Filter中了。
我們這個過濾器不打算做攔截操作!因為我們只是用來做統計的。
用什么東西來裝載統計的數據。Map<String, Integer>
整個網站只需要一個Map即可!
Map什么時候創建(使用ServletContextListener,在服務器啟動時完成創建,並保存到ServletContext中),Map保存到哪里!(Map保存到ServletContext中!!!)
> Map需要在Filter中用來保存數據
> Map需要在頁面使用,打印Map中的數據
1).網站統計每個IP地址訪問本網站的次數。
使用的是注解(代替了在web.xml中的配置)
AListener監聽器:
package com.xjs.web.listener; import java.util.LinkedHashMap; import java.util.Map; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.annotation.WebListener; @WebListener public class AListener implements ServletContextListener { /** * 在服務器啟動時創建Map,保存到ServletContext */ public void contextInitialized(ServletContextEvent sce) { //創建Map Map<String,Integer> map=new LinkedHashMap<String,Integer>(); //得到ServletContext ServletContext application = sce.getServletContext(); application.setAttribute("map", map); } public void contextDestroyed(ServletContextEvent arg0) { } }
AFilter攔截器----負責統計訪問次數
package com.xjs.web.filter; import java.io.IOException; import java.util.Map; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebFilter; /** * 從application中獲取Map * 從request中獲取當前客戶端的IP * 進行統計工作,結果保存到Map中 * @author hp * */ @WebFilter("/*") public class AFliter implements Filter { private FilterConfig config; public void destroy() { } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { /* * 1.得到application中的 map * 2.從request中獲取當前客戶端的ip地址 * 3.查看map中是否存在這個IP對應的訪問次數,如果存在,把次數+1再保存回去 * 4.如果不存在這個Ip ,那么說明第一次訪問本站,設置訪問次數為1 */ ServletContext application = config.getServletContext(); Map<String,Integer> map = (Map<String,Integer>)application.getAttribute("map"); //獲取客戶端IP地址 String ip = request.getRemoteAddr(); //進行判斷 if(map.containsKey(ip)){//這個IP在map中存在 Integer count = map.get(ip); map.put(ip, count+1); }else{//不存在 map.put(ip, 1); } application.setAttribute("map", map);//把map在放回到application中 chain.doFilter(request, response); } /** * 在服務器啟動時調用,而且只執行一次 */ public void init(FilterConfig fConfig) throws ServletException { this.config=fConfig; } }
show.jsp---顯示本項目的訪問次數
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <body> <h1 align="center">顯示結果</h1> <table align="center" width="60%" border="1"> <tr> <th>IP</th> <th>COUNT</th> </tr> <c:forEach items="${applicationScope.map }" var="entry"> <tr> <td>${entry.key }</td> <td>${entry.value }</td> </tr> </c:forEach> </table> </body>
結果:
案例2:粗粒度權限控制(攔截是否登錄、攔截用戶名admin權限)
1)什么是粗粒度和細粒度權限
粗粒度權限管理,對資源類型的權限管理。資源類型比如:菜單、url鏈接、用戶添加頁面、用戶信息、類方法、頁面中按鈕,等等。。。
粗粒度權限管理比如:超級管理員可以訪問用戶添加頁面、用戶信息等全部頁面。
細粒度權限管理,對資源實例的權限管理。資源實例就資源類型的具體化,比如:用戶id為001的修改鏈接,1110班的用戶信息。
細粒度權限管理就是數據級別的權限管理。比如:部門經理可以訪問本部門的員工信息,用戶只可以看到自己的菜單,大區經理只能查看本轄區的銷售訂單。。
粗粒度和細粒度例子:
系統有一個用戶列表查詢頁面,對用戶列表查詢分權限,如果粗顆粒管理,張三和李四都有用戶列表查詢的權限,張三和李四都可以訪問用戶列表查詢。
進一步進行細顆粒管理,張三(行政部)和李四(開發部)只可以查詢自己本部門的用戶信息。張三只能查看行政部 的用戶信息,李四只能查看開發部門的用戶信息。細粒度權限管理就是數據級別的權限管理。
分為管理員,普通用戶,游客。
管理員能訪問的資源(jsp) 放到admin文件夾下。
普通用戶能訪問的資源(jsp)放到users文件夾下。
寫兩個Filter過濾器對資源的訪問進行過濾。
UserFilter.java:----用戶和管理員都能訪問
package com.xjs.web.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; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; @WebFilter("/admin/*") public class AdminFilter implements Filter { public void destroy() { } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { /* * 1.得到session 2.判斷session域中是否存在 admin,如果存在,放行 * 3.判讀session域中是否存在username,如果存在,放行,否則打回login.jsp */ HttpServletRequest req = (HttpServletRequest) request; HttpSession session = req.getSession(); String name = (String) session.getAttribute("admin"); if (name != null) {//管理員 chain.doFilter(request, response); } else { req.setAttribute("msg", "您可能是個啥,但肯定不是管理員!"); req.getRequestDispatcher("/login.jsp").forward(request, response); } } public void init(FilterConfig fConfig) throws ServletException { } }
AdminFilter.java:---只有管理員才能訪問
package com.xjs.web.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; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; @WebFilter("/admin/*") public class AdminFilter implements Filter { public void destroy() { } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { /* * 1.得到session 2.判斷session域中是否存在 admin,如果存在,放行 * 3.判讀session域中是否存在username,如果存在,放行,否則打回login.jsp */ HttpServletRequest req = (HttpServletRequest) request; HttpSession session = req.getSession(); String name = (String) session.getAttribute("admin"); if (name != null) {//管理員 chain.doFilter(request, response); } else { req.setAttribute("msg", "您可能是個啥,但肯定不是管理員!"); req.getRequestDispatcher("/login.jsp").forward(request, response); } } public void init(FilterConfig fConfig) throws ServletException { } }
LoginServlet:----用登錄來區分身份
package com.xjs; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public final 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"); /* * 1.獲取用戶名 * 2.判斷用戶名中是否包含itcast * 3.包含,就是管理員 * 4.不包含,就是普通會員 * 5.要把登錄的信息保存到session中 * 6.轉發到index.jsp */ String username = request.getParameter("username"); if(username.contains("itcast")){ request.getSession().setAttribute("admin", username);; }else{ request.getSession().setAttribute("username", username); } request.getRequestDispatcher("/index.jsp").forward(request, response); } }
案例3:解決全站字符亂碼(POST和GET中文編碼問題)
總在Servlet中寫:Post----request.setCharacterEncoding("utf-8"); Get-----String username=request.getParameter("username"); username=new String(username.getBytes("ISO-8859-1"), "utf-8");
在Tomcat8之前,使用get方式得到參數會有亂碼,在Tomcat8中默認編碼為UTF-8。所以在Tomcat8中Get獲取參數,不用考慮編碼問題。
1) 編寫一個過濾器EncodingFilter.java:---完成對POST請求和GET請求編碼問題
package com.xjs.web.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; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; @WebFilter("/*") public class EncodingFilter implements Filter { public void destroy() { } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { //處理post請求編碼問題 request.setCharacterEncoding("utf-8"); HttpServletRequest req=(HttpServletRequest) request; /* * 處理Get請求的編碼問題 */ /* * 掉包request * 1.寫一個request的裝飾類 * 2.在放行時,使用我們自己的request */ if(req.getMethod().equals("GET")){ //向這個類傳遞一個HttpServletRequest,完成getParameter()方法的重寫,然后回返回一個重寫后的Request對象(具有得到參數為utf-8的能力) EncodingRequest er=new EncodingRequest(req); chain.doFilter(er, response);//--這個er(request)是裝飾之后的request,它的getParameter();得到的參數是utf-8編碼 }else if(req.getMethod().equals("POST")){ chain.doFilter(request, response); } } public void init(FilterConfig fConfig) throws ServletException { } }
這里面在處理GET請求時,涉及到Filter中的參數為ServletRequest需要強制類型轉換成HttpServletRequest,然后就是通過一個EncodingRequest類完成對request對象的getParameter()方法的重寫。然后把對getParameter()裝飾過的request對象放行------->傳遞到相應的Servlet的doGet方法中。
2)EncodingRequest.java:
(1)原本EncodingRequest實現HttpServletRequest接口,重寫String getParameter(String name)方法
EncodingRequest implements HttpServletRequest 重寫getParameter()方法
private HttpServletRequest request; public EncodingRequest(HttpServletRequest request) { this.request = request; } public String getParameter(String name) { String value = request.getParameter(name); // 處理編碼問題 try { value = new String(value.getBytes("iso-8859-1"), "utf-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(); } return value; }
(2)Java提供了HttpServletRequestWrapper類,只需EncodingRequest繼承HttpServletRequestWrapper該類,重寫getParameter()方法即可。
Wrapper:包裝器的意思
package com.xjs.web.filter; import java.io.UnsupportedEncodingException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; /** * 裝飾request * * @author hp * */ public class EncodingRequest extends HttpServletRequestWrapper { private HttpServletRequest req; // java提供了這個類,只需實現這個構造器 public EncodingRequest(HttpServletRequest request) { super(request); this.req = request; } public String getParameter(String name) { String value = req.getParameter(name); // 處理編碼問題 try { value = new String(value.getBytes("iso-8859-1"), "utf-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(); } return value; } }
案例4:頁面靜態化之准備工作()
什么是頁面靜態化!
首次訪問去數據庫獲取數據,然后把數據保存到一個html頁面中
二次訪問,就不再去數據庫獲取了,而是直接顯示html
1.給出一個過濾器,把servlet請求的資源所做輸出保存到html中,重定向到html頁面。
二次訪問時,這個html頁面已經存在,那么直接重定向,不用再去訪問servlet!
//////////////////////////////////////////////////////////////////
原來在jsp上顯示,HttpServletResponse的getWriter()方法得到的PrintWriter對象是與jsp綁定在一起,把響應的內容輸出到了jsp頁面上了。PrintWriter(String fileName, String can)構造方法可以創建指定文件的名稱(路徑),和字符集。
所以我們調包response的getWriter()方法:----返回一個jsp頁面使用它向html頁面輸出內容的PrintWriter對象
package com.xjs.web.filter; import java.io.FileNotFoundException; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponseWrapper; /** * 是你還有你,一切拜托你 * @author hp * */ public class StaticResponse extends HttpServletResponseWrapper { private HttpServletResponse response; private PrintWriter pw; /** * String path:html文件路徑! * @param response * @param path * @throws UnsupportedEncodingException * @throws FileNotFoundException */ public StaticResponse(HttpServletResponse response,String path) throws FileNotFoundException, UnsupportedEncodingException { super(response);//一切拜托你 this.response=response; //創建一個與html文件路徑綁定在一起的流對象 pw=new PrintWriter(path,"utf-8"); } //自己需要增強的部分 public PrintWriter getWriter(){ //返回一個與html綁定在一起的printWriter對象 //jsp會使用它進行輸出,這樣數據都輸出到了html文件中! return pw; } }
這個過濾器判斷是否是第一次訪問(也就是有無html頁面)
在這個里面生成html文件也就是在執行chain.doFilter(req,sr)放行時,傳遞的是裝飾過的Response對象,會執行BookServlet,然后轉發到show.jsp(並沒有顯示),使用裝飾后的Response對象中的getWriter()方法得到PrintWriter對象,向html上輸出內容,生成了html頁面。
然后再重定向到html頁面即可!
package com.xjs.web.filter; import java.io.File; 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.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebFilter(servletNames = { "BookServlet" }) public class StaticFilter implements Filter { public void destroy() { } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req=(HttpServletRequest) request; HttpServletResponse res=(HttpServletResponse) response; /* * 1.第一次訪問時,查找請求對應的html頁面是否存在,如果存在重定向到html * 2.如果不存在,放行!把servlet訪問數據庫后,輸出給客戶端的數據保存到一個html文件中 * 再重定向到html */ /* * 一、獲取category參數! * null--->null.html * 1--->1.html * 2--->2.html * 3---->3.html * htmls目錄下 */ String category = req.getParameter("category"); String htmlPage=category+".html";//得到對應文件的名稱 String htmlPath=req.getServletContext().getRealPath("/htmls");//得到文件存放目錄 File destFile=new File(htmlPath,htmlPage); if(destFile.exists()){//如果文件存在 //重定向到這個文件 res.sendRedirect(req.getContextPath()+"/htmls/"+htmlPage); return; } /* * 二、如果html文件不存在,我們要生成html文件 * 1.放行,show.jsp會做出很多的輸出,我們要讓它不再輸出客戶端,而是輸出到我們指定的一個html文件中 * 完成: * 調包response,讓它的getWriter()與一個html文件綁定,那么show.jsp的輸出就到了html文件中 * */ StaticResponse sr=new StaticResponse(res, destFile.getAbsolutePath());//文件的絕對路徑。 chain.doFilter(req, sr);//放行,即生成了html文件 //這時頁面已經存在,重定向到html文件 res.sendRedirect(req.getContextPath()+"/htmls/"+htmlPage); } public void init(FilterConfig fConfig) throws ServletException { } }
BookServlet.java:
package com.xjs.web.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.xjs.web.dao.BookDao; import cn.itcast.servlet.BaseServlet; public class BookServlet extends BaseServlet { private BookDao bookDao=new BookDao(); public String findAll(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setAttribute("bookList", bookDao.findAll()); return "/show.jsp"; } public String findByCategory(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String value = request.getParameter("category"); int category = Integer.parseInt(value); request.setAttribute("bookList", bookDao.findByCategory(category)); return "/show.jsp"; } }
link.jsp:顯示鏈接
<body> <h1>鏈接頁面</h1> <a href='<c:url value="/BookServlet?method=findAll"></c:url>'>查看所有</a><br> <a href='<c:url value="/BookServlet?method=findByCategory&category=1"></c:url>'>查看SE</a><br> <a href='<c:url value="/BookServlet?method=findByCategory&category=2"></c:url>'>查看EE</a><br> <a href='<c:url value="/BookServlet?method=findByCategory&category=3"></c:url>'>查看Framework</a><br> </body>
show.jsp:負責向html頁面響應內容
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<!-- 模擬html的響應頭,告訴服務器html使用的編碼 -->
相當於向html頁面加的響應頭
<body>
<h1 align="center">圖書列表</h1>
<table border="1" align="center">
<tr>
<th>書名</th>
<th>單價</th>
<th>分類</th>
</tr>
<c:forEach items="${bookList }" var="book">
<tr>
<td>${book.bname }</td>
<td>${book.price }</td>
<td>${book.category }</td>
</tr>
</c:forEach>
</table>
</body>