# JavaWeb
###### tags: `Master of JAVA`
## 1.基本概念
### 前言
Web開發:
- 靜態Web
- html,css
- 提供給所有人看的數據始終不會發生變化
- 動態Web
- 幾乎所有的網站都是動態
- 提供給所有人看的數據始終會發生變化,在不同時間、地點所看到的資訊皆不相同
- 相關技術:Servlet/JSP , ASP , PHP
在JAVA中,動態資源開發的技術統稱為JavaWeb
### Web應用程序
Web應用程序:可以提供瀏覽器訪問的程序
- a.html , b.html...多個Web資源,這些Web資源可以被外界訪問,對外界提供服務
- 我們能夠訪問到的任何頁面或資源都存在於這個世界上的某一個角落的電腦上
- URL
- 這些統一的Web資源會被放在同一個資料夾下,web應用程序-->Tomcat:伺服器
- 一個Web應用由多個部分組成(靜態web,動態web)
- html, css, js
- jsp, servlet
- java程序
- jar包
- 配置文件(Properties)
web應用程序編寫完畢後,若想提供給外界訪問,就需要一個伺服器來統一管理
### 靜態Web

- 靜態Web存在的缺點
- Web頁面無法動態更新,所有User看到的都是同一個頁面
- 滑動視窗、點擊特效:偽動態
- 透過JavaScript、VB做出偽動態
- 無法與資料做互動(數據無法持久化,User不能互動)
### 動態Web

缺點:
- 載入伺服器的動態web資源如果出現錯誤,我們必須重新編寫==後端程式==,然後重新發布
- 停機維修
優點:
- Web頁面可以動態更新,所有User看到的都不是同一個頁面
- 可以與資料庫互動(數據持久化:User、商品訊息)

## 2.Web伺服器
### 技術講解
ASP:
- 微軟:最早流行的就是ASP
- 在HTML中嵌入了VB腳本,ASP+DOM
- 在ASP開發中,基本一個頁面都有幾千行程式碼,極其混亂,高耦合
- 維護成本高!
- C#
php:
- PHP開發速度很快,功能很強大,跨平台,程式碼很簡單
- 無法承受大訪問量的情況(侷限性)
JSP/Servlet:
B/S:瀏覽器與伺服器
C/S:客戶端與伺服器
- sun公司主推的B/S架構
- 基於Java語言
- 可以承載三高問題帶來的影響
- 語法像ASP
### web伺服器
伺服器是一種被動的操作,用來處理User的一些請求核給用戶一些回應訊息
**IIS**
微軟的,ASP...,Windows中內建的
**TomCat**
Tomcat是由Apache軟體基金會屬下Jakarta專案開發的Servlet容器,按照Sun Microsystems提供的技術規範,實現了對==Servlet==和==JavaServer Page(JSP)== 的支援,並提供了作為Web伺服器的一些特有功能,如Tomcat管理和控制平台、安全域管理和Tomcat閥等。由於Tomcat本身也內含了HTTP伺服器,因此也可以視作單獨的Web伺服器。但是,不能將Tomcat和Apache HTTP伺服器混淆,Apache HTTP伺服器是用C語言實現的HTTPWeb伺服器;這兩個HTTP web server不是捆綁在一起的。Apache Tomcat包含了組態管理工具,也可以通過編輯XML格式的設定檔來進行組態。
## 3.Tomcat
### 安裝Tomcat
Tomcat官網:https://tomcat.apache.org/
### Tomcat啟動與配置
資料夾結構:

bin:啟動、關閉伺服器的腳本文件
conf:配置
lib:依賴的jar包
logs:日誌
webapps:存放網站的
開啟時可能遇到的問題:
1. Java環境變數沒有配置
2. 閃退問題:需要配置相容性
3. 亂碼問題:配置文件中設定
### 配置

