1. 程式人生 > 實用技巧 >Listener&Filter

Listener&Filter

Listener&Filter

1.Listener

監聽器,用於監聽某一個事件的發生。

監聽器的內部機制:介面回撥。

1.1介面回撥

需求:

A類執行迴圈,當迴圈次數為5時,通知B。

  • 傳統做法

    傳遞B的例項給A。

public class A {
    public static void loop(){
        for(int i=0;i<10;i++){
            if(i==5){
                B b=new B();
                b.print();
            }
        }
    }
}

public class B {
    public void print(){
        System.out.println("A迴圈了5次,通知到B");
    }
}

public class Test1 {
    public static void main(String[] args) {
        A a=new A();
        a.loop();
    }
}
  • 介面回撥做法

    傳遞一個介面PrintListener給A,B實現了該介面,因為A可能在B之前就寫好了,此時A並不知道有B的存在,也就更無法直接接收B的例項了,所以可以利用介面回撥解決。

public class A {
    public static void loop(PrintListener printListener){
        for(int i=0;i<10;i++){
            if(i==5){
                printListener.print();
            }
        }
    }
}

public class B implements PrintListener{
    public void print(){
        System.out.println("A迴圈了5次,通知到B");
    }
}

public interface PrintListener {
    public void print();
}

public class Test1 {
    public static void main(String[] args) {
        A a=new A();
        a.loop(new B());//A中的loop方法接收的是PrintListener,而這裡可以傳遞PrintListener的實現類的例項物件,這是多型的體現。
    }
}

1.2Web監聽器

總共有8個,分為三種類型。

使用步驟都一致:

  1. 定義一個類,實現監聽器介面
  2. 註冊/配置監聽器

1.2.1監聽作用域的建立和銷燬

作用域共有4個:page,request,session,application,監聽作用域的建立和銷燬是監聽request,session,application三個作用域的建立和銷燬。

  • ServletRequestListener

監聽request作用域物件的建立和銷燬。

request對應型別為HttpServletRequest。

reqeust建立:訪問伺服器上的任意資源時,如html,jsp,servlet;

request銷燬:伺服器已經對這次請求做出了響應後。

@WebListener//註冊監聽器,相當於在web.xml中配置的那一段
public class MyServletRequestListener implements ServletRequestListener {

    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
        System.out.println("request銷燬了");
    }

    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        System.out.println("request初始化了");
    }
}

在web.xml中註冊監聽器:

<listener>
        <listener-class>com.itheima.listener.MyServletRequestListener</listener-class>
</listener>
  • ServletContextListener

監聽application作用域物件的建立和銷燬。

application對應型別為ServletContext。

ServletContext建立:伺服器啟動時;

ServletContext銷燬:正常關閉伺服器。

作用:完成初始化工作,執行自定義任務排程。

@WebListener
public class MyServletContextListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("servletContext初始化了");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("servletContext銷燬了");
    }
  • HttpSessionListener

監聽session作用域物件的建立和銷燬。

session對應型別為HttpSession。

session建立:呼叫request.getSession(),jsp中預設建立了session;

session銷燬:關閉伺服器(包括正常和非正常);Session會話時間過期,預設是30分鐘。

作用:可以統計線上人數。

@WebListener
public class MyHttpSessionListener implements HttpSessionListener {
    @Override
    public void sessionCreated(HttpSessionEvent se) {
        System.out.println("session建立了");
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        System.out.println("session銷燬了");
    }
}

1.2.2監聽作用域屬性狀態變更

監聽在servletContext,request,session作用域中屬性的新增,替換,移除動作。

  • ServletContextAttributeListener

@WebListener
public class MyServletContextAttributeListener implements ServletContextAttributeListener {
    @Override
    public void attributeAdded(ServletContextAttributeEvent scae) {
        System.out.println("servletContext屬性添加了");
    }

    @Override
    public void attributeRemoved(ServletContextAttributeEvent scae) {
        System.out.println("servletContext屬性移除了");
    }

    @Override
    public void attributeReplaced(ServletContextAttributeEvent scae) {
        System.out.println("servletContext屬性替換了");
    }
}
<body>
    <%
        //新增name屬性
        application.setAttribute("name","zhangsan");
        //替換name屬性
        application.setAttribute("name","wanger");
        //移除name屬性
        application.removeAttribute("name");
    %>
</body>
  • ServletRequestAttributeListener

  • HttpSessionAttributeListener

1.2.3監聽HttpSession裡存值的狀態的變更

此類監聽器無須註冊。

  • HttpSessionBindingListener

監聽物件與session繫結和解除繫結的動作,此物件要實現HttpSessionBindingListener。

