“別小看任何人,越不起眼的人。往往會做些讓人想不到的事。"你好我是夢陽辰,快來和我一起學習吧!
文章目錄
- 01.Filter概述
- 02.Filter快速入門
- 03.Filter深入
- 04.Filter案例
- 05.動態代理
- 06.Listener概述
- 07.ServletContextListener
01.Filter概述
Filter和Listener是Servlet規範中的兩個高級特性,不同於Servlet,他們不用於處理客戶端請求。
Filter用於對request,response對象進行修改。
Filter被稱作過濾器,其基本功能就是對Servlet容器調用Servlet的過程進行攔截,從而在Servlet進行響應處理前後實現一些特殊的功能。這就好比現實中的污水淨化設備,它可以看作一個過濾器,專門用於過濾污水雜質。
當瀏覽器訪問服務器中的目標資源時,會被Filter攔截,在Filter中進行預處理操作,然後再將請求轉發給目標資源。
當服務器接收到這個請求後會對其進行響應,在服務器處理響應的過程中,也需要先將響應結果發送給過濾器,在過濾器中對響應結果進行處理後,纔會發送給客戶端。
過濾器的作用:
一般完成通用性的操作。
比如:登錄驗證,判斷用戶是否登錄;統一編碼處理,敏感字符處理等。
其實Filter過濾器就是實現了javax.servlet.Filter接口的類,在javax.servlet.Filter定義了三個方法。
Listener用於對context,session,request事件進行監聽。
02.Filter快速入門
步驟:
1.定義一個類,實現接口javax.servlet.Filter。
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>
03.Filter深入
過濾器的流程:
@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>
誰定義在上面,誰就先執行。
04.Filter案例
案例一:登錄驗證
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");
05.動態代理
實現步驟:
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);
}}
06.Listener概述
概念:web的三大組件之一。
事件監聽機制
程序開發中,經常需要對某些事件進行監聽,如鼠標單擊事件,監聽鍵盤按下事件等,此時就需要監聽器。
事件:用戶的一個操作,如點擊按鈕…
事件源:產生事件的對象。
監聽器:負責監聽發生在事件源上的事件。
註冊監聽:將事件,事件源,監聽器綁定在一起。當事件源上發生某個事件後,執行監聽器代碼。
ServletContextListener:監聽ServletContext對象的創建和銷燬。
//Servlet對象被銷燬前會調用此方法void contextDestroyed(ServletContextEvent sce) //ServletContext對象被創建後會調用該方法。void contextInitialized(ServletContextEvent sce)
07.ServletContextListener
步驟:
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) {
}}
一切事無法追求完美,唯有追求盡力而爲。這樣心無壓力,出來的結果反而會更好。