#### 1.Servlet開發入門
- ##### 1.1 Servlet由來
- 第一天我們就說過,需要服務器,使用Java代碼,訪問數據庫。因此,我們需要書寫java程序,去訪問數據庫,那么java程序在哪里運行?
- 用戶的請求從瀏覽器發出,由服務器接收,因此,用戶需要什么服務器才知道由此可見,這些java程序必須安裝到服務器中,讓服務器調用執行。
- 那么我們可以隨意寫java程序碼?不能,因為就像螺絲和螺帽一樣,必須按照一定大小尺寸規定,生成,后期才可以匹配使用。
- 因此sun公司為了讓程序員書寫的java程序可以嚴絲合縫的安裝到服務器中,定義了一個接口Servlet
- ##### 1.2 Servlet介紹
- servlet是運行在Web服務器中的小型java程序,servlet通常通過HTTP(超文本傳輸協議)接收和響應來自Web客戶端的請求。
- 要實現此接口,可以編寫一個擴展javax.servlet.http.HttpServlet的HTTP servlet
- servlet生命周期方法的調用順序
- 1.構造servlet,然后使用init方法將其初始化
- 2.處理來自客戶端的對servlce方法的所有調用
- 3.從服務中取出servlet,然后使用destroy方法銷毀它,最后進行垃圾回收并中止它。
- 什么是servlet?
- 處理請求和響應請求的java程序
- 怎么創建servlet?
- 繼承HttpServlet類,在web.xml中配置,servlet 3.0版本之后不需要在web.xml中配置
#### 2.Servlet開發的步驟
- ##### 2.1 在cn.igeek.web包下 創建一個類 實現 HttpServlet 類
```
package cn.yiran.web.servlet;
import javax.servlet.http.HttpServlet;
public class ServletDemo extends HttpServlet {
}
```
- ##### 2.2 重寫doGet和doPost,init()方法
```
package cn.yiran.web.servlet;
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 ServletDemo extends HttpServlet {
@Override
public void init(ServletConfig config) throws ServletException{
System.out.println("init()....");
}
@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 {
System.out.println("doPost....");
}
}
```
- ##### 2.3 在當前項目中的web.xml文件中配置當前開發好的Servlet程序
```
<!-- servlet路徑 -->
<servlet>
<!-- servlet的名稱 -->
<servlet-name>ServletDemo</servlet-name>
<!-- servlet的具體實現類 -->
<servlet-class>cn.yiran.web.servlet.ServletDemo</servlet-class>
</servlet>
<!-- servlet映射 -->
<servlet-mapping>
<!-- servlet的名稱 -->
<servlet-name>ServletDemo</servlet-name>
<!-- 訪問這個servlet程序的請求路徑 -->
<!-- 可以定義多個Servlet,這里配置請求路徑為,所有的servlet -->
<url-pattern>/ServletDemo</url-pattern>
</servlet-mapping>
```
- ##### 2.4 發布這個項目,運行服務器,測試
- 直接在網頁鏈接后面輸入servlet類名即可,如:http://localhost:8080/web/ServletDemo
- 在Tomcat server信息中查看調用方法為:init()... Get()...
#### 3.Servlet中的細節
- ##### 3.1 http://localhost:8080/web/ServletDemo
- http:// --> 協議
- localhost --> 電腦
- 8080 --> 服務器
- web --> web目錄
- ServletDemo --> web.xml中的/ServletDemo --> 所有的ServletDemo類 --> 某個ServletDemo類 --> init()方法+doGet()方法
- 總結:通過請求路徑,查詢web.xml中servlet配置,獲取當前servlet對象,調用java程序。
#### 4.使用IDEA模版開發Servlet(重點):可以更快捷的創建servlet對象
- ##### 4.1 如何直接創建servlet對象
- 選中包名 --> New --> servlet
- Create Java EE 6 annotated class ,創建Java EE 6 注解類,servlet3.0之后,可以利用注解來配置servlet,而不需要在web.xml中配置
- 會自動創建doGet和doPost方法
#### 5.servlet3.0注解
- ##### 5.1 @WebListener注解
- 表示在web.xml中配置的
```
<listener>
<listener-class>ListenerClass</listener-class>
</listener>
```
- ##### 5.2 @WebFilter
- 這個注解就是表示的攔截器
- 注解的主要參數及其含義

