1. 程式人生 > >Java進階學習第十二天(監聽器、國際化)

Java進階學習第十二天(監聽器、國際化)

一、監聽器

1、監聽器(listener):主要是用來監聽特定物件的建立或銷燬、屬性的變化的!是一個實現特定介面的普通java類!

2、物件: 自己建立自己用(不用監聽) 別人建立自己用(需要監聽)

3、Servlet中哪些物件需要監聽? request / session / servletContext 分別對應的是request監聽器、session相關監聽器、servletContext監聽器

4、監聽器介面 ① 監聽物件建立/銷燬的監聽器介面

Interface ServletRequestListener     //監聽request物件的建立或銷燬
Interface
HttpSessionListener //監聽session物件的建立或銷燬 Interface ServletContextListener //監聽servletContext物件的建立或銷燬

② 監聽物件屬性的變化

Interface ServletRequestAttributeListener  //監聽request物件屬性變化: 新增、移除、修改
Interface HttpSessionAttributeListener     //監聽session物件屬性變化: 新增、移除、修改
Interface ServletContextAttributeListener  //監聽servletContext物件屬性變化: 新增、移除、修改

③ session相關監聽器(瞭解)

Interface HttpSessionBindingListener      //監聽物件繫結到session上的事件  
Interface HttpSessionActivationListener   //監聽session序列化及反序列化的事件

5、生命週期監聽器 ① 宣告週期監聽器: 監聽物件的建立、銷燬的過程! ② 監聽器開發步驟: ◆ 寫一個普通java類,實現相關介面; ◆ 配置(web.xml) 總結:先寫類,實現介面; 再配置 ③ ServletRequestListener:監聽request物件的建立或銷燬 程式碼:

/**
 *  監聽request物件的建立或銷燬
 */
public class MyRequestListener implements ServletRequestListener{
    // 物件銷燬
    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
        // 獲取request中存放的資料
        Object obj = sre.getServletRequest().getAttribute("cn");
        System.out.println(obj);
        System.out.println("MyRequestListener.requestDestroyed()");
    }

    // 物件建立
    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        System.out.println("MyRequestListener.requestInitialized()");
    }
}

Web.xml:

<!-- 監聽request物件建立、銷燬 -->
    <listener>
        <listener-class>cn.itcast.a_life.MyRequestListener</listener-class>
    </listener>

④ HttpSessionListener:監聽session物件的建立或銷燬 程式碼:

/**
 * 監聽Session物件建立、銷燬
 */
public class MySessionListener implements HttpSessionListener{
    // 建立
    @Override
    public void sessionCreated(HttpSessionEvent se) {
        System.out.println("MySessionListener.sessionCreated()");
    }

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

Web.xml:

<!-- 監聽session物件建立、銷燬 -->
    <listener>
        <listener-class>cn.itcast.a_life.MySessionListener</listener-class>
    </listener>

⑤ ServletContextListener:監聽servletContext物件的建立或銷燬 程式碼:

/**
 * 監聽ServletContext物件建立或銷燬
 */
public class MyServletContextListener implements ServletContextListener{
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("MyServletContextListener.contextDestroyed()");
    }

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("1..........MyServletContextListener.contextInitialized()");
    }
}

Web.xml:

<!-- 監聽servletContext物件建立、銷燬 -->
    <listener>
        <listener-class>cn.itcast.a_life.MyServletContextListener</listener-class>
    </listener>

6、屬性監聽器 其中一個:HttpSessionAttributeListener

/**
 * 監聽session物件屬性的變化
 */
public class MySessionAttrListener implements HttpSessionAttributeListener {
    // 屬性新增
    @Override
    public void attributeAdded(HttpSessionBindingEvent se) {
        // 先獲取session物件
        HttpSession session = se.getSession();
        // 獲取新增的屬性
        Object obj = session.getAttribute("userName");
        // 測試
        System.out.println("新增的屬性:" + obj);
    }
    // 屬性移除
    @Override
    public void attributeRemoved(HttpSessionBindingEvent se) {
        System.out.println("屬性移除");
    }
    // 屬性被替換
    @Override
    public void attributeReplaced(HttpSessionBindingEvent se) {
        // 獲取sesison物件
        HttpSession session = se.getSession();
        // 獲取替換前的值
        Object old = se.getValue();
        System.out.println("原來的值:" + old);
        // 獲取新值
        Object obj_new = session.getAttribute("userName");
        System.out.println("新值:" + obj_new);    
    }
}
<!-- 屬性監聽器 -->
    <listener>
        <listener-class>cn.itcast.b_attr.MySessionAttrListener</listener-class>
    </listener>

