新聞中心
問題所在

創(chuàng)新互聯(lián)專注于企業(yè)成都全網(wǎng)營銷推廣、網(wǎng)站重做改版、新干網(wǎng)站定制設(shè)計(jì)、自適應(yīng)品牌網(wǎng)站建設(shè)、HTML5建站、商城網(wǎng)站開發(fā)、集團(tuán)公司官網(wǎng)建設(shè)、成都外貿(mào)網(wǎng)站制作、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁設(shè)計(jì)等建站業(yè)務(wù),價(jià)格優(yōu)惠性價(jià)比高,為新干等各大城市提供網(wǎng)站開發(fā)制作服務(wù)。
如果要編寫一個購物車,通常需要寫很多個不同功能的Servlet。例如用戶登錄、添加商品、查詢購物車、結(jié)帳等。
在這些 Servlet 中都需要讀寫數(shù)據(jù)庫。如果我們在每個 Servlet 中都進(jìn)行連接 -> 讀寫 -> 斷開連接的操作,就會消耗大量的服務(wù)器資源,不僅程序響應(yīng)速度減緩,而且會加重服務(wù)器和數(shù)據(jù)庫的負(fù)擔(dān)。
把希望寄托于HttpSession
如我們所學(xué),Servlet API 提供了一些方法和類來專門處理短期的會話跟蹤。網(wǎng)站的每個用戶都和 Javax.Servlet.http.HttpSession 對象有關(guān),Servlet使用這個對象來記錄和檢索每個用戶的信息。
幸運(yùn)的是,我們可以在會話對象中存儲任意的 Java 對象。存儲的方法大家都已經(jīng)很熟悉,就是使用 setAttribute()方法。代表數(shù)據(jù)庫連接的Connection也不例外。
這就為我們讓不同的Servlet在session中共享鏈接帶來的希望。
安全問題
那么,僅僅像下面這樣做就可以了么?
1、在Servlet1中,顯示Servlet在session中設(shè)置一個屬性:
session.setAttribute("connection", connection);
2、在Servlet2中,取出這個屬性:
Connection connection = (Connection) session.getAttribute ("connection");
理論上,沒有問題。在 Servlet1 中產(chǎn)生的 Connection 對象,到了 Servlet2 中可以繼續(xù)使用。
但是如果 Servlet2 不小心改變了 connection 的引用,例如 connection = null; 那么,當(dāng)它再次把這個connection放入session的屬性當(dāng)中,其它的 Servlet 就會得到一個指向 null 的 connection!
解決之道
把 connection 直接在 session 中傳來傳去,看來不怎么安全。
解決思路是,我們找一個專門的人來保管這個 connection,在得到請求的時(shí)候,由這個人把 connection 的引用返回給調(diào)用者。這樣,即使調(diào)用者不小心把它得到的那份 connection 搞壞了,保管著手里也總還有一個備份。
相應(yīng)的,在 session 的屬性中,我們不再保存 connection 本身,而是把這個保管者存進(jìn)去。因?yàn)樗茈S時(shí)給我們一個可用的 connection。
這個類的具體寫法是:
- public class ConnectionHolder {
- public ConnectionHolder(Connection con) {
- // 保存連接
- this.con = con;
- try {
- // 禁用自動提交,以隔離不同session之間的操作。
- con.setAutoCommit(false);
- }
- catch(SQLException e) {
- // 錯誤處理代碼
- }
- }
- public Connection getConnection() {
- // 通過這個getter方法獲取連接
- return con;
- }
- private Connection con = null; // 設(shè)置為私有變量,這很重要,以確保變量安全。
- }
使用方法
每個 Servlet 在希望取得數(shù)據(jù)庫連接的時(shí)候,先看看Servlet在session中是否有這個“保管者”(即上面的ConnectionHolder)。
如果有的話,直接調(diào)用它的get方法,取得數(shù)據(jù)庫連接。
如果沒有的話,說明這個session還沒有連接過數(shù)據(jù)庫,那么當(dāng)前類就立刻創(chuàng)建一個數(shù)據(jù)庫連接,并把這個連接交給保管者,然后再把保管者放入 session 中,以便后續(xù)的 Servlet 使用。
下面是一個實(shí)例:
- protected void doGet(HttpServletRequest req, HttpServletResponse res)
- throws ServletException, IOException {
- // 同步代碼取得數(shù)據(jù)庫連接
- synchronized (session) {
- // 看看這個持有者是否已經(jīng)在 session 中了
- ConnectionHolder holder = (ConnectionHolder) session.getAttribute
- ("servletapp.connection");
- // 如果不在,就創(chuàng)建一個數(shù)據(jù)庫連接,并把它交給持有者。
- if (holder == null) {
- try {
- holder = new ConnectionHolder(DriverManager.getConnection(
- "Connection URL"));
- session.setAttribute("servletapp.connection", holder);
- }
- catch (SQLException sqle) {
- // 錯誤處理代碼
- }
- }
- // 從容器取得實(shí)際連接
- conn = holder.getConnection();
- }
- .... // 別忘了commit
- }
這段代碼看起來有那么幾行。但實(shí)際上,在每個session中,只有第一次執(zhí)行的Servlet需要進(jìn)行數(shù)據(jù)庫連接操作,此后的Servlet只會執(zhí)行第4、6、20這三行。
誰來負(fù)責(zé)斷開連接?
當(dāng) Servlet 們不必再為創(chuàng)建數(shù)據(jù)庫連接費(fèi)心的時(shí)候,也就沒有人愿意管關(guān)閉連接這檔子事了。事實(shí)上,更重要的是,他們沒法管。因?yàn)檫@個連接是放在 session 中的,而沒有誰能準(zhǔn)確的預(yù)測,一個 session 會何時(shí)終止。
好在有一種叫做“監(jiān)聽器”(Listener)的東西可以專門管這件事。Listener有很多方法,其中的兩個方法是:
◆public void valueBound(HttpSessionBingEvent event);
◆public void valueUnbound(HttpSessionBingEvent event);
這兩個方法可以在一個 session 被創(chuàng)建/失效的時(shí)候分別自動執(zhí)行。我們就把關(guān)閉連接的代碼放在第二個方法中,這樣,當(dāng)一個 session 失效的時(shí)候,數(shù)據(jù)庫連接就會自動關(guān)閉。
要想讓一個類成為Listener,只需讓它實(shí)現(xiàn) HttpSessionBindingListener 接口。我們的 connection 是由 ConnectionHolder 這個類來保管的,因此最方便的辦法就是把它注冊成一個監(jiān)聽器。
具體方法是:
- public void valueUnbound(HttpSessionBindingEvent event) {
- // 當(dāng)從Session刪除或當(dāng)Session結(jié)束時(shí),關(guān)閉數(shù)據(jù)連接。
- try {
- if (con != null) {
- con.rollback(); // 放棄所有未提交的數(shù)據(jù)
- con.close();
- }
- }
- catch (SQLException e) {
- // 錯誤處理代碼
- }
- }
- ConnectionHolder:
- import javax.servlet.http.HttpSessionBindingListener;
- import javax.servlet.http.HttpSessionBindingEvent;
- import java.sql.Connection;
- import java.sql.SQLException;
- public class ConnectionHolder implements HttpSessionBindingListener {
- public ConnectionHolder(Connection con) {
- // 保存連接
- this.con = con;
- try {
- con.setAutoCommit(false);
- }
- catch(SQLException e) {
- // 錯誤處理代碼
- }
- }
- public Connection getConnection() {
- return con;
- }
- public void valueBound(HttpSessionBindingEvent event) {
- // 當(dāng)增加Session時(shí),什么也不做
- }
- public void valueUnbound(HttpSessionBindingEvent event) {
- // 當(dāng)從Session刪除或當(dāng)Session結(jié)束時(shí),關(guān)閉數(shù)據(jù)連接。
- try {
- if (con != null) {
- con.rollback(); // 放棄所有未發(fā)送數(shù)據(jù)
- con.close();
- }
- }
- catch (SQLException e) {
- // 錯誤處理代碼
- }
- }
- private Connection con = null;
- }
文章標(biāo)題:Servlet在session中共享鏈接
URL標(biāo)題:http://m.fisionsoft.com.cn/article/djoecpo.html


咨詢
建站咨詢
