1. 程式人生 > >JavaEE之WEB專案開發(使用Servlet+Tomcat)

JavaEE之WEB專案開發(使用Servlet+Tomcat)

一、WEB專案的演變

1.發展規律

  • 由單機向網路發展
  • 由CS向BS發展

2.CS和BS的區別

1)CS

  • Client Server
  • 客服端伺服器程式
  • 客戶端需要單獨開發,使用者需要單獨下載並安裝

2)BS

  • Browser Server
  • 瀏覽器伺服器程式
  • 客戶端不用單獨開發,使用者不用單獨安裝

二、Servlet介紹(*)

1.伺服器如何儲存並返回一個網頁?

1)靜態網頁

  • 無論誰看其內容都一樣
  • 百科、新聞
  • 伺服器直接存HTML,直接返回HTML即可

2)動態網頁

  • 不同人看到的內容有差異
  • 淘寶、微博
  • 伺服器儲存一個元件,動態給每個使用者拼一個網頁
  • 在Java語言中這個元件就是Servlet
    元件:滿足規範的物件

2.Servlet的特點

  • 是伺服器端的元件
  • 滿足sun的規範
  • 可以動態拼資源(HTML/IMG等)
    術語:處理HTTP協議

3.什麼是Servlet?

  • 是sun推出的用於在伺服器端處理HTTP協議的元件

三、伺服器

1.名稱

  • Java伺服器
  • WEB伺服器
  • Java WEB伺服器
  • Servlet容器

2.本質

  • 是一個軟體
  • 它和瀏覽器是平級的關係

3.舉例

  • Tomcat(Apache)
  • JBoss
  • WebLogic
  • WebSphere

四、Tomcat的使用方式

1.單獨使用(專案上線時)

1)配置好JAVA_HOME

2)下載及安裝

  • 去Apache官網
  • 安裝配置自行百度

3)啟動tomcat

  • Linux:開啟/tomcat/bin,在終端輸入chmod +x *sh
  • Linux:開啟/tomcat/bin,在終端輸入./startup.sh
  • windows:開啟/tomcat/bin,雙擊startup.bat

4)訪問tomcat

5)關閉tomcat

  • Linux:開啟/tomcat/bin,在終端輸入./shutdown.sh
  • windows:開啟/tomcat/bin,雙擊shutdown.bat

2.通過Eclipse呼叫(開發時)

  • 點選window -> preferences -> server -> Runtime Environments -> add …
  • 在Eclipse中將自動生成Servers專案

補充

1.tomcat常見使用問題

1)問題描述

  • 在啟tomcat時看到如下錯誤
  • adress already in use,JVM_BIND:8080

2)產生的原因

  • 重複啟動tomcat造成8080埠衝突
  • 可能其他軟體佔用了8080埠

3)解決方案

  • 啟動原因:開啟/tomcat/bin目錄,通過命令強制關閉它
  • 其它軟體:開啟server.xml,在65行修改tomcat埠
    建議修改為8088/8089,修改後重啟tomcat才生效

五、Servlet開發步驟

1.建立WEB專案

  • 必須具備標準的WEB目錄
  • /webapp/WEB-INF/web.xml

2.匯入jar包

1)使用maven

  • 使用maven搜尋javaee
  • 在結果中選擇javaee-api

2)使用tomcat自帶的包(一般使用這個)

  • 選擇專案右鍵點選properties
  • 彈出框裡在左側選擇Targeted Runtimes
  • 在右側勾選Apache Tomcat
  • Apply

3.開發Servlet

1)編寫Servlet

  • 建立package
  • 建立一個類,名為XxxServlet
  • 繼承HttpServlet,從而間接的實現了Servlet介面
  • 重寫父類的service()

2)配置Servlet

  • 先宣告類,並給它去別名
  • 在通過別名引用此類,給他取一個訪問路徑

4.部署(拷貝)

  • 在Servers檢視下,選擇tomcat7
  • 右鍵點選Add and Remove
  • 在彈出框內將左邊的待部署專案移動到右側
  • 啟動tomcat即可

5.訪問

六、案例(瀏覽器顯示系統時間)

這裡寫圖片描述

目錄結構

這裡寫圖片描述