7、其他監聽器: session相關監聽器 HttpSessionBindingListener:監聽物件繫結/解除繫結到sesison上的事件

步驟:物件實現介面,再把物件繫結/解除繫結到session上就會觸發監聽程式碼 作用:上線提醒!

/**
 * 監聽此物件繫結到session上的過程,需要實現session特定介面
 */
public class Admin implements HttpSessionBindingListener {
    private int id;
    private String name;
    public Admin() {
        super();
    }
    public Admin(int id, String name) {
        super();
        this.id = id;
        this.name = name;
    }
    // 建構函式
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    // 物件放入session
    @Override
    public void valueBound(HttpSessionBindingEvent event) {
        System.out.println("Admin物件已經放入session");
    }
    // 物件從session中移除
    @Override
    public void valueUnbound(HttpSessionBindingEvent event) {
        System.out.println("Admin物件從session中移除!");
    }
}

思考:這個session監聽器,和上面的宣告週期、屬性監聽器區別? ◆ 不用再web.xml配置 ◆ 因為監聽的物件是自己建立的物件,不是伺服器物件!

8、案例 ① 需求:做一個線上列表提醒的功能 使用者登陸 > 顯示登陸資訊,列表展示(list.jsp) > 顯示線上使用者列表(list.jsp) > 列表點選進入“線上列表頁面”(onlineuser.jsp) ② 實現: ◆ 先增加退出功能; 再把session活躍時間設定為1min ◆ 寫監聽器,監聽servletContext物件的建立:初始化集合(onlineuserlist) ◆ 登陸功能: 使用者登陸時候,把資料儲存到servletContext中 ◆ List.jsp 增加超連結, 點選時候提交直接跳轉到online.jsp ◆ 寫監聽器: 監聽session銷燬,把當前登陸使用者從onlineuserlist移除

/**
 * 初始化線上列表集合監聽器 
 */
public class OnlineAdminListener implements ServletContextListener {
    //1. ServletContext物件建立
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        // 建立集合:存放線上使用者
        // 每次當用戶登陸後,就往這個集合中新增添加當前登陸者
        List<Admin> onlineList = new ArrayList<Admin>();
        // 放入ServletContext中
        sce.getServletContext().setAttribute("onlineList", onlineList);
    }

    //2. ServletContext物件銷燬
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        // 獲取ServletContext
        ServletContext sc = sce.getServletContext();
        // 獲取線上列表
        Object obj = sc.getAttribute("onlineList");
        // 移除線上列表集合
        if (obj != null) {
            sc.removeAttribute("onlineList");
        }
    }
}
/**
 * 監聽Session銷燬的動作:
 *  當伺服器銷燬session的時候,從線上列表集合中移除當前的登陸使用者
 */
public class SessionListener implements HttpSessionListener{
    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        //1. 獲取Session物件、ServletContext物件
        HttpSession session = se.getSession();
        ServletContext sc = session.getServletContext();
        //2. 獲取Session中儲存的當前登陸使用者
        Object obj = session.getAttribute("loginInfo");//?
        //3. 獲取ServletContext中儲存的線上使用者列表集合
        List<Admin> list = (List<Admin>) sc.getAttribute("onlineList");
        // 先判斷
        if (obj != null){
            //4. 把“當前登陸使用者”從線上列表集合中移除
            list.remove(obj);
        }
    }

    @Override
    public void sessionCreated(HttpSessionEvent se) {
    }
}

二、國際化

1、Javaweb增強:過濾器、監聽器、國際化、檔案上傳下載、javaMail

2、國際化又簡稱為:i18n:internationalization

