Servlet、ServletConfig、ServletContext深入學習


1.Servlet學習

1.Servlet生命周期 

Servlet 加載—>實例化—>服務—>銷毀。

  • init(servletConfig):(經過自己的測試發現會先調用這個而不是init(),而且不會調用空參的init(),是在執行init(servletConfig)的時候調用init())
  在Servlet的生命周期中, 僅執行一次init(servletConfig)方法。它是在服務器裝入Servlet時執行的,負責初始化Servlet對象 (也就是容器中只有一個servlet對象,所以線程非安全)。可以配置服務器,以在啟動服務器或客戶機首次訪問Servlet時裝入Servlet。無論有多少客戶機訪問Servlet,都不會重復執行 init(servletConfig)。查看父類這個方法的源代碼發現它做了兩件事:初始化servletConfig和調用init(),所以我們在重寫了init()方法也會生效:
public void init(ServletConfig config) throws ServletException {  
    this.config = config;  
    this.init();  
}  
    public void init() throws ServletException {

    }   
  • service():

  它是Servlet的核心,負責響應客戶的請求。每當一個客戶請求一個HttpServlet對象,該對象的Service()方法就要調用,而且傳遞給這個方法一個“請求”(ServletRequest)對象和一個“響應”(ServletResponse)對象作為參數。在HttpServlet中已存在Service()方法。默認的服務功能是調用與HTTP請求的方法相應的do功能。並根據請求的方式調用對應的doGet和doPost方法,所以我們需要在程序中做的就是重寫doGet或者doPost方法。查看下面一段源碼:

 

  • destroy():
  僅執行一次,在服務器端停止且卸載Servlet時執行該方法。當Servlet對象退出生命周期時,負責釋放占用的資源。一個Servlet在運行service()方法時可能會產生其他的線程,因此需要確認在調用destroy()方法時,這些線程已經終止或完成。

 

  應該知道,在Servlet初始化的時候,會自動調用init(ServletConfig config),Container會自動收集一些該Servlet的配置信息,生成一個ServletConfig的實例,通過調用該實例的四個getXXX方法(即ServletConfig接口中的四個方法),我們可以得到該Servlet的這些配置信息。

  而該實例我們如何獲取呢?是通過Servlet#getServletConfig()得到的,該方法同樣在GenericServlet中實現,具體如下:(在init(servletConfig方法中會調用init()方法))

public void init(ServletConfig config) throws ServletException {  
    this.config = config;  
    this.init();  
}  
public ServletConfig getServletConfig() {  
    return config;  
} 

 

 

  我認為init()的出現,是為了解決一個問題,那就是有些人最開始時候,重寫init(ServletConfig config)方法,但卻總是忘記去調用“super.init(config);”。試想,如果真的出現這種情況,那么,就會造成一種結果,容器收集的Servlet的配置信息,不能初始化給GenericServlet的config屬性,以至於當調用getServletConfig()的時候,會得不到有用的config實例,而只能得到一個null。這樣也就無法獲取該Servlet的配置信息了。(我剛開始重寫這個方法沒有調用super.init(config)所以就獲取不到servletConfig對象了)

    而無參數的init()方法在GenericServlet中的出現,解決了這種或許人為造成的獲取不到ServletConfig對象的尷尬。可以看到含參init方法調用了無參init,這就讓我們可以在處理Servlet初始化參數的時候,只需要重寫無參的init()方法就行了。初始化config對象的操作仍然在Container調用init(ServletConfig config)時候完成,然后調用你重寫了的init()方法,完成其他初始化操作。

 

測試代碼如下:

package cn.qlq.servlet;

import java.io.IOException;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ServletConfigTest extends HttpServlet {

    private static Logger log = LoggerFactory.getLogger(ServletConfigTest.class);

    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);// 必須調用這個,否則會導致servlet的ServletConfig為null,且不會調用init()方法

        // 調用init的方法進行初始化工作
        System.out.println("init(ServletConfig config)方法--------");
    }

    @Override
    public void init() throws ServletException {
        System.out.println("無參的構造函數init()");
    }

    @Override
    public void destroy() {
        System.out.println("ServletConfigTest  被銷毀");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("doGet方法進行服務------------");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("service()方法執行----------");
        super.service(req, resp);
    }
    
}

 

 

