Servlet學習的大綱
- servlet概念及相關接口簡介
- servet 執(zhí)行過程
- servlet映射路徑
<url-pattern> - 缺省servlet -- 應用
- servlet生命周期(重點) -- 理解(重點)
- Servlet自動加載
- Servlet線程安全
- servletConfig對象
- Servlet相關接口詳解
- ServletContext對象 -- 知識點
Servlet學習內容
- HttpServletRequest請求對象 : 獲取請求信息
- HttpServletResponse響應對象 : 設置響應對象
- ServletConfig對象 : servlet配置對象
- ServletContext對象 : servlet的上下文對象
servlet概念及相關接口簡介
- 如何開發(fā)一個Servlet
- 基本步驟:
- 編寫java類,繼承HttpServlet類
- 重新doGet和doPost方法
- Servlet程序交給tomcat服務器運行!
3.1 servlet程序的class碼拷貝到WEB-INF/classes目錄
3.2 在web.xml文件中進行配置
- Servlet配置信息 (配置一個Servlet)
// 訪問一個Servlet: http://localhost:8080/myServlet/first
<!-- Servlet的配置-->
<!-- Servlet的配置-->
<Servlet>
<!-- Servlet的內部名稱, 自定義, 盡量起的要有意義-->
<servlet-name>First</servlet-name>
<!-- Servlet的類全名: 包名+簡單類名-->
<servlet-class>myServlet.first</servlet-class>
</Servlet>
<!-- Servlet的映射配置 -->
<servlet-mapping>
<!-- Servlet的內部名稱, 一定要和上面的內部名稱保持一致-->
<servlet-name>First</servlet-name>
<!-- Servlet的映射路徑 (訪問Servlet的名稱)-->
<url-pattern>/first<url-pattern>
</servlet-mapping>
- 具體詳解: 訪問一個 http://localhost:8080/myServlet/first (前提: tomcat服務器啟動時, 首先加載webapps中的每個web應用的web.xml配置文件)
- http: 協(xié)議名稱
- localhost : 到本地的host文件查找是否存在改域名對應的IP地址
- 8080 : 找到tomcat服務器
- /myServlet : 在tomcat的webapps目錄下面找myServlet的目錄
- /first : 資源名稱
1)在day10的web.xml中查找是否有匹配的url-pattern的內容(/first)
2)如果找到匹配的url-pattern,則使用當前servlet-name的名稱到web.xml文件中查詢是否相同名稱的servlet配置
3)如果找到,則取出對應的servlet配置信息中的servlet-class內容: myServlet.first(字符串) - 通過反射:
a: 構造first的對象
b: 然后調用first里面的方法
Servlet的映射路徑<url-pattern>
- 精確匹配:
- url-pattern:
/first - 瀏覽器輸入: http://localhost:8080/myServlet/first
- url-pattern:
- 模糊匹配:
- url-pattern --> 瀏覽器地址
-
/*--> http://localhost:8080/myServlet/任意路徑 -
/myFirst/*--> http://localhost:8080/myServlet/myFirst/任意路徑 -
*.后綴名--> http://localhost:8080/myServlet/任意路徑.后綴名 *.html(偽靜態(tài))
-
- url-pattern --> 瀏覽器地址
- 注意:
1)url-pattern要么以/開頭,要么以*開頭。 例如, myServlet是非法路徑。
2)不能同時使用兩種模糊匹配,例如 /myServlet/*.do是非法路徑
3)當有輸入的URL有多個servlet同時被匹配的情況下:- 3.1 精確匹配優(yōu)先。(長的最像優(yōu)先被匹配)
- 3.2 以后綴名結尾的模糊url-pattern優(yōu)先級最低!
Servlet缺省路徑
Servlet的缺省路徑(<url-pattern>/</url-pattern>)是在tomcat服務器內置的一個路徑。該路徑對應的是一個DefaultServlet (缺省Servlet) 這個缺省的Servlet的作用是用于解析web應用的靜態(tài)資源文件
- 問題: URL輸入http://localhost:8080/myServlet/index.html 如何讀取文件?
1)到當前myServlet應用下的web.xml文件查找是否有匹配的url-pattern。
2)如果沒有匹配的url-pattern,則交給tomcat的內置的DefaultServlet處理
3)DefaultServlet程序到myServlet應用的根目錄下查找是存在一個名稱為index.html的靜態(tài)文件
4)如果找到該文件,則讀取該文件內容,返回給瀏覽器
5)如果找不到該文件,則返回404錯誤頁面 - 結論: 先找動態(tài)資源,再找靜態(tài)資源
Servlet 執(zhí)行過程(Servlet的生命周期)
引入: Servlet的生命周期: servlet類對象什么時候創(chuàng)建,什么時候調用什么方法,什么時候銷毀。Servlet程序的生命周期由tomcat服務器控制的!
-
Servlet重要的四個生命周期方法
- 構造方法 : 創(chuàng)建servlet對象的時候調用。默認情況下,第一次訪問servlet的時候創(chuàng)建servlet對象只調用1次。證明servlet對象在tomcat是單實例的
- init方法 : 創(chuàng)建完servlet對象的時候調用。只調用1次
- 有參的init方法和無參的init方法 - 有參的init方法: 該方法是servlet的生命周期方法,一定會被tomcat服務器調用 (如果要編寫初始代碼,不需要覆蓋有參數(shù)的init方法; 如果覆蓋了有參數(shù)的init方法, 那么只會調用有參的init()方法 ) - 無參的init方法: 該方法是servlet的編寫初始化代碼的方法。是Sun公司設計出來專門給開發(fā)者進行覆蓋,然后在里面編寫servlet的初始邏輯代碼的方法。- service方法 : 每次發(fā)出請求時調用。調用n次
- destroy方法 : 銷毀servlet對象的時候調用。停止服務器或者重新部署web應用時銷毀servlet對象。只調用1次
-
偽代碼演示servlet的生命周期 - Tomtcat內部代碼運行:
1)通過映射找到到servlet-class的內容,字符串 : my.servlet.FirstServlet
2)通過反射構造FirstServlet對象 :- 得到字節(jié)碼對象 :
Class clazz = class.forName("my.servlet.FirstServlet"); - 調用無參數(shù)的構造方法來構造對象 :
Object obj = clazz.newInstance();--> *** 1.servlet的構造方法被調用***
3)創(chuàng)建ServletConfig對象,通過反射調用init方法 :
- 得到方法對象:
Method m = clazz.getDeclareMethod("init",ServletConfig.class); - 調用方法:
m.invoke(obj,config);--> ***2.servlet的init方法被調用 ***
4)創(chuàng)建request, response對象, 通過反射調用service方法 :
- 得到方法對象 :
Methodm m =clazz.getDeclareMethod("service",HttpServletRequest.class,HttpServletResponse.class);
2 調用方法 :m.invoke(obj,request,response);--> 3.servlet的service方法被調用
5)當tomcat服務器停止或web應用重新部署,通過反射調用destroy方法
- 得到方法對象 :
Method m = clazz.getDeclareMethod("destroy",null); - 調用方法 :
m.invoke(obj,null);-->4.servlet的destroy方法被調用
- 得到字節(jié)碼對象 :
Servlet的自動加載 (提高用戶體驗)
- 需求 : 默認情況下,第一次訪問servlet的時候創(chuàng)建servlet對象。如果servlet的構造方法或init方法中執(zhí)行了比較多的邏輯代碼,那么導致用戶第一次訪問sevrlet的時候比較慢。
- 解決方案 : 改變servlet創(chuàng)建對象的時機, 提前到加載web應用的時候; 在servlet的配置信息中,加上一個<load-on-startup>即可!
<servlet>
<servlet-name>LifeDemo</servlet-name>
<servlet-class>MyLife.LifeDemo</servlet-class>
<!-- 讓servlet對象自動加載 -->
<load-on-startup>1</load-on-startup>
<!-- 注意: 整數(shù)值越大,創(chuàng)建優(yōu)先級越低!(創(chuàng)建多個servlet的時候, 數(shù)值會起作用) -->
</servlet>
Servlet的多線程并發(fā)問題(Servlet對象在tomcat服務器是單實例多線程的)
- 引入 : 因為servlet是多線程的, 所以當多個servlet的線程同時訪問了servlet的共享數(shù)據(jù), 如成員變量, 可能會引發(fā)線程安全問題
- 解決辦法:
- 把使用到共享數(shù)據(jù)的代碼塊進行同步 (使用synchronized關鍵字進行同步)
- 建議在Servlet類中盡量不要使用成員變量; 如果確實要使用成員, 必須同步; 而且盡量縮小同步代碼塊的范圍 (哪里使用到了成員變量, 就同步哪里), 以避免因為同步而導致并發(fā)效率降低
/** Servlet的多線程并發(fā)問題 */
public class TheradDemo extends HttpServlet {
int count = 1;
public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
synchronized (TheradDemo.class) {//鎖對象必須唯一。建議使用類對象
response.getWriter().write("你現(xiàn)在是當前網站的第"+count+"個訪客"); //線程1執(zhí)行完, 線程2執(zhí)行
//線程1還沒有執(zhí)行count++
/*try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
count++;
}
}
}
ServletConfig對象
- ServletConfig對象的作用 : 主要是用于加載servlet的初始化參數(shù)。在一個web應用可以存在多個ServletConfig對象 (一個Servlet對應一個ServletConfig對象)
- 對象創(chuàng)建和得到 :
- 創(chuàng)建時機 : 在創(chuàng)建完servlet對象之后,在調用init方法之前創(chuàng)建
- 得到對象 : 直接從有參數(shù)的init方法中得到
- Servlet的初始化參數(shù)配置 : servlet的參數(shù)只能由當前的這個servlet獲?。?/li>
- ServletConfig的API:
- java.lang.String ->
getInitParameter(java.lang.String name): 根據(jù)參數(shù)名獲取參數(shù)值 - java.util.Enumeration ->
getInitParameterNames(): 獲取所有參數(shù) - ServletContext ->
getServletContext(): 得到servlet上下文對象 - java.lang.String ->
getServletName(): 得到servlet的名稱
- java.lang.String ->
// 配置ServletConfig對象 : web.xml文件中
<servlet>
<servlet-name>ConfigDemo</servlet-name>
<servlet-class>gz.itcast.f_config.ConfigDemo</servlet-class>
<!-- 初始參數(shù): 這些參數(shù)會在加載web應用的時候,封裝到ServletConfig對象中 -->
<init-param>
<param-name>path</param-name>
<param-value>e:/b.txt</param-value>
</init-param>
<init-param>
<param-name>BBB</param-name>
<param-value>BBB's value</param-value>
</init-param>
<init-param>
<param-name>CCCC</param-name>
<param-value>CCCC's value</param-value>
</init-param>
</servlet>
// 獲取ServletConfig對象
public class ConfigDemo extends HttpServlet {
/**
* 以下兩段代碼GenericServlet已經寫了,我們無需編寫!
*/
/*private ServletConfig config;*/
/**
* 1)tomcat服務器把這些參數(shù)會在加載web應用的時候,封裝到ServletConfig對象中
* 2)tomcat服務器調用init方法傳入ServletConfig對象
*/
/*@Override
public void init(ServletConfig config) throws ServletException {
this.config = config;
}*/
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/**
* 讀取servlet的初始參數(shù) : this.getServletConfig() -> 獲取ServletConfig
*/
String path = this.getServletConfig().getInitParameter("path");
File file = new File(path);
//讀取內容
BufferedReader br = new BufferedReader(new FileReader(file));
String str = null;
while( (str=br.readLine())!=null ){
System.out.println(str);
}
//查詢當前servlet的所有初始化參數(shù)
Enumeration<String> enums = this.getServletConfig().getInitParameterNames();
while(enums.hasMoreElements()){
String paramName = enums.nextElement();
String paramValue = this.getServletConfig().getInitParameter(paramName);
System.out.println(paramName+"="+paramValue);
}
//得到servlet的名稱
String servletName = this.getServletConfig().getServletName();
System.out.println(servletName);
}
}
ServletContext對象引入
- 引入 : ServletContext對象, 叫做Servlet的上下文對象; 表示一個當前的web應用環(huán)境; 一個web應用中只有一個ServletContext對象
- 對象創(chuàng)建和得到
- 創(chuàng)建時機 : 加載web應用時創(chuàng)建ServletContext對象
- 得到對象 : 從ServletConfig對象的getServletContext方法得到
- 設計ServletConfig對象:
- 我們設計:創(chuàng)建ServletConfig對象
public void init( ServletConfig config,ServletContext context ){ 多了一個參數(shù)
得到ServletConfig對象
得到ServletContext對象;
}
- Sun公司設計 :
- 創(chuàng)建ServletContext對象 :
ServletContext context = new ServletContext(); - 創(chuàng)建ServletConfig對象 :
ServetConfig config = new ServletConfig();
config.setServletContxt(context); - 得到 ServletContext 對象:
SerlvetContext context = config.getServletContext();
- 創(chuàng)建ServletContext對象 :
class ServletConfig{
ServletContext context;
public ServletContext getServletContxt(){
return contxt;
}
}
public void init( ServletConfig config ){
// 得到ServletConfig對象
// 從ServletConfig對象中得到ServletContext對象
SerlvetContext context = config.getServletContext();
}
ServletContext對象的使用
- ServletContext對象的核心API(作用)
- 得到當前web應用的路徑 :
getContextPath()->java.lang.String - 得到web應用的初始化參數(shù)
-
getInitParameter(java.lang.String name)->java.lang.String -
getInitParameterNames()->java.util.Enumeration
-
- 域對象有關的方法
-
void setAttribute->java.lang.String name, java.lang.Object object -
getAttribute(java.lang.String name)->java.lang.Object -
void removeAttribute->java.lang.String name
-
- 轉發(fā)(類似于重定向) : RequestDispatcher
-
getRequestDispatcher->java.lang.String path
-
- 得到web應用的資源文件
-
getRealPath(java.lang.String path)->java.lang.String -
getResourceAsStream(java.lang.String path)->java.io.InputStream
-
- 得到web應用路徑 :
- java.lang.String :
getContextPath()-- > 用在請求重定向的資源名稱中
public class ContextDemo1 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//1.得到ServletContext對象
//ServletContext context = this.getServletConfig().getServletContext();
ServletContext context = this.getServletContext(); //(推薦使用)
//2.得到web應用路徑 /day10
/**
* web應用路徑:部署到tomcat服務器上運行的web應用名稱
*/
String contextPath = context.getContextPath();
System.out.println(contextPath);
/**
* 案例:應用到請求重定向
*/
response.sendRedirect(contextPath+"/index.html");
}
}
- 得到web應用的初始化參數(shù) (全局)
<!-- 配置web應用參數(shù) -->
<context-param>
<param-name>AAA</param-name>
<param-value>AAA's value</param-value>
</context-param>
<context-param>
<param-name>BBB</param-name>
<param-value>BBB's value</param-value>
</context-param>
<context-param>
<param-name>CCC</param-name>
<param-value>CCC's value</param-value>
</context-param>
- 得到web應用的初始化參數(shù) :
-
getInitParameter(java.lang.String name)->java.lang.String -
getInitParameterNames()->java.util.Enumeration
-
public class ContextDemo2 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//得到SErvletContext對象
ServletContext context = this.getServletContext();
System.out.println("參數(shù)"+context.getInitParameter("AAA"));
Enumeration<String> enums = context.getInitParameterNames();
while(enums.hasMoreElements()){
String paramName = enums.nextElement();
String paramValue =context.getInitParameter(paramName);
System.out.println(paramName+"="+paramValue);
}
//嘗試得到ConfigDemo中的servlet參數(shù)
String path = this.getServletConfig().getInitParameter("path");
System.out.println("path="+path);
}
}
web應用參數(shù)可以讓當前web應用的所有servlet獲??!
- 域對象有關的方法
- 域對象 : 作用是用于保存數(shù)據(jù), 獲取數(shù)據(jù); 可以在不同的動態(tài)資源之間共享數(shù)據(jù)
- 案例 : 將Servlet1的name=eric / Student 對象, 傳遞給 Servlet2
- 方案1: 可以通過傳遞參數(shù)的形式,共享數(shù)據(jù)。局限:只能傳遞字符串類型
- 方案2: 可以使用域對象共享數(shù)據(jù),好處:可以共享任何類型的數(shù)據(jù)!
- ServletContext就是一個域對象, 作用范圍在整個web應用中有效!
- 保存數(shù)據(jù):void setAttribute(java.lang.String name, java.lang.Object object)
- 獲取數(shù)據(jù): java.lang.Object : getAttribute(java.lang.String name)
- 刪除數(shù)據(jù): void removeAttribute(java.lang.String name)
- 所有域對象:
- HttpServletRequet 域對象
- ServletContext域對象
- HttpSession 域對象
- PageContext域對象
public class ContextDemo3 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 保存數(shù)據(jù)
//1.得到域對象
ServletContext context = this.getServletContext();
//2.把數(shù)據(jù)保存到域對象中
//context.setAttribute("name", "eric");
context.setAttribute("student", new Student("jacky",20));
System.out.println("保存成功");
}
}
class Student{
private String name;
private int age;
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 Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student [age=" + age + ", name=" + name + "]";
}
}
public class ContextDemo4 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 獲取數(shù)據(jù)
//1.得到域對象
ServletContext context = this.getServletContext();
//2.從域對象中取出數(shù)據(jù)
//String name = (String)context.getAttribute("name");
Student student = (Student)context.getAttribute("student");
//System.out.println("name="+name);
System.out.println(student);
}
}
- 轉發(fā) / 重定向 :
API : RequestDispatcher : getRequestDispatcher(java.lang.String path)
轉發(fā)
a. 地址欄不會改變
b. 轉發(fā)只能轉發(fā)到當前web應用內的資源
c. 可以在轉發(fā)過程中,可以把數(shù)據(jù)保存到request域對象中
RequestDispatcher rd = this.getServletContext().getRequestDispatcher("/GetDataServlet");
rd.forward(request, response);重定向
a. 地址欄會改變,變成重定向到地址。
b. 重定向可以跳轉到當前web應用,或其他web應用,甚至是外部域名網站。
c. 不能再重定向的過程,把數(shù)據(jù)保存到request中
response.sendRedirect("/day10/GetDataServlet");結論: 如果要使用request域對象進行數(shù)據(jù)共享,只能用轉發(fā)技術!
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 轉發(fā)(效果:跳轉頁面)
* @author APPle
*
*/
public class ForwardDemo1 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/**
* 保存數(shù)據(jù)到request域對象
*/
request.setAttribute("name", "rose");
//轉發(fā)
/**
* 注意:不能轉發(fā)當前web應用以外的資源。
*/
/*RequestDispatcher rd = this.getServletContext().getRequestDispatcher("/GetDataServlet");
rd.forward(request, response);*/
this.getServletContext().getRequestDispatcher("/GetDateServlet").forward(request, response);
}
}
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class GetDataServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/**
* 從request域對象中獲取數(shù)據(jù)
*/
String name = (String)request.getAttribute("name");
System.out.println("name="+name);
}
}
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class RedirectDemo1 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/**
* 保存數(shù)據(jù)到request域對象
*/
request.setAttribute("name", "rose");
//重定向
/**
* 注意:可以跳轉到web應用內,或其他web應用,甚至其他外部域名。
*/
//response.sendRedirect("/day09/adv.html");
response.sendRedirect("/day10/GetDataServlet");
}
}
讀取web應用下的資源文件
- 示例: 讀取web應用下的properties文件
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ResourceDemo extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/**
* . 代表java命令運行目錄。java運行命令在哪里?? 在tomcat/bin目錄下
* 結論: 在web項目中, . 代表在tomcat/bin目錄下開始,所以不能使用這種相對路徑。
*/
//讀取文件。在web項目下不要這樣讀取。因為.表示在tomcat/bin目錄下
/*File file = new File("./src/db.properties");
FileInputStream in = new FileInputStream(file);*/
/**
* 使用web應用下加載資源文件的方法
*/
/**
* 1. getRealPath讀取,返回資源文件的絕對路徑
*/
/*
String path = this.getServletContext().getRealPath("/WEB-INF/classes/db.properties");
System.out.println(path);
File file = new File(path);
FileInputStream in = new FileInputStream(file);
*/
/**
* 2. getResourceAsStream() 得到資源文件,返回的是輸入流
*/
InputStream in = this.getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties");
Properties prop = new Properties();
//讀取資源文件
prop.load(in);
String user = prop.getProperty("user");
String password = prop.getProperty("password");
System.out.println("user="+user);
System.out.println("password="+password);
}
}
練習:
- 改造通訊錄程序, servlet+xml版本
- 要求 : 使用瀏覽器操作系統(tǒng)
- 提示 :
- 添加聯(lián)系人 : 設計一個添加聯(lián)系人html頁面
- 保存邏輯 : AddServlet (接收頁面數(shù)據(jù) (getParameter() ),使用dom4j保存到xml)
- 修改聯(lián)系人 : QueryServlet (xml查詢修改的聯(lián)系人, 把聯(lián)系人顯示到一個html頁面(response.getWriter.write("<html></html>")) );
- 修改保存邏輯 : UpdateServlet ( 接收頁面數(shù)據(jù),把數(shù)據(jù)保存xml )
- 刪除聯(lián)系人 : 輸入刪除id的html頁面
刪除邏輯 : DeleteServle( 接收id, 在xml文件中刪除對應的聯(lián)系人)