javaweb(4)之Listener&Filter

監聽器 (Listener)

介紹

監聽器用於監聽 web 應用中某些對象、信息的建立、銷燬、增長,修改,刪除等動做的發生,而後做出相應的響應處理。當範圍對象的狀態發生變化的時候,服務器自動調用監聽器對象中的方法。經常使用於統計在線人數和在線用戶,系統加載時進行信息初始化,統計網站的訪問量等等。html

監聽器的做用實際上就是在特定的時間觸發監聽器提供給咱們重寫的鉤子函數。

分類及使用

按監聽對象可分爲如下幾類:java

  • ServletContext

    ServletContextListener 監聽 ServletContext 對象的建立和銷燬:
    package com.zze.listener;
    
    import javax.servlet.ServletContextEvent;
    import javax.servlet.ServletContextListener;
    
    public class MyServletContextListener implements ServletContextListener {
        @Override
        public void contextInitialized(ServletContextEvent servletContextEvent) {
            /*
             * 初始化時調用
             * */
            System.out.println("初始化");
        }
    
        @Override
        public void contextDestroyed(ServletContextEvent servletContextEvent) {
            /*
             * 銷燬時調用(服務器正常關閉或從服務器移除項目時)
             * */
            System.out.println("銷燬");
        }
    }
    com.zze.listener.MyServletContextListener
    <listener>
        <listener-class>com.zze.listener.MyServletContextListener</listener-class>
    </listener>
    web.xml
    ServletContextAttributeListener 監聽對 ServletContext 屬性操做:
    package com.zze.listener;
    
    import javax.servlet.ServletContextAttributeEvent;
    import javax.servlet.ServletContextAttributeListener;
    
    public class MyServletContextAttributeListener implements ServletContextAttributeListener {
        @Override
        public void attributeAdded(ServletContextAttributeEvent servletContextAttributeEvent) {
            System.out.println("添加屬性時觸發");
        }
    
        @Override
        public void attributeRemoved(ServletContextAttributeEvent servletContextAttributeEvent) {
            System.out.println("移除屬性時觸發");
        }
    
        @Override
        public void attributeReplaced(ServletContextAttributeEvent servletContextAttributeEvent) {
            System.out.println("修改屬性時觸發");
        }
    }
    com.zze.listener.MyServletContextAttributeListener
    <listener>
        <listener-class>com.zze.listener.MyServletContextAttributeListener</listener-class>
    </listener>
    web.xml
  • HttpSession

    HttpSessionListener 監聽 Session 對象的建立和銷燬:web

    package com.zze.listener;
    
    import javax.servlet.http.HttpSessionEvent;
    import javax.servlet.http.HttpSessionListener;
    
    public class MyHttpSessionListener implements HttpSessionListener {
        @Override
        public void sessionCreated(HttpSessionEvent httpSessionEvent) {
            System.out.println("session 建立時調用");
        }
    
        @Override
        public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
            System.out.println("session 銷燬時調用");
        }
    }
    com.zze.listener.MyHttpSessionListener
    <listener>
        <listener-class>com.zze.listener.MyHttpSessionListener</listener-class>
    </listener>
    web.xml

    HttpSessionAttributeListener 監聽 Session 中的屬性操做:apache

    package com.zze.listener;
    
    import javax.servlet.http.HttpSessionAttributeListener;
    import javax.servlet.http.HttpSessionBindingEvent;
    
    public class MyHttpSessionAttributeListener implements HttpSessionAttributeListener {
    
        @Override
        public void attributeAdded(HttpSessionBindingEvent httpSessionBindingEvent) {
            System.out.println("添加屬性時觸發");
        }
    
        @Override
        public void attributeRemoved(HttpSessionBindingEvent httpSessionBindingEvent) {
            System.out.println("移除屬性時觸發");
        }
    
        @Override
        public void attributeReplaced(HttpSessionBindingEvent httpSessionBindingEvent) {
            System.out.println("修改屬性時觸發");
        }
    }
    com.zze.listener.MyHttpSessionAttributeListener
    <listener>
        <listener-class>com.zze.listener.MyHttpSessionAttributeListener</listener-class>
    </listener>
    web.xml
    HttpSessionBindingListener 監聽對象在 Session 中的綁定和解綁操做:
    package com.zze.com.zze.bean;
    
    import javax.servlet.http.HttpSessionBindingEvent;
    import javax.servlet.http.HttpSessionBindingListener;
    
    public class User implements HttpSessionBindingListener {
        private String name;
        private Integer age;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        @Override
        public void valueBound(HttpSessionBindingEvent httpSessionBindingEvent) {
            System.out.println("被綁定到 session");
        }
    
        @Override
        public void valueUnbound(HttpSessionBindingEvent httpSessionBindingEvent) {
            System.out.println("從 session 中解綁");
        }
    }
    com.zze.bean.User
    package com.zze.servlet;
    
    import com.zze.com.zze.bean.User;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    import java.io.IOException;
    
    @WebServlet("/test")
    public class TestServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            HttpSession session = req.getSession();
            session.setAttribute("user", new User()); // 綁定 觸發 valueBound
            session.removeAttribute("user");// 解綁 觸發 valueUnbound
        }
    }
    servlet

    HttpSessionActivationListener 監聽對象在 Session 中的鈍化與活化:數組

    package com.zze.bean;
    
    import javax.servlet.http.HttpSessionActivationListener;
    import javax.servlet.http.HttpSessionEvent;
    import java.io.Serializable;
    
    public class User implements HttpSessionActivationListener,Serializable {
        private String name;
        private Integer age;
    
        public User(String name, Integer age) {
            this.name = name;
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        @Override
        public void sessionWillPassivate(HttpSessionEvent httpSessionEvent) {
            System.out.println("鈍化");
        }
    
        @Override
        public void sessionDidActivate(HttpSessionEvent httpSessionEvent) {
            System.out.println("活化");
        }
    }
    com.zze.bean.User
    package com.zze.servlet;
    
            import com.zze.com.zze.bean.User;
    
            import javax.servlet.annotation.WebServlet;
            import javax.servlet.http.HttpServlet;
            import javax.servlet.http.HttpServletRequest;
            import javax.servlet.http.HttpServletResponse;
            import javax.servlet.http.HttpSession;
    
    @WebServlet("/test")
    public class TestServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
            HttpSession session = req.getSession();
            session.setAttribute("user", new User("zhangsan",19)); // 存儲到 session
        }
    }
    servlet

    鈍化:將內存中的數據序列化保存到硬盤。tomcat

    活化:將硬盤中的數據反序列化加載到內存。服務器

    session 的鈍化和活化用意何在?

    session 中的值可能會不少,而且可能咱們很長一段時間都不會使用這個值,那麼能夠考慮將 session 中的值存儲到硬盤,等要使用的時候再從硬盤中提取,減輕內存壓力。session

    默認狀況下,在服務器正常關閉時會鈍化保存在 session 中的數據,而在服務器再次啓動時會活化以前鈍化的數據。app

    配置鈍化活化:
    <?xml version="1.0" encoding="UTF-8"?>
    
    <Context>
        <!--maxIdleSwap :設置間隔多久時間鈍化,單位爲分鐘-->
        <Manager className="org.apache.catalina.session.PersistentManager" maxIdleSwap="1">
            <!--directory:session 鈍化保存的目錄-->
            <Store className="org.apache.catalina.session.FileStore" directory="sessionDir"/>
        </Manager>
    </Context>
    context.xml

    在 tomcat 目錄下 /conf/context.xml 中配置以上內容,對託管到當前服務器的全部程序生效。ide

    在 tomcat 目錄下 /conf/Catalina/localhost/context.xml 中配置以上內容,對託管到當前服務器中使用 localhost 訪問的程序生效。

    在項目目錄下 /META-INF/context.xml 中配置以上內容,僅對當前項目生效。

    注意:由於鈍化和活化其實是序列化和反序列化的過程,因此要鈍化的對象類必須實現 Serializable 接口。
  • HttpServletRequest

    ServletRequestListener 監聽 Request 對象的建立和銷燬:
    package com.zze.listener;
    
    import javax.servlet.ServletRequestEvent;
    import javax.servlet.ServletRequestListener;
    
    public class MyServletRequestListener implements ServletRequestListener {
    
        @Override
        public void requestInitialized(ServletRequestEvent servletRequestEvent) {
            System.out.println("初始化");
        }
    
        @Override
        public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
            System.out.println("銷燬");
        }
    
    }
    com.zze.listener.MyServletRequestListener
    <listener>
        <listener-class>com.zze.listener.MyServletRequestListener</listener-class>
    </listener>
    web.xml

    ServletRequestAttributeListener 監聽 Request 中的屬性操做:

    package com.zze.listener;
    
    import javax.servlet.ServletRequestAttributeEvent;
    import javax.servlet.ServletRequestAttributeListener;
    
    public class MyServletRequestAttributeListener implements ServletRequestAttributeListener {
        @Override
        public void attributeAdded(ServletRequestAttributeEvent servletRequestAttributeEvent) {
            System.out.println("添加屬性時觸發");
        }
    
        @Override
        public void attributeRemoved(ServletRequestAttributeEvent servletRequestAttributeEvent) {
            System.out.println("移除屬性時觸發");
        }
    
        @Override
        public void attributeReplaced(ServletRequestAttributeEvent servletRequestAttributeEvent) {
            System.out.println("修改屬性時觸發");
        }
    }
    com.zze.listener.MyServletRequestAttributeListener
    <listener>
        <listener-class>com.zze.listener.MyServletRequestAttributeListener</listener-class>
    </listener>
    web.xml

