1. 程式人生 > >【每日一學190729】監聽器

【每日一學190729】監聽器

監聽器(listener)

介紹

  • 監聽器來自於servlet規範中
  • 監聽器專門由於監聽【域物件生命週期變化】以及【域物件共享資料變化情況】
  • 監聽器介面實現類,必能由開發人員負責實現

上面提到監聽器是監聽域物件生命週期變化以及域物件共享資料變化情況,那麼什麼是域物件呢?

域物件

定義

在某一個範圍之內,可以為servlet之間提供共享資料的物件。

分類

  • ServletContext application:全域性作用於物件,是在tomcat執行期間,可以為當前工程中所有的servlet提供共享資料
  • HTTPSession session:會話作用域物件,在一次會話過程中,為本次參與會話的servlet提供共享資料
  • HttpServletRequest request: 請求作用域物件,在一次請求處理過程中,比如【請求轉發】,為參與本次請求的所有servlet提供共享資料

監聽器實現步驟

  • 根據監聽的物件,選擇一個合適的監聽器介面來實現
  • 重寫監聽器介面中的監聽處理方法
  • 在web.xml中註冊監聽器,通過tomcat

小案例(監聽ServletContext的生命週期)

寫一個類實現ServletContextListener介面,也就是監聽ServletContext生命週期的變化,這裡是重寫了初始化和銷燬的方法

@javax.servlet.annotation.WebListener()
public class OneListener implements ServletContextListener {

    @Override
    // 在application被初始化時被呼叫
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        System.out.println("全域性作用域物件被初始化");
    }

    @Override
    // 在application被銷燬時被呼叫
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        System.out.println("全域性作用域物件被銷燬");
    }
}

在上面的Listener中可以看到有一個@javax.servlet.annotation.WebListener(),這個註解就是宣告該類是一個監聽器,如果已經有了這個註解那麼就不需要在下面的xml中進行配置,如果沒有那個註解就按照下面的方式進行配置即可。 在web.xml中註冊監聽器

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!--監聽器的註冊,當我們程式啟動時tomcat會幫我們建立監聽器物件,建立的時機是在全域性作用域被建立之前-->
    <listener>
        <listener-class>top.listener.OneListener</listener-class>
    </listener>
</web-app>

小案例(監聽ServletContext資料的變化)

按照上面監聽ServletContext作為案例就是ServletContextAttributeListener 如果是要看session的就是SessionAttributeListener

如果要看Request就是RequestAttributeListener

上面說監聽器主要是做【域物件共享資料變化情況】,資料變化主要有三種情形:

新增共享資料

application.setAttribute("key", 100);

更新共享資料(key已經存在時就是更新)

application.setAttribute("key", 100);

刪除共享資料

application.removeAttribute("key", 100);

新建一個類來實現ServletContextAttributeListener介面

package top.one.listener;

import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;

@javax.servlet.annotation.WebListener()
public class TestListener 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("監聽共享資料的更新");
    }
}

由於上面的類上面有一個@javax.servlet.annotation.WebListener()註解所以可以不用再web.xml中註冊監聽器了

寫一個servlet

package top.one.listener;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class TestServlet extends HttpServlet {
    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext servletContext = req.getServletContext();
        servletContext.setAttribute("key", 100);
        servletContext.setAttribute("key", 200);
        servletContext.removeAttribute("key");
    }
}

在web.xml中配置servlet

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <servlet>
        <servlet-name>testServletContext</servlet-name>
        <servlet-class>top.one.listener.TestServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>testServletContext</servlet-name>
        <url-pattern>/first.do</url-pattern>
    </servlet-mapping>
</web-app>

啟動專案訪問 http://localhost:8080/ServletContext_war_exploded/first.do 即可看到下面的效果:

訪問地址

上面的訪問地址ip:埠以及後面的first.do(這個就是url-pattern裡面配置的)都好說,那個ServletContext_war_exploded是怎麼來的呢?其中這裡是我們將專案部署到tomcat中時預設的訪問路徑名: 前提條件是我們已經在idea中配置好了tomcat,然後點選控制檯那一塊的application Servers

在左邊有一個筆形的圖示,那裡是edit configuration,點選後出現下面的畫面:

然後選擇我們要部署的專案即可:

可以看到後面有一個ApplicationContext的路徑,那裡可以自己修改。

監聽器的應用

jDBC的使用步驟(mysql版)

  • 載入資料庫驅動(好像在5點幾之後這一步不用自己寫,在jar中已經幫我們註冊了)
    	Class.forName("com.mysql.jdbc.Driver");
    
  • 獲取連線
    	Connection conn = DriverManage.getConnection(url, username, password);
    
    這裡的url是告訴jdbc程式要連線哪一個資料庫,常用的mysql和oracle的url分別如下:
  • sql預編譯
    	String sql = "select name, password from users where id=? ";
    	PrepareStatement ps = conn.prepareStatement(sql);
    
    這裡不使用Statement物件是因為Statement不能防止sql注入問題。
  • 執行sql語句
    	ps.setString(1, '1');
    	ps.executeQuery(); // 這裡不需要傳入sql
    
  • 獲取結果
    	ResultSet rs = ps.executeQuery();
    	while(rs.next()){
    		String name = rs.getString(2); // 這裡的2表示name欄位在資料庫中是第二列
    		String name = rs.getString("name"); // 這種也是可以的,直接獲取name欄位的值
    	}
    
  • 關閉連線 主要是有ResultSet的關閉,PrepareStatement的關閉以及Connection的關閉

具體應用

上面jdbc連線資料庫的例子中,獲取連線和關閉連線的操作時最耗時的,解決頻繁的獲取連線和關閉連線的方法是使用了連線池,也就是在專案啟動時就建立好了一些連線,這樣當需要使用的時候直接從連線池中去獲取,而不用在使用的時候去建立連線,這樣速度就提高了很多,但是這些連線總是需要在某一個時刻建立和銷燬,上面說是在專案啟動的時候建立,在專案關閉的時候關閉這些連線,那麼怎麼知道專案啟動和專案關閉的時刻呢?這時候就需要使用ServletContextListener監聽器了。因為該監聽器可以監聽專案的生命週期的變化,自然可以知道專案的啟動