「別小看任何人,越不起眼的人。每每會作些讓人想不到的事。"你好我是夢陽辰,快來和我一塊兒學習吧!css
Filter和Listener是Servlet規範中的兩個高級特性,不一樣於Servlet,他們不用於處理客戶端請求。java
Filter用於對request,response對象進行修改。
Filter被稱做過濾器,其基本功能就是對Servlet容器調用Servlet的過程進行攔截,從而在Servlet進行響應處理先後實現一些特殊的功能。這就比如現實中的污水淨化設備,它能夠看做一個過濾器,專門用於過濾污水雜質。web
當瀏覽器訪問服務器中的目標資源時,會被Filter攔截,在Filter中進行預處理操做,而後再將請求轉發給目標資源。設計模式
當服務器接收到這個請求後會對其進行響應,在服務器處理響應的過程當中,也須要先將響應結果發送給過濾器,在過濾器中對響應結果進行處理後,纔會發送給客戶端。數組
過濾器的做用:
通常完成通用性的操做。
好比:登陸驗證,判斷用戶是否登陸;統一編碼處理,敏感字符處理等。瀏覽器
其實Filter過濾器就是實現了javax.servlet.Filter接口的類,在javax.servlet.Filter定義了三個方法。服務器
Listener用於對context,session,request事件進行監聽。session
步驟:
1.定義一個類,實現接口javax.servlet.Filter。app
2.複寫方法異步
3.配置攔截路徑
3.1.web.xml配置
3.2.註解配置
javax.servlet.Filter定義了三個方法
@WebFilter("/*")//攔截全部資源public class FilterTest1 implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { //初始化過濾器,在Web程序加載的時候調用,配置初始化參數 } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.print("我是過濾器!"); //是否放行,即轉發到請求資源 filterChain.doFilter(servletRequest,servletResponse); } @Override public void destroy() { //用於釋放被Filter打開的資源,當Web服務器卸載Filter對象以前被調用 }}
web.xml配置
取消註解配置,使用web.xml配置。
<filter> <filter-name>FilterTest1</filter-name> <filter-class>filter.FilterTest1</filter-class> </filter> <filter-mapping> <filter-name>FilterTest1</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
過濾器的流程:
@WebFilter("/*")public class FilterTest2 implements Filter { public void destroy() { } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { //用於攔截用戶的請求,若是和當前過濾器的攔截路徑匹配,此方法會被調用 //對request對象請求消息加強 System.out.println("我被執行了request!"); chain.doFilter(req, resp); //對response對象的響應消息加強 System.out.println("我被又執行了response!"); } public void init(FilterConfig config) throws ServletException { }}
攔截路徑的配置:
具體資源路徑:/index.jsp 只有訪問index.jsp資源時,過濾器纔會被執行。
2.攔截目錄:/user/* 訪問/uer下的全部資源時,過濾器都會被執行。
3.後綴名攔截:*.jsp 訪問全部後綴名爲jsp資源時,過濾器執行。
4.攔截全部資源:/*
攔截方式的配置:
資源被訪問的方式:
請求轉發過濾器不會被執行。
1.註解配置,能夠配置多個值
設置dispatcherTypes屬性
REQUEST:默認值,瀏覽器直接請求資源 FORWARD:轉發訪問資源 INCLUDE:包含訪問資源 ERROR:錯誤跳轉資源 ASYNC:異步訪問資源
2.web.xml註釋
//瀏覽器直接請求資源時,會被執行,轉發不會@WebFilter(value = "/*",dispatcherTypes = DispatcherType.REQUEST)public class FilterTest3 implements Filter { public void destroy() { } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { System.out.println("我被執行啦!"); chain.doFilter(req, resp); } public void init(FilterConfig config) throws ServletException { }}
@WebFilter(value = "/*", dispatcherTypes = {DispatcherType.REQUEST,DispatcherType.FORWARD})
<filter> <filter-name>FilterTest1</filter-name> <filter-class>filter.FilterTest1</filter-class> </filter> <filter-mapping> <filter-name>FilterTest1</filter-name> <url-pattern>/*</url-pattern> <dispatcher>FORWARD</dispatcher> </filter-mapping>
過濾器鏈(多個過濾器):
執行順序
先執行過濾器1,再執行過濾器2,回來先執行過濾器2,再執行過濾器1。
怎麼判斷過濾器誰在前面:
1.註解配置
按照類名的字符串比較規則,較小的先執行。
如:AFilter,BFilter
AFilter就先執行。
2.web.xml配置:
<filter-mapping>
誰定義在上面,誰就先執行。
案例一:登陸驗證
1.訪問某些資源,驗證其是否登陸
2.若是登陸了,則直接放行。
3.若是沒有登陸,則跳轉到登陸頁面,提示,「你還沒有登陸,請先登陸」。
/** * 登陸驗證的過濾器 */@WebFilter("/*")//除了登陸相關資源外(如login.jsp)public class FilterTest4 implements Filter { public void destroy() { } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { //1.強轉 HttpServletRequest request = (HttpServletRequest)req; //2.獲取資源請求路徑 String uri = request.getRequestURI(); //排除登陸相關資源,css/js/fonts等 if(uri.contains("/login.jsp")||uri.contains("/loginServlet")||uri.contains("/css/")||uri.contains("/js/")){ //用戶就是像登陸,放行 chain.doFilter(req, resp); }else{ //不包含,須要驗證用戶是否登陸 //3.從獲取session中獲取user Object user = request.getSession().getAttribute("user"); if(user!=null){ //登陸了,放行 chain.doFilter(req, resp); }else{ //沒有登陸,跳轉到登陸頁面 request.setAttribute("login_msg","你爲登陸,請先登陸!"); request.getRequestDispatcher("/login.jsp"); } } } public void init(FilterConfig config) throws ServletException { }}
案例二:敏感詞彙的過濾
分析:
案例須要對request對象進行加強。
那如進行加強呢?
加強對象的功能
設計模式:一些通用的解決固定問題的方式。
裝飾模式:
代理模式:
概念:
1.真實對象:被代理的對象。
2.代理對象
3.代理模式:代理對象代理真實對象,達到加強真實對象的目的。
實現方式:
1.靜態代理
在一個類文件描述代理模式。
2.動態代理
在內存中造成代理類(在內存中動態生成)。
注:請先看如下動態代理相關知識再看案例二的實現
案例二實現:
1.對requset對象進行加強,加強獲取參數相關方法。
2.放行,傳遞代理對象。
代碼實現:
/** * 敏感詞彙過濾器 */@WebFilter("/*")public class FilterSensitiveWords implements Filter { public void destroy() { } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { //建立代理對象,加強getParameter方法 ServletRequest proxy_req=(ServletRequest) Proxy.newProxyInstance(req.getClass().getClassLoader(), req.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //加強getParameter方法 //判斷是不是該方法 if(method.getName().equals("getParameter")){ //加強返回值 //獲取返回值 String value = (String)method.invoke(req,args); if(value!=null){ for(String str:list){ if(value.contains(str)){ value = value.replaceAll(str,"**"); } } } return value; } return method.invoke(req,args); } }); //2.放行 chain.doFilter(proxy_req, resp); } private List<String> list = new ArrayList<>();//敏感詞彙 public void init(FilterConfig config) throws ServletException { try { //1.加載配置文件(獲取文件的真實路徑) ServletContext servletContext = config.getServletContext(); String realPath = servletContext.getRealPath("/WEB-INF/classes/SensitiveWords.txt"); //2.讀取文件 BufferedReader br = new BufferedReader(new FileReader(realPath)); //3.將文件的每一行加載到list中 String line =null; while ((line = br.readLine())!=null){ list.add(line); } br.close(); System.out.println(list); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }}
測試的servlet:
@WebServlet("/ServletSensitiveWordsTest")public class ServletSensitiveWordsTest extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String name = request.getParameter("name"); String msg =request.getParameter("msg"); System.out.println(name+":"+msg); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request,response); }}
結果:
重點:注意路徑問題:
SensitiveWords在src源文件目錄下。
//1.加載配置文件(獲取文件的真實路徑)
ServletContext servletContext = config.getServletContext();
String realPath = servletContext.getRealPath
("/WEB-INF/classes/SensitiveWords.txt");
實現步驟:
1.代理對象和真實對象實現相同的接口。
2.代理對象= Proxy.newProxyInstance();
3.使用代理對象調用方法。
4.加強方法。
加強方式:
1.加強參數列表。
2.加強返回值類型。
3.加強方法體執行邏輯。
練習理解:
package proxy;public interface SaleComputer { public String sale(double money); public void show();}
/** * 真實類 */public class ASUS implements SaleComputer{ @Override public String sale(double money) { System.out.println("花了"+money+"拍下一臺電腦!"); return "ASUS"; } @Override public void show() { System.out.println("展現電腦!"); }}
public class ProxyTest { public static void main(String[] args) { //建立真實對象 ASUS asus1 = new ASUS(); //動態代理加強ASUS對象 /* 三個參數: 1.類加載器:真實對象.getClass().getClassLoader() 2.接口數組:真實對象.getClass().getInterfaces() 3.處理器:new InvocationHandler() */ SaleComputer proxy_asus=(SaleComputer) Proxy.newProxyInstance(asus1.getClass().getClassLoader(), asus1.getClass().getInterfaces(), new InvocationHandler() { /** * 代理邏輯編寫的方法:代理對象調用的全部方法都會觸發該方法執行 * @param proxy 代理對象 * @param method 代理對象調用的方法被封裝成對象 * @param args 代理對象調用方法時,傳遞的實際參數 * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { /*System.out.println("該方法執行了..."); System.out.println(method.getName());*/ //使用真實對象調用該方法 /* Object obj = method.invoke(asus1,args);*/ //加強參數;判斷是否時sale方法 if(method.getName().equals("sale")){ //加強參數 double money = (double)args[0]; money*=0.8; //使用真實對象調用該方法 String obj = (String) method.invoke(asus1,money); //加強返回值類型 return obj+"_鼠標墊"; }else { Object obj = method.invoke(asus1,args); return obj; } } }); //3.調用方法 String computer = proxy_asus.sale(9000); System.out.println(computer); }}
概念:web的三大組件之一。
事件監聽機制
程序開發中,常常須要對某些事件進行監聽,如鼠標單擊事件,監聽鍵盤按下事件等,此時就須要監聽器。
事件:用戶的一個操做,如點擊按鈕…
事件源:產生事件的對象。
監聽器:負責監聽發生在事件源上的事件。
註冊監聽:將事件,事件源,監聽器綁定在一塊兒。當事件源上發生某個事件後,執行監聽器代碼。
ServletContextListener:監聽ServletContext對象的建立和銷燬。
//Servlet對象被銷燬前會調用此方法void contextDestroyed(ServletContextEvent sce) //ServletContext對象被建立後會調用該方法。void contextInitialized(ServletContextEvent sce)
步驟:
1.定義一個類實現ServletContextListener接口。
2.複寫方法。
3.配置
1.web.xml
<listener> <listener-class>listener/ServletContextListenerTest1</listener-class> </listener>
指定初始化參數:
<context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/classes/applicationContext.xml</param-value> </context-param>
2.註解配置
@WebListener
@WebListenerpublic class ServletContextListenerTest1 implements ServletContextListener { //監聽ServletContext建立,ServletContext對象服務器啓動自動建立 @Override public void contextInitialized(ServletContextEvent sce) { //加載資源文件 //1.獲取ServletContext對象 ServletContext servletContext = sce.getServletContext(); //2.加載資源文件 String contextConfigLocation = servletContext.getInitParameter("contextConfigLocation"); //3.獲取真實路徑 String realPath = servletContext.getRealPath(contextConfigLocation); //4.加載進內存 try { FileInputStream fis = new FileInputStream(realPath); System.out.println(fis); } catch (FileNotFoundException e) { e.printStackTrace(); } } //在服務器關閉後,ServletContext對象被銷燬。當服務器正常關閉後該方法被調用 @Override public void contextDestroyed(ServletContextEvent sce) { }}
一切事沒法追求完美,惟有追求盡力而爲。這樣心無壓力,出來的結果反而會更好。