TimeServlet程式碼

package web;

import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;

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

public class TimeServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse res) 
                          throws ServletException, IOException {
        //1.使用request接收請求資料
        //1)請求行(3)
        System.out.println("協議型別:"+req.getProtocol());
        System.out.println("訪問路徑:"+req.getServletPath());
        System.out.println("請求方式:"+req.getMethod());
        //2)訊息頭(鍵值對)
        //該資料是按照鍵值對的方式儲存的
        //Enumeration是一個古老的迭代器,
        //其用法和Iterator相似.
        Enumeration<String> e = req.getHeaderNames();
        while(e.hasMoreElements()) {
            String key = e.nextElement();
            String value = req.getHeader(key);
            System.out.println(key + ":" + value);
        }
        //3)實體內容
        //本案例沒有傳遞具體的業務資料
        //2.使用response傳送響應資料
        //1)狀態行
        //該資料由伺服器自動填充
        //2)訊息頭
        //訊息頭中的大部分資料由伺服器填充,但返回的內容格式需要程式設計師指定


        //告訴瀏覽器向它傳送的是什麼型別的內容
        res.setContentType("text/html");
        //獲取輸出流,該流指向的目標是瀏覽器
        PrintWriter pw = res.getWriter();
        //獲取伺服器的時間
        //將時間拼到一個網頁裡給瀏覽器返回
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
        String now = sdf.format(date);
        //3)實體內容
        //我們輸出的網頁就是實體內容
        //此處偷懶,拼一個簡化版的網頁
        pw.println("<p>" + now + "</p>");
        //關閉輸出流
        pw.close();
    }

}

web.xml配置檔案程式碼

<!-- 配置Servlet -->
  <!-- 1.宣告Servlet,並給它取個別名 -->
  <servlet>
     <servlet-name>time</servlet-name>
     <servlet-class>web.TimeServlet</servlet-class>
  </servlet>
  <!-- 2.通過別名引用Servlet,並給它取個網名(網路訪問路徑) -->
  <servlet-mapping>
    <servlet-name>time</servlet-name>
    <!-- 網名必須以/開頭 -->
    <url-pattern>/ts</url-pattern>
  </servlet-mapping>

這裡寫圖片描述

這裡寫圖片描述

這裡寫圖片描述

這裡寫圖片描述

七、HTTP協議

1.什麼是HTTP

  • 就是一個規範(w3c)
  • 規定了瀏覽器和伺服器如何通訊以及通訊的資料格式

2.如何通訊

  • 瀏覽器和伺服器建立連線
  • 瀏覽器向伺服器傳送請求
  • 瀏覽器接受響應
  • 斷開連線
    一次請求一次連結,降低伺服器的壓力

3.資料格式

1)請求資料

  • 請求行:請求的基本資訊
  • 訊息頭:請求資料的描述
  • 實體內容:具體的業務資料

2)響應資料

  • 狀態行:響應的基本資訊
  • 訊息頭:響應資料的描述
  • 實體內容:具體的返回資料

程式碼實現,見上一個案例的程式碼註釋

4.HTTP對開發者的要求

1)不用開發者處理的地方

  • 瀏覽器自動打包傳送請求資料
  • 伺服器自動打包傳送響應資料

2)需要開發者處理的地方

  • 提供具體的請求中的業務資料
  • 提供具體的響應中的返回資料
  • 通過request處理請求資料,通過response處理響應資料
    要求開發者會使用request和response就行了

八、註冊案例

