1. 程式人生 > 實用技巧 >七、監聽器和國際化

七、監聽器和國際化

Web 三大元件:

  • Servlet

  • Listener

  • Filter

一、JavaWeb監聽器

監聽器:

  • 它是一個介面,內容由我們來實現;

  • 它需要註冊,例如註冊在按鈕上!

  • 監聽器中的方法,會在特殊事件發生時被呼叫!

觀察者:

  • 事件源:小偷

  • 事件:偷東西

  • 監聽器:警察;監聽器中的方法:抓捕

JavaWeb中的監聽器

事件源:三大域

  • ServletContext

    • 生命週期監聽:ServletContextListener,它有兩個方法,一個在出生時呼叫,一個在死亡時呼叫

//建立SErvletcontext時
void contextInitialized(ServletContextEvent sce):
 
//銷燬Servletcontext時 void contextDestroyed(ServletContextEvent sce):

    • ¨屬性監聽:ServletContextAttributeListener,它有三個方法,一個在新增屬性時呼叫,一個在替換屬性時呼叫,最後一個是在移除屬性時呼叫。

//新增屬性時;
void attributeAdded(ServletContextAttributeEvent event):
//替換屬性時;
void attributeReplaced(ServletContextAttributeEvent event):
//移除屬性時;
void attributeRemoved(ServletContextAttributeEvent event):

  • HttpSession

    • 生命週期監聽:HttpSessionListener,它有兩個方法,一個在出生時呼叫,一個在死亡時呼叫;

//建立session時
void sessionCreated(HttpSessionEvent se):
//銷燬session時
void sessionDestroyed(HttpSessionEvent se):

    • 屬性監聽:HttpSessioniAttributeListener,它有三個方法,一個在新增屬性時呼叫,一個在替換屬性時呼叫,最後一個是在移除屬性時呼叫。

//新增屬性時;
void attributeAdded(HttpSessionBindingEvent event):
//替換屬性時 void attributeReplaced(HttpSessionBindingEvent event): //移除屬性時 void attributeRemoved(HttpSessionBindingEvent event):
  • ServletRequest

    • 生命週期監聽:ServletRequestListener,它有兩個方法,一個在出生時呼叫,一個在死亡時呼叫

//建立request時
void requestInitialized(ServletRequestEvent sre):
//銷燬request時
void requestDestroyed(ServletRequestEvent sre):

    • 屬性監聽:ServletRequestAttributeListener,它有三個方法,一個在新增屬性時呼叫,一個在替換屬性時呼叫,最後一個是在移除屬性時呼叫

//新增屬性時
void attributeAdded(ServletRequestAttributeEvent srae):
//替換屬性時
void attributeReplaced(ServletRequestAttributeEvent srae):
//移除屬性時
void attributeRemoved(ServletRequestAttributeEvent srae):

javaWeb中完成編寫監聽器

  • 寫一個監聽器類:要求必須去實現某個監聽器介面;

  • 註冊,是在web.xml中配置來完成註冊!

1.1、JavaWeb監聽器概述

在JavaWeb被監聽的事件源為:ServletContext、HttpSession、ServletRequest,即三大域物件。

  • 監聽域物件“建立”與“銷燬”的監聽器;

  • 監聽域物件“操作域屬性”的監聽器;

  • 監聽HttpSession的監聽器。

1.2、建立與銷燬監聽器

建立與銷燬監聽器一共有三個:

  • ServletContextListener:Tomcat啟動和關閉時呼叫下面兩個方法

    • public void contextInitialized(ServletContextEvent evt):ServletContext物件被建立後呼叫;

    • public void contextDestroyed(ServletContextEvent evt):ServletContext物件被銷燬前呼叫;

  • HttpSessionListener:開始會話和結束會話時呼叫下面兩個方法

    • public void sessionCreated(HttpSessionEvent evt):HttpSession物件被建立後呼叫;

    • public void sessionDestroyed(HttpSessionEvent evt):HttpSession物件被銷燬前呼叫;

  • ServletRequestListener:開始請求和結束請求時呼叫下面兩個方法

    • public void requestInitiallized(ServletRequestEvent evt):ServletRequest物件被建立後呼叫;

    • public void requestDestroyed(ServletRequestEvent evt):ServletRequest物件被銷燬前呼叫。