可以配置啟動的port number
- tomcat預設的port number為:8080
- mysql:3306
- http:80
- https:443
```xml=
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
```
可以配置主機的名稱
- 預設的主機名稱為:localhost->127.0.0.1
- 預設的網站應用存放位置為:webapps
```xml=
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
```
==補充==:請問網站是如何進行訪問的
1. 輸入一個域名,Enter
2. 檢查本機的C:\Windows\System32\drivers\etc\host配置文件下有沒有這個域名映射
1. 有:直接返回對應的ip位置,直接訪問
2. 沒有:去DNS伺服器找,找到就回傳,找不到就回傳找不到
### 發布一個web網站
不會就先模仿
- 將自己寫的網站放到tomcat伺服器中指定的資料夾(webapps)內,就可以訪問該網站了
==網站應該有的結構==:
```
--webapps :Tomcat伺服器的web目錄
-ROOT
-ykmaTest :網站的目錄名稱
-WEB-INF
-classes :Java程序
-lib :web應用依賴的jar包
-web.xml :網站配置文件
-index.html :預設的首頁
-static
-css
-style.css
-js
-img
-...
```
## 4.Http
### 什麼是HTTP
HTTP(超文本傳輸協議)是一個簡單的請求-反應協議,通常運行在TCP之上。
- 文本:html,字串,...
- 超文本:圖片,音樂,影片,定位,地圖...
- 80
HTTPs:安全的
- 443
### 兩個時代
- http1.0
- HTTP/1.0:客戶端可以與web伺服器連接後,只能獲得一個web資源,斷開連接
- http2.0
- HTTP/1.1:客戶端可以與web伺服器連接後,可以獲得多個web資源
### Http請求
- 客戶端---發請求(Request)---伺服器
Yahoo:
```java=
Request URL: https://tw.yahoo.com/ 請求網址
Request Method: GET get方法/post方法
Status Code: 200 狀態碼:200 OK!
Remote Address: 180.222.102.202:443 實體IP:PORT
Referrer Policy: origin
```
```java=
accept:text/html
accept-encoding: gzip, deflate, br
accept-language: zh-TW,zh;q=0.9,en-US;q=0.8,en;q=0.7
```
1. 請求行
* 請求行中的方式:GET
* 請求方式:==Get, Post==, Head, DELETE, PUT, TRACT
* get:請求能夠攜帶的參數比較少,大小有限制,會在URL顯示數據內容,不安全,但高效。
* post:請求能夠攜帶的參數沒有限制,大小沒有限制,不會在URL顯示數據內容,安全,但不高效。
2. 消息頭
```java=
Accept: 告訴瀏覽器,他所支援的數據類型
Accept-Encoding: 支援哪種編碼格式 GBK UTF-8 GB2312 ISO8859-1
Accept-Language: 告訴瀏覽器,他的語言環境
Cache-Control: 暫存控制
Connection: 告訴瀏覽器,請求完成是斷開還是保持連線
HOST: 主機...
```
### Http回應
- 伺服器---回應(Response)---客戶端
Google:
```java=
cache-control: private, max-age=0
content-encoding: br
content-length: 42288
content-type: text/html; charset=UTF-8
link: </?hl=zh_tw>;rel="canonical"
server: gws
set-cookie: 1P_JAR=2022-04-14-01; expires=Sat, 14-May-2022 01:33:28 GMT; path=/; domain=.google.com.tw; Secure; SameSite=none
set-cookie: SEARCH_SAMESITE=CgQImJUB; expires=Tue, 11-Oct-2022 01:33:28 GMT; path=/; domain=.google.com.tw; SameSite=strict
strict-transport-security: max-age=31536000
x-frame-options: SAMEORIGIN
x-xss-protection: 0
```
1. 回應體
```java=
Accept: 告訴瀏覽器,他所支援的數據類型
Accept-Encoding: 支援哪種編碼格式 GBK UTF-8 GB2312 ISO8859-1
Accept-Language: 告訴瀏覽器,他的語言環境
Cache-Control: 暫存控制
Connection: 告訴瀏覽器,請求完成是斷開還是保持連線
HOST: 主機...
Refresh: 告訴客戶端,多久刷新一次
Location: 讓網頁重新定位
```
2. 回應狀態碼
200:請求回應成功 200
3xx:請求重定向(重定向:你重新到我給你的新位置去)
4xx:找不到資源(資源不存在) 404
5xx:伺服器程式碼錯誤 500 502(網路閘道錯誤)
## 5.Maven
為何要學習這個技術?
1. 在Javaweb開發中,需要使用大量的jar包,需要我們手動導入
2. 如何能夠讓一個東西自動幫助我們導入與配置這個jar包
由此,Maven誕生了!
### Maven項目架構管理工具
我們目前使用maven就是為了導入jar包
Maven的核心思想:==約定大於配置==
* 有約束,不要去違反
Maven會規定好該如何編寫我們的JAVA程式碼,必須要按照這個規範來
### 下載安裝Maven
官網:https://maven.apache.org/
### 環境變數設定
在系統環境變數中設定:
* M2_HOME maven目錄下的bin目錄
* MAVEN_HOME maven的目錄
* 在系統的path中設定 %MAVEN_HOME%\bin
### 本機倉庫
在本地的倉庫, 遠程倉庫
**建立一個本地倉庫**: localRespository
```xml=
<localRepository>D:\apache-maven-3.8.5\maven-repo</localRepository>
```
### 在IDEA中使用Maven
1. 啟動IDEA
2. 創建一個MavenWeb項目

3. 觀察maven倉庫中多了甚麼東西
4. IDEA中的Maven設定

未來創建項目時如果自動生成Maven,那很可能會使用IDEA內建的Maven版本,需要手動更改
5. 到這裡,Maven在IDEA中的配置與使用就OK了!
但是因為在創建時有勾選使用maven模板,導致有兩個包沒有載入:

### 創建一個普通的Maven項目


一個乾淨的Maven項目,main裡面的java資料夾是用來放java的核心程式碼的,resources是用來放配置文件的,下方test資料夾內的綠色java資料夾是用來測試的

這個則是只有在web應用裡面才會有
### 在IDEA中標記資料夾功能

由上到下分別為核心程式碼目錄、測試核心程式碼目錄、資源目錄、測試資源目錄
### 在IDEA中設定Tomcat



警告沒有一個artifaces,我們必須要創建,為什麼會有這個問題,因為當我們要訪問一個網站時,必須要指定一個資料夾名字


這個地方可以寫,也可以不寫,預設訪問路徑為localhost:8080,假如寫一個/ykma那就是localhost:8080/ykma,這個過程叫做虛擬路徑映射

啟動Tomcat

成功顯示
### pom文件
pom.xml是Maven的核心配置文件

maven架構由上到下分為3個區塊,分別是maven的終端機操作、插件(甚至可以刪除)、項目依賴
```xml=
<?xml version="1.0" encoding="UTF-8"?>
<!--這裡就是光才配置的GAV-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.ykma</groupId>
<artifactId>javaweb-01-maven</artifactId>
<version>1.0-SNAPSHOT</version>
<!--Package: 項目的打包方式
jar:java應用
war: JavaWeb應用
-->
<packaging>war</packaging>
<name>javaweb-01-maven Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<!--配置-->
<properties>
<!--項目的預設構件編碼-->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!--編碼版本-->
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<!--項目依賴-->
<dependencies>
<!--具體依賴的jar包配置文件-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
<!--項目構建用的東西-->
<build>
<finalName>javaweb-01-maven</finalName>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
```

maven由於他的約定大於配置,我們之後可能遇到我們寫的配置文件,無法被導出或著生效的問題,解決方案:
```xml=
<!--在build中配置resources,來防止我們資源導出失敗的問題-->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>**/*.properties</exclude>
<exclude>**/*.xml</exclude>
</excludes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
```
### IDEA操作(使用架構圖來看)