![這裡寫圖片描述](https://img-blog.csdn.net/20180330224221243?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0RheWN5bQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

1. 以下涉及的亂碼問題的解決方法(三種)

![這裡寫圖片描述](https://img-blog.csdn.net/20180330224243643?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0RheWN5bQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

2. RegServlet.java程式碼

package web;
import java.io.IOException;
import java.io.PrintWriter;

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

public class RegServlet extends HttpServlet {

    @Override
    protected void service(
        HttpServletRequest req, 
        HttpServletResponse res) throws ServletException, IOException {
        //採用方案三解決POST請求亂碼問題(在接受資料之前)
        req.setCharacterEncoding("utf-8");
        //處理請求的一般流程
        //1.接收引數
        String name = req.getParameter("userName");
        String pwd = req.getParameter("pwd");
        String sex = req.getParameter("sex");
        String[] interests = req.getParameterValues("interest");

        //採用方案一解決亂碼問題
        //byte[] bs = name.getBytes("iso8859-1");
        //name = new String(bs,"utf-8");

        //2.處理業務
        //常規的註冊業務應該儲存這些資料,此處就輸出下
        System.out.println(name);
        System.out.println(pwd);
        System.out.println(sex);
        if(interests != null) {
            for(String interest : interests) {
                System.out.println(interest);
            }
        }
        //3.傳送響應
        res.setContentType("text/html;charset=utf-8");
        PrintWriter w = res.getWriter();
        w.println("<p>ok,"+name+"</p>");
        w.close();
    }
}

3. register.heml程式碼

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>註冊</title>
</head>
<body>
    <!-- 
        1.完整路徑
            http://ip:port/專案名/網名
        2.絕對路徑
            格式: /專案名/網名
            舉例: /servlet2/reg
        3.相對路徑(*)
            當前: /servlet2/reg.html
            目標: /servlet2/reg
            二者平級,相對路徑: reg
     -->
    <form action="reg" method="post">
        <p>
            賬號:<input type="text" name="userName"/>
        </p>
        <p>
            密碼:<input type="password" name="pwd"/>
        </p>
        <p>
            性別:
            <input type="radio" name="sex" value="M"/>男
            <input type="radio" name="sex" value="F"/>女
        </p>
        <p>
            興趣:
            <input type="checkbox" name="interest" value="food"/>美食
            <input type="checkbox" name="interest" value="game"/>競技
            <input type="checkbox" name="interest" value="friend"/>社交
        </p>
        <p>
            <input type="submit" value="註冊"/>
        </p>
    </form>
</body>
</html>

4. 配置檔案程式碼

<servlet>
  <servlet-name>reg</servlet-name>
  <servlet-class>web.RegServlet</servlet-class>
</servlet>
  <servlet-mapping>
  <servlet-name>reg</servlet-name>
  <url-pattern>/reg</url-pattern>
</servlet-mapping>
![這裡寫圖片描述](https://img-blog.csdn.net/20180330224325310?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0RheWN5bQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70) ![這裡寫圖片描述](https://img-blog.csdn.net/20180330224340919?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0RheWN5bQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70) ![這裡寫圖片描述](https://img-blog.csdn.net/20180330224402452?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0RheWN5bQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

九、Servlet執行原理

![這裡寫圖片描述](https://img-blog.csdn.net/20180330224420977?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0RheWN5bQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

十、請求方式

1.什麼是起你去方式?

  • 就是瀏覽器向伺服器傳送資料的方式
  • 我們需要掌握眾多方式中的2種:GET+POST

2.GET

  • 採用請求路徑傳參
  • 引數在傳遞過程中可見,導致隱私性差
  • 路徑可以容納的資料有限,只能傳少量資料
    所有的請求預設都是GET請求

3.POST

  • 採用實體內容傳參
  • 引數在傳遞過程中不可見,隱私性好
  • 實體內容專門用來傳資料,大小沒有限制
    在form上加method=”post”

4.觀察GET和POST請求

  • 在瀏覽器上按快捷鍵F12
  • 看NetWork選項

5.GET和POST使用場景(建議)

  • 查詢資料時用GET,因為通常查詢條件較少
  • 提交資料(表單)時用POST,因為通常提交的資料較多。

十一、亂碼解決方案

這裡寫圖片描述

補充:

JavaBean

滿足如下規範的類

  • 有包
  • 有預設的構造器
  • 實現序列化介面
  • 有get/set方法

十二、模擬查詢員工案例

這裡寫圖片描述

1. Emp.java實體類

package entity;

import java.io.Serializable;
/**
 * 建議:
 * 1.儘量使用封裝型別,因為它比基本型別多了null
 * 2.使用java.sql包下的日期,因為JDBC支援這樣的日期型別
 *
 */
public class Emp implements Serializable {

    private Integer empno;
    private String ename;
    private String job;
    private Double sal;

    public Integer getEmpno() {
        return empno;
    }
    public void setEmpno(Integer empno) {
        this.empno = empno;
    }
    public String getEname() {
        return ename;
    }
    public void setEname(String ename) {
        this.ename = ename;
    }
    public String getJob() {
        return job;
    }
    public void setJob(String job) {
        this.job = job;
    }
    public Double getSal() {
        return sal;
    }
    public void setSal(Double sal) {
        this.sal = sal;
    }

}

2. EmpDao.java介面

package dao;

import java.util.List;
import entity.Emp;

public interface EmpDao {

    List<Emp> findAll();

    void save(Emp e);
}

3. EmpDaoImpl.java

package dao;

import java.util.ArrayList;
import java.util.List;

import entity.Emp;

public class EmpDaoImpl implements EmpDao {

    public List<Emp> findAll() {
        //模擬查詢所有的員工
        List<Emp> list = new ArrayList<Emp>();

        Emp e1 = new Emp();
        e1.setEmpno(1);
        e1.setEname("唐僧");
        e1.setJob("領導");
        e1.setSal(9000.0);
        list.add(e1);

        Emp e2 = new Emp();
        e2.setEmpno(2);
        e2.setEname("悟空");
        e2.setJob("職員");
        e2.setSal(5000.0);
        list.add(e2);

        Emp e3 = new Emp();
        e3.setEmpno(3);
        e3.setEname("八戒");
        e3.setJob("職員");
        e3.setSal(6000.0);
        list.add(e3);

        return list;
    }

    //模擬增加一個員工(此用於後面增加員工案例)
    public void save(Emp e) {
        System.out.println("增加了員工: " + e.getEname());
    }
}

4. FindEmpServlet.java

package web;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

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

import dao.EmpDao;
import dao.EmpDaoImpl;
import entity.Emp;

public class FindEmpServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, 
            HttpServletResponse res) 
            throws ServletException, IOException {
        //1.接收引數
        //2.處理業務(查詢)
        EmpDao dao = new EmpDaoImpl();
        List<Emp> list = dao.findAll();
        //3.輸出響應(表格)
        res.setContentType("text/html;charset=utf-8");
        PrintWriter w = res.getWriter();
        //當前: /EmpManager/findEmp
        //目標: /EmpManager/add_emp.html
        w.println("<a href='add_emp.html'>增加</a>");
        //上面一行用於後面增加員工案例
        w.println("<table border='1' cellspacing='0' width='40%'>");
        w.println(" <tr>");
        w.println("     <td>編號</td>");
        w.println("     <td>姓名</td>");
        w.println("     <td>職位</td>");
        w.println("     <td>薪資</td>");
        w.println(" </tr>");
        //以下是表格資料
        if(list != null) {
            for(Emp e : list) {
                w.println("<tr>");
                w.println(" <td>"+e.getEmpno()+"</td>");
                w.println(" <td>"+e.getEname()+"</td>");
                w.println(" <td>"+e.getJob()+"</td>");
                w.println(" <td>"+e.getSal()+"</td>");
                w.println("</tr>");
            }
        }
        w.println("</table>");
        w.close();
    }

}

5. web.xml

<servlet>
    <servlet-name>findEmp</servlet-name>
    <servlet-class>web.FindEmpServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>findEmp</servlet-name>
    <url-pattern>/findEmp</url-pattern>
</servlet-mapping>

這裡寫圖片描述

這裡寫圖片描述

十二、模擬增加員工案例

這裡寫圖片描述

1. AddEmpServlet.java

package web;

import java.io.IOException;
import java.io.PrintWriter;

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

import dao.EmpDao;
import dao.EmpDaoImpl;
import entity.Emp;

public class AddEmpServlet extends HttpServlet {

    @Override
    protected void service(
        HttpServletRequest req, 
        HttpServletResponse res) throws ServletException, IOException {

        req.setCharacterEncoding("utf-8");

        //1.接收引數
        String ename = req.getParameter("ename");
        String job = req.getParameter("job");
        String sal = req.getParameter("sal");
        //2.處理業務:儲存員工資料
        Emp e = new Emp();
        e.setEname(ename);
        e.setJob(job);
        if(sal != null && sal.equals("")) {
            e.setSal(new Double(sal));
        }       
        EmpDao dao = new EmpDaoImpl();
        dao.save(e);
        //3.傳送響應
        res.setContentType("text/html;charset=utf-8");
        PrintWriter w = res.getWriter();
        w.println("<p>儲存成功</p>");
        w.close();
    }
}

2. add_emp.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>增加員工</title>
</head>
<body>
    <!-- 
        當前: /EmpManager/add_emp.html
        目標: /EmpManager/addEmp.do
     -->
    <form action="addEmp" method="post">
        <p>
            姓名:<input type="text" name="ename"/>
        </p>
        <p>
            職位:<input type="text" name="job"/>
        </p>
        <p>
            薪資:<input type="text" name="sal"/>
        </p>
        <p>
            <input type="submit" value="儲存"/>
        </p>
    </form>
</body>
</html>

3. web.xml 配置檔案在加上一下程式碼

<servlet>
    <servlet-name>addEmp</servlet-name>
    <servlet-class>web.AddEmpServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>addEmp</servlet-name>
    <url-pattern>/addEmp</url-pattern>
</servlet-mapping>

這裡寫圖片描述

這裡寫圖片描述

注:此案例沒連資料庫,儲存成功並不能真正的增加

這裡寫圖片描述

十三、重定向

這裡寫圖片描述

1. 修改AddEmpServlet.java

package web;

import java.io.IOException;

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

import dao.EmpDao;
import dao.EmpDaoImpl;
import entity.Emp;

public class AddEmpServlet extends HttpServlet {

    @Override
    protected void service(
        HttpServletRequest req, 
        HttpServletResponse res) throws ServletException, IOException {

        req.setCharacterEncoding("utf-8");

        //1.接收引數
        String ename = req.getParameter("ename");
        String job = req.getParameter("job");
        String sal = req.getParameter("sal");
        //2.儲存員工資料
        Emp e = new Emp();
        e.setEname(ename);
        e.setJob(job);
        if(sal != null && sal.equals("")) {
            e.setSal(new Double(sal));
        }       
        EmpDao dao = new EmpDaoImpl();
        dao.save(e);
        //3.傳送響應
//      res.setContentType("text/html;charset=utf-8");
//      PrintWriter w = res.getWriter();
//      w.println("<p>儲存成功</p>");
//      w.close();
        //重定向到查詢頁面,即
        //建議瀏覽器自己去訪問查詢頁面.
        //當前: /EmpManager/addEmp
        //目標: /EmpManager/findEmp
        res.sendRedirect("findEmp");
    }
}

增加員工提交後,會重定向到查詢介面

十四、路徑

1.什麼是路徑

這裡寫圖片描述

2.URI和URL的區別

1)俠義的理解

  • 只在Java專案中理解URI和URL
  • URI:絕對路徑
  • URL:完整路徑
    從表面上看URI包含了URL

2)廣義的理解

  • 在任意的WEB專案中理解URI和URL
  • URI:資源的名稱
  • URL:資源的真名
    URI包含了URL