public class Student implements HttpSessionBindingListener {
    private int id;
    private String name;
    @Override
    public void valueBound(HttpSessionBindingEvent event) {
        System.out.println("Student物件被繫結到session了");
    }

    @Override
    public void valueUnbound(HttpSessionBindingEvent event) {
        System.out.println("Student物件從session中解除綁定了");
    }
	...
}
<%
        session.setAttribute("obj1",new Student(1,"小王"));
        session.removeAttribute("obj1");
%>
  • HttpSessionActivationListener

用於監聽現在session的值是鈍化(序列化)還是活化(反序列化)的動作。

鈍化:把記憶體中的資料儲存到硬碟上;

活化:把硬碟中的資料讀取到記憶體中。

鈍化活化作用:session中的值可能會很多,並且可能很長一段時間不使用這個記憶體中的值,那麼可以考慮把session的值可以儲存到硬碟上【鈍化】,等下一次使用時,再從硬碟上提取出來【活化】。

  1. Student2.java
public class Student2 implements HttpSessionActivationListener, Serializable {
    private int id;
    private String name;

    @Override
    public void sessionWillPassivate(HttpSessionEvent se) {
        System.out.println("session被鈍化了");
    }

    @Override
    public void sessionDidActivate(HttpSessionEvent se) {
        System.out.println("session被活化了");
    }
   ...
}
  1. index2.jsp

    index2.jsp將Student2存入到session,然後正常關閉伺服器,session 將被鈍化。

<%
    //存入到session
    session.setAttribute("obj2",new Student2(1,"coco"));
%>
  1. index3.jsp

    重啟伺服器後,session將被活化,此時訪問index3.jsp可以取到值。

<%--取值--%>
${obj2.name}
  • 配置鈍化活化

配置讓session在一定時間內鈍化。

三種方式:

  1. 在tomcat的conf/context.xml裡面配置;

對所有的執行在這個伺服器的專案生效。

  1. 在conf/Catalina/localhost/context.xml配置;

對localhost生效。 localhost:8080

  1. 在自己的web工程專案中的 META-INF/context.xml配置;

只對當前的工程生效。

maxIdleSwap:1分鐘不用就鈍化
directory:鈍化後的那個檔案存放的目錄位置。

D:\tomcat\apache-tomcat-7.0.52\work\Catalina\localhost\ListenerDemo\itheima

​ //itheima為相對路徑,存放到了D:\tomcat\apache-tomcat-7.0.52\work\Catalina\localhost\ListenerDemo\itheima


2.Filter

過濾器,其實就是對客戶端發出來的請求進行過濾,起到攔截作用。

作用:過濾敏感詞彙;統一設定編碼;自動登入等。

2.1如何使用Filter

  1. 定義一個類,實現Filter(注意是javax.servlet.Filter)
@WebFilter(filterName = "FilterDemo1",urlPatterns = "/*")//可簡寫成下面這行
//@WebFilter("/*")
public class FilterDemo1 implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("過濾器1初始化了");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("來到了過濾器1");
        //放行,請求會到達下一個目標
  		filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void destroy() {
        System.out.println("過濾器1被銷燬了");
    }
}
  1. 註冊過濾器

兩種註冊方式:在web.xml中註冊,或在類上用註解註冊,見上。

<filter>
    <filter-name>FilterDemo1</filter-name>
    <filter-class>com.itheima.Filter.FilterDemo1</filter-class>
</filter>
<filter-mapping>
    <filter-name>FilterDemo1</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

2.2Filter的生命週期

  • 建立

在伺服器啟動時就建立。

  • 銷燬

在伺服器關閉時就銷燬。

2.3Filter的執行順序

  1. 客戶端發出請求,先經過過濾器,如果過濾器放行,那麼才能到servlet;
  2. 如果有多個過濾器,請求過來時,會按照在web.xml中註冊的對映順序()依次通過,只要其中任意一個不放行,那麼後面的過濾器及servlet都不會收到請求,伺服器響應回去時也會依次倒著經過過濾器。