## 6.Servlet
### Servlet簡介
* Servlet就是sun公司開發動態web的一門技術
* Sun在這些API中提供一個接口叫做:Servlet,如果你想開發一個Servlet程序,只須完成兩個小步驟:
* 編寫一個類,實作(實現)Servlet接口(介面)
* 把開發好的Java類部屬到web伺服器中
把實現了Servlet接口的Java程序就叫做Servlet
### HelloServlet
Servlet接口在Sun公司有兩個預設的實現類:HttpServlet、
1. 構建一個普通的Maven項目,刪掉裡面的src目錄,以後我們的學習就在這個項目裡面建立Moudel,這個空的工程就是Maven主工程
2. 關於Maven父子工程的理解:
父項目中會有
```xml=
<modules>
<module>servlet-01</module>
</modules>
```
子項目會有
```xml=
<parent>
<artifactId>javaweb-02-servlet</artifactId>
<groupId>com.ykma</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
```
父項目中的java子項目可以直接使用
```xml=
son extends father
```
3. Maven環境優化
1. 修改web.xml為最新的
2. 將maven的結構搭建完整
4. 編寫一個Servlet程序
1. 編寫一個普通類
2. 實現Servlet接口,這裡我們直接繼承HttpServlet
```java=
public class HelloServlet extends HttpServlet {
//由於get或是post只是請求實現的不同方式,可以相互調用,業務邏輯都一樣
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//ServletOutputStream outputStream = resp.getOutputStream();
PrintWriter writer = resp.getWriter(); //反應流
writer.print("Hello,Servlet");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
```
5. 編寫Servlet的映射
為什麼需要映射:我們寫的是JAVA程序,但是要通過瀏覽器訪問,而瀏覽器需要連接web伺服器,所以我們需要在web服務中註冊我們寫的Servlet,還需要給他一個瀏覽器能夠訪問的路徑
```xml=
<!--註冊Servlet-->
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.ykma.servlet.HelloServlet</servlet-class>
</servlet>
<!--Servlet的請求路徑-->
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
```
6. 配置Tomcat
注意:配置項目發布的路徑就可以了
7. 啟動測試,OK!
### Servlet原理
servlet是由Web伺服器調用,web伺服器在收到瀏覽器請求之後,會:


### Mapping問題
1. 一個Servlet可以指定一個映射路徑
```xml=
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
```
2. 一個Servlet可以指定多個映射路徑
```xml=
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello2</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello3</url-pattern>
</servlet-mapping>
```
3. 一個Servlet可以指定通用映射路徑
```xml=
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello/*</url-pattern>
</servlet-mapping>
```
4. 預設請求路徑
```xml=
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
```
5. 指定一些後綴或著前綴等等...
```xml=
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>*.ykma</url-pattern>
</servlet-mapping>
```
6. 優先級問題
指定了固有的映射路徑優先級最高,如果找不到就會走預設的處理請求
```xml=
<servlet>
<servlet-name>error</servlet-name>
<servlet-class>com.ykma.servlet.ErrorServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>error</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
```
### ServletContext
web容器在啟動的時候,它會為每個web程序都創建一個對應的ServletContext物件,它代表了當前的web應用:
1. 共享數據
我在這個Servlet中保存的數據,可以在另外一個servlet中拿到:
```java=
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//this.getInitParameter() 初始化參數
//this.getServletConfig() Servlet配
//this.getServletContext() Servlet上下文
ServletContext context = this.getServletContext();
String username = "ykma"; //數據
context.setAttribute("username",username); //將一個數據保存在了ServletContext中,名字為:username , 值 username
}
}
```
```java=
public class GetServlet extends HelloServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
String username = (String) context.getAttribute("username");
resp.setContentType("text/html");
resp.setCharacterEncoding("utf-8");
resp.getWriter().print("名字"+username);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
```
```xml=
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.ykma.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>getc</servlet-name>
<servlet-class>com.ykma.servlet.GetServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>getc</servlet-name>
<url-pattern>/getc</url-pattern>
</servlet-mapping>
```
測試訪問結果。
2. 獲取初始化參數
```xml=
<!--配置一些web應用的初始化參數-->
<context-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost:3306/mybatis</param-value>
</context-param>
```
```java=
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
String url = context.getInitParameter("url");
resp.getWriter().print(url);
}
```
#### 請求轉發
```java=
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
System.out.println("進入了ServletDemo04");
//RequestDispatcher requestDispatcher = context.getRequestDispatcher("/gp"); //轉發的請求路徑
//requestDispatcher.forward(req,resp); //調用forward方法實現請求轉發
context.getRequestDispatcher("/gp").forward(req,resp);
}
```
#### 讀取資源文件
Properties
* 在java目錄下新建properties
* 在resources目錄下新建properties
發現:都被打包到了同一個路徑下:classes,我們俗稱這個路徑叫做classpath(類路徑)
思路:需要一個文件流
```yaml=
username=root
password=123456
```
```java=
public class ServletDemo05 extends HttpServlet {
public ServletDemo05() {
}
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
InputStream is = this.getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties");
Properties prop = new Properties();
prop.load(is);
String user = prop.getProperty("username");
String pwd = prop.getProperty("password");
resp.getWriter().print(user + ":" + pwd);
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
```
### HttpServletResponse
web伺服器接收到客戶端的http請求,針對這個請求,分別創建一個代表請求的HttpServletRequest物件,代表回應的一個HttpServletResponse。
* 如果要獲取客戶端請求過來的參數:找HttpServletRequest
* 如果要給客戶端回應一些訊息:找HttpServletResponse
1. 簡單分類
負責向瀏覽器發送數據的方法
```java=
ServletOutputStream getOutputStream() throws IOException;
PrintWriter getWriter() throws IOException;
```
負責向瀏覽器發送回應頭的方法
```java=
void setCharacterEncoding(String var1);
void setContentLength(int var1);
void setContentLengthLong(long var1);
void setContentType(String var1);
void setDateHeader(String var1, long var2);
void addDateHeader(String var1, long var2);
void setHeader(String var1, String var2);
void addHeader(String var1, String var2);
void setIntHeader(String var1, int var2);
void addIntHeader(String var1, int var2);
```
回應的狀態碼
```java=
int SC_CONTINUE = 100;
int SC_SWITCHING_PROTOCOLS = 101;
int SC_OK = 200;
int SC_CREATED = 201;
int SC_ACCEPTED = 202;
int SC_NON_AUTHORITATIVE_INFORMATION = 203;
int SC_NO_CONTENT = 204;
int SC_RESET_CONTENT = 205;
int SC_PARTIAL_CONTENT = 206;
int SC_MULTIPLE_CHOICES = 300;
int SC_MOVED_PERMANENTLY = 301;
int SC_MOVED_TEMPORARILY = 302;
int SC_FOUND = 302;
int SC_SEE_OTHER = 303;
int SC_NOT_MODIFIED = 304;
int SC_USE_PROXY = 305;
int SC_TEMPORARY_REDIRECT = 307;
int SC_BAD_REQUEST = 400;
int SC_UNAUTHORIZED = 401;
int SC_PAYMENT_REQUIRED = 402;
int SC_FORBIDDEN = 403;
int SC_NOT_FOUND = 404;
int SC_METHOD_NOT_ALLOWED = 405;
int SC_NOT_ACCEPTABLE = 406;
int SC_PROXY_AUTHENTICATION_REQUIRED = 407;
int SC_REQUEST_TIMEOUT = 408;
int SC_CONFLICT = 409;
int SC_GONE = 410;
int SC_LENGTH_REQUIRED = 411;
int SC_PRECONDITION_FAILED = 412;
int SC_REQUEST_ENTITY_TOO_LARGE = 413;
int SC_REQUEST_URI_TOO_LONG = 414;
int SC_UNSUPPORTED_MEDIA_TYPE = 415;
int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
int SC_EXPECTATION_FAILED = 417;
int SC_INTERNAL_SERVER_ERROR = 500;
int SC_NOT_IMPLEMENTED = 501;
int SC_BAD_GATEWAY = 502;
int SC_SERVICE_UNAVAILABLE = 503;
int SC_GATEWAY_TIMEOUT = 504;
int SC_HTTP_VERSION_NOT_SUPPORTED = 505;
```
2. 下載檔案(文件)
1. 向瀏覽器輸出消息
2. 下載文件
1. 要獲取下載文件的路徑
2. 下載的文件名是甚麼?
3. 設定想辦法讓瀏覽能夠支援下載我們需要的東西
4. 獲取下載文件的輸入流
5. 創建緩衝區
6. 獲取OutputStream物件
7. 將FileOutputStream流寫入到buffer緩衝區
8. 使用OutputStream將緩衝區中的數據輸出到客戶端
```java=
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1. 要獲取下載文件的路徑
String realPath = "E:\\javaweb-02-servlet\\response\\src\\main\\resources\\馬凱.PNG";
System.out.println("下載文件的路徑:"+realPath);
//2. 下載的文件名是甚麼?
String fileName = realPath.substring(realPath.lastIndexOf("\\") + 1);
//3. 設定想辦法讓瀏覽能夠支援(Content-Disposition)下載我們需要的東西,中文檔名URLEncoder,encode編碼,否則可能有亂碼
resp.setHeader("Content-Disposition","attachment;filename="+ URLEncoder.encode(fileName,"UTF-8"));
//4. 獲取下載文件的輸入流
FileInputStream in = new FileInputStream(realPath);
//5. 創建緩衝區
int len = 0;
byte[] buffer = new byte[1024];
//6. 獲取OutputStream物件
ServletOutputStream out = resp.getOutputStream();
//7. 將FileOutputStream流寫入到buffer緩衝區,使用OutputStream將緩衝區中的數據輸出到客戶端
while ((len=in.read(buffer))>0){
out.write(buffer,0,len);
}
in.close();
out.close();
}
```
3. 驗證碼功能
驗證碼怎麼來的?
* 前端實現
* 後端實現,需要用到Java的圖片類,產生一個圖片
```java=
package com.ykma.servlet;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
public class ImageServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//如何讓瀏覽器3秒自動刷新一次
resp.setHeader("refresh","3");
//在記憶體中創建一個圖片
BufferedImage image = new BufferedImage(80,20,BufferedImage.TYPE_INT_RGB);
//得到圖片
Graphics2D g = (Graphics2D) image.getGraphics(); //筆
//設定圖片的背景顏色
g.setColor(Color.white);
g.fillRect(0,0,80,20);
//給圖片寫數據
g.setColor(Color.BLUE);
g.setFont(new Font(null,Font.BOLD,20));
g.drawString(makeNum(),0,20);
//告訴瀏覽器,這個請求用圖片的方式打開
resp.setContentType("image/png");
//網站存在緩存,不讓瀏覽器緩存
resp.setDateHeader("expires",-1);
resp.setHeader("Cache-Control","no-cache");
resp.setHeader("Pragma","no-cache");
//把圖片寫給瀏覽器
ImageIO.write(image,"jpg",resp.getOutputStream());
}
//生成隨機數
private String makeNum(){
Random random = new Random();
String num = random.nextInt(9999999) + "";
//不直接回傳,而是透過StringBuffer,這樣可以透過一個for迴圈去補如果長度小於7位數就補0
StringBuffer sb = new StringBuffer();
//保證回傳必定是7位數,否則補0,缺多少位就建立多少0
for (int i = 0 ; i < 7-num.length() ; i++){
sb.append("0");
}
num = sb.toString() + num;
return num;
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
}
}
```
4. 實現重定向