3.如何配置Servlet訪問路徑

1)精確匹配

  • 舉例: /abc
  • 只有/abc才能訪問此Servlet
  • 此Servlet只能處理這一個請求
    適合規模很小的專案

2)萬用字元

  • 舉例: /*
  • 所有的路徑的都能訪問此Servlet
  • 此Servlet能處理所有請求
    適合一個專案只寫一個Servlet

3)字尾

  • 舉例: *.hi
  • 表示所有以hi為字尾的請求都能訪問此Servlet
  • 此Servlet能處理多個請求
    適合一個專案寫少量的幾個Servlet

4)配置檔案web.xml

<servlet>
    <servlet-name>abc</servlet-name>
    <servlet-class>web.AbcServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>abc</servlet-name>
<!-- 1.精確定位 -->
    <!-- <url-pattern>/abc</url-pattern> -->
    <!-- 2.萬用字元 -->
    <!-- <url-pattern>/*</url-pattern> -->
    <!-- 3.字尾,不能以斜線開頭-->
    <url-pattern>*.dung</url-pattern>
</servlet-mapping>
![這裡寫圖片描述](https://img-blog.csdn.net/20180330225031212?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0RheWN5bQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

4. 按規範處理路徑修改員工查詢、增加案例

![這裡寫圖片描述](https://img-blog.csdn.net/20180330225043741?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0RheWN5bQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

新增MianServlet.java

package web;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

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

import dao.EmpDao;
import dao.EmpDaoImpl;
import entity.Emp;
/**
 * 路徑規範:
 * 查詢員工:/findEmp.do
 * 增加員工:/addEmp.do
 *
 */