過濾器 (filter)

介紹

Filter 也稱之爲過濾器,它是 Servlet 技術中最實用的技術,Web 開發人員經過 Filter 技術,對 web 服務器管理的全部 web 資源:例如 Jsp , Servlet ,  靜態圖片文件或靜態 html 文件等進行攔截,從而實現一些特殊的功能。例如實現 URL 級別的權限訪問控制、過濾敏感詞彙、壓縮響應信息等一些高級功能。

它主要用於對用戶請求進行預處理,也能夠對 HttpServletResponse 進行後處理。使用 Filter 的完整流程:Filter 對用戶請求進行預處理,接着將請求交給 Servlet 進行處理並生成響應,最後 Filter 再對服務器響應進行後處理。

使用

package com.zze.filter;

import javax.servlet.*;
import java.io.IOException;

public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("初始化");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("前處理");
        filterChain.doFilter(servletRequest, servletResponse); // 若是不調用 後續的過濾器及 servlet 都不會執行
        System.out.println("後處理");
    }

    @Override
    public void destroy() {
        System.out.println("銷燬");
    }
}
com.zze.filter.MyFilter
<filter>
    <filter-name>myFilter</filter-name>
    <filter-class>com.zze.filter.MyFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>myFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
<!--    
    <filter>:指定一個過濾器。
    <filter-name>:用於爲過濾器指定一個名字,該元素的內容不能爲空。
    <filter-class>:用於指定過濾器的完整的限定類名。
    <init-param>:用於爲過濾器指定初始化參數,它的子元素 <param-name> 指定參數的名字,<param-value> 指定參數的值。在過濾器中,可使用 FilterConfig 接口對象來訪問初始化參數。
    <filter-mapping>:用於設置一個 Filter 所負責攔截的資源。一個 Filter 攔截的資源可經過兩種方式來指定:Servlet 名稱和資源訪問的請求路徑
    <filter-name>子元素用於設置 filter 的註冊名稱。該值必須是在 <filter> 元素中聲明過的過濾器的名字
    <url-pattern>設置 filter 所攔截的請求路徑(過濾器關聯的URL樣式)
    <servlet-name>指定過濾器所攔截的 Servlet 名稱。
    <dispatcher>指定過濾器所攔截的資源被 Servlet 容器調用的方式,能夠是 REQUEST , INCLUDE , FORWARD 和 ERROR 之一,默認REQUEST。用戶能夠設置多個 <dispatcher> 子元素用來指定 Filter 對資源的多種調用方式進行攔截。
        REQUEST:當用戶直接訪問頁面時,Web容器將會調用過濾器。若是目標資源是經過RequestDispatcher的include()或forward()方法訪問時,那麼該過濾器就不會被調用。
        INCLUDE:若是目標資源是經過RequestDispatcher的include()方法訪問時,那麼該過濾器將被調用。除此以外,該過濾器不會被調用。
        FORWARD:若是目標資源是經過RequestDispatcher的forward()方法訪問時,那麼該過濾器將被調用,除此以外,該過濾器不會被調用。
        ERROR:若是目標資源是經過聲明式異常處理機制調用時,那麼該過濾器將被調用。除此以外,過濾器不會被調用。
-->
web.xml
Filter鏈:

在一個web應用中,能夠開發編寫多個 Filter ,這些 Filter 組合起來稱之爲一個 Filter 鏈。

web 服務器根據 Filter 在 web.xml 文件中的註冊順序,決定先調用哪一個 Filter,當第一個 Filter 的 doFilter 方法被調用時,web 服務器會建立一個表明 Filter 鏈的 FilterChain 對象傳遞給該方法。在 doFilter 方法中,開發人員若是調用了 FilterChain 對象的 doFilter 方法,則 web 服務器會檢查 FilterChain 對象中是否還有 filter,若是有,則調用第2個 filter ,若是沒有,則調用目標資源。

生命週期:
public void init(FilterConfig filterConfig) throws ServletException;// 初始化
/*
和咱們編寫的 Servlet 程序同樣,Filter 的建立和銷燬由 WEB 服務器負責。
web 應用程序啓動時,web 服務器將建立 Filter 的實例對象,並調用其 init 方法,讀取 web.xml 配置,完成對象的初始化功能,從而爲後續的用戶請求做好攔截的準備工做(filter 對象只會建立一次,init 方法也只會執行一次)。
開發人員經過 init 方法的參數,可得到表明當前 filter 配置信息的 FilterConfig 對象。
*/

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException;// 攔截請求
/*
這個方法完成實際的過濾操做。當客戶請求訪問與過濾器關聯的URL的時候,Servlet 過濾器將先執行 doFilter 方法。FilterChain 參數用於訪問後續過濾器。
*/