配置:

    <servlet>
        <servlet-name>servletConfigTest</servlet-name>
        <servlet-class>cn.qlq.servlet.ServletConfigTest</servlet-class>
        <init-param>
            <param-name>name</param-name>
            <param-value>qlq</param-value>
        </init-param>
        <init-param>
            <param-name>password</param-name>
            <param-value>111</param-value>
        </init-param>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>servletConfigTest</servlet-name>
        <url-pattern>/servletConfigTest</url-pattern>
    </servlet-mapping>

 

 

結果:當我們瀏覽器訪問的時候才會創建該servlet對象,而且多次訪問只會創建一個對象,所以servlet是線程非安全的。

 

信息: Server startup in 1987 ms
無參的構造函數init()
init(ServletConfig config)方法--------
service()方法執行----------
doGet方法進行服務------------
service()方法執行----------
doGet方法進行服務------------
service()方法執行----------
doGet方法進行服務------------
................
ServletConfigTest  被銷毀

 

 

 

 

 

 

將上面的調用父類對象的init(servletConfig)的方法注釋掉,同時在服務方法中調用servletConfig讀取配置的參數會報錯:

package cn.qlq.servlet;

import java.io.IOException;
import java.util.Enumeration;

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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ServletConfigTest extends HttpServlet {

    private static Logger log = LoggerFactory.getLogger(ServletConfigTest.class);

    @Override
    public void init(ServletConfig config) throws ServletException {
//        super.init(config);// 必須調用這個,否則會導致servlet的ServletConfig為null,且不會調用init()方法

        // 調用init的方法進行初始化工作
        System.out.println("init(ServletConfig config)方法--------");
    }

    @Override
    public void init() throws ServletException {
        System.out.println("無參的構造函數init()");
    }

    @Override
    public void destroy() {
        System.out.println("ServletConfigTest  被銷毀");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.getServletConfig().getInitParameter("name");
        System.out.println("doGet方法進行服務------------");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

}

 

結果: 

init(ServletConfig config)方法--------
八月 04, 2018 3:26:06 下午 org.apache.catalina.core.StandardWrapperValve invoke
嚴重: Servlet.service() for servlet [servletConfigTest] in context with path [/jwxt] threw exception
java.lang.NullPointerException
    at cn.qlq.servlet.ServletConfigTest.doGet(ServletConfigTest.java:40)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:624)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:731)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:110)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:498)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:169)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:1025)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:445)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1139)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:637)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:316)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)

 

 

解釋:

  覆蓋掉init(servletConfig)沒有調用super.init(servletConfig)所以不會調用init()方法,也不會初始化servletConfig對象,所以執行getServletConfig也會報錯。

 

2.Servlet對象的創建時機:

在servlet的配置當中,<load-on-startup>5</load-on-startup>的含義是:

  標記容器是否在啟動的時候就加載這個servlet。

  當值為0或者大於0時,表示容器在應用啟動時就加載這個servlet;

  當是一個負數時或者沒有指定時,則指示容器在該servlet被選擇時才加載。

  正數的值越小,啟動該servlet的優先級越高。

 

例如:修改servlet的配置如下:

    <servlet>
        <servlet-name>servletConfigTest</servlet-name>
        <servlet-class>cn.qlq.servlet.ServletConfigTest</servlet-class>
        <init-param>
            <param-name>name</param-name>
            <param-value>qlq</param-value>
        </init-param>
        <init-param>
            <param-name>password</param-name>
            <param-value>111</param-value>
        </init-param>
        <!-- 參數為0或這大於0表示容器啟動加載servlet,為負數或者沒設置表示第一次訪問的時候創建對象 -->
        <load-on-startup>0</load-on-startup>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>servletConfigTest</servlet-name>
        <url-pattern>/servletConfigTest</url-pattern>
    </servlet-mapping>

 

結果:

 

3.Servlet的路徑映射問題: 