public class MainServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, 
            HttpServletResponse res) 
            throws ServletException, IOException {
        //獲取請求路徑
        String path = req.getServletPath();
        //根據規範處理路徑
        if("/findEmp.do".equals(path)) {
            findEmp(req,res);
        }else if("/addEmp.do".equals(path)) {
            addEmp(req,res);
        }else {
            //該異常拋給伺服器,伺服器可以統一處理
            throw new RuntimeException("查無此頁");
        }
    }

    protected void findEmp(HttpServletRequest req, 
            HttpServletResponse res) 
            throws ServletException, IOException {
        //1.接收引數
        //2.處理業務(查詢)
        EmpDao dao = new EmpDaoImpl();
        List<Emp> list = dao.findAll();
        //3.輸出響應(表格)
        res.setContentType("text/html;charset=utf-8");
        PrintWriter w = res.getWriter();
        //當前: /EmpManager/findEmp
        //目標: /EmpManager/add_emp.html
        w.println("<a href='add_emp.html'>增加</a>");
        w.println("<table border='1' cellspacing='0' width='40%'>");
        w.println(" <tr>");
        w.println("     <td>編號</td>");
        w.println("     <td>姓名</td>");
        w.println("     <td>職位</td>");
        w.println("     <td>薪資</td>");
        w.println(" </tr>");
        //以下是表格資料
        if(list != null) {
            for(Emp e : list) {
                w.println("<tr>");
                w.println(" <td>"+e.getEmpno()+"</td>");
                w.println(" <td>"+e.getEname()+"</td>");
                w.println(" <td>"+e.getJob()+"</td>");
                w.println(" <td>"+e.getSal()+"</td>");
                w.println("</tr>");
            }
        }
        w.println("</table>");
        w.close();
    }

    protected void addEmp(HttpServletRequest req, 
            HttpServletResponse res) 
            throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");

        //1.接收引數
        String ename = req.getParameter("ename");
        String job = req.getParameter("job");
        String sal = req.getParameter("sal");
        //2.儲存員工資料
        Emp e = new Emp();
        e.setEname(ename);
        e.setJob(job);
        if(sal != null && sal.equals("")) {
            e.setSal(new Double(sal));
        }       
        EmpDao dao = new EmpDaoImpl();
        dao.save(e);
        //3.傳送響應