public void destroy();//銷燬
/*
Filter 對象建立後會駐留在內存,當 web 應用移除或服務器中止時才銷燬。
在Web容器卸載 Filter 對象以前被調用。該方法在 Filter 的生命週期中僅執行一次。
在這個方法中,能夠釋放過濾器使用的資源。
*/

補充

記住密碼自動登陸示例

點擊下載

過濾器解決亂碼

package com.zze.filter;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Map;
/**
 * 統一編碼
 */
public class EncodingFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

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

        chain.doFilter(new MyRequest(request), response);
    }

    @Override
    public void destroy() {

    }

}
class MyRequest extends HttpServletRequestWrapper{
    private HttpServletRequest request;
    private boolean flag=true;

    public MyRequest(HttpServletRequest request) {
        super(request);
        this.request=request;
    }
    @Override
    public String getParameter(String name) {  
        if(name==null || name.trim().length()==0){
            return null;
        }
        String[] values = getParameterValues(name);
        if(values==null || values.length==0){
            return null;
        }
        return values[0];
    }

    @Override
    public String[] getParameterValues(String name) {
        if(name==null || name.trim().length()==0){
            return null;
        }
        Map<String, String[]> map = getParameterMap();
        if(map==null || map.size()==0){
            return null;
        }
        return map.get(name);
    }

    @Override
    public Map<String,String[]> getParameterMap() {
        String method = request.getMethod();
        if("post".equalsIgnoreCase(method)){
            try {
                request.setCharacterEncoding("utf-8");
                return request.getParameterMap();
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }else if("get".equalsIgnoreCase(method)){
            Map<String,String[]> map = request.getParameterMap();
            if(flag){
                for (String key:map.keySet()) {
                    String[] arr = map.get(key);
                    //繼續遍歷數組
                    for(int i=0;i<arr.length;i++){
                        //編碼
                        try {
                            arr[i]=new String(arr[i].getBytes("iso8859-1"),"utf-8");
                        } catch (UnsupportedEncodingException e) {
                            e.printStackTrace();
                        }
                    }
                }
                flag=false;
            }
            return map;
        }
        return super.getParameterMap();
    }
}
EncodingFilter
相關文章
相關標籤/搜索