Filter:過濾器
1. 概念:
- 生活中的過濾器:淨水器,空氣淨化器
- web中的過濾器:當訪問服務器的資源時,過濾器可以將請求攔截下來,完成一些特殊的功能。
- 過濾器的作用:
一般用於完成通用的操作。如:登錄驗證、統一編碼處理、敏感字符過濾…
2. 快速入門:
1. 步驟:
- 定義一個類,實現接口Filter
- 複寫方法
- 配置攔截路徑
1. web.xml
2. 註解
2. 代碼:
@WebFilter("/*")//訪問所有資源之前,都會執行該過濾器
public class FilterDemo1 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("filterDemo1被執行了....");
//放行
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
}
3. 過濾器細節:
- web.xml配置
<filter>
<filter-name>demo1</filter-name>
<filter-class>cn.itcast.web.filter.FilterDemo1</filter-class>
</filter>
<filter-mapping>
<filter-name>demo1</filter-name>
<!-- 攔截路徑 -->
<url-pattern>/*</url-pattern>
</filter-mapping>
- 過濾器執行流程
1. 執行過濾器
2. 執行放行後的資源
3. 回來執行過濾器放行代碼下邊的代碼
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//對request對象請求消息增強
System.out.println("filterDemo2執行了....");
//放行
chain.doFilter(req, resp);
//對response對象的響應消息增強
System.out.println("filterDemo2回來了...");
}
- 過濾器生命週期方法
- init:在服務器啓動後,會創建Filter對象,然後調用init方法。只執行一次。用於加載資源
- doFilter:每一次請求被攔截資源時,會執行。執行多次
- destroy:在服務器關閉後,Filter對象被銷燬。如果服務器是正常關閉,則會執行destroy方法。只執行一次。用於釋放資源
- 過濾器配置詳解
- 攔截路徑配置:
- 具體資源路徑: /index.jsp 只有訪問index.jsp資源時,過濾器纔會被執行
- 攔截目錄: /user/* 訪問/user下的所有資源時,過濾器都會被執行
- 後綴名攔截: *.jsp 訪問所有後綴名爲jsp資源時,過濾器都會被執行
- 攔截所有資源:/* 訪問所有資源時,過濾器都會被執行
- 攔截方式配置:資源被訪問的方式
- 註解配置( 設置dispatcherTypes屬性):
- REQUEST:默認值。瀏覽器直接請求資源
- FORWARD:轉發訪問資源
- INCLUDE:包含訪問資源
- ERROR:錯誤跳轉資源
- ASYNC:異步訪問資源
- web.xml配置: 設置<dispatcher></dispatcher>標籤即可
- 過濾器鏈(配置多個過濾器)
- 執行順序:如果有兩個過濾器:過濾器1和過濾器2
- 過濾器1
- 過濾器2
- 資源執行
- 過濾器2
- 過濾器1
- 過濾器先後順序問題:
- 註解配置:按照類名的字符串比較規則比較,值小的先執行。如: AFilter 和 BFilter,AFilter就先執行了。
- web.xml配置: <filter-mapping>誰定義在上邊,誰先執行
案例:
1. 案例1_登錄驗證
需求:
1. 訪問day17_case案例的資源。驗證其是否登錄
2. 如果登錄了,則直接放行。
3. 如果沒有登錄,則跳轉到登錄頁面,提示"您尚未登錄,請先登錄"。
@WebFilter("/*")
public class LoginFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//0.強制轉換
HttpServletRequest request = (HttpServletRequest) req;
//1.獲取資源請求路徑
String uri = request.getRequestURI();
//2.判斷是否包含登錄相關資源路徑,要注意排除掉 css/js/圖片/驗證碼等資源
if (uri.contains("/login.jsp")||uri.contains("/LoginServlet")||uri.contains("/css")||
uri.contains("/js")||uri.contains("/fonts")||uri.contains("/CheckCodeServlet"))
{
//包含,用戶就是想登錄。放行
chain.doFilter(req, resp);
}else {
Object user = request.getSession().getAttribute("loginUser");
if (user!=null){
chain.doFilter(req, resp);
}else {
//沒有登錄。跳轉登錄頁面
request.setAttribute("login_msg","您尚未登錄!!");
request.getRequestDispatcher("/login.jsp").forward(request,resp);
}
}
}
public void init(FilterConfig config) throws ServletException {
}
}
2. 案例2_敏感詞彙過濾
需求:
1. 對day17_case案例錄入的數據進行敏感詞彙過濾
2. 敏感詞彙參考《敏感詞彙.txt》
3. 如果是敏感詞彙,替換爲 ***
分析:
1. 對request對象進行增強。增強獲取參數相關方法
2. 放行。傳遞代理對象
增強對象的功能:通過設計模式解決(一些通用的解決固定問題的方式)
- 裝飾模式
- 代理模式
- 概念:
- 真實對象:被代理的對象
- 代理對象:
- 代理模式:代理對象代理真實對象,達到增強真實對象功能的目的
- 實現方式:
- 靜態代理:有一個類文件描述代理模式
- 動態代理:在內存中形成代理類
實現步驟:
1. 代理對象和真實對象實現相同的接口
2. 代理對象 = Proxy.newProxyInstance();
3. 使用代理對象調用方法。
4. 增強方法
增強方式:
1. 增強參數列表
2. 增強返回值類型
3. 增強方法體執行邏輯
動態代理舉例
public class ProxyTest {
public static void main(String[] args) {
//1.創建真實對象
Lenovo lenovo = new Lenovo();
//2.動態代理增強lenovo對象
/*
三個參數:
1. 類加載器:真實對象.getClass().getClassLoader()
2. 接口數組:真實對象.getClass().getInterfaces()
3. 處理器:new InvocationHandler()
*/
SaleComputer proxy_lenovo = (SaleComputer) Proxy.newProxyInstance(lenovo.getClass().getClassLoader(), lenovo.getClass().getInterfaces(), new InvocationHandler() {
/*
代理邏輯編寫的方法:代理對象調用的所有方法都會觸發該方法執行
參數:
1. proxy:代理對象
2. method:代理對象調用的方法,被封裝爲的對象
3. args:代理對象調用的方法時,傳遞的實際參數
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//判斷是否是sale方法
if(method.getName().equals("sale")){
//1.增強參數
double money = (double) args[0];
money = money * 0.85;
System.out.println("專車接你....");
//使用真實對象調用該方法
String obj = (String) method.invoke(lenovo, money);
System.out.println("免費送貨...");
//2.增強返回值
return obj+"_鼠標墊";
}else{
Object obj = method.invoke(lenovo, args);
return obj;
}
}
});
//3.調用方法
/* String computer = proxy_lenovo.sale(8000);
System.out.println(computer);*/
proxy_lenovo.show();
}
}
敏感詞彙過濾器
/**
* 敏感詞彙過濾器
*/
@WebFilter("/*")
public class SensitiveWordsFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//1.創建代理對象,增強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方法
//判斷是否是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;
}
//判斷方法名是否是 getParameterMap
//判斷方法名是否是 getParameterValue
return method.invoke(req,args);
}
});
//2.放行
chain.doFilter(proxy_req, resp);
}
private List<String> list = new ArrayList<String>();//敏感詞彙集合
public void init(FilterConfig config) throws ServletException {
try{
//1.獲取文件真實路徑
ServletContext servletContext = config.getServletContext();
String realPath = servletContext.getRealPath("/WEB-INF/classes/敏感詞.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 (Exception e){
e.printStackTrace();
}
}
public void destroy() {
}
}
Listener:監聽器
概念:web的三大組件之一。
事件監聽機制
- 事件 :一件事情
- 事件源 :事件發生的地方
- 監聽器 :一個對象
- 註冊監聽:將事件、事件源、監聽器綁定在一起。 當事件源上發生某個事件後,執行監聽器代碼
ServletContextListener:監聽ServletContext對象的創建和銷燬
- 方法:
- void contextDestroyed(ServletContextEvent sce) :ServletContext對象被銷燬之前會調用該方法
- void contextInitialized(ServletContextEvent sce) :ServletContext對象創建後會調用該方法
- 步驟:
- 定義一個類,實現ServletContextListener接口
- 複寫方法
- 配置
- web.xml–指定初始化參數<context-param>
<listener>
<listener-class>cn.itcast.web.listener.ContextLoaderListener</listener-class>
</listener>
2. 註解:@WebListener
@WebListener
public class ContextLoaderListener implements ServletContextListener {
/**
* 監聽ServletContext對象創建的。ServletContext對象服務器啓動後自動創建。
*
* 在服務器啓動後自動調用
* @param servletContextEvent
*/
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
//加載資源文件
//1.獲取ServletContext對象
ServletContext servletContext = servletContextEvent.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 (Exception e){
e.printStackTrace();
}
System.out.println("ServletContext對象被創建了。。。");
}
/**
* 在服務器關閉後,ServletContext對象被銷燬。當服務器正常關閉後該方法被調用
* @param servletContextEvent
*/
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.println("ServletContext對象被銷燬了。。。");
}
}