3、軟體的國際化 軟體: ① 在中國: 顯示中文,以及服務符合中國習慣的文字字串! ② 在美國: 顯示英文,以及服務符合他國習慣的文字字串! 這種軟體,就叫國際化的軟體!

4、如何做到國際化的軟體,要求 ① 軟體中儲存特定的字串 ② 知道瀏覽器當前使用哪種語言(Locale類)

5、Locale:本地化 Java提供了一個本地化的物件,封裝當前語言、國家、環境等特徵!

public class App {

    @Test
    //1. 本地化物件:Locale
    // 封裝語言、國家資訊的物件,由java.util提供
    public void testLocale() throws Exception {
        // 模擬中國語言等環境
        //Locale locale = Locale.CHINA;
        Locale locale = Locale.getDefault();            // 當前系統預設的語言環境
        System.out.println(locale.getCountry());        // CN:國家的簡稱
        System.out.println(locale.getDisplayCountry()); // 國家名稱
        System.out.println(locale.getLanguage());       // zh:語言簡稱  
        // 模擬美國國家
        Locale l_us = Locale.US;
        System.out.println(l_us.getCountry());
        System.out.println(l_us.getDisplayCountry());
    }
}

6、國際化 ① 靜態資料國際化:網站中顯示的固定文字的國際化(“使用者名稱”“密碼“) ② 動態文字國際化 中文:1987-09-19 ¥1000 英文: Sep/09 1987 $100

7、國際化的軟體 ① 儲存所有國家顯示的文字的字串 ◆檔案:properties ◆ 命名:基礎名_語言簡稱_國家簡稱.properties 例如:msg_zh_CN.properties :儲存所有中文 Msg_en_US.properties:儲存所有英文 ② 程式中獲取 ResourceBundle類,可以讀取國際化的資原始檔 ③ 數值,貨幣,時間,日期等資料由於可能在程式執行時動態產生,所以無法像文字一樣簡單地將它們從應用程式中分離出來,而是需要特殊處理。Java 中提供瞭解決這些問題的 API 類(位於 java.util 包和 java.text 包中)

    // 國際化 - 靜態資料
    @Test
    public void testI18N() throws Exception {   
        // 中文語言環境
        Locale locale = Locale.CHINA;   
        // 建立工具類物件ResourceBundle
        ResourceBundle bundle = ResourceBundle.getBundle("cn.itcast.f_i18n.msg", locale);
        // 根據key獲取配置檔案中的值
        System.out.println(bundle.getString("hello"));
        System.out.println(bundle.getString("username"));
        System.out.println(bundle.getString("pwd"));        
    }

    // 國際化 - 動態文字 - 0. 概述
    @Test
    public void testI18N2() throws Exception {
        // 國際化貨幣
        NumberFormat.getCurrencyInstance();
        // 國際化數字
        NumberFormat.getNumberInstance();
        // 國際化百分比
        NumberFormat.getPercentInstance();  
        // 國際化日期
        //DateFormat.getDateTimeInstance(dateStyle, timeStyle, aLocale)
    }

    // 國際化 - 動態文字 - 1. 國際化貨幣
    @Test
    public void testI18N3() throws Exception {
        // 模擬語言環境
        Locale locale = Locale.US;
        // 資料準備
        double number = 100;
        // 工具類
        NumberFormat nf = NumberFormat.getCurrencyInstance(locale);
        // 國際化貨幣
        String m = nf.format(number);
        // 測試
        System.out.println(m);
    }

    //面試題:  程式碼計算:  $100 * 10  
    @Test
    public void eg() throws Exception {
        String str = "$100";
        int num = 10;       
        // 1. 分析str值是哪一國家的貨幣
        Locale us = Locale.US;
        // 2. 國際化工具類
        NumberFormat nf = NumberFormat.getCurrencyInstance(us);
        // 3. 解析str貨幣
        Number n = nf.parse(str);       
        System.out.println(n.intValue() * num);
    }

    // 國際化 - 動態文字 - 2. 國際化數值
    @Test
    public void testI18N4() throws Exception {
        // 模擬語言環境
        Locale locale = Locale.CHINA;
        NumberFormat nf = NumberFormat.getNumberInstance(Locale.CHINA);
        String str = nf.format(1000000000);
        System.out.println(str);
    }

    // 國際化 - 動態文字 - 3. 國際化日期
    /*
     * 日期:
     *    FULL   2018年9月11日 星期二
     *    LONG   2018年9月11日
     *    FULL   2018年9月11日 星期二
     *    MEDIUM 2018-9-11
     *    SHORT  18-9-11  
     * 時間:
     *    FULL   下午02時31分59秒 CST
     *    LONG   下午02時32分37秒
     *    MEDIUM 14:33:00
     *    SHORT  下午2:33 
     */
    @Test
    public void testI18N5() throws Exception {  
        // 日期格式
        int dateStyle = DateFormat.SHORT;
        // 時間格式
        int timeStyle = DateFormat.SHORT;   
        // 工具類
        DateFormat df = DateFormat.getDateTimeInstance(dateStyle, timeStyle, Locale.CHINA);     
        String date = df.format(new Date());
        System.out.println(date);
    }

    // 面試2: 請將時間值:09-11-28 上午10時25分39秒 CST,反向解析成一個date物件。
    @Test
    public void eg2() throws Exception {
        String str = "09-11-28 上午10時25分39秒 CST";
        // 建立DateFormat工具類,國際化日期
        DateFormat df = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.FULL, Locale.getDefault());
        Date d = df.parse(str);
        System.out.println(d);
    }