B一個web資源收到客戶端A的請求之後,B會通知A客戶端去訪問另外一個web資源C,這個過程就叫做重定向
常見場景:
* 用戶登入
```java=
void sendRedirect(String var1) throws IOException;
```
測試:
```java=
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
/*
resp.setHeader("Location","/r/img");
resp.setStatus(302);
*/
resp.sendRedirect("/r/img"); //重定向
}
```
重定向與轉發的區別為:
相同點
* 頁面都會實現跳轉
不同點
* 請求轉發的時候,url不會發生變化
* 重定向的時候,url地址欄會發生變化
```java=
<html>
<body>
<h2>Hello World!</h2>
<%--這裡提交的路徑,需要尋找到項目的路徑--%>
<%--${pageContext.request.contextPath}代表當前的項目 --%>
<form action="${pageContext.request.contextPath}/login" method="get">
用戶名: <input type="text" name="username"> <br>
密碼: <input type="text" name="password"> <br>
<input type="submit">
</form>
</body>
</html>
```
```java=
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//處理請求
String username = req.getParameter("username");
String password = req.getParameter("password");
System.out.println(username+":"+password);
//重定向的時候一定要注意,路徑問題,否則404
resp.sendRedirect("/r/success.jsp");
}
```
```java=
<servlet>
<servlet-name>request</servlet-name>
<servlet-class>com.ykma.servlet.RequestTest</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>request</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
```
```java=
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>Success</h1>
</body>
</html>
```
### HttpServletRequest
HttpServletRequest代表客戶端的請求,用戶通過Http協議訪問伺服器,HTTP請求中的所有訊息會被封裝到HttpServletRequest,透過這個HttpServlet的方法來獲得客戶端的所有訊息