1.3、事件物件

  • ServletContextEvent:ServletContext getServletContext();

  • HttpSeessionEvent:HttpSession getSession();

  • ServletRequestEvent:

    • ServletRequest getServletRequest()

    • ServletContext getServletContext()

編寫測試例子:

  • 編寫MyServletContextListener類,實現ServletContextListener介面;

  • 在web.xml檔案中部署監聽器;

  • 為了看到session銷燬的效果,在web.xml檔案中設定session失效時間為1分鐘;

/*
 * ServletContextListener實現類
 * contextDestroyed() -- 在ServletContext物件被銷燬前呼叫
 * contextInitialized() --  -- 在ServletContext物件被建立後呼叫
 * ServletContextEvent -- 事件類物件
 *     該類有getServletContext(),用來獲取ServletContext物件,即獲取事件源物件
 */
public class MyServletContextListener implements ServletContextListener {
  public void contextDestroyed(ServletContextEvent evt) {
    System.out.println("銷燬ServletContext物件");
  }
​
  public void contextInitialized(ServletContextEvent evt) {
    System.out.println("建立ServletContext物件");
  }
}

/*
 * HttpSessionListener實現類
 * sessionCreated() -- 在HttpSession物件被建立後被呼叫
 * sessionDestroyed() --  -- 在HttpSession物件被銷燬前呼叫
 * HttpSessionEvent -- 事件類物件
 *     該類有getSession(),用來獲取當前HttpSession物件,即獲取事件源物件
 */
public class MyHttpSessionListener implements HttpSessionListener {
  public void sessionCreated(HttpSessionEvent evt) {
    System.out.println("建立session物件");
  }
​
  public void sessionDestroyed(HttpSessionEvent evt) {
    System.out.println("銷燬session物件");
  }
}

/*
 * ServletRequestListener實現類
 * requestDestroyed() -- 在ServletRequest物件被銷燬前呼叫
 * requestInitialized() -- 在ServletRequest物件被建立後呼叫
 * ServletRequestEvent -- 事件類物件
 *     該類有getServletContext(),用來獲取ServletContext物件
 *     該類有getServletRequest(),用來獲取當前ServletRequest物件,即事件源物件
 */
public class MyServletRequestListener implements ServletRequestListener {
    public void requestDestroyed(ServletRequestEvent evt) {
        System.out.println("銷燬request物件");
    }

    public void requestInitialized(ServletRequestEvent evt) {
        System.out.println("建立request物件");
    }
}

<listener>
<listener-class>cn.jdy.listener.MyServletContextListener</listener-class>
</listener>
<listener>
<listener-class>cn.jdy.listener.MyHttpSessionListener</listener-class>
</listener>
<listener>
<listener-class>cn.jdy.listener.MyServletRequestListener</listener-class>
</listener>
<session-config>
 <session-timeout>1</session-timeout>
</session-config>

1.4、操作域屬性的監聽器

當對域屬性進行增、刪、改時,執行的監聽器一共有三個:

  • ServletContextAttributeListener:在ServletContext域進行增、刪、改屬性時呼叫下面方法。

    • public void attributeAdded(ServletContextAttributeEvent evt)

    • public void attributeRemoved(ServletContextAttributeEvent evt)

    • public void attributeReplaced(ServletContextAttributeEvent evt)

  • HttpSessionAttributeListener:在HttpSession域進行增、刪、改屬性時呼叫下面方法

    • public void attributeAdded(HttpSessionBindingEvent evt)

    • public void attributeRemoved (HttpSessionBindingEvent evt)

    • public void attributeReplaced (HttpSessionBindingEvent evt)

  • ServletRequestAttributeListener:在ServletRequest域進行增、刪、改屬性時呼叫下面方法

    • public void attributeAdded(ServletRequestAttributeEvent evt)

    • public void attributeRemoved (ServletRequestAttributeEvent evt)

    • public void attributeReplaced (ServletRequestAttributeEvent evt)

