Servlet一些細節:
1. 由於客戶端是通過URL地址訪問web服務器中的資源,所以Servlet程序若想被外界訪問的話,必須把servlet程序映射到一個URL地址上,這個工作在web.xml文件中使用<servlet>元素和<servlet-mapping>元素完成。
2. <servlet>元素用於註冊Servlet,它包含有兩個主要的子元素:<servlet-name>和<servlet-class>,分別用於設置Servlet的註冊名稱和Servlet的完整類名。
3. 一個<servlet-mapping>元素用於映射一個已經註冊的Servlet的一個對外的訪問路徑,它包含兩個子元素:<servlet-name>和<url-pattern>,分別用於指定Servlet的註冊名稱和Servlet的對外訪問路徑。
注意:對於一個已經註冊的Servlet可以被多次映射。
4. 在對servlet進行映射的時候,可以使用通配符
有兩種格式:
第一種格式:*.擴展名 比如說*.do *.ss
第二種格式:以/開頭並且以/*結尾 比如/* /news/*
在通配符進行匹配的時候,要參考的標準
(1) 看誰的匹配度高,誰就被選中
(2) *.do的優先級最低
5. Servlet單例問題
當Servlet被第一次訪問後,就被加載到內存,以後該實例對各個請求服務即在使用中是單例的。服務器只會創建一個Servlet實例對象,也就是說該Servlet實例對象一旦創建就會駐留在內存中,爲後續的其他請求服務,直到web容器退出或者reload該web應用程序,servlet對象纔會被銷燬。
因爲Servlet是單例,因此會出現線程安全問題,比如:
售票系統,如果不加同步機制,則會出現問題。
給大家的一個原則:
(1) 如果一個變量需要多個用戶共享,則應當在訪問該變量的時候,加同步機制synchronize(對象){
//同步代碼
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
int j = 0;
j++;
out.println("hello "+new java.util.Date().toString()+" j="+j);
synchronized (this) {
if(ticket > 0) {
System.out.println("你買到票了");
//休眠
try {
Thread.sleep(10 * 1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ticket--;
}else {
System.out.println("你沒有買到票");
}
}
}
(2) 如果一個變量不需要共享,則直接在doGet()或者doPost()中定義即可。這樣不會存在線程安全問題。
6. servlet中的<load-on-startup>配置
需求:當我們的網站啓動的時候,可能會要求初始化一些數據,(比如創建臨時表),再比如:
我們的網站要有一些定時完成的任務【定時寫日誌,定時備份數據庫…定時發送郵件】
解決方法:可以通過<load-on-startup>配合線程相關知識搞定。
先說明<load-on-startup>:通過配置它,我麼可以指定某個Servlet自動創建。
<servlet>
<description>This is the description of my J2EE component</description>
<display-name>This is the display name of my J2EE component</display-name>
<servlet-name>MyInitServlet1</servlet-name>
<servlet-class>com.sgr.servlet.MyInitServlet1</servlet-class>
<!--1表示該Servlet被init的順序-->
<load-on-startup>2</load-on-startup>
</servlet>
<servlet>
<description>This is the description of my J2EE component</description>
<display-name>This is the display name of my J2EE component</display-name>
<servlet-name>MyInitServlet2</servlet-name>
<servlet-class>com.sgr.servlet.MyInitServlet2</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
配置好<load-on-startup>之後就可以在init函數當中進行相關代碼的編寫了,例如我在另外一個包當中寫了一個簡單的模擬發郵件java程序,代碼如下:
package com.sgr.model;
public class SendEmailThread extends Thread{
public void run() {
// TODO Auto-generated method stub
int i = 0;
try {
//每休眠一分鐘,就去掃描sendmail,看看哪封信應當被髮出
while(true) {
Thread.sleep(10*1000);
System.out.println("發出第"+(++i)+"郵件");//javamail技術
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
之後我在我之前寫好的Servlet的init函數當中寫一些需要初始化而做的工作。代碼如下:
public void init() throws ServletException {
// Put your code here
System.out.println("MyInitServlet1的Init被調用");
//完成初始化任務
System.out.println("創建數據庫,表,讀取");
//創建一個線程
SendEmailThread sendEmailThread = new SendEmailThread();
sendEmailThread.start();
}