- ##### 5.3 @WebServlet
- 這個注解表示的就是一般的Servlet
- 注解的主要參數及其含義,參數和Filter差不多

- ##### 5.4 @WebInitParam
- 表示的就是參數,相當于<init-param>
- 注解的主要參數及其含義

- ##### 5.5 @MultipartConfig
- 該注解主要是為了輔助 Servlet 3.0 中 HttpServletRequest 提供的對上傳文件的支持。
- 該注解標注在 Servlet 上面,以表示該 Servlet 希望處理的請求的 MIME 類型是 multipart/form-data。
- 另外,它還提供了若干屬性用于簡化對上傳文件的處理。具體如下

#### 6.獲取請求
- ##### 6.1 準備html
- 在web目錄下新建一個regist.jsp
```
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>注冊</title>
</head>
<body>
<form action="MyServlet" method="post">
用戶名:<input type="text" name="username" /><br />
愛 好:<input type="checkbox" name="hobby" value="football" />足球
<input type="checkbox" name="hobby" value="basketball" />籃球<br />
<input type="submit" value="提交" />
</form>
</body>
</html>
```
- ##### 6.2 完善MyServlet方法
- 給@WebServlet加上value屬性 --> value = "/MyServlet"
- 完善doPost方法
- 服務器給我們提供了一個Request對象,為HTTP servlet 提供請求信息
- getParameter(String name)
- 返回類型 --> String
- 根據name 獲取對應的值
- getParameterMap()
- 返回類型 --> Map
- 將請求信息中,參數名作為key,參數值作為value,封裝到map中
- getParameterValues(String name)
- 返回類型 --> String[]
- 獲取name相同的所有value 例如復選框
```
package cn.yiran.web.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(name = "MyServlet",value = "/MyServlet")
public class MyServlet extends HttpServlet {
@Override
public void init() throws ServletException {
super.init();
System.out.println("3.0 init()");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("3.0 doPost");
// getParameter(name) --> 根據name屬性獲取值
String username = request.getParameter("username");
System.out.println("username:"+username);
// getParameterValues(name) --> 根據name屬性獲取多個值,得到的是一個String數組
String[] hobby = request.getParameterValues("hobby");
for (String str:hobby){
System.out.println("hobby:"+str);
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
```
- ##### 6.3 測試控制臺是否能輸出
- 注意:在輸入框中輸入中文,讀取出來輸出為? 這是因為沒有設置字符的編碼格式
- request.setCharacterEncoding("utf-8") --> 設置接收數據的字符編碼格式為utf-8
- response.setCharacterEncoding("utf-8"),同理。一般設置字符集,位于方法的最前。
- response.setContentType("text/html; charset=utf-8"); --> 設置返回內容類型
#### 7.發送響應
- ##### 7.1 發送響應的對象Response對象
- 提供特定于HTTP的發送響應功能。
- servlet容器創建HttpServletResPonse對象,并將該對象作為參數傳遞給servlet的service方法(doGet,doPost,等等,這些都屬于service方法)
- ##### 7.2 Response方法
- getWriter()
- 返回 --> java.io.PrintWriter
- 返回可將字符文本發送到客戶端的 PrintWriter 對象
- PrintWriter對象
- 繼承于 --> java.io.Writer
- 方法 --> writer(String s) --> 寫入字符串
- ##### 7.3 發送響應
- 直接在service(服務)方法中調用Response對象使用方法即可
```
System.out.println("========發出響應===========");
response.getWriter().write("test OK!!!!");
```
#### 8.Servlet開發應用——登錄案例
- ##### 8.1 準備數據表
- 需要3個字段,id、username、password
```
create database JavaEE charset=utf8;
use javaee
create table user(
id int auto_increment primary key not null,
username varchar(20) not null,
password varchar(20) not null
);
insert into user(username,password) value('yiran','yiran'),('chunjue','chunjue'),('miku','miku');
```
- ##### 8.2 導入jar包
- c3p0-0.9.1.2.jar
- commons-dbutils-1.6.jar
- mysql-connector-java-5.0.8-bin.jar
- ##### 8.3 新建包結構
- src
- cn.yiran.web
- dao --> 數據訪問對象,實現對數據庫表user的增刪改查
- UserDAO
- domain --> 實體層,javabean,mvc中的modle
- User
- service --> 業務邏輯層
- UserService
- servlet --> 控制器,接收和返回結果
- LoginServlet
- utils --> 工具層
- JDBCUtils
- c3p0-config-xml --> c3p0連接池的配置文件
- ##### 8.4 dao層 -- UserDAO
```
package cn.yiran.web.dao;
import cn.yiran.web.domain.User;
import cn.yiran.web.utils.JDBCUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import java.sql.SQLException;
/**
* 數據訪問對象,實現對數據庫表user的增刪改查
* */
public class UserDAO {
private QueryRunner qr = new QueryRunner(JDBCUtils.getDataSource());
// 定義一個實現登錄的方法
public User login(User user){
String sql = "select * from user where username=? and password=?";
try{
return qr.query(sql,new BeanHandler<User>(User.class),user.getUsername(),user.getPassword());
} catch (SQLException e){
e.printStackTrace();
throw new RuntimeException("登錄失敗");
}
}
}
```
- ##### 8.5 damain層 -- User
```
package cn.yiran.web.domain;
public class User {
private int id;
private String username;
private String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", password=" + password + "]";
}
}
```
- ##### 8.6 service層 -- UserService
```
package cn.yiran.web.service;
import cn.yiran.web.dao.UserDAO;
import cn.yiran.web.domain.User;
/**
* 封裝對用戶進行操作的業務邏輯
* */
public class UserService {
private UserDAO userDAO = new UserDAO();
// 實現用戶登錄的業務邏輯
public User login(User user){
return userDAO.login(user);
}
}
```
- ##### 8.7 servlet層 -- LoginServlet
```
package cn.yiran.web.servlet;
import cn.yiran.web.domain.User;
import cn.yiran.web.service.UserService;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(name = "LoginServlet",value = "/LoginServlet")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
response.setContentType("text/html; charset=utf-8");
// 1.獲取請求頁面中的數據
String username = request.getParameter("username");
String password = request.getParameter("password");
// 2.將獲取的數據進行封裝
User user = new User();
user.setUsername(username);
user.setPassword(password);
// 3.進行用戶登錄業務邏輯的判斷
UserService userService = new UserService();
User loginUser = userService.login(user);
// 4.判斷返回值,做出響應
if (loginUser == null){
response.getWriter().write("賬號或密碼錯誤");
}
else{
response.getWriter().write("登錄成功");
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
```
- ##### 8.8 utils -- JDBCUtils
```
package cn.yiran.web.utils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class JDBCUtils {
// 創建c3p0的連接池
static ComboPooledDataSource cpds = new ComboPooledDataSource();
// 獲得連接
public static Connection getConnection() throws Exception {
// 獲得連接
return cpds.getConnection();
}
public static DataSource getDataSource() {
return cpds;
}
// 釋放資源
public static void release(Connection conn, Statement stmt, ResultSet rs) {
try {
if (rs != null) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
rs = null;
release(conn, stmt);
}
// 釋放資源
public static void release(Connection conn, Statement stmt) {
try {
if (stmt != null) {
stmt.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
stmt = null;
try {
if (conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
}
```
- ##### 8.9 src層 -- c3p0-config.xml
```
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://192.168.13.22:3306/javaee</property>
<property name="user">root</property>
<property name="password">root</property>
</default-config>
</c3p0-config>
```
- ##### 8.10 報錯500
- 錯誤信息:java.lang.NoClassDefFoundError:org/apache/commons/dbutils/ResultSetHandler
- 由于Tomcat沒有找到jar包,自己放在lib中的jar包沒有添加到項目資源中,項目依賴出了問題。需要在Artifacts點擊fix,將lib包重新加載一次
- 解決教程:https://blog.csdn.net/silentkare/article/details/78160735o)
#### 9.servlet生命周期
- ##### 9.1 生命周期圖
-