下面對這三個監聽器的事件物件功能進行介紹:

  • ServletContextAttributeEvent

    • String getName():獲取當前操作的屬性名;

    • Object getValue():獲取當前操作的屬性值;

    • ServletContext getServletContext():獲取ServletContext物件。

  • HttpSessionBindingEvent

    • String getName():獲取當前操作的屬性名;

    • Object getValue():獲取當前操作的屬性值;

    • HttpSession getSession():獲取當前操作的session物件。

  • ServletRequestAttributeEvent

    • String getName():獲取當前操作的屬性名;

    • Object getValue():獲取當前操作的屬性值;

    • ServletContext getServletContext():獲取ServletContext物件;

    • ServletRequest getServletRequest():獲取當前操作的ServletRequest物件。

public class MyListener implements ServletContextAttributeListener,
        ServletRequestAttributeListener, HttpSessionAttributeListener {
    public void attributeAdded(HttpSessionBindingEvent evt) {
        System.out.println("向session中新增屬性:" + evt.getName() + "=" + evt.getValue());
    }

    public void attributeRemoved(HttpSessionBindingEvent evt) {
        System.out.println("從session中移除屬性:" + evt.getName() + "=" + evt.getValue());
    }

    public void attributeReplaced(HttpSessionBindingEvent evt) {
        System.out.println("修改session中的屬性:" + evt.getName() + "=" + evt.getValue());
    }

    public void attributeAdded(ServletRequestAttributeEvent evt) {
        System.out.println("向request中新增屬性:" + evt.getName() + "=" + evt.getValue());
    }

    public void attributeRemoved(ServletRequestAttributeEvent evt) {
        System.out.println("從request中移除屬性:" + evt.getName() + "=" + evt.getValue());
    }

    public void attributeReplaced(ServletRequestAttributeEvent evt) {
        System.out.println("修改request中的屬性:" + evt.getName() + "=" + evt.getValue());
    }

    public void attributeAdded(ServletContextAttributeEvent evt) {
        System.out.println("向context中新增屬性:" + evt.getName() + "=" + evt.getValue());
    }

    public void attributeRemoved(ServletContextAttributeEvent evt) {
        System.out.println("從context中移除屬性:" + evt.getName() + "=" + evt.getValue());
    }

    public void attributeReplaced(ServletContextAttributeEvent evt) {
        System.out.println("修改context中的屬性:" + evt.getName() + "=" + evt.getValue());
    }
}

public class ListenerServlet extends BaseServlet {
    public String contextOperation(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        ServletContext context = this.getServletContext();
        context.setAttribute("a", "a");
        context.setAttribute("a", "A");
        context.removeAttribute("a");
        return "/index.jsp";
    }
    
    ///////////////////////////////
    
    public String sessionOperation(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        HttpSession session = request.getSession();
        session.setAttribute("a", "a");
        session.setAttribute("a", "A");
        session.removeAttribute("a");
        return "/index.jsp";
    }

    ///////////////////////////////
    
    public String requestOperation(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        request.setAttribute("a", "a");
        request.setAttribute("a", "A");
        request.removeAttribute("a");
        return "/index.jsp";
    }
}

<body>
    <a href="<c:url value='/ListenerServlet?method=contextOperation'/>">SevletContext操作屬性</a>
    <br/>
    <a href="<c:url value='/ListenerServlet?method=sessionOperation'/>">HttpSession操作屬性</a>
    <br/>
    <a href="<c:url value='/ListenerServlet?method=requestOperation'/>">ServletRequest操作屬性</a> | 
  </body>

1.5、HttpSession的監聽器

還有兩個與HttpSession相關的特殊的監聽器,這兩個監聽器的特點如下:

  • 不用在web.xml檔案中部署;

  • 這兩個監聽器不是給session新增,而是給Bean新增。即讓Bean類實現監聽器介面,然後再把Bean物件新增到session域中。