2.4Filter細節

  1. init方法中的FilterConfig引數可以用於獲取Filter註冊的名字及初始化引數,與ServletConfig類似;

  2. Filter的攔截路徑,寫法與servlet的一樣:

    • 全路徑匹配,以/開始

      如/LoginServlet

    • 目錄匹配,以/開始,以*結束

      /demo01/*

    • 字尾名匹配,以*開始,以後綴名結束

      .jsp,.html,*.do等

  3. 攔截規則也可針對dispatcher設定

    filter-mapping>
        <filter-name>FilterDemo1</filter-name>
        <dispatcher>REQUEST</dispatcher>
    	<!--只要是請求過來都攔截,預設就是REQUEST-->
        <dispatcher>FORWARD</dispatcher>
    	<!--只要是轉發都攔截-->
        <dispatcher>ERROR</dispatcher>
    	<!--只要是頁面出錯跳轉時就攔截-->
        <dispatcher>INCLUDE</dispatcher>
    	<!--包含頁面時就攔截-->
    </filter-mapping>
    

2.5自動登入案例

訪問login.jsp頁面不攔截,訪問index2.jsp攔截,依次判斷session和cookie,實現自動登入。

  • login.jsp
<form action="LoginServlet" method="post" >
        賬號:<input type="text" name="username"><br/>
        密碼:<input type="text" name="password"><br/>
        <input type="checkbox" name="autoLogin">自動登入<br/>
        <input type="submit" value="登入">
</form>
  • LoginServlet
@WebServlet("/LoginServlet")
public class LoginServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        UserDao userDao=new UserDaoImpl();
        User user = userDao.loginCheck(username, password);
        if(user!=null){
            //判斷是否勾選了自動登入
            String autoLogin = request.getParameter("autoLogin");
            if("on".equals(autoLogin)){
                //勾選了自動登入
                Cookie cookie=new Cookie("info",username+"#"+password);
                cookie.setMaxAge(60*60*24*7);
                //cookie.setPath("/StuSysMvc");
                cookie.setPath(request.getContextPath());//當前應用名
                //傳送cookie到客戶端
                response.addCookie(cookie);
            }
            //將使用者資訊儲存到session
            request.getSession().setAttribute("user",user);
            response.sendRedirect("index2.jsp");
        }else{
            response.sendRedirect("login.jsp");
        }
    }
}
  • index2.jsp
<body>
    <c:if test="${not empty sessionScope.user}">
        歡迎來自${sessionScope.user.address}的${sessionScope.user.username}!!!
    </c:if>
    <c:if test="${empty sessionScope.user}">
        您好,請<a href="login.jsp">登入</a>!
    </c:if>
</body>
  • AutoLoginFilter
@WebFilter("/index2.jsp")
public class AutoLoginFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        //1.先判斷session是否失效,如果失效再判斷cookie
        User sessionUser = (User) request.getSession().getAttribute("user");
        if (sessionUser == null) {
            //2.如果session失效,判斷cookie
            Cookie[] cookies = request.getCookies();
            Cookie autoLoginCookie = CookieUtils.findCookie(cookies, "info");
            if (autoLoginCookie != null) {
                //session失效了,但cookie不為空,所以要重新登入
                String value = autoLoginCookie.getValue();
                String[] split = value.split("#");
                UserDao userDao = new UserDaoImpl();
                User user = userDao.loginCheck(split[0], split[1]);
                //重新登入成功後,再把session放回request
                request.getSession().setAttribute("user", user);
            }
        }
        //放行到index2.jsp,在index2.jsp判斷是否登入
        filterChain.doFilter(request, servletResponse);
    }

    @Override
    public void destroy() {
    }
}

2.6BeanUtils

依賴jar包:

對於這種情況,提交上來的引數很多,而這些引數又恰好能封裝成一個JavaBean,可以使用BeanUtils簡化程式碼。

  • 傳統做法:
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");//只針對post請求有效
        String username = request.getParameter("username");
        String password=request.getParameter("password");
        String address=request.getParameter("addresss");
        String phone=request.getParameter("phone");
        String birthday=request.getParameter("birthday");
        String email=request.getParameter("email");
		User user=new User();
        
        user.setUsername(username);
        user.setPassword(password);
        user.setAddress(address);
        user.setPhone(phone);
        user.setEmail(email);
        try {
            user.setBirthday(new SimpleDateFormat("yyyy-MM-dd").parse(birthday));
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
  • BeanUtils做法
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");

        //註冊自己的日期轉換器,將String型別的birthday轉換為Date型別
        ConvertUtils.register(new MyDateConverter(),Date.class);

        User user=new User();
        Map<String, String[]> parameterMap = request.getParameterMap();
        try {
            BeanUtils.populate(user,parameterMap);
        } catch (Exception e)  {
            e.printStackTrace();
        }
    }

自定義時間轉換器MyDateConverter:

public class MyDateConverter implements Converter {
	@Override
	// 將value 轉換 c 對應型別
	// 存在Class引數目的編寫通用轉換器,如果轉換目標型別是確定的,可以不使用c 引數
	public Object convert(Class c, Object value) {
		String strVal = (String) value;
		// 將String轉換為Date --- 需要使用日期格式化
		DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
		try {
			Date date = dateFormat.parse(strVal);
			return date;
		} catch (ParseException e) {
			e.printStackTrace();
		}
		return null;
	}
}