獲取前端傳遞的參數,並且請求轉址(轉發)

```java=
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
String username = req.getParameter("username");
String password = req.getParameter("password");
String[] hobbys = req.getParameterValues("hobbys");
//後端接收中文亂碼問題
System.out.println("====================================");
System.out.println(username);
System.out.println(password);
System.out.println(Arrays.toString(hobbys));
System.out.println("====================================");
System.out.println(req.getContextPath());
//透過請求轉發
//這裡的 / 代表當前的web應用
req.getRequestDispatcher("/success.jsp").forward(req,resp);
resp.setCharacterEncoding("utf-8");
}
```
## 7.Cookie、Session
### 會話
**會話**:用戶打開一個瀏覽器,點擊了很多超連結,訪問多個web資源,關閉瀏覽器,這個過程可以稱之為會話
**有狀態會話**:一個同學來過教室,下次再來教室,我們會知道這個同學,曾經來過,稱之為有狀態會話
**你怎麼證明你就是中興大學的學生?**
1. 學費繳費發票 (中興大學給你發票)
2. 學校登記 (中興大學標記你來過了)
**那一個網站,怎麼證明你來過?**
1. 伺服器給客戶端一個信件,客戶端下次訪問伺服器時帶上該信件就可以了 (信件==cookie)
2. 伺服器登記你來過了,下次你來的時候我來匹配你 (session)
### 保存會話的兩種技術
cookie
* 客戶端技術 (回應,請求)
session
* 伺服器技術,利用這個技術,可以保存用戶的會話訊息,我們可以把訊息或著數據放在Session中!
常見場景:網站登入後,你下次不用再登入了,第二次訪問直接就上去了!
### Cookie
1. 從請求中拿到cookie訊息
2. 伺服器回應給客戶端cookie
```
Cookie[] cookies = req.getCookies(); //獲得Cookie
cookie.getName(); //獲得cookie中的Key
cookie.getValue(); //獲得cookie中的value
new Cookie("lastLoginTime", System.currentTimeMillis()+""); //新建一個cookie
cookie.setMaxAge(24*60*60); //設定cookie的有效期
resp.addCookie(cookie); //回應給客戶端一個cookie
```
**cookie:一般會保存在本機的用戶目錄下appdata**
一個網站cookie是否存在上限!?**聊聊細節問題**
* 一個Cookie只能保存一個訊息
* 一個web站點可以給瀏覽器發送多個cookie,最多存放20個cookie
* Cookie大小有限制4kb
* 300個cookie瀏覽器上限
**刪除Cookie**
* 不設定有效期,關閉瀏覽器,自動失效
* 設定有效期時間為0
編碼解碼:
```
URLEncoder.encode("馬凱","utf-8")
URLDecoder.decode(cookie.getValue(),"utf-8")
```
### Session(重點)
甚麼是Session:
* 伺服器會給每一個User(瀏覽器)建立一個Session物件
* 一個Session獨佔一個瀏覽器,只要瀏覽器沒有關閉,這個Session就存在
* 用戶登入之後,整個網站他都可以訪問 --> 保存用戶訊息,保存購物車的訊息

Session和Cookie的區別:
* Cookie是把用戶的數據寫給用戶的瀏覽器,瀏覽器保存(可以保存多個)
* Session把用戶的數據寫到用戶獨佔的Session中,伺服器端保存(保存重要的訊息,減少伺服器資源的浪費)
* Session物件由伺服器建立
使用場景:
* 保存一個登入用戶的訊息
* 購物車訊息
* 在整個網站中經常會使用的數據,我們將它保存在Session中
使用Session:
```java=
package com.ykma.servlet;
import com.ykma.pojo.Person;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import java.io.IOException;
public class SessionDemo01 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//解決亂碼問題
req.setCharacterEncoding("UTF-8");
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=utf-8");
//得到Session
HttpSession session = req.getSession();
//給Session中存東西
session.setAttribute("name",new Person("馬凱",23));
//獲取Session的ID
String sessionId = session.getId();
//判斷Session是不是新建立的
if (session.isNew()){
resp.getWriter().write("session建立成功,ID:" + sessionId);
}else {
resp.getWriter().write("session已經在伺服器中存在了,ID" + sessionId);
}
//Session建立的時候做了甚麼事情:
// Cookie cookie = new Cookie("JSESSIONID", sessionId);
// resp.addCookie(cookie);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
//得到Session
HttpSession session = req.getSession();
Person person = (Person) session.getAttribute("name");
System.out.println(person.toString());
HttpSession session = req.getSession();
session.removeAttribute("name");
//手動註銷Session
session.invalidate();
```
會話自動過期:web.xml配置
```xml=
<!--設定Session預設的失效時間-->
<session-config>
<!--15分鐘後Session自動失效,以分鐘為單位-->
<session-timeout>15</session-timeout>
</session-config>
```
## 8.JSP
### 什麼是JSP?
Java Server Pages:Java伺服器端頁面,也和Servlet一樣,用於動態Web技術!
最大的特點:
* 寫JSP就像在寫HTML
* 區別:
* HTML只給用戶提供靜態的數據
* JSP頁面中可以嵌入JAVA程式碼,為用戶提供動態數據
### JSP原理
思路:JSP到底怎麼執行的!
* 程式碼層面沒有任何問題
* 伺服器內部工作
tomcat中有一個work目錄,IDEA中使用Tomcat會在IDEA的tomcat中產生一個work目錄

我電腦的位置:
```xml=
C:\Users\User\AppData\Local\JetBrains\IntelliJIdea2021.3\tomcat\00c1eead-1d2e-4da0-92be-acd6f715b9cc\work\Catalina\localhost\ykma\org\apache\jsp
```
發現頁面轉變成了java程序!