下面對這兩個監聽器介紹一下:

  • HttpSessionBindingListener:當某個類實現了該介面後,可以感知本類物件新增到session中,以及感知從session中移除。例如讓Person類實現HttpSessionBindingListener介面,那麼當把Person物件新增到session中,或者把Person物件從session中移除時會呼叫下面兩個方法:

    • public void valueBound(HttpSessionBindingEventevent):當把監聽器物件新增到session中會呼叫監聽器物件的本方法;

    • public void valueUnbound(HttpSessionBindingEvent event):當把監聽器物件從session中移除時會呼叫監聽器物件的本方法;

這裡要注意,HttpSessionBindingListener監聽器的使用與前面介紹的都不相同,當該監聽器物件新增到session中,或把該監聽器物件從session移除時會呼叫監聽器中的方法。並且無需在web.xml檔案中部署這個監聽器。

示例步驟:

  • 編寫Person類,讓其實現HttpSessionBindingListener監聽器介面;

  • 編寫Servlet類,一個方法向session中新增Person物件,另一個從session中移除Person物件;

  • 在index.jsp中給出兩個超連結,分別訪問Servlet中的兩個方法。

public class Person implements HttpSessionBindingListener {
    private String name;
    private int age;
    private String sex;
    
    public Person(String name, int age, String sex) {
        super();
        this.name = name;
        this.age = age;
        this.sex = sex;
    }

    public Person() {
        super();
    }

    public String toString() {
        return "Person [name=" + name + ", age=" + age + ", sex=" + sex + "]";
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public void valueBound(HttpSessionBindingEvent evt) {
        System.out.println("把Person物件存放到session中:" + evt.getValue());
    }

    public void valueUnbound(HttpSessionBindingEvent evt) {
        System.out.println("從session中移除Pseron物件:" + evt.getValue());
    }
}

public class ListenerServlet extends BaseServlet {
    public String addPerson(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        Person p = new Person("zhangSan", 23, "male");
        request.getSession().setAttribute("person", p);
        return "/index.jsp";
    }
    
    public String removePerson(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        request.getSession().removeAttribute("person");
        return "/index.jsp";
    }

<body>
    <a href="<c:url value='/ListenerServlet?method=addPerson'/>">addPerson</a>
    <br/>
    <a href="<c:url value='/ListenerServlet?method=removePerson'/>">removePerson</a>
    <br/>
  </body>

  • HttpSessionActivationListener:Tomcat會在session從時間不被使用時鈍化session物件,所謂鈍化session,就是把session通過序列化的方式儲存到硬碟檔案中。當用戶再使用session時,Tomcat還會把鈍化的物件再活化session,所謂活化就是把硬碟檔案中的session在反序列化回記憶體。當session被Tomcat鈍化時,session中儲存的物件也被純化,當session被活化時,也會把session中儲存的物件活化。如果某個類實現了HttpSessionActiveationListener介面後,當物件隨著session被鈍化和活化時,下面兩個方法就會被呼叫:

    • public void sessionWillPassivate(HttpSessionEvent se):當物件感知被活化時呼叫本方法;

    • public void sessionDidActivate(HttpSessionEvent se):當物件感知被鈍化時呼叫本方法;

HttpSessionActivationListener監聽器與HttpSessionBindingListener監聽器相似,都是感知型的監聽器,例如讓Person類實現了HttpSessionActivationListener監聽器介面,並把Person物件新增到了session中後,當Tomcat鈍化session時,同時也會鈍化session中的Person物件,這時Person物件就會感知到自己被鈍化了,其實就是呼叫Person物件的sessionWillPassivate()方法。當用戶再次使用session時,Tomcat會活化session,這時Person會感知到自己被活化,其實就是呼叫Person物件的sessionDidActivate()方法。

注意,因為鈍化和活化session,其實就是使用序列化和反序列化技術把session從記憶體儲存到硬碟,和把session從硬碟載入到記憶體。這說明如果Person類沒有實現Serializable介面,那麼當session鈍化時就不會鈍化Person,而是把Person從session中移除再鈍化!這也說明session活化後,session中就不在有Person物件了。

示例步驟:

  • 先不管HttpSessionActivationListener監聽器介面,先來配置Tomcat鈍化session的引數,把下面配置檔案放到tomcat\conf\catalina\localhost目錄下!檔名稱為專案名稱。

<Context>
    <Manager className="org.apache.catalina.session.PersistentManager" maxIdleSwap="1">
        <Store className="org.apache.catalina.session.FileStore" directory="mysession"/>
    </Manager>
</Context>

 訪問專案的index.jsp頁面,這會使Tomcat建立Session物件,然後等待一分鐘後,檢視Tomcat\work\Catalina\localhost\listener\mysession目錄下是否會產生檔案,如果產生了,說明鈍化session的配置成功了,可以開始下一步了。

  • 建立Person類,讓Person類實現HttpSessionActivationListener和Serializable介面:

public class Person implements HttpSessionActivationListener, Serializable {
    private String name;
    private int age;
    private String sex;
    
    public Person(String name, int age, String sex) {
        super();
        this.name = name;
        this.age = age;
        this.sex = sex;
    }

    public Person() {
        super();
    }

    public String toString() {
        return "Person [name=" + name + ", age=" + age + ", sex=" + sex + "]";
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public void sessionDidActivate(HttpSessionEvent evt) {
        System.out.println("session已經活化");
    }

    public void sessionWillPassivate(HttpSessionEvent evt) {
        System.out.println("session被鈍化了!");
    }
}

  • 與上例一樣,編寫Servlet,提供兩個方法:一個向session中新增Person物件,另一個從session中移除Person物件:

public class ListenerServlet extends BaseServlet {
    public String addPerson(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        Person p = new Person("zhangSan", 23, "male");
        request.getSession().setAttribute("person", p);
        return "/index.jsp";
    }
    
    public String removePerson(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        request.getSession().removeAttribute("person");
        return "/index.jsp";
    }
}

  • 在index.jsp頁面中給出訪問addPerson()和removePerson()的方法:

 <body>
    <a href="<c:url value='/ListenerServlet?method=addPerson'/>">addPerson</a>
    <br/>
    <a href="<c:url value='/ListenerServlet?method=removePerson'/>">removePerson</a>
    <br/>
  </body>

  • 開啟index.jsp頁面,這時Tomcat會建立session,必須在1分鐘之前點選addPerson連結,這能保證在session被鈍化之前把Person物件新增到session中;

  • 等待一分鐘,這時session會被鈍化,也就會呼叫Person的sessionWillPassivate();

  • 重新整理一下index.jsp頁面,這會使session活化,會呼叫Person的sessionDidActivate()方法。

二、國際化

2.1、什麼是國際化

國際化就是可以把頁面中的中文變成英文。例如在頁面中的登入表單:

2.2、理解國際化

想把頁面中的文字修改,那麼就不能再使用硬編碼,例如下面的頁面中都是硬編碼:

上圖中的中文想轉換成英文,那麼就需要把它們都變成活編碼:

2.3、Locale類

建立Locale類物件:

  • new Locale(“zh”, “CN”);

  • new Locale(“en”, “US”);

你一定對zh、CN或是en、US並不陌生,zh、en表示語言,而CN、US表示國家。一個Locale物件表示的就是語言和國家。

2.4、ResourceBundle類

ReourceBundle類用來獲取配置檔案中的內容。 下面是兩個配置檔案內容: res_zh_CN.properties

res_en_US.properties

public class Demo1 {
    @Test
    public void fun1() {
        ResourceBundle rb = ResourceBundle.getBundle("res", new Locale("zh", "CN" ));
        String username = rb.getString("msg.username");
        String password = rb.getString("msg.password");
        System.out.println(username);
        System.out.println(password);        
    }
    
    @Test
    public void fun2() {
        ResourceBundle rb = ResourceBundle.getBundle("res", new Locale("en", "US" ));
        String username = rb.getString("msg.username");
        String password = rb.getString("msg.password");
        System.out.println(username);
        System.out.println(password);        
    }
}

ResourceBundle的getBundle()方法需要兩個引數:

  • 第一個引數:配置檔案的基本名稱

  • 第二個引數:Locale

getBundle()方法會通過兩個引數來鎖定配置檔案!

2.5、頁面國際化

其實頁面國際化也是同一道理,只需要通過切換Locale來切換配置檔案。我們寫一個MessageUtils類,內部需要ResourceBundle的例項。

我們再寫一個過濾器MessageFilter,它會通過引數建立Locale物件,傳遞給MessageUtils,然後在頁面中使用MessageUtils來獲取文字資訊。

public class MessageUtils {
    private static String baseName = "res";
    private static Locale locale;
    public static String getText(String key) {
        return ResourceBundle.getBundle(baseName, locale).getString(key);
    }
    public static Locale getLocale() {
        return locale;
    }
    public static void setLocale(Locale locale) {
        MessageUtils.locale = locale;
    }
}

public class MessageFilter implements Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        String l = req.getParameter("request_locale");
        Locale locale = null;
        if(l != null && !l.isEmpty()) {
            String[] strs = l.split("_");
            locale = new Locale(strs[0], strs[1]);
            req.getSession().setAttribute("WW_TRANS_I18N_LOCALE", locale);
        } else {
            locale = (Locale)req.getSession().getAttribute("WW_TRANS_I18N_LOCALE");
        }
        if(locale == null) {
            locale = req.getLocale();
        }
        MessageUtils.setLocale(locale);
        chain.doFilter(request, response);
    }

    public void init(FilterConfig fConfig) throws ServletException {
    }
}

login.jsp

<body>
    <h1><%=MessageUtils.getText("msg.login") %></h1>
    <form action="<c:url value='/index.jsp'/>" method="post">
        <%=MessageUtils.getText("msg.username")%><input type="text" name="username"/><br/>
        <%=MessageUtils.getText("msg.password")%><input type="password" name="password"/><br/>
        <input type="submit" value='<%=MessageUtils.getText("msg.login")%>'/>
    </form>
  </body>

index.jsp

 <body>
    <p><%=MessageUtils.getText("hello") + ":" +request.getParameter("username") %></p>
  </body>

2.6、NumberFormat

NumberFormat類用來對數字進行格式化,我們只需要使用String format(double)一個方法即可。下面是獲取NumberFormat例項的方法:

  • NumberFormat format = NumberFormat.getNumberFormat()

  • NumberFormat format = NumberFormat.getNumberFormat(Locale)

  • NumberFormat format = NumberFormat.getCurrentcyFormat()

  • NumberFormat format = NumberFormat.getCurrentcyFormat(Locale)

  • NumberFormat format = NumberFormat.getPercentFormat()

  • NumberFormat format = NumberFormat.getPercentFormat(Locale)

2.7、DateFormat

DateFormat類用來對日期進行格式化,我們只需要使用String format(Date)一個方法即可。下面是獲取DateFormat例項的方法:

  • DateFormat format = DateFormat.getDateFormat();

  • DateFormat format = DateFormat.getTimeFormat();

  • DateFormat format = DateFormat.getDateTimeFormat();

  • DateFormat format = DateFormat.getDateFormat(int style, Locale locale);

  • DateFormat format = DateFormat.getTimeFormat(int style, Locale locale);

  • DateFormat format = DateFormat.getDateTimeFormat(int style, Locale locale);

其中style是對日期的長、中、短,以及完整樣式。

  • SHORT; 

  • MEDIUM; 

  • LONG;

  • FULL

2.8、MessageFormat

MessageFormat可以把模式中的{N}使用引數來替換。我們把{N}稱之為點位符。其中點位符中的N是從0開始的整數。 MessageFormat.format(String pattern, Object… params),其中pattern中可以包含0~n個點位符,而params表示對點位符的替換文字。注意,點位符需要從0開始。 String p = “{0}或{1}錯誤”; String text = MessageFormat.format(p, “使用者名稱”, “密碼”); System.out.println(text);//使用者名稱或密碼錯誤