//      res.setContentType("text/html;charset=utf-8");
//      PrintWriter w = res.getWriter();
//      w.println("<p>儲存成功</p>");
//      w.close();
        //重定向到查詢頁面,即
        //建議瀏覽器自己去訪問查詢頁面.
        //當前: /EmpManager/addEmp.do
        //目標: /EmpManager/findEmp.do
        res.sendRedirect("findEmp.do");
    }

}

修改web.xml配置檔案(之前的註釋掉)

<servlet>
    <servlet-name>main</servlet-name>
    <servlet-class>web.MainServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>main</servlet-name>
    <url-pattern>*.do</url-pattern>
</servlet-mapping>
![這裡寫圖片描述](https://img-blog.csdn.net/20180330225108915?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0RheWN5bQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

將add_emp.html中的action=”addEmp” 改成 action=”addEmp.do”

![這裡寫圖片描述](https://img-blog.csdn.net/20180330225124210?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0RheWN5bQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70) ![這裡寫圖片描述](https://img-blog.csdn.net/2018033022513888?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0RheWN5bQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

十五、Servlet生命週期

![這裡寫圖片描述](https://img-blog.csdn.net/20180330225151613?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0RheWN5bQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

示例

以下幾個案例都寫在了Servlet3專案裡了,目錄結構如下

![這裡寫圖片描述](https://img-blog.csdn.net/20180330225206161?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0RheWN5bQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

HiServlet.java

package web;

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;

public class HiServlet extends HttpServlet {
    public HiServlet() {
        System.out.println("例項化HiServlet");
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        System.out.println("初始化HiServlet");
    }

    @Override
    protected void service(HttpServletRequest req, 
            HttpServletResponse res) 
            throws ServletException, IOException {
        System.out.println("呼叫HiServlet處理請求");
    }

    @Override
    public void destroy() {
        super.destroy();
        System.out.println("銷燬HiServlet");
    }   
}

web.xml配置檔案

<servlet>
    <servlet-name>hi</servlet-name>
    <servlet-class>web.HiServlet</servlet-class>
    <!-- 啟動時載入此servlet,中間的數字是載入的順序。 -->
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>hi</servlet-name>
    <url-pattern>/hi</url-pattern>
</servlet-mapping>

十六、ServletConfig和ServletContext

1.它們的作用

  • 都能夠讀取web.xml中為Servlet預置的引數

這裡寫圖片描述

2.它們的區別

1)config

  • config和Servlet是1對1的關係
  • Tomcat在初始化每個Servlet前會給它建立一個config
  • 呼叫HiServelt.init()前給他建立1個config
  • 呼叫HelloServlet.init()前給它建立1個config
  • 如果先給某個Servlet預置資料,使用config

2)context

  • context和Servlet是1對多的關係
  • Tomact在啟動前就建立唯一的一個context
  • 所有的Servlet都可以共享這個物件中的資料
  • 如果想給多個Servlet預置資料,使用context

3.它們的應用

1)config(預置引數)

  • 假設要開發一個網頁遊戲,若超過人數上限則要排隊
  • 登入時判斷是否達到最大人數
  • 開發登入功能LoginServlet
  • 人數上限應該是一個可配置的引數maxOnline
  • 該引數由LoginServlet使用
    由於該引數只是LoginServlet使用,由config讀取即可

示例(登入上限)

LoginServlet.java

package web;

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;

public class LoginServlet extends HttpServlet {

    //Tomcat建立Servlet的過程:
    //LoginServlet ls = new LoginServlet();
    //ServletConfig cfg = new ServletConfig();
    //ls.init(cfg);
    //ls.service();

    @Override
    public void init(ServletConfig config)throws ServletException {
        super.init(config);
        String maxOnline = config.getInitParameter("maxOnline");
        System.out.println(maxOnline);
    }   

    @Override
    protected void service(
        HttpServletRequest req, 
        HttpServletResponse res) throws ServletException, IOException {
        //此config就是init()傳入的那個
        ServletConfig cfg = getServletConfig();
        String maxOnline = cfg.getInitParameter("maxOnline");
        System.out.println(maxOnline);
        System.out.println("正在登入...");
    }   
}

web.xml

<servlet>
    <servlet-name>login</servlet-name>
    <servlet-class>web.LoginServlet</servlet-class>
    <!-- 給此servlet預置引數 -->
    <init-param>
        <param-name>maxOnline</param-name>
        <param-value>3000</param-value>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>login</servlet-name>
    <url-pattern>/login</url-pattern>
</servlet-mapping>
![這裡寫圖片描述](https://img-blog.csdn.net/20180330225248114?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0RheWN5bQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70) ![這裡寫圖片描述](https://img-blog.csdn.net/20180330225302147?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0RheWN5bQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

2)context(分頁)

  • 軟體內有很多查詢功能,都帶有分頁功能
  • 每頁顯示的行數size是常量,並且可配置
  • 該資料在多個查詢功能之間共用,使用context讀取

示例

FindDeptServlet.java

package web;

import java.io.IOException;

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

public class FindDeptServlet extends HttpServlet {

    @Override
    protected void service(
        HttpServletRequest req, 
        HttpServletResponse res) throws ServletException, IOException {
        //tomcat啟動時就會建立唯一的context,並且會呼叫它的方法
        //載入web.xml中的公用引數,context是全域性的,任何servlet都可以使用
        ServletContext ctx = getServletContext();
        String size = ctx.getInitParameter("size");
        System.out.println(size);
        System.out.println("分頁查詢部門資料");
        //統計流量
        //Integer count = (Integer)ctx.getAttribute("count");
        //ctx.setAttribute("count", ++count);
        //System.out.println(count);
    }

}

FindEmpServlet.java

package web;

import java.io.IOException;

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

public class FindEmpServlet extends HttpServlet {

    @Override
    protected void service(
        HttpServletRequest req, 
        HttpServletResponse res) throws ServletException, IOException {
        ServletContext ctx = getServletContext();
        String size = ctx.getInitParameter("size");
        System.out.println(size);
        System.out.println("分頁查詢員工資料");
        //統計流量
        //Integer count = (Integer)ctx.getAttribute("count");
        //ctx.setAttribute("count", ++count);
        //System.out.println(count);
    }

}

web.xml

<servlet>
    <servlet-name>findDept</servlet-name>
    <servlet-class>web.FindDeptServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>findDept</servlet-name>
    <url-pattern>/findDept</url-pattern>
</servlet-mapping>

<servlet>
    <servlet-name>findEmp</servlet-name>
    <servlet-class>web.FindEmpServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>findEmp</servlet-name>
    <url-pattern>/findEmp</url-pattern>
</servlet-mapping>

<!-- 在標籤外配置的引數是給所有Servlet公用的引數,它們都可以通過context讀取該資料 -->
<context-param>
    <param-name>size</param-name>
    <param-value>20</param-value>
</context-param>

這裡寫圖片描述

這裡寫圖片描述

這裡寫圖片描述

4.context的特殊用法(context可以存取變數)

  • 前提:之前使用config和context讀取的是常量
  • 而context還有能力讀寫變數
  • 用該物件讀寫的變數是可以被所有Servelt共用的
  • setAttribute()/getAttribute()
  • 案例:開發流量統計功能,無論訪問哪個功能,流量+1
  • 由於流量是變數,並且在多功能間共用,所以用context

這裡寫圖片描述

示例

InitServlet.java

package web;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;

/**
 * 此類僅僅是在伺服器啟動時初始化引數的,
 * 不負責處理任何具體的請求.
 * 一般web專案都要1-2個這樣的servlet
 */
public class InitServlet extends HttpServlet {

    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        //tomcat啟動時會優先建立context,然後在建立Servlet
        ServletContext ctx = getServletContext();
        //流量預設為0
        ctx.setAttribute("count", 0);
    }
}