8、Jsp頁面國際化

<html>
  <head>
    <%
    ResourceBundle bundle = ResourceBundle.getBundle("cn.itcast.f_i18n.msg",request.getLocale());
    %>
    <title><%=bundle.getString("title") %></title>
    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">    
  </head>

  <body>
  <form name="frmLogin" action="${pageContext.request.contextPath }/admin?method=login" method="post">
    <table align="center" border="1">
        <tr>
            <td><%=bundle.getString("username") %></td>
            <td>
                <input type="text" name="userName">
            </td>
        </tr>
        <tr>
            <td><%=bundle.getString("pwd") %></td>
            <td>
                <input type="password" name="pwd">
            </td>
        </tr>
        <tr>
            <td>
                <input type="submit" value="<%=bundle.getString("submit") %>">
            </td>
        </tr>
    </table>
  </form>
  </body>
</html>

9、Jsp頁面國際化 – 使用jstl標籤(fmt.tld) JSTL標籤(核心標籤庫、國際化與格式化標籤庫、資料庫標籤庫(沒用)、函式庫)

<fmt:setLocale value=""/>        //設定本地化物件
<fmt:setBundle basename=""/>     //設定工具類
<fmt:message></fmt:message>      //顯示國際化文字
//格式化數值(格式:0.00 保留兩位小數,會自動補零;#.## 保留兩位小數,不會自動補零)
<fmt:formatNumber pattern="#.##" value="100.99"></fmt:formatNumber>
//格式化日期
<fmt:formatDate pattern="yyyy-MM-dd" value="<%=new Date() %>"/>
<html>
  <head>
    <!-- 一、設定本地化物件 -->
    <fmt:setLocale value="${pageContext.request.locale}"/>
    <!-- 二、設定工具類 -->
    <fmt:setBundle basename="cn.itcast.f_i18n.msg" var="bundle"/>

    <title><fmt:message key="title" bundle="${bundle}"></fmt:message></title>
    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">    
  </head>

  <body>
  <form name="frmLogin" action="${pageContext.request.contextPath }/admin?method=login" method="post">
    <table align="center" border="1">
        <tr>
            <td><fmt:message key="username" bundle="${bundle}"></fmt:message></td>
            <td>
                <input type="text" name="userName">
            </td>
        </tr>
        <tr>
            <td><fmt:message key="pwd" bundle="${bundle}"></fmt:message></td>
            <td>
                <input type="password" name="pwd">
            </td>
        </tr>
        <tr>
            <td>
                <input type="submit" value="<fmt:message key="submit" bundle="${bundle}"/>">
            </td>
        </tr>
    </table>
  </form>
  </body>
</html>