**瀏覽器像伺服器發送請求,不管訪問甚麼資源,其實都是在訪問Servlet!**
JSP最終也會被轉換成為一個Java類!
**JSP本質上就是一個Servlet**
```java=
//初始化
public void _jspInit() {
}
//銷毀
public void _jspDestroy() {
}
//JSPService
public void _jspService(HttpServletRequest request, HttpServletResponse response)
```
1. 判斷請求
2. 內置一些物件
```java=
final javax.servlet.jsp.PageContext pageContext; //頁面上下文
javax.servlet.http.HttpSession session = null; //session
final javax.servlet.ServletContext application; //applicationContext
final javax.servlet.ServletConfig config; //config
javax.servlet.jsp.JspWriter out = null; //out
final java.lang.Object page = this; //page:當前頁
HttpServletRequest request //請求
HttpServletResponse response //回應
```
3. 輸出頁面前增加的程式碼
```java=
response.setContentType("text/html"); //設定回應的頁面類型
pageContext = _jspxFactory.getPageContext(this, request, response,
null, true, 8192, true);
_jspx_page_context = pageContext;
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
_jspx_out = out;
```
4. 以上的這些物件我們可以在JSP頁面中直接使用!

在JSP頁面中,只要是JAVA程式碼就會原封不動的輸出,如果是HTML程式碼,就會被轉換為:
```java=
out.write("<html>\r\n");
```
這樣的格式,輸出到前端!
### JSP基礎語法
任何語言都有自己的語法,JAVA中有,JSP作為JAVA技術的一種應用,他擁有一些自己擴充的語法(了解,知道即可!),JAVA所有語法都支援!
**JSP表達式**
```java=
<%--JSP表達式
作用:用來將程序的輸出,輸出到客戶端
<%= 變數或著表達式%>
--%>
<%= new java.util.Date()%>>
```
**jsp腳本片段**
```java=
<%--jsp腳本片段--%>
<%
int sum = 0;
for (int i = 0; i <= 100 ; i++) {
sum+=i;
}
out.println("<h1>Sum="+sum+"</h1>");
%>
```
**腳本片段的再實現**
```java=
<%
int x = 10;
out.println(x);
%>
<p>這是一個JSP文件</p>
<%
int y = 2;
out.println(y);
%>
<hr>
<%--在程式碼中嵌入HTML元素--%>
<%
for (int i = 0; i < 5; i++) {
%>
<h1>Hello,World <%=i%> </h1>
<%
}
%>
```
**JSP聲明**
```java=
<%!
static {
System.out.println("Loading Servlet!");
}
private int globalVar = 0;
public void ykma(){
System.out.println("進入了方法ykma!");
}
%>
```
JSP聲明:會被編譯到JSP生成Java的類中!其他的,就會被生成到jspService方法中!
在JSP中嵌入java程式碼即可!
```java=
<%%>
<%=%>
<%!%>
<%--注釋--%>
```
JSP的注釋不會在客戶端顯示,HTML就會!
### JSP指令
```java=
<%@page args.... %>
<%@include file=""%>
<%--@include會將兩個頁面合二為一--%>
<%@include file="common/header.jsp" %>
<h1>網頁主體</h1>
<%@include file="common/footer.jsp" %>
<hr>
<%--jsp標籤
jsp:include: 拼接頁面,本質還是三個
--%>
<jsp:include page="/common/header.jsp"/>
<h1>網頁主體</h1>
<jsp:include page="/common/footer.jsp"/>
```
### 9大內置物件
* PageContext 存東西
* Request 存東西
* Response
* Session 存東西
* Application (ServletContext) 存東西
* config (ServletConfig)
* out
* page (幾乎不用,不用了解)
* exception
```java=
pageContext.setAttribute("name1","馬凱1號"); //保存的數據只在一個頁面中有效
request.setAttribute("name2","馬凱2號"); //保存的數據只在一個請求中有效,請求轉發也會攜帶這個數據
session.setAttribute("name3","馬凱3號"); //保存的數據只在一次會話中有效,從打開瀏覽器到關閉瀏覽器
application.setAttribute("name4","馬凱4號"); //保存的數據只在伺服器中有效,從打開伺服器到關閉伺服器
```
request:客戶端向伺服器發送請求,產生的數據,用戶看完就沒用了,比如:新聞,用戶看完沒用的!
Session:客戶端向伺服器發送請求,產生的數據,用戶用完一會還有用,比如:購物車。
application:客戶端向伺服器發送請求,產生的數據,一個用戶用完了,其他用戶還可能使用,比如:聊天數據。
### JSP標籤、JSTL標籤、EL表達式
```xml=
<!--JSTL表達式的依賴-->
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl-api</artifactId>
<version>1.2</version>
</dependency>
```
EL表達式: ${}
* 獲取數據
* 執行運算
* 獲取web開發的常用物件
**JSP標籤**
```java=
<%--jsp:include--%>
<%--
http://localhost:8080/jsptag.jsp?name=makai&age=24
--%>
<jsp:forward page="/jsptag2.jsp">
<jsp:param name="name" value="makai"/>
<jsp:param name="age" value="24"/>
</jsp:forward>
```
**JSTL表達式**
JSTL標籤庫的使用就是為了彌補HTML標籤的不足,它自定義許多標籤,可以供我們使用,標籤的功能和java程式碼一樣!
**格式化標籤**
**SQL標籤**
**XML標籤**
**核心標籤**(掌握部分)