將FindDeptServlet.java和FindEmpServlet.java中統計流量的註釋去掉

web.xml

<!-- 沒人呼叫可以只寫一半 -->
<servlet>
    <servlet-name>init</servlet-name>
    <servlet-class>web.InitServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

這裡寫圖片描述

5.總結

  • 當需要給Servlet預置引數時使用這樣的物件
  • 若引數只給一個Servlet使用,用config
  • 若引數給多個Servlet使用,用context

十七、Servlet層次結構

1.整體結構

這裡寫圖片描述

2.HttpServlet(我們一般重寫黃色的service)

這裡寫圖片描述

十八、Servlet執行緒安全問題

1.同時修改區域性變數

  • 區域性變數存於棧內
  • 每個執行緒有自己的棧幀
    此時沒有執行緒安全問題

2.同時修改成員變數

  • 成員變數存於堆內
  • 所有執行緒共享這樣的資料
    此時存線上程安全問題

3.解決方案

  • 加鎖

4.案例

這裡寫圖片描述

UpServlet.java

package web;

import java.io.IOException;
import java.io.PrintWriter;

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

public class UpServlet extends HttpServlet {

    double salary = 2000.0;

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        //synchronized(this) {
            //模擬漲薪
            salary += 100.0;
            //網路延遲
            try {
                Thread.sleep(8000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //顯示資料
            res.setContentType("text/html");
            PrintWriter out =  res.getWriter();
            out.println(salary);
            out.close();
        //}     
    }   
}

web.xml

<servlet>
    <servlet-name>up</servlet-name>
    <servlet-class>web.UpServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>up</servlet-name>
    <url-pattern>/up</url-pattern>
</servlet-mapping>

不加鎖下

這裡寫圖片描述

加鎖(把UpServlet.java中的程式碼註釋去掉)

這裡寫圖片描述