- 上圖描述了servlet的生命周期。按照功能的不同,大致分為三個階段,分別是 初始化階段,運行階段(最頻繁) 和 銷毀階段。
注意:當服務器關閉的時候,項目中的servlet就調用第8步銷毀。
- 總結:Servlet 的創建,在用戶第一次請求,之后,創建。銷毀,是發生在服務器關閉的時候。
- ##### 9.2 初始化階段
- 當客戶端向tomcat發送http請求訪問servelt程序,tomcat首先會解析請求,檢查內存中是否已經有了該servlet對象:
- 如果有直接使用對應的servlet對象
- 如果沒有就創建servlet實例對象,然后通過調用init()方法實現Servlet的初始化工作。
- 需要注意的是,在整個servlet的生命周期內,init方法只被調用了一次。除非配置發生改變。
- ##### 9.3 運行階段
- 在這個階段,tomcat服務器會為這個請求 創建 代表HTTP請求的ServletRequest對象 和 代表HTTP響應的 ServletResponse對象,然后將他們作為參數傳遞給service() 方法。
- servcie() 方法從ServletRequest對象獲取請求的信息并做出處理;通過ServletResponse 對象生成響應的結果。
- 在servlet的整個生命周期內,對于servlet的每一次訪問請求,tomcat都會調用servlet的service方法,并且創建新的ServletRequest對象和ServletResponse對象。也就是說service() 方法在servlet的生命周期內會被調用多次。
- ##### 9.4 銷毀階段
- 當服務器關閉時,servlet會隨著Web應用的銷毀而銷毀
- 在銷毀serlvet之前,tomcat會調用Servlet的destory方法,以便讓Servlet對象釋放他所占用的資源。
#### 10.路徑書寫問題
- 在學習Servlet的時候,每個Servlet程序都需要在當前的這個項目的web.xml文件中注冊和映射。
- 在映射中書寫的url-pattern標簽的書寫方式:
- ##### 10.1 全路徑匹配
- 在書寫url-pattern的時候,必須以/開始,后面書寫具體瀏覽器訪問時的路徑。
- 配置:<url-pattern>/DemoServlet</url-pattern>
- 外界的訪問方式:
- http://localhost:8080/web/DemoServlet
- ##### 10.2 路徑通配符匹配
- 在書寫url-pattern 的時候,以/開始,后面可以使用*號表示任意的匹配
- 配置:<url-pattern>/DemoServlet/*</url-pattern>
- 外界在訪問的時候,只要能夠和/demo2匹配上,后面寫任何東西都可以:
- http://localhost:9090/test/DemoServlet/11111/aaa
- ##### 10.3 擴展名匹配
- 在書寫url-pattern 的時候,不能以/開始,以*開始,后面書寫擴展名
- 配置: <url-pattern>*.do</url-pattern>
- 常見的擴展名書寫:
- *.action *.do *.go
- 訪問的方式:
- http://localhost:9090/test/xxxx.do
- ##### 10.4 優先級
- url-pattern標簽中的路徑可以按照上述的三種書寫,它們的優先級:
- 全路徑匹配 > 路徑通配符匹配 > 擴展名匹配