**JSTL標籤庫使用步驟
* 導入對應的taglib
* 使用其中的方法
* **在Tomcat也需要導入jstl的包,否則會報錯:JSTL解析錯誤**
c:if
```java=
<head>
<title>Title</title>
</head>
<body>
<h4>if測試</h4>
<hr>
<form action="coreif.jsp" method="get">
<%--
EL表達式獲取表單中的數據
${param.參數名}
--%>
<input type="text" name="username" value="${param.username}">
<input type="submit" value="登入">
</form>
<%--判斷如果提交的用戶名是管理員,則登入成功--%>
<c:if test="${param.username=='admin'}" var="isAdmin">
<c:out value="管理員歡迎您!"></c:out>
</c:if>
<c:out value="${isAdmin}"></c:out>
</body>
```
c:choose c:when
```java=
<body>
<%--定義一個變數score,值為85--%>
<c:set var="score" value="85"></c:set>
<c:choose>
<c:when test="${score>=90}">
你的成績為優秀
</c:when>
<c:when test="${score>=80}">
你的成績為一般
</c:when>
<c:when test="${score>=70}">
你的成績為良好
</c:when>
<c:when test="${score<=60}">
你的成績為不及格
</c:when>
</c:choose>
</body>
```
c:forEach
```java=
<%
ArrayList<String> people = new ArrayList<>();
people.add(0,"張三");
people.add(1,"李四");
people.add(2,"王五");
people.add(3,"趙六");
people.add(4,"田七");
request.setAttribute("list",people);
%>
<%--
var,每一次迭代出來的變數
items, 要迭代的物件
begin, 哪裡開始
end, 到哪裡
step, 步長
--%>
<c:forEach var="people" items="${list}">
<c:out value="${people}"></c:out> <br>
</c:forEach>
<hr>
<c:forEach var="people" items="${list}" begin="1" end="3" step="1">
<c:out value="${people}"></c:out> <br>
</c:forEach>
```
## 9.JavaBean
實體類
JavaBean有特定的寫法:
* 必須要有一個無參建構
* 屬性必須私有化
* 必須有對應的get/set方法
一般用來和資料庫的字段做映射 ORM
ORM:物件關係映射
* 表 --> 類
* 字段 --> 屬性
* 行紀錄 --> 物件
**people表**
| id | name | age | address |
| --- | ---- | ---- | ------- |
| 1 | 馬凱1號 | 3 |桃園|
| 2 |馬凱2號|18|桃園|
| 3 |馬凱3號|100|桃園|
```java=
class People{
private int id;
private String name;
private int age;
private String address;
}
class A{
new People(1,"馬凱1號",3,"桃園");
new People(2,"馬凱2號",18,"桃園");
new People(3,"馬凱3號",100,"桃園");
}
```
## 10.MVC三層架構
MVC:Model(模型) View(視圖) Controller(控制器)
### 早些年

用戶直接訪問控制層,控制層就可以直接操作資料庫。
```
servlet --> CRUD --> 資料庫
弊端:程序十分臃腫,不利於維護,servlet的程式碼中:處理請求、回應、視圖跳轉、處理JDBC、處理業務程式碼、處理邏輯程式碼
架構:沒有甚麼是加一層解決不了的!
程序猿調用
|
JDBC
|
MySQL Oracle sqlServer
```
### MVC三層架構

Model
* 業務處理:業務邏輯(Service)
* 數據持久層:CRUD(Dao)
View
* 展示數據
* 提供鏈結發起的Servlet請求(a、form、img...)
Controller(Servlet)
* 接收用戶的請求:(req:請求參數、Session訊息)
* 交給業務層處理對應的程式碼
* 控制視圖的跳轉
```
登入 --> 接收用戶的登入請求 --> 處理用戶的請求(獲取用戶登入的參數,username、password) -->
交給業務層處理登入業務(判斷用戶名密碼是否正確:事務) --> Dao層查詢用戶明和密碼是否正確 --> 資料庫
```
## 11.Filter(重點)
Filter:過濾器,用來過濾網站的數據
* 處理中文亂碼
* 登入驗證...

Filter開發步驟:
1. 導入包
2. 編寫過濾器
1. 導包不要錯

2. 實現Filter接口,重寫對應的方法即可
```java=
public class CharacterEncodingFilter implements Filter {
//初始化:web伺服器啟動,就已經初始化了,隨時等待過濾對象出現!
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("CharacterEncodingFilter初始化");
}
//Chain:鍊
/*
1. 過濾中的所有程式碼,在過濾特定請求的時候都會執行
2. 必須要讓過濾器繼續通行
*/
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
servletRequest.setCharacterEncoding("utf-8");
servletResponse.setCharacterEncoding("utf-8");
servletResponse.setContentType("text/html;charset=UTF-8");
System.out.println("CharacterEncodingFilter執行前...");
filterChain.doFilter(servletRequest,servletResponse); //讓我們的請求繼續走,如果不寫,程序到這裡就被攔截停止!
System.out.println("CharacterEncodingFilter執行後...");
}
//銷毀:web伺服器關閉的時候,過濾器會消毀
public void destroy() {
System.out.println("CharacterEncodingFilter銷毀");
}
}
```
3. 在web.xml中配置Filter
```xml=
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>com.ykma.filter.CharacterEncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<!--只要是 /servlet的任何請求,都會經過這個過濾器-->
<url-pattern>/servlet/*</url-pattern>
</filter-mapping>
```
## 12.監聽器
實現一個監聽器的接口(有N種)。
1. 編寫一個監聽器
實現監聽器的接口
```java=
//統計網站在線人數:統計session
public class OnlineCountListener implements HttpSessionListener {
//建立session監聽:看你的一舉一動
//一旦建立Session就會觸發一次這個事件
public void sessionCreated(HttpSessionEvent se) {
ServletContext ctx = se.getSession().getServletContext();
System.out.println(se.getSession().getId());
Integer onlineCount = (Integer) ctx.getAttribute("OnlineCount");
if(onlineCount == null){
onlineCount = new Integer(1);
}else {
int count = onlineCount.intValue();
onlineCount = new Integer(count+1);
}
ctx.setAttribute("OnlineCount",onlineCount);
}
//銷毀session監聽
//一旦銷毀Session就會觸發一次這個事件
public void sessionDestroyed(HttpSessionEvent se) {
ServletContext ctx = se.getSession().getServletContext();
Integer onlineCount = (Integer) ctx.getAttribute("OnlineCount");
if(onlineCount == null){
onlineCount = new Integer(0);
}else {
int count = onlineCount.intValue();
onlineCount = new Integer(count-1);
}
ctx.setAttribute("OnlineCount",onlineCount);
}
/*
Session銷毀:
1. 手動銷毀 getSession.invalidate();
2. 自動銷毀
*/
}
```
2. web.xml中註冊監聽器
```xml=
<!--註冊監聽器-->
<listener>
<listener-class>com.ykma.listener.OnlineCountListener</listener-class>
</listener>
```
3. 看情況是否使用!!!
## 13.過濾器、監聽器常見應用
**監聽器:GUI編程中經常使用**
```java=
public class TestPanel {
public static void main(String[] args) {
Frame frame = new Frame("端午節快樂"); //新建一個視窗
Panel panel = new Panel(null); //面板
frame.setLayout(null); //設定視窗的布局
frame.setBounds(300,300,500,500);
frame.setBackground(new Color(0,0,255)); //設定背景顏色
panel.setBounds(50,50,300,300);
panel.setBackground(new Color(0,255,0)); //設定背景顏色
frame.add(panel);
frame.setVisible(true);
//監聽事件,監聽關閉事件
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
super.windowClosing(e);
}
});
}
}
```
使用者登入之後才能進入首頁!使用者登出後就不能進入首頁了
1. 用戶登入之後,向Session中放入用戶的數據
2. 進入首頁的時候要判斷用戶是否已經登入,在過濾器中實現!
```java=
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
if (request.getSession().getAttribute("USER_SESSION")==null){
response.sendRedirect("/error.jsp");
}
filterChain.doFilter(request,response);
```
## 14.JDBC
甚麼是JDBC:Java連接資料庫

需要jar包的支持:
* java.sql
* javax.sql
* mysql-connector-java...連接驅動(必須要導入)
**實驗環境搭建:**
```sql=
CREATE TABLE users(
id INT PRIMARY KEY,
`name` VARCHAR(40),
`password` VARCHAR(40),
email VARCHAR(60),
birthday DATE
);
INSERT INTO users(id,`name`,`password`,email,birthday)
VALUES(1,'張三','123456','zs@gmail.com','2000-01-01');
INSERT INTO users(id,`name`,`password`,email,birthday)
VALUES(2,'李四','123456','ls@gmail.com','2000-01-01');
INSERT INTO users(id,`name`,`password`,email,birthday)
VALUES(3,'王五','123456','ww@gmail.com','2000-01-01');
SELECT * FROM users;
```
導入資料庫依賴:
```xml=
<!--mysql的驅動-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
```
IDEA中連接資料庫:

**JDBC固定步驟:**
1. 加載驅動
2. 連接資料庫,代表資料庫
3. 向資料庫發送SQL的物件Statement:CRUD
4. 編寫SQL(根據業務,不同的SQL)
5. 執行SQL
6. 關閉連接
```java=
public class TestJdbc {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//配置訊息
//useUnicode=true&characterEncoding=utf-8 解決中文亂碼
String url= "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf-8&userSSL=true";
String username = "root";
String password = "123456";
//1.加載驅動
Class.forName("com.mysql.jdbc.Driver");
//2.連接資料庫,代表資料庫
Connection connection = DriverManager.getConnection(url, username, password);
//3.向資料庫發送SQL的物件Statement,PreparedStatement:CRUD
Statement statement = connection.createStatement();
//4.編寫SQL
String sql = "select * from users";
//5.執行查詢SQL,返回一個ResultSet:結果集
ResultSet rs = statement.executeQuery(sql);
while (rs.next()){
System.out.println("id="+rs.getObject("id"));
System.out.println("name="+rs.getObject("name"));
System.out.println("password="+rs.getObject("password"));
System.out.println("email="+rs.getObject("email"));
System.out.println("birthday="+rs.getObject("birthday"));
}
//6.關閉連接,釋放資源(一定要做) 先開後關
rs.close();
statement.close();
connection.close();
}
}
```
預編譯SQL
```java=
public class TestJDBC2 {
public static void main(String[] args) throws Exception {
//配置訊息
//useUnicode=true&characterEncoding=utf-8 解決中文亂碼
String url= "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf-8&userSSL=true";
String username = "root";
String password = "123456";
//1.加載驅動
Class.forName("com.mysql.jdbc.Driver");
//2.連接資料庫,代表資料庫
Connection connection = DriverManager.getConnection(url, username, password);
//3.編寫SQL
String sql = "INSERT INTO users(id,`name`,`password`,email,birthday) VALUES (?,?,?,?,?);";
//4.預編譯
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1,4);//給第一個佔位符 ? 的值賦值為1
preparedStatement.setString(2,"ykma");//給第二個佔位符 ? 的值賦值為ykma
preparedStatement.setString(3,"123456");//給第三個佔位符 ? 的值賦值為123456
preparedStatement.setString(4,"43242@gmail.com");//給第四個佔位符 ? 的值賦值為43242@gmail.com
preparedStatement.setDate(5,new Date(new java.util.Date().getTime()));//給第五個佔位符 ? 的值賦值為new java.util.Date().getTime()
//5.執行SQL
int i = preparedStatement.executeUpdate();
if (i>0){
System.out.println("插入成功");
}
//6.關閉連接,釋放資源(一定要做) 先開後關
preparedStatement.close();
connection.close();
}
}
```
### 事務
要馬都成功,要馬都失敗!
ACID原則:保證數據的安全
```
開啟事務
事務提交 commit()
事務回滾 rollback()
關閉事務
轉帳:
A:1000
B:1000
A(900) --100--> B(1100)
```
**Junit單元測試**
依賴
```xml=
<!--單元測試-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
```
簡單使用
@Test註解只有在方法上有效,只要加了這個註解的方法,就可以直接運行!
```java=
@Test
public void test(){
System.out.println("Hello");
}
```
**搭建一個環境**
```sql=
CREATE TABLE account(
id INT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(10),
money FLOAT
);
INSERT INTO account(`name`,money) VALUES('A',1000);
INSERT INTO account(`name`,money) VALUES('B',1000);
INSERT INTO account(`name`,money) VALUES('C',1000);
```
事務,順利執行則commit提交,有異常則rollback:
```java=
public class TestJDBC3 {
@Test
public void test() {
//配置訊息
//useUnicode=true&characterEncoding=utf-8 解決中文亂碼
String url= "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf-8&userSSL=true";
String username = "root";
String password = "123456";
Connection connection = null;
//1.加載驅動
try {
Class.forName("com.mysql.jdbc.Driver");
//2.連接資料庫,代表資料庫
connection = DriverManager.getConnection(url, username, password);
//3.通知資料庫開啟事務,false開啟
connection.setAutoCommit(false);
String sql = "update account set money = money-100 where name = 'A'";
connection.prepareStatement(sql).executeUpdate();
//製造錯誤
int i = 1/0;
String sql2 = "update account set money = money+100 where name = 'B'";
connection.prepareStatement(sql2).executeUpdate();
connection.commit();//以上兩條SQL都執行成功了,就提交事務!
System.out.println("success");
} catch (Exception e) {
try {
//如果出現異常,就通知資料庫回滾事務
connection.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
e.printStackTrace();
} finally {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
```