如下面一段配置:

    <servlet>
        <servlet-name>servletConfigTest</servlet-name>
        <servlet-class>cn.qlq.servlet.ServletConfigTest</servlet-class>
        <init-param>
            <param-name>name</param-name>
            <param-value>qlq</param-value>
        </init-param>
        <init-param>
            <param-name>password</param-name>
            <param-value>111</param-value>
        </init-param>
        <!-- 參數為0或這大於0表示容器啟動加載servlet,為負數或者沒設置表示訪問的時候創建對象 -->
        <load-on-startup>0</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>servletConfigTest</servlet-name>
        <url-pattern>/servletConfigTest</url-pattern>
    </servlet-mapping>

 

 

需要注意的是:

1、一個<servlet>可以對應多個<serlvet-mapping>,從而一個Servlet可以有多個路徑來訪問

2、url-partten中的路徑也可以使用*通配符,但是只能有兩種固定的格式:一種格式是“*.擴展名”,另一種格式是以正斜杠(/)開頭並以“/*”結尾。

由於*的引入,有可能一個路徑被多個urlpartten匹配,這是優先級判斷條件如下:

  • 哪個越精確找哪個
  • *.后綴的格式永遠匹配級最低

【舉例】

對於如下的一些映射關系:

  • Servlet1 映射到 /abc/*
  • Servlet2 映射到 /*    (表示任何路徑都能匹配)
  • Servlet3 映射到 /abc
  • Servlet4 映射到 *.do

問題:

  • 當請求URL為“/abc/a.html”時,“/abc/*”和“/*”都匹配,哪個servlet響應?Servlet引擎將調用Servlet1。
  • 當請求URL為“/abc”時,“/abc/*”和“/abc”都匹配,哪個servlet響應?Servlet引擎將調用Servlet3。
  • 當請求URL為“/abc/a.do”時,“/abc/*”和“*.do”都匹配,哪個servlet響應?Servlet引擎將調用Servlet1。
  • 當請求URL為“/a.do”時,“/*”和“*.do”都匹配,哪個servlet響應?Servlet引擎將調用Servlet2。
  • 當請求URL為“/xxx/yyy/a.do”時,“/*”和“*.do”都匹配,哪個servlet響應?Servlet引擎將調用Servlet2。

 

缺省Servlet:

  如果有一個Servlet的url-partten被配置為了一根正斜杠"/",這個Servlet就變成了缺省Serlvet.其他Servlet都不處理的請求,由缺省Servlet來處理.

  其實對於靜態資源的訪問就是由缺省Servlet來執;設置404頁面、500頁面等提示頁面也是由缺省Servlet來執行。通常我們不會自己去配置缺省Servlet

 

總結:

  servlet在創建的時候調用init(ServletConfig)進行實例化,在init(ServletConfig)方法中調用init()方法。在整個容器中只有一個servlet的實例,因此Servlet是線程非安全的。servlet的創建時機可以由load-on-start參數指定是在容器啟動的時候創建還是在第一次訪問該servlet的時候創建。

  當有servlet請求的時候servlet調用service()方法進行服務,service方法根據請求方式是get、Post或者其他調用對應的doXXX方法提供服務。

  容器關閉的時候servlet銷毀,會調用destroy()方法。

 

 2.ServletConfig深入學習 (代表當前Servlet在web.xml中的配置信息,每個servlet都有一個此實例(用的不多))

  • String getServletName()  -- 獲取當前Servlet在web.xml中配置的名字

  • String getInitParameter(String name) -- 獲取當前Servlet指定名稱的初始化參數的值

  • Enumeration getInitParameterNames()  -- 獲取當前Servlet所有初始化參數的名字組成的枚舉

  • ServletContext getServletContext()  -- 獲取代表當前web應用的ServletContext對象

  在Servlet的配置文件中,可以使用一個或多個<init-param>標簽為servlet配置一些初始化參數。

  當servlet配置了初始化參數后,web容器在創建servlet實例對象時,會自動將這些初始化參數封裝到ServletConfig對象中,並在調用servlet的init(servletConfig)方法時,將ServletConfig對象傳遞給servlet。進而,程序員通過ServletConfig對象就可以得到當前servlet的初始化參數信息。(重寫了Servlet的init(ServletConfig)方法必須調用父類的init(ServletConfig)方法,否則在該servlet中獲取不到servletConfig對象)

  這樣做的好處是:如果將數據庫信息、編碼方式等配置信息放在web.xml中,如果以后數據庫的用戶名、密碼改變了,則直接很方便地修改web.xml就行了,避免了直接修改源代碼的麻煩

例如:

package cn.qlq.servlet;

import java.io.IOException;
import java.util.Enumeration;

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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ServletConfigTest extends HttpServlet {

    private static Logger log = LoggerFactory.getLogger(ServletConfigTest.class);

    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);//調用父類的該方法初始化servletConfig對象,否則在該實例中獲取不到servletConfig對象
        System.out.println("-----------init servlet-----------------");
    }
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //獲取Servlet對象
        ServletConfig config = getServletConfig();
        
        //獲取servletName
        String servletName = config.getServletName();//獲取servlet的名稱
        System.out.println("------------servletName :"+servletName);
        
        //獲取servletContext對象
        ServletContext servletContext = config.getServletContext();//獲取ServletContext對象
        
        //獲取配置文件的單個參數
        String name = config.getInitParameter("name");
        System.out.println("-------------the value of name param:"+name);
        
        //獲取枚舉類型的參數
        Enumeration<String> params = config.getInitParameterNames();
        while (params.hasMoreElements()) {
            String param = params.nextElement();
            System.out.println("參數:"+param+"\t值:"+config.getInitParameter(param));
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

}

 

web.xml配置:

    <servlet>
        <servlet-name>servletConfigTest</servlet-name>
        <servlet-class>cn.qlq.servlet.ServletConfigTest</servlet-class>
        <init-param>
            <param-name>name</param-name>
            <param-value>qlq</param-value>
        </init-param>
        <init-param>
            <param-name>password</param-name>
            <param-value>111</param-value>
        </init-param>
        <!-- 參數為0或這大於0表示容器啟動加載servlet,為負數或者沒設置表示訪問的時候創建對象 -->
        <load-on-startup>0</load-on-startup>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>servletConfigTest</servlet-name>
        <url-pattern>/servletConfigTest</url-pattern>
    </servlet-mapping>

結果:

 

 

總結:

  getServlerConfig()獲取的是init(ServletConfig)的實例,而且servlet也有getInitPatameter(name)方法,實際是獲取servletConfig之后調用該servletConfig的getInitParameter(name)方法。查看源碼如下:

    public void init(ServletConfig config) throws ServletException {
    this.config = config;
    this.init();
    }
    public ServletConfig getServletConfig() {
    return config;
    }

 

 

servlet的  getInitParameter(name)方法原理:

    public String getInitParameter(String name) {
        ServletConfig sc = getServletConfig();
        if (sc == null) {
            throw new IllegalStateException(
                lStrings.getString("err.servlet_config_not_initialized"));
        }

        return sc.getInitParameter(name);
    }
    

 

3.ServletContext深入學習(jsp的application域)

WEB容器在啟動時,它會為每個WEB應用程序都創建一個對應的ServletContext對象,它代表當前web應用

ServletConfig對象中維護了ServletContext對象的引用,開發人員在編寫servlet時,可以通過ServletConfig.getServletContext方法獲得ServletContext對象。

由於一個WEB應用中的所有Servlet共享同一個ServletContext對象,因此Servlet對象之間可以通過ServletContext對象來實現通訊。ServletContext對象通常也被稱之為context域對象

0.ServletContext的三種獲取方式:

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //3種獲取ServletContext的方法
        ServletContext servletContext1 = this.getServletContext();        
        ServletContext servletContext2 = this.getServletConfig().getServletContext();
        ServletContext servletContext3 = req.getSession().getServletContext();
    }

 

 

下面分別闡述ServletContext對象的作用:

1.讀取web.xml中的全局配置

  類似於ServletConfig,只不過ServletConfig讀取的是單個Servlet的配置參數,而ServletContext讀取的全局的配置參數。

例如:

web.xml中配置:

    <context-param>
        <param-name>user.name</param-name>
        <param-value>張三</param-value>
    </context-param>
    <context-param>
        <param-name>user.password</param-name>
        <param-value>123456</param-value>
    </context-param>
    

代碼:

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext servletContext = this.getServletContext();        
        
        //1.讀取全局配置文件
        String username = servletContext.getInitParameter("user.name");
        System.out.println("user.name:"+username);
        //獲取枚舉類型的參數
        Enumeration<String> params = servletContext.getInitParameterNames();
        while (params.hasMoreElements()) {
            String param = params.nextElement();
            System.out.println("參數:"+param+"\t值:"+servletContext.getInitParameter(param));
        }
    }

 

結果:

user.name:張三
參數:user.password    值:123456
參數:user.name    值:張三

 

 

2.做為域對象可以在整個web應用范圍內共享數據。(JSP的application域)

這里涉及到一些概念:

  • 域對象:在一個可以被看見的范圍內共享數據用到對象
  • 作用范圍:整個web應用范圍內共享數據
  • 生命周期:當服務器啟動web應用加載后創建出ServletContext對象后,域產生。當web應用被移除出容器或服務器關閉,隨着web應用的銷毀域銷毀

 

例如:存、取、刪數據

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext servletContext = this.getServletContext();
        // 存數據
        servletContext.setAttribute("name", "nonono");
        servletContext.setAttribute("password", "123456");

        // 取數據
        servletContext.getAttribute("name");
        
        //刪除域數據
        servletContext.removeAttribute("password");
    }

 

 

 

3.實現Servlet的請求轉發

  • 請求重定向:302+Location(兩次請求兩次響應)
  • 請求轉發:服務器內不進行資源流轉 (一次請求一次響應,來實現資源流轉)

注:上方括號中的內容是二者的區別。打個比方,假如你找我借錢,如果是請求重定向的話,我告訴你張三有,你再去找張三借;如果是請求轉發的話,那我去找張三借,然后再借給你。

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         ServletContext servletContext = this.getServletContext();
         servletContext.getRequestDispatcher("/index.jsp").forward(req, resp);//注意路徑是從項目的根路徑開始
    }

 

 補充:request對象也可以實現請求轉發,response可以實現重定向:

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         req.getRequestDispatcher("/index.jsp").forward(req, resp);//注意路徑是從項目的根路徑開始
         resp.sendRedirect("http://qiaoliqiang.cn/");//地址可以從http協議開始
    }

 

4.加載資源

  加載從webapp目錄下的文件,可以解決路徑問題:

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         ServletContext servletContext = this.getServletContext();
         String realPath2 = servletContext.getRealPath("/index.jsp");//我們獲取的是webapp目錄開始的文件
         String readFileToString = FileUtils.readFileToString(new File(realPath2));//讀取文件
         System.out.println(realPath2);
         System.out.println(readFileToString);
    }

 

結果:

 

 如果是非web項目用類加載器加載資源參考我的另一篇博客:http://www.cnblogs.com/qlqwjy/p/7272821.html

 

 

 5.可以獲取服務器信息、Servlet版本以及項目的映射名稱等信息 

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         ServletContext servletContext = this.getServletContext();
         String serverInfo = servletContext.getServerInfo();//獲取tomcat服務器信息
         int majorVersion = servletContext.getMajorVersion();//最大的ServletAPI版本
         int minorVersion = servletContext.getMinorVersion();//最小的ServletAPI版本
         String contextPath = servletContext.getContextPath();//獲取項目名稱
         System.out.println("服務器信息"+serverInfo);
         System.out.println("支持最大的ServletAPI版本:"+majorVersion);
         System.out.println("支持最小的ServletAPI版本:"+minorVersion);
         System.out.println("項目名稱::"+contextPath);
    }

 

結果:

服務器信息Apache Tomcat/7.0.88
支持最大的ServletAPI版本:3
支持最小的ServletAPI版本:0
項目名稱::/jwxt

 

6.servletContext還有一個重要作用就是統計一個網站的訪問量:

  可以向servletContext中存一個全局變量,當然也可以通過數據庫實現

 

 

 

參考:http://www.cnblogs.com/smyhvae/p/4140529.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM