文章目錄
JavaWeb快速入門
1.基本概念
1.前言
web開發:
-
web,網頁的意思 , www.baidu.com
-
靜態web
- html,css
- 提供給所有人看的數據始終不會發生變化!
-
動態web
- 淘寶,幾乎是所有的網站;
- 提供給所有人看的數據始終會發生變化,每個人在不同的時間,不同的地點看到的信息各不相同!
- 技術棧:Servlet/JSP,ASP,PHP。
-
在Java中,動態web資源開發的技術統稱為JavaWeb。
2.web應用程序
web應用程序:可以提供瀏覽器訪問的程序。
-
a.html.b.html…多個web資源,這些web資源可以被外界訪問,對外界提供服務。
-
我們能訪問到的任何一個頁面或者資源,都存在於這個世界的某一個角落的計算機上。
-
URL
-
這些統一的web資源會被放在同一個文件夾下,web應用程序–>Tomcat:服務器。
-
一個web應用由多部分組成 (靜態web,動態web)。
- html,css,js
- jsp,servlet
- Java程序
- jar包
- 配置文件 (Properties)
-
web應用程序編寫完畢后,若想提供給外界訪問:需要一個服務器來統一管理;
3.靜態web
- *.htm, *.html,這些都是網頁的后綴,如果服務器上一直存在這些東西,我們就可以直接進行讀取。通絡;
- 靜態web存在的缺點:
- Web頁面無法動態更新,所有用戶看到都是同一個頁面;
- 輪播圖,點擊特效:偽動態;
- JavaScript [實際開發中,它用的最多];
- VBScript;
- 它無法和數據庫交互(數據無法持久化,用戶無法交互)。
- Web頁面無法動態更新,所有用戶看到都是同一個頁面;
4.動態web
頁面會動態展示: “Web的頁面展示的效果因人而異”;
缺點:
- 加入服務器的動態web資源出現了錯誤,需要重新編寫后台程序,重新發布;
- 停機維護。
優點:
- Web頁面可以動態更新,所有用戶看到都不是同一個頁面;
- 它可以與數據庫交互 (數據持久化:注冊,商品信息,用戶信息…);
2.web服務器
1.技術講解
ASP:
-
微軟:國內最早流行的就是ASP;
-
在HTML中嵌入了VB的腳本, ASP + COM;
-
在ASP開發中,基本一個頁面都有幾千行的業務代碼,頁面極其換亂,維護成本高!
-
C#
-
IIS
<h1> <h1></h1> <h1> <h1> <h1> <h1> </h1> <% System.out.println("hello") %> <h1> </h1> <h1></h1> <h1> </h1>
php:
- PHP開發速度很快,功能很強大,跨平台,代碼很簡單 (70% , WP)
- 無法承載大訪問量的情況(局限性)
JSP/Servlet :
B/S:瀏覽和服務器
C/S: 客戶端和服務器
- sun公司主推的B/S架構
- 基於Java語言的 (所有的大公司,或者一些開源的組件,都是用Java寫的)
- 可以承載三高問題帶來的影響;
- 語法像ASP , ASP–>JSP , 加強市場強度;
…
2.web服務器
-
服務器是一種被動的操作,用來處理用戶的一些請求和給用戶一些響應信息;
-
IIS
- 微軟的; ASP…,Windows中自帶的
-
Tomcat
-
面向百度編程;
-
Tomcat是Apache 軟件基金會(Apache Software Foundation)的Jakarta 項目中的一個核心項目,最新的Servlet 和JSP 規范總是能在Tomcat 中得到體現,因為Tomcat 技術先進.性能穩定,而且免費,因而深受Java 愛好者的喜愛並得到了部分軟件開發商的認可,成為目前比較流行的Web 應用服務器。
-
Tomcat 服務器是一個免費的開放源代碼的Web 應用服務器,屬於輕量級應用服務器,在中小型系統和並發訪問用戶不是很多的場合下被普遍使用,是開發和調試JSP 程序的首選。對於一個Java初學web的人來說,它是最佳的選擇。
-
Tomcat 實際上運行JSP 頁面和Servlet。Tomcat最新版本為9.0。
…
工作3-5年之后,可以嘗試手寫Tomcat服務器;
- 下載tomcat:
- 安裝 or 解壓;
- 了解配置文件及目錄結構;
- 這個東西的作用。
3.Tomcat
1. 安裝tomcat
- Tomcat官網:http://tomcat.apache.org/
2.Tomcat啟動和配置
- 文件夾作用:
- 啟動。關閉Tomcat
-
訪問測試:http://localhost:8080/
-
可能遇到的問題:
- Java環境變量沒有配置;
- 閃退問題:需要配置兼容性;
- 亂碼問題:配置文件中設置。
3.配置
- 可以配置啟動的端口號
- Tomcat的默認端口號為:8080
- mysql:3306
- http:80
- https:443
<Connector port="8081" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
- 可以配置主機的名稱
- 默認的主機名為:localhost->127.0.0.1
- 默認網站應用存放的位置為:webapps
<Host name="www.subeily.com" appBase="webapps" unpackWARs="true" autoDeploy="true">
高難度面試題
請你談談網站是如何進行訪問的!
-
輸入一個域名;回車
-
檢查本機的 C:\Windows\System32\drivers\etc\hosts配置文件下有沒有這個域名映射;
-
有:直接返回對應的ip地址,這個地址中,有我們需要訪問的web程序,可以直接訪問
127.0.0.1 www.subeily.com
-
沒有:去DNS服務器找,找到的話就返回,找不到就返回找不到;
-
- 可以配置一下環境變量(可選性)。
4.發布一個web網站
-
不會就先模仿:
- 將自己寫的網站,放到服務器(Tomcat)中指定的web應用的文件夾(webapps)下,就可以訪問了。
-
網站應該有的結構
--webapps :Tomcat服務器的web目錄
-ROOT
-books :網站的目錄名
- WEB-INF
-classes : java程序
-lib:web應用所依賴的jar包
-web.xml :網站配置文件
- index.html 默認的首頁
- static
-css
-style.css
-js
-img
-.....
-
HTTP協議 : 面試
-
Maven:構建工具
- Maven安裝包
-
Servlet 入門
- HelloWorld!
- Servlet配置
- 原理
4.Http
1.什么是HTTP
HTTP(超文本傳輸協議)是一個簡單的請求-響應協議,它通常運行在TCP之上。
- 文本:html,字符串 ….
- 超文本:圖片,音樂,視頻,定位,地圖…….
- 80
Https:安全的
- 443
2.兩個時代
-
http1.0
- HTTP/1.0:客戶端可以與web服務器連接后,只能獲得一個web資源,斷開連接
-
http2.0
- HTTP/1.1:客戶端可以與web服務器連接后,可以獲得多個web資源。‘
3.Http請求
- 客戶端—發請求(Request)—服務器
百度:
Request URL:https://www.baidu.com/ 請求地址
Request Method:GET get方法/post方法
Status Code:200 OK 狀態碼:200
Remote(遠程) Address:14.215.177.39:443
Accept:text/html
Accept-Encoding:gzip, deflate, br
Accept-Language:zh-CN,zh;q=0.9 語言
Cache-Control:max-age=0
Connection:keep-alive
請求行
- 請求行中的請求方式:GET
- 請求方式:Get,Post,HEAD,DELETE,PUT,TRACT…
- get:請求能夠攜帶的參數比較少,大小有限制,會在瀏覽器的URL地址欄顯示數據內容,不安全,但高效
- post:請求能夠攜帶的參數沒有限制,大小沒有限制,不會在瀏覽器的URL地址欄顯示數據內容,安全,但不高效。
消息頭
Accept:告訴瀏覽器,它所支持的數據類型
Accept-Encoding:支持哪種編碼格式 GBK UTF-8 GB2312 ISO8859-1
Accept-Language:告訴瀏覽器,它的語言環境
Cache-Control:緩存控制
Connection:告訴瀏覽器,請求完成是斷開還是保持連接
HOST:主機..../.
4.Http響應
- 服務器—響應-----客戶端
百度:
Cache-Control:private 緩存控制
Connection:Keep-Alive 連接
Content-Encoding:gzip 編碼
Content-Type:text/html 類型
響應體
Accept:告訴瀏覽器,它所支持的數據類型
Accept-Encoding:支持哪種編碼格式 GBK UTF-8 GB2312 ISO8859-1
Accept-Language:告訴瀏覽器,它的語言環境
Cache-Control:緩存控制
Connection:告訴瀏覽器,請求完成是斷開還是保持連接
HOST:主機..../.
Refresh:告訴客戶端,多久刷新一次;
Location:讓網頁重新定位;
響應狀態碼
200:請求響應成功 200
3xx:請求重定向
- 重定向:你重新到我給你新位置去;
4xx:找不到資源 404
- 資源不存在;
5xx:服務器代碼錯誤 500 502:網關錯誤
常見面試題:
當你的瀏覽器中地址欄輸入地址並回車的一瞬間到頁面能夠展示回來,經歷了什么?
5.Maven
為什么要學習這個技術?
-
在Java Web開發中,需要使用大量的jar包,我們手動去導入,這種操作很麻煩,PASS!!!
-
如何能夠讓一個東西自動幫我導入和配置這個jar包。
由此,Maven誕生了!
1. Maven項目架構管理工具
在Javaweb的學習中,就是用來就是方便導入jar包的!
Maven的核心思想:約定大於配置。
- 有約束,不要去違反。
Maven會規定好你該如何去編寫自己的Java代碼,必須要按照具體規范來。
2.下載安裝Maven
下載完成后,解壓即可;
建議將電腦上的所有環境都放在一個文件夾下,方便后期管理。
3.配置環境變量
環境不進行配置也可以使用,但建議配置環境!!!
在電腦的系統環境變量中,進行如下配置:
- 變量名:M2_HOME;變量值:你的maven目錄下的bin目錄。
- 變量名:MAVEN_HOME;變量值:你的maven的目錄。
-
在系統的path中配置:
%MAVEN_HOME%\bin
測試Maven是否安裝成功,在cmd命令行輸入:mvn -version,如下圖則表示成功。
4.阿里雲鏡像
- 鏡像:mirrors
- 作用:加速我們的下載
- 國內建議使用阿里雲的鏡像。
- 在setttins.xml文件中找到標簽對,進行修改:
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>*</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
5.本地倉庫
本地倉庫:在本地的倉庫。
建立一個本地倉庫:localRepository
<localRepository>F:\java\Maven\apache-maven-3.8.1\maven-repo</localRepository>
6.在IDEA中使用Maven
-
啟動IDEA;
-
創建一個Maven項目;
- 等待項目初始化完畢。最后運行結果如下圖:
- 觀察maven倉庫中多了什么東西?
- IDEA中的Maven設置
注意:IDEA項目創建成功后,看一眼Maven的配置。
注意:在IDEA中配置,Maven經常在IDEA中會出現一個問題就是項目自動創建完成后,它這個Maven Home會使用IDEA默認,如果發先了這個問題,手動改為本地的。
- 到這里,Maven在IDEA中的配置和使用就OK了!
7.創建一個普通的Maven項目
這個只有在Web應用下才會有!
創建中報錯:Cannot resolve plugin org.apache.maven.plugins:maven-jar-plugin
主要原因是本地maven的配置文件和倉庫地址不一致。
修改后如果仍是不行,可修改一下項目的pom.xml文件,加入如下代碼:
<repositories>
<repository>
<id>alimaven</id>
<name>Maven Aliyun Mirror</name>
<url>https://repo.maven.apache.org/maven2/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
再次進行刷新。如果你有更好的方法,歡迎留言交流!!!
8.標記文件夾功能
9.在 IDEA中配置Tomcat
解決警告問題。必須要的配置:為什么會有這個問題:我們訪問一個網站,需要指定一個文件夾名字;
中間的地址,可以自定義。它是在瀏覽器中的訪問地址。
10. pom文件
pom.xml是Maven的核心配置文件。
<?xml version="1.0" encoding="UTF-8"?>
<!--Maven版本和頭文件-->
<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>
<!--這里就是我們剛才配置的GAV-->
<groupId>com.github</groupId>
<artifactId>Javaweb-01-maven</artifactId>
<version>1.0-SNAPSHOT</version>
<!--Package:項目的打包方式 jar:java應用 war:JavaWeb應用 -->
<packaging>war</packaging>
<!--配置-->
<properties>
<!--項目的默認構建編碼-->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!--編碼版本-->
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<!--項目依賴-->
<dependencies>
<!-- 具體依賴的jar包配置文件-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</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>
<?xml version="1.0" encoding="UTF-8"?>
<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.github</groupId>
<artifactId>JavaWeb-01-Maven02</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!-- 具體依賴的jar包配置文件-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!-- maven的高級之處在於可以自動導入這個jar包所依賴的其他jar包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
</dependencies>
</project>
maven由於他的約定大於配置,我們之后可以能遇到我們寫的配置文件,無法被導出或者生效的問題,解決方案:
<!--在build中配置resources,來防止資源導出失敗的問題-->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
IDEA操作
11. 解決遇到的問題
-
IDEA中每次都要重復配置Maven;在IDEA中的全局默認配置中去配置。
- maven默認web項目中的web.xml版本問題。
-
替換為webapp4.0版本和tomcat一致。
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0" metadata-complete="true"> </web-app>
-
Maven倉庫的使用。
地址:https://mvnrepository.com/
6.Servlet
1.Servlet簡介
-
Servlet就是sun公司開發動態web的一門技術。
-
Sun在這些API中提供一個接口叫做:Servlet,如果你想開發一個Servlet程序,只需要完成兩個小步驟:
- 編寫一個類,實現Servlet接口。
- 把開發好的Java類部署到web服務器中。
-
把實現了Servlet接口的Java程序叫做,Servlet。
2.HelloServlet
Serlvet接口Sun公司有兩個默認的實現類:HttpServlet,GenericServlet。
- 構建一個普通的Maven項目,刪掉里面的src目錄,后面的筆記就在這個項目里面建立Moudel;這個空的工程就是Maven主工程;之后在其中建立一個maven子項目。
-
關於Maven父子工程的理解:
- 父項目中會有:
<modules> <module>Servlet-01</module> </modules>
- 子項目會有:
<parent> <artifactId>JavaWeb-02-Servlet</artifactId> <groupId>com.github</groupId> <version>1.0-SNAPSHOT</version> </parent>
- 父項目中的java子項目可以直接使用。
son extends father
-
Maven環境優化。
-
修改web.xml為最新的。
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0" metadata-complete="true"> </web-app>
-
將maven的結構搭建完整。
-
- 編寫一個Servlet程序。
-
編寫一個普通類。
-
實現Servlet接口,這里我們直接繼承HttpServlet
package com.github.servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; 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,Serlvet"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doPost(req, resp); } }
-
編寫Servlet的映射
- 為什么需要映射:我們寫的是JAVA程序,但是要通過瀏覽器訪問,而瀏覽器需要連接web服務器,所以我們需要再web服務中注冊我們寫的Servlet,還需給他一個瀏覽器能夠訪問的路徑;
<!--注冊Servlet--> <servlet> <servlet-name>hello</servlet-name> <servlet-class>com.github.servlet.HelloServlet</servlet-class> </servlet> <!--Servlet的請求路徑--> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping>
-
配置Tomcat
- 注意:配置項目發布的路徑就可以了。
-
啟動測試,OK!
3.Servlet原理
- Servlet是由Web服務器調用,web服務器在收到瀏覽器請求之后,會:
4.Mapping問題
-
一個Servlet可以指定一個映射路徑。
<servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping>
-
一個Servlet可以指定多個映射路徑。
<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> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello4</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello5</url-pattern> </servlet-mapping>
-
一個Servlet可以指定通用映射路徑。
<servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello/*</url-pattern> </servlet-mapping>
-
默認請求路徑。
<!--默認請求路徑--> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping>
-
指定一些后綴或者前綴等等….
<!-- 可以自定義后綴實現請求映射 注意點,*前面不能加項目映射的路徑 hello/subei.github --> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>*.github</url-pattern> </servlet-mapping>
-
優先級問題。
- 指定了固有的映射路徑優先級最高,如果找不到就會走默認的處理請求;
package com.github.servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; public class ErrorServelt extends HelloServlet{ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/html"); resp.setCharacterEncoding("utf-8"); PrintWriter writer = resp.getWriter(); writer.println("<h1>404</h1>"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doPost(req, resp); } }
<!-- 404 --> <servlet> <servlet-name>error</servlet-name> <servlet-class>com.github.servlet.ErrorServelt</servlet-class> </servlet> <servlet-mapping> <servlet-name>error</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping>
5.ServletContext
- web容器在啟動的時候,它會為每個web程序都創建一個對應的ServletContext對象,它代表了當前的web應用;
1.共享數據
- 在這個Servlet中保存的數據,可以在另外一個servlet中拿到;
package com.github.servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
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 name = "學習超好"; // 數據
// 將一個數據保存在了ServletContext中,名字為:name ,值 name
context.setAttribute("name",name);
System.out.println("Hello");
}
}
package com.github.servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class GetServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
String name = (String)context.getAttribute("name");
resp.setContentType("text/html");
resp.setCharacterEncoding("utf-8");
resp.getWriter().print("名字:"+name);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.github.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.github.servlet.GetServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>getc</servlet-name>
<url-pattern>/getc</url-pattern>
</servlet-mapping>
- 測試訪問結果:
Java-IDEA2020-IDEA或者啟動Tomcat控制台中文亂碼解決
- 解決后如下:
2.獲取初始化參數
<!-- 配置一些Web應用初始化參數 -->
<context-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost:3306/mybatis</param-value>
</context-param>
<servlet>
<servlet-name>gp</servlet-name>
<servlet-class>com.github.servlet.ServletDemo03</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>gp</servlet-name>
<url-pattern>/gp</url-pattern>
</servlet-mapping>
package com.github.servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ServletDemo03 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
String url = context.getInitParameter("url");
resp.getWriter().print(url);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
3.請求轉發
package com.github.servlet;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ServletDemo04 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
System.out.println("進入ServletDemo04文件!");
// 轉發的請求路徑
// RequestDispatcher requestDispatcher = context.getRequestDispatcher("/gp");
// 調用forward實現請求轉發;
// requestDispatcher.forward(req,resp);
context.getRequestDispatcher("/gp").forward(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
<servlet>
<servlet-name>gp02</servlet-name>
<servlet-class>com.github.servlet.ServletDemo04</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>gp02</servlet-name>
<url-pattern>/gp02</url-pattern>
</servlet-mapping>
4.讀取資源文件
- Properties
- 在java目錄下新建properties
- 在resources目錄下新建properties
- 發現:都被打包到了同一個路徑下:classes,我們俗稱這個路徑為classpath:
- 思路:需要一個文件流;
username=root365
password=123456
package com.github.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class ServletDemo05 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
InputStream is = this.getServletContext().getResourceAsStream("/WEB-INF/classes/com/github/servlet/aa.properties");
Properties prop = new Properties();
prop.load(is);
String user = prop.getProperty("username");
String pwd = prop.getProperty("password");
resp.getWriter().print(user+":"+pwd);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
<servlet>
<servlet-name>db</servlet-name>
<servlet-class>com.github.servlet.ServletDemo05</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>db</servlet-name>
<url-pattern>/db</url-pattern>
</servlet-mapping>
- 訪問測試!
6.HttpServletResponse
- web服務器接收到客戶端的http請求,針對這個請求,分別創建一個代表請求的HttpServletRequest對象,代表響應的一個HttpServletResponse;
- 如果要獲取客戶端請求過來的參數:找HttpServletRequest;
- 如果要給客戶端響應一些信息:找HttpServletResponse。
1.簡單分類
- 負責向瀏覽器發送數據的方法。
ServletOutputStream getOutputStream() throws IOException;
PrintWriter getWriter() throws IOException;
- 負責向瀏覽器發送響應頭的方法。
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);
- 響應的狀態碼。
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.下載文件
- 向瀏覽器輸出消息;
- 下載文件:
- 要獲取下載文件的路徑;
- 下載的文件名是啥?
- 設置想辦法讓瀏覽器能夠支持下載我們需要的東西;
- 獲取下載文件的輸入流;
- 創建緩沖區;
- 獲取OutputStream對象;
- 將FileOutputStream流寫入到buffer緩沖區;
- 使用OutputStream將緩沖區中的數據輸出到客戶端!
package com.github.servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLEncoder;
public class FileServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
// 1. 要獲取下載文件的路徑;
// F:\java\IDEA2020.2\JavaWeb-02-Servlet\response\target\classes\02.png
String realPath = "F:\\java\\IDEA2020.2\\JavaWeb-02-Servlet\\response\\target\\classes\\02.png";
System.out.println("下載文件的路徑:" + realPath);
// 2. 下載的文件名是啥?
String fileName = realPath.substring(realPath.lastIndexOf("\\") + 1);
// 3. 設置想辦法讓瀏覽器能夠支持下載我們需要的東西;
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緩沖區;
while ((len=in.read(buffer)) > 0){
out.write(buffer,0,len);
}
in.close();
out.close();
// 8. 使用OutputStream將緩沖區中的數據輸出到客戶端!
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0">
<servlet>
<servlet-name>/response</servlet-name>
<servlet-class>com.github.servlet.FileServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>/response</servlet-name>
<url-pattern>/down</url-pattern>
</servlet-mapping>
</web-app>
3.驗證碼功能
- 驗證怎么來的?
- 前端實現;
- 后端實現,需要用到 Java 的圖片類,生產一個圖片。
package com.github.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 IOException {
// 如何讓瀏覽器3秒自動刷新一次;
resp.setHeader("refresh","3");
// 在內存中創建一個圖片
BufferedImage image = new BufferedImage(90,40,BufferedImage.TYPE_INT_RGB);
// 得到圖片,筆
Graphics2D g = (Graphics2D) image.getGraphics();
// 設置圖片的背景顏色
g.setColor(Color.white);
g.fillRect(0,0,90,40);
// 給圖片寫數據
g.setColor(Color.RED);
g.setFont(new Font(null,Font.BOLD,20));
g.drawString(makeNum(),8,30);
// 告訴瀏覽器,這個請求用圖片的方式打開
resp.setContentType("image/jpeg");
// 網站存在緩存,不讓瀏覽器緩存
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 sb = new StringBuffer();
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.doPost(req, resp);
}
}
<servlet>
<servlet-name>/imgs</servlet-name>
<servlet-class>com.github.servlet.ImageServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>/imgs</servlet-name>
<url-pattern>/img</url-pattern>
</servlet-mapping>
4.實現重定向
- B一個web資源收到客戶端A請求后,B他會通知A客戶端去訪問另外一個web資源C,這個過程叫
重定向
。
常見場景:
- 用戶登錄
void sendRedirect(String var1) throws IOException;
- 測試:
package com.github.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class RedirectServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
/* resp.setHeader("Location","/response/img"); resp.setStatus(302); */
resp.sendRedirect("/response/img"); // 重定向
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
<servlet>
<servlet-name>/redirectServlet</servlet-name>
<servlet-class>com.github.servlet.RedirectServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>/redirectServlet</servlet-name>
<url-pattern>/red</url-pattern>
</servlet-mapping>
面試題:請你聊聊重定向和轉發的區別?
- 相同點:頁面都會實現跳轉;
- 不同點:
- 請求轉發的時候,url不會產生變化;
- 重定向時候,url地址欄會發生變化;
5.簡單實現登錄重定向
<%--
這里提交的路徑需要尋找到項目的路徑
${pageContext.request.contextPath} : 代表當前項目
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登錄</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/login" method="get">
用戶名:<input type="text" name="username"><br>
密 碼:<input type="password" name="password"><br>
<input type="submit">
</form>
</body>
</html>
package com.github.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class RequestTest extends HttpServlet {
@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;
// /response/success.jsp
resp.sendRedirect("/response/success.jsp");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
<servlet>
<servlet-name>/request</servlet-name>
<servlet-class>com.github.servlet.RequestTest</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>/request</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>成功</title>
</head>
<body>
<h1>登錄成功!!!</h1>
</body>
</html>
7.HttpServletRequest
- HttpServletRequest代表客戶端的請求,用戶通過Http協議訪問服務器,HTTP請求中的所有信息會被封裝到HttpServletRequest,通過這個HttpServletRequest的方法,獲得客戶端的所有信息;
獲取參數,請求轉發:
package com.github.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
public class HttpServletRequestTest extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
resp.setCharacterEncoding("UTF-8");
String username = req.getParameter("username");
String password = req.getParameter("password");
String[] hobbys = req.getParameterValues("hobbies");
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);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
<servlet>
<servlet-name>/hsrt</servlet-name>
<servlet-class>com.github.servlet.HttpServletRequestTest</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>/hsrt</servlet-name>
<url-pattern>/hsrt</url-pattern>
</servlet-mapping>
7.Cookie.Session
1.會話
-
會話:用戶打開一個瀏覽器,點擊了很多超鏈接,訪問多個web資源,關閉瀏覽器,這個過程可以稱之為會話;
-
有狀態會話:一個同學來過教室,下次再來教室,我們會知道這個同學,曾經來過,稱之為有狀態會話;
你能怎么證明你是西開的學生?
你 西開
1. 發票 西開給你發票
2. 學校登記 西開標記你來過了
一個網站,怎么證明你來過?
客戶端 服務端
1. 服務端給客戶端一個 信件,客戶端下次訪問服務端帶上信件就可以了; cookie
2. 服務器登記你來過了,下次你來的時候我來匹配你; seesion
2.保存會話的兩種技術
cookie
- 客戶端技術 (響應,請求)
session
- 服務器技術,利用這個技術,可以保存用戶的會話信息? 我們可以把信息或者數據放在Session中!
常見常見:網站登錄之后,你下次不用再登錄了,第二次訪問直接就上去了!
3.Cookie
- 從請求中拿到cookie信息;
- 服務器響應給客戶端cookie;
Cookie[] cookies = req.getCookies(); // 獲得Cookie
cookie.getName(); // 獲得cookie中的key
cookie.getValue(); // 獲得cookie中的vlaue
new Cookie("lastLoginTime", System.currentTimeMillis()+""); // 新建一個cookie
cookie.setMaxAge(24*60*60); // 設置cookie的有效期
resp.addCookie(cookie); // 響應給客戶端一個cookie
- cookie:一般會保存在本地的 用戶目錄下 appdata;
- 案例:
package com.github.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
/** * 保存用戶上一次訪問的時間 */
public class CookieDemo01 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");
PrintWriter writer = resp.getWriter();
// Cookie,服務端從客戶端獲取
Cookie[] cookies = req.getCookies();
// 這里返回數組,說明Cookie可能存在多個
// 判斷Cookie是否存在
if(cookies!=null){
// 如果存在,遍歷數組
writer.write("你上一次訪問的時間是:");
for (int i = 0; i < cookies.length; i++) {
Cookie cookie = cookies[i];
// 獲取Cookie的名字
if(cookie.getName().equals("lastLoginTime")) {
// 獲得cookie中的value
long parseLong = Long.parseLong(cookie.getValue());
Date date = new Date(parseLong);
writer.write(date.toLocaleString());
}
}
}else{
writer.write("第一次訪問本站!!!");
}
// 服務端給客戶端響應一個Cookie;
Cookie cookie = new Cookie("lastLoginTime", System.currentTimeMillis()+"");
resp.addCookie(cookie);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
<servlet>
<servlet-name>/cookieDemo01</servlet-name>
<servlet-class>com.github.servlet.CookieDemo01</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>/cookieDemo01</servlet-name>
<url-pattern>/cd01</url-pattern>
</servlet-mapping>
一個網站cookie是否存在上限!聊聊細節問題。
- 一個Cookie只能保存一個信息;
- 一個web站點可以給瀏覽器發送多個cookie,最多存放20個cookie;
- Cookie大小有限制4kb;
- 300個cookie瀏覽器上限。
刪除Cookie;
- 不設置有效期,關閉瀏覽器,自動失效;
- 設置有效期時間為 0 ;
編碼解碼:
URLEncoder.encode("哇哈哈","utf-8")
URLDecoder.decode(cookie.getValue(),"UTF-8")
package com.github.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Date;
/** * 中文數據傳遞 */
public class CookieDemo03 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");
PrintWriter writer = resp.getWriter();
Cookie[] cookies = req.getCookies();
// 判斷Cookie是否存在
if(cookies!=null){
// 如果存在,遍歷數組
writer.write("你上一次訪問的用戶是:");
for (int i = 0; i < cookies.length; i++) {
Cookie cookie = cookies[i];
// 獲取Cookie的名字
if(cookie.getName().equals("name")) {
// 解碼
writer.write(URLDecoder.decode(cookie.getValue(),"UTF-8"));
}
}
}else{
writer.write("第一次訪問本站!!!");
}
// 編碼
Cookie cookie = new Cookie("name", URLEncoder.encode("哇哈哈","UTF-8"));
resp.addCookie(cookie);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
<servlet>
<servlet-name>/cookieDemo03</servlet-name>
<servlet-class>com.github.servlet.CookieDemo03</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>/cookieDemo03</servlet-name>
<url-pattern>/cd03</url-pattern>
</servlet-mapping>
4.Session(重點)
什么是Session:
- 服務器會給每一個用戶(瀏覽器)創建一個Seesion對象;
- 一個Seesion獨占一個瀏覽器,只要瀏覽器沒有關閉,這個Session就存在;
- 用戶登錄之后,整個網站它都可以訪問!–> 保存用戶的信息;保存購物車的信息……
Session和cookie的區別:
- Cookie是把用戶的數據寫給用戶的瀏覽器,瀏覽器保存 (可以保存多個)
- Session把用戶的數據寫到用戶獨占Session中,服務器端保存 (保存重要的信息,減少服務器資源的浪費)
- Session對象由服務創建;
使用場景:
- 保存一個登錄用戶的信息;
- 購物車信息;
- 在整個網站中經常會使用的數據,我們將它保存在Session中;
使用Session:
package com.github.pojo;
public class Person {
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person() {
}
public Person(String name, int age) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
package com.github.servlet;
import com.github.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("懶羊羊",1));
// 獲取Session中的ID
String id = session.getId();
// 判斷Session是否為新創建的
if(session.isNew()){
resp.getWriter().write("session創建成功!ID:" + id);
}else{
resp.getWriter().write("session已經在服務器中存在了!ID:" + id);
}
// Session創建的時候做了什么事情:
// Cookie cookie = new Cookie("JSESSIONID", id);
// resp.addCookie(cookie);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(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();
<servlet>
<servlet-name>/sessionDemo</servlet-name>
<servlet-class>com.github.servlet.SessionDemo01</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>/sessionDemo</servlet-name>
<url-pattern>/sd01</url-pattern>
</servlet-mapping>
會話自動過期:web.xml配置!
<!--設置Session默認的失效時間-->
<session-config>
<!--15分鍾后Session自動失效,以分鍾為單位-->
<session-timeout>15</session-timeout>
</session-config>
8.JSP
1.什么是JSP
Java Server Pages : Java服務器端頁面,也和Servlet一樣,用於動態Web技術!
- 最大的特點:
- 寫JSP就像在寫HTML;
- 區別:
- HTML只給用戶提供靜態的數據;
- JSP頁面中可以嵌入JAVA代碼,為用戶提供動態數據;
2.JSP原理
思路:JSP到底怎么執行的!
-
代碼層面沒有任何問題;
-
服務器內部工作:
- tomcat中有一個work目錄;
-
IDEA中使用Tomcat的會在IDEA的tomcat中生產一個work目錄;
- 我電腦的地址:
C:\Users\subei\AppData\Local\JetBrains\IntelliJIdea2020.2\tomcat\Unnamed_JavaWeb-02-Servlet_4\work\Catalina\localhost\Session\org\apache\jsp
- 發現頁面轉變成了Java程序!
-
瀏覽器向服務器發送請求,不管訪問什么資源,其實都是在訪問Servlet!
-
JSP最終也會被轉換成為一個Java類!
-
JSP 本質上就是一個Servlet!
// 初始化
public void _jspInit() {
}
// 銷毀
public void _jspDestroy() {
}
// JSPService
public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
throws java.io.IOException, javax.servlet.ServletException {
-
判斷請求;
-
內置一些對象;
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 // 響應
-
輸出頁面前增加的代碼;
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;
-
以上的這些個對象我們可以在JSP頁面中直接使用!
- 在JSP頁面中:
- 只要是 JAVA代碼就會原封不動的輸出;
- 如果是HTML代碼,就會被轉換為:
out.write("<html>\n");
out.write(" <head>\n");
out.write(" <title>$Title$</title>\n");
out.write(" </head>\n");
out.write(" <body>\n");
out.write(" $END$\n");
out.write(" </body>\n");
out.write("</html>\n");
- 這樣的格式,輸出到前端!
3.JSP基礎語法
- 任何語言都有自己的語法,JAVA中有,JSP 作為java技術的一種應用,它擁有一些自己擴充的語法(了解,知道即可!),Java所有語法都支持!
- 配置必需的maven環境:
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>JavaWeb-02-Servlet</artifactId>
<groupId>com.github</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>Jsp</artifactId>
<dependencies>
<!-- Servlet 依賴 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<!-- JSP 依賴 -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
</dependency>
<!-- JSTL表達式的依賴-->
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl-api</artifactId>
<version>1.2</version>
</dependency>
<!-- standard標簽庫-->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
</dependencies>
</project>
JSP表達式
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<%--JSP表達式
作用:用來將程序的輸出,輸出到客戶端
<%= 變量或者表達式%>
--%>
<%= new java.util.Date()%>
</body>
</html>
jsp腳本片段
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<%--jsp腳本片段--%>
<%
int sum = 0;
for (int i = 1; i <=100 ; i++) {
sum+=i;
}
out.println("<h1>Sum="+sum+"</h1>");
%>
</body>
</html>
- 腳本片段的再實現
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<%
int x = 10;
out.println(x);
%>
<p>這是一個JSP文檔</p>
<%
int num = 2;
out.println(num);
%>
<hr>
<%--在代碼嵌入HTML元素--%>
<%
for (int i = 0; i < 5; i++) {
%>
<h1>Hello,World <%=i%> </h1>
<%
}
%>
</body>
</html>
JSP聲明
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<%!
static {
System.out.println("Loading Servlet!");
}
private int globalVar = 0;
public void guo(){
System.out.println("進入了方法guo!");
}
%>
</body>
</html>
- JSP聲明:會被編譯到JSP生成Java的類中!其他的,就會被生成到_jspService方法中!
- 在JSP,嵌入Java代碼即可!
<%%>
<%=%>
<%!%>
<%--注釋--%>
- JSP的注釋,不會在客戶端顯示,HTML就會!
4.JSP指令
404與500頁面實現
- jsp2.jsp
<%@ page contentType="text/html;charset=UTF-8" %>
<%--定制錯誤頁面--%>
<%--<%@ page errorPage="error/500.jsp" %>--%>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
int x = 1/0;
%>
</body>
</html>
- 404.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<img src="${pageContext.request.contextPath}/img/2-404.png" alt="404">
</body>
</html>
- 500.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h2>自定義500錯誤的界面</h2>
<img src="${pageContext.request.contextPath}/img/1-500.png" alt="500">
</body>
</html>
- web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0">
<error-page>
<error-code>404</error-code>
<location>/error/404.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/error/500.jsp</location>
</error-page>
</web-app>
頭部和尾部頁面拼接
- footer.jsp
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
<title>footer</title>
</head>
<body>
<h1>我是footer</h1>
</body>
</html>
- header.jsp
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
<title>head</title>
</head>
<body>
<h1>我是header</h1>
</body>
</html>
- jsp3.jsp
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
<title>jsp3</title>
</head>
<body>
<%-- @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"/>
</body>
</html>
5.9大內置對象
- PageContext 存東西
- Request 存東西
- Response
- Session 存東西
- Application 【SerlvetContext】 存東西
- config 【SerlvetConfig】
- out
- page ,不用了解
- exception
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--內置對象--%>
<%
pageContext.setAttribute("name1","天啟1號"); // 保存的數據只在一個頁面中有效
request.setAttribute("name2","天啟2號"); // 保存的數據只在一次請求中有效,請求轉發會攜帶這個數據
session.setAttribute("name3","天啟3號"); // 保存的數據只在一次會話中有效,從打開瀏覽器到關閉瀏覽器
application.setAttribute("name4","天啟4號"); // 保存的數據只在服務器中有效,從打開服務器到關閉服務器
%>
<%--
腳本片段中的代碼,會被原封不動生成到.jsp.java
要求:這里面的代碼,必須保證Java語法的正確性
--%>
<%
// 從pageContent取出,我們通過尋找的方式來
// 從底層到高層(作用域):
String name1 = (String) pageContext.findAttribute("name1");
String name2 = (String) pageContext.findAttribute("name2");
String name3 = (String) pageContext.findAttribute("name3");
String name4 = (String) pageContext.findAttribute("name4");
String name5 = (String) pageContext.findAttribute("name5"); // 作用域
%>
<%--使用EL表達式輸出 ${} --%>
<h1>取出的值:</h1>
<h3>${name1}</h3>
<h3>${name2}</h3>
<h3>${name3}</h3>
<h3>${name4}</h3>
<h3> <%=name5%> </h3>
</body>
</html>
如果EL表達式不生效,請在JSP頁面最上面加上:<%@page isELIgnored=“false” %>
- request:客戶端向服務器發送請求,產生的數據,用戶看完就沒用了,比如:新聞,用戶看完沒用的!
- session:客戶端向服務器發送請求,產生的數據,用戶用完一會還有用,比如:購物車;
- application:客戶端向服務器發送請求,產生的數據,一個用戶用完了,其他用戶還可能使用,比如:聊天數據;
6.JSP標簽.JSTL標簽.EL表達式
- EL表達式: ${ }
- 獲取數據
- 執行運算
- 獲取web開發的常用對象
<!-- JSTL表達式的依賴 -->
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl-api</artifactId>
<version>1.2</version>
</dependency>
<!-- standard標簽庫 -->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
- JSP標簽
- jspTag.jsp
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
<title>jspTag</title>
</head>
<body>
<h1>Tag1</h1>
<%--jsp:include--%>
<%--
http://localhost:8080/Jsp/jspTag.jsp?name=subeily&age=18
--%>
<jsp:forward page="/jspTag2.jsp">
<jsp:param name="name" value="subeiLY"/>
<jsp:param name="age" value="18"/>
</jsp:forward>
</body>
</html>
- jspTag2.jsp
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
<title>jspTag2</title>
</head>
<body>
<h1>Tag2</h1>
<%--取出參數--%>
名字:<%=request.getParameter("name")%>
年齡:<%=request.getParameter("age")%>
</body>
</html>
- JSTL表達式
- JSTL標簽庫的使用就是為了彌補HTML標簽的不足;它自定義許多標簽,可以供我們使用,標簽的功能和Java代碼一樣!
- 格式化標簽
- SQL標簽
- XML 標簽
- 核心標簽 (掌握部分)
-
JSTL標簽庫使用步驟:
- 引入對應的 taglib;
- 使用其中的方法;
- 在Tomcat 的lib目錄下也需要引入 jstl-api-1.2.jar、standard-1.1.2.jar的包,否則會報錯:JSTL解析錯誤;
-
c:if
<%@ page contentType="text/html;charset=UTF-8" %>
<%--引入jstl核心標簽庫--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>core-if</title>
</head>
<body>
<h4>if測試</h4>
<hr>
<form action="core-if.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:if>
<%--自閉合標簽--%>
<c:out value="${isAdmin}"/>
</body>
</html>
- c:choose c:when
<%@ page contentType="text/html;charset=UTF-8" %>
<%--引入jstl核心標簽庫--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>core-for</title>
</head>
<body>
<%--定義一個變量score,值為85--%>
<c:set var="score" value="65"/>
<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>
</html>
- c:forEach
<%@ page import="java.util.ArrayList" %>
<%@ page contentType="text/html;charset=UTF-8" %>
<%--引入jstl核心標簽庫--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>core-foreach</title>
</head>
<body>
<%
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}"/> <br>
</c:forEach>
<hr>
<c:forEach var="people" items="${list}" begin="1" end="3" step="1" >
<c:out value="${people}"/> <br>
</c:forEach>
</body>
</html>
9.JavaBean
實體類,JavaBean有特定的寫法:
- 必須要有一個無參構造
- 屬性必須私有化
- 必須有對應的get/set方法;
一般用來和數據庫的字段做映射 ORM;
ORM :對象關系映射
- 表—>類
- 字段–>屬性
- 行記錄---->對象
people表
id | name | age | address |
---|---|---|---|
1 | 餃子1號 | 3 | 成都 |
2 | 餃子2號 | 18 | 成都 |
3 | 餃子3號 | 85 | 成都 |
class People{
private int id;
private String name;
private int age;
private String address;
}
class A{
new People(1,"餃子1號",3,"成都");
new People(2,"餃子1號",18,"成都");
new People(3,"餃子1號",85,"成都");
}
- javaBean.jsp
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
// People people = new People();
// people.setAddress();
// people.setId();
// people.getAge()
// people.setName();
%>
<jsp:useBean id="people" class="com.github.pojo.People" />
<jsp:setProperty name="people" property="address" value="成都"/>
<jsp:setProperty name="people" property="id" value="1"/>
<jsp:setProperty name="people" property="age" value="2"/>
<jsp:setProperty name="people" property="name" value="哇哈哈AD鈣"/>
姓名:<jsp:getProperty name="people" property="name"/>
ID:<jsp:getProperty name="people" property="id"/>
年齡:<jsp:getProperty name="people" property="age"/>
地址:<jsp:getProperty name="people" property="address"/>
</body>
</html>
- 過濾器
- 文件上傳
- 郵件發送
- JDBC 復習 : 如何使用JDBC , JDBC crud, jdbc 事務
10.MVC三層架構
- 什么是MVC: Model view Controller 模型.視圖.控制器
1.早些年
- 用戶直接訪問控制層,控制層就可以直接操作數據庫;
servlet--CRUD-->數據庫
弊端:程序十分臃腫,不利於維護
servlet的代碼中:處理請求.響應.視圖跳轉.處理JDBC.處理業務代碼.處理邏輯代碼
架構:沒有什么是加一層解決不了的!
程序猿調用
|
JDBC
|
Mysql Oracle SqlServer ....
2.MVC三層架構
Model
- 業務處理 :業務邏輯(Service)
- 數據持久層:CRUD (Dao)
View
- 展示數據
- 提供鏈接發起Servlet請求 (a,form,img…)
Controller (Servlet)
-
接收用戶的請求 :(req:請求參數.Session信息….)
-
交給業務層處理對應的代碼
-
控制視圖的跳轉
登錄--->接收用戶的登錄請求--->處理用戶的請求(獲取用戶登錄的參數,username,password)---->交給業務層處理登錄業務(判斷用戶名密碼是否正確:事務)--->Dao層查詢用戶名和密碼是否正確-->數據庫
11.Filter (重點)
Filter:過濾器 ,用來過濾網站的數據;
- 處理中文亂碼
- 登錄驗證….
Filter開發步驟:
-
導包
-
編寫過濾器
-
導包不要錯;
<?xml version="1.0" encoding="UTF-8"?> <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"> <parent> <artifactId>JavaWeb-02-Servlet</artifactId> <groupId>com.github</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>Filer</artifactId> <dependencies> <!-- Servlet 依賴 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> </dependency> <!-- JSP 依賴 --> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>javax.servlet.jsp-api</artifactId> <version>2.3.3</version> </dependency> <!-- JSTL表達式的依賴--> <dependency> <groupId>javax.servlet.jsp.jstl</groupId> <artifactId>jstl-api</artifactId> <version>1.2</version> </dependency> <!-- standard標簽庫--> <dependency> <groupId>taglibs</groupId> <artifactId>standard</artifactId> <version>1.1.2</version> </dependency> <!-- 連接數據庫--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> </dependencies> </project>
-
-
實現Filter接口,重寫對應的方法即可;
package com.github.filter; import javax.servlet.*; import java.io.IOException; public class CharacterEncodingFilter implements Filter { /** * 初始化:web服務器啟動,就以及初始化了,隨時等待過濾對象出現! */ public void init(FilterConfig filterConfig) { System.out.println("CharacterEncodingFilter初始化"); } /** * Chain : 鏈 * * 1. 過濾中的所有代碼,在過濾特定請求的時候都會執行 * 2. 必須要讓過濾器繼續同行 * chain.doFilter(request,response); */ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { request.setCharacterEncoding("utf-8"); response.setCharacterEncoding("utf-8"); response.setContentType("text/html;charset=UTF-8"); System.out.println("CharacterEncodingFilter執行前...."); // 讓我們的請求繼續走,如果不寫,程序到這里就被攔截停止! chain.doFilter(request,response); System.out.println("CharacterEncodingFilter執行后...."); } /** * 銷毀:web服務器關閉的時候,過濾會銷毀 */ public void destroy() { System.out.println("CharacterEncodingFilter銷毀"); } }
-
在web.xml中配置 Filter;
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>com.github.filter.CharacterEncodingFilter</filter-class> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <!--只要是 /servlet的任何請求,會經過這個過濾器--> <url-pattern>/servlet/*</url-pattern> <!--<url-pattern>/*</url-pattern>--> </filter-mapping> </web-app>
12.監聽器
實現一個監聽器的接口;(有N種)
-
編寫一個監聽器;
實現監聽器的接口…
package com.github.listener; import javax.servlet.ServletContext; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; /** * @author: subei * @Description: 統計網站在線人數 : 統計session */ public class OnlineCountListener implements HttpSessionListener { /** * 創建session監聽: 看你的一舉一動 * 一旦創建Session就會觸發一次這個事件! * @param se */ 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就會觸發一次這個事件! * @param se */ 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. 自動銷毀 */ }
-
web.xml中注冊監聽器;
<!--注冊監聽器--> <listener> <listener-class>com.github.listener.OnlineCountListener</listener-class> </listener>
-
看情況是否使用!
13.過濾器.監聽器常見應用
- 監聽器:GUI編程中經常使用;
package com.github.listener;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
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);
// 設置背景顏色1
frame.setBackground(new Color(68, 227, 177));
panel.setBounds(50,50,300,300);
// 設置背景顏色2
panel.setBackground(new Color(255, 242,0));
frame.add(panel);
frame.setVisible(true);
// 監聽事件,監聽關閉事件
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
super.windowClosing(e);
}
});
}
}
案例:用戶登錄之后才能進入主頁!用戶注銷后就不能進入主頁了!
- 用戶登錄之后,向Sesison中放入用戶的數據
- 進入主頁的時候要判斷用戶是否已經登錄;要求:在過濾器中實現!
- 因為:個人tomcat配置的為 /Filer 如下圖:
- LoginServlet.java
package com.github.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
super.doGet(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 獲取前端請求的參數
String username = req.getParameter("username");
// 登陸成功
if("admin".equals(username)){
req.getSession().setAttribute("USER_SESSION",req.getSession().getId());
resp.sendRedirect("/Filer/sys/success.jsp");
} else { // 登陸失敗
resp.sendRedirect("/Filer/error.jsp");
}
}
}
- LogoutServlet.java
package com.github.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class LogoutServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Object user_session = req.getSession().getAttribute("USER_SESSION");
if(user_session!=null){
req.getSession().removeAttribute("USER_SESSION");
resp.sendRedirect("/Filer/Login.jsp");
} else {
resp.sendRedirect("/Filer/Login.jsp");
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req,resp);
}
}
- Login.jsp
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
<title>Login</title>
</head>
<body>
<h1>登陸界面</h1>
<form action="/Filer/servlet/login" method="post">
<input type="text" name="username">
<input type="submit">
</form>
</body>
</html>
- success.jsp
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
<title>成功</title>
</head>
<body>
<%--<%--%>
<%-- Object userSession = request.getSession().getAttribute("USER_SESSION");--%>
<%-- if(userSession==null){--%>
<%-- response.sendRedirect("Filer/Login.jsp");--%>
<%-- }--%>
<%--%>--%>
<h1>主頁</h1>
<p><a href="/Filer/servlet/logout">注銷</a> </p>
</body>
</html>
- error.jsp
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
<title>error</title>
</head>
<body>
<h1>錯誤</h1>
<h3>沒有權限,用戶名錯誤</h3>
<p> <a href="/Filer/Login.jsp">返回登錄主頁</a></p>
</body>
</html>
- 進行登錄注銷無法登錄判斷;
- Constant.java
package com.github.Util;
public class Constant {
public static String USER_SESSION="USER_SESSION";
}
- SysFilter.java
package com.github.listener;
import com.github.Util.Constant;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class SysFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
if (request.getSession().getAttribute(Constant.USER_SESSION)==null){
response.sendRedirect("/Filer/error.jsp");
}
chain.doFilter(request,response);
}
public void destroy() {
}
}
- web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0">
<servlet>
<servlet-name>LoginServlet</servlet-name>
<servlet-class>com.github.servlet.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/servlet/login</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>LogoutServlet</servlet-name>
<servlet-class>com.github.servlet.LogoutServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LogoutServlet</servlet-name>
<url-pattern>/servlet/logout</url-pattern>
</servlet-mapping>
<filter>
<filter-name>SysFilter</filter-name>
<filter-class>com.github.listener.SysFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>SysFilter</filter-name>
<url-pattern>/sys/*</url-pattern>
</filter-mapping>
</web-app>
- 運行結果如下:
14.JDBC
- 什么是JDBC : Java連接數據庫!
需要jar包的支持:
- java.sql
- javax.sql
- mysql-conneter-java… 連接驅動(必須要導入)
實驗環境搭建
USE jdbc;
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@qq.com','2000-01-01');
INSERT INTO users(id,`name`,`password`,email,birthday)
VALUES(2,'李四','123456','ls@qq.com','2000-01-01');
INSERT INTO users(id,`name`,`password`,email,birthday)
VALUES(3,'王五','123456','ww@qq.com','2000-01-01');
SELECT * FROM users;
- 導入數據庫依賴
<!--mysql的驅動-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
- IDEA中連接數據庫:
- JDBC 固定步驟:
- 加載驅動
- 連接數據庫,代表數據庫
- 向數據庫發送SQL的對象Statement : CRUD
- 編寫SQL (根據業務,不同的SQL)
- 執行SQL
- 關閉連接
package com.github.test;
import java.sql.*;
public class TestJdbc {
public static void main(String[] args) throws SQLException, ClassNotFoundException {
// 配置信息
// useUnicode=true&characterEncoding=utf-8 解決中文亂碼
String url="jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf-8";
String username = "root";
String password = "root";
// 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
package com.github.test;
import java.sql.*;
public class TestJDBC2 {
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";
String username = "root";
String password = "root";
// 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);
// 給第一個占位符? 的值賦值為1;
preparedStatement.setInt(1,2);
// 給第二個占位符? 的值賦值為狂神說Java;
preparedStatement.setString(2,"哇哈哈AD鈣");
// 給第三個占位符? 的值賦值為123456;
preparedStatement.setString(3,"123456");
// 給第四個占位符? 的值賦值為1;
preparedStatement.setString(4,"24736743@qq.com");
// 給第五個占位符? 的值賦值為new Date(new java.util.Date().getTime());
preparedStatement.setDate(5,new Date(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單元測試
- 依賴
<!--單元測試-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
簡單使用
- @Test注解只有在方法上有效,只要加了這個注解的方法,就可以直接運行!
@Test
public void test(){
System.out.println("Hello");
}
- 失敗的時候是紅色:
- 搭建一個環境
USE jdbc;
CREATE TABLE account(
id INT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(40),
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);
package com.github.test;
import org.junit.Test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class TestJdbc3 {
@Test
public void test() {
// 配置信息
// useUnicode=true&characterEncoding=utf-8 解決中文亂碼
String url="jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf-8";
String username = "root";
String password = "root";
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();
// 以上兩條SQL都執行成功了,就提交事務!
connection.commit();
System.out.println("success");
} catch (Exception e) {
try {
// 如果出現異常,就通知數據庫回滾事務
connection.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}finally {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
15.smbms項目
參考smbms項目
16.文件傳輸原理及實現
此部分內容參考:博客園-Jpbito
- 新建一個空項目;
- 修改web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0" metadata-complete="true">
</web-app>
- 配置Tomcat,運行空項目。可運行再執行下面操作。
- 整體框架;
具體實現
-
文件在網絡上都是使用IO的方式,即流的方式進行的傳輸,要實現的文件上傳功能可以直接使用apache的組件commons-fileupload(針對文件上傳的工具類包),這個jar包又依賴commons-io包(封裝了大量的IO操作的工具類),所以在實現文件上傳功能的時候需要導入如下兩個依賴:
-
maven導入URL:
- https://mvnrepository.com/artifact/commons-io/commons-io
- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload
-
在Maven網站上我們有兩種導入jar包的方式。
如何查看所下載的jar包中的源碼是什么?
- 雙擊打開下載的jar包;
- 這些都是別人寫好的Java代碼編譯之后的*.class文件,如果想要查看源碼,可以使用反編譯工具來獲得,最簡單的反編譯工具就是我們使用的IDEA,直接將 *.class拷貝到IDEA中的文件夾,就可以查看它的源碼了。
- 為什么壓縮的是.class文件?
- 因為這個文件已經通過了編譯器的編譯,引入 *.class文件可以減少編譯這些引用的代碼的步驟與時間,直接拿過來就可以使用;更是一種原作者保護自己源碼的一種手段!
- 如何查看這些*.class中的源碼?
- 反編譯。
手動導入需要的jar包。
- 在IDEA中,新建一個lib包,復制jar包到lib文件夾。
上傳文件都是通過使用表單實現的。
<p>
<input type="file" name="file">
</p>
- 注意:上傳文件的表單是上面這么寫的,但是如果一個表單中包含文件數據,那么該表單如果想要提交文件數據,那么它必須包含enctype屬性,且屬性值必須為:
enctype="multipart/form-data"
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h2>Hello World!</h2>
<%--
通過表單上傳文件
get:上傳文件有大小限制
post:上傳文件無大小限制
--%>
<form action="${pageContext.request.contextPath}/upload.do" enctype="multipart/form-data" method="post">
上傳用戶: <input type="text" name="username"><br>
上傳文件1: <input type="file" name="file1"><br>
上傳文件2: <input type="file" name="file2"><br>
<input type="submit" value="提交文件">
</form>
</body>
</html>
- 【面試:文件上傳調優】
- 上傳的文件存放在一個不能使用外界URL訪問的目錄下面;
- 上傳到同一個文件夾中的文件名稱應該唯一:使用時間戳/UUID/MD5等手段實現;
- 限制上傳文件的最大值:因為服務器上硬盤資源很貴,不能讓用戶隨意的使用;
- 限制文件上傳類型:比如這個文件夾只用來存儲圖片,那你就不能上傳一個.mp4的文件;
編寫servlet
- 前端jsp頁面——index.jsp;
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h2>Hello World!</h2>
<%--
通過表單上傳文件
get:上傳文件有大小限制,只能提交4-5kb的數據
post:上傳文件無大小限制
注意:文件一般比較大,所以上傳文件都是使用post方式提交
${pageContext.request.contextPath}:獲取到webapp路徑
--%>
<form action="${pageContext.request.contextPath}/upload.do" enctype="multipart/form-data" method="post">
上傳用戶: <input type="text" name="username"><br>
上傳文件1: <input type="file" name="file1"><br>
上傳文件2: <input type="file" name="file2"><br>
<p> <input type="submit" value="提交文件"> | <input type="reset" value="重置"> </p>
</form>
</body>
</html>
- 上傳成功后的頁面——success.jsp;
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>成功</title>
</head>
<body>
<%=request.getAttribute("msg")%>
<h2>🎉上傳成功🎉</h2>
</body>
</html>
- 新建servlet包
- 修改web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0" metadata-complete="true">
<servlet>
<servlet-name>upload</servlet-name>
<servlet-class>com.github.servlet.FileSerlvet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>upload</servlet-name>
<url-pattern>/upload.do</url-pattern>
</servlet-mapping>
</web-app>
- servlet層
package com.github.servlet;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.UUID;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
/** * @author subei */
public class FileSerlvet extends HttpServlet {
private static final long serialVersionUID = 1L;
/** * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse * response) */
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// TODO Auto-generated method stub
// response.getWriter().append("Served at: ").append(request.getContextPath());
// 判斷上傳的文件普通表單還是帶文件的表單
if (!ServletFileUpload.isMultipartContent(request)) {
return;//終止方法運行,說明這是一個普通的表單,直接返回
}
//創建上傳文件的保存路徑,建議在WEB-INF路徑下,安全,用戶無法直接訪間上傳的文件;
String uploadPath =this.getServletContext().getRealPath("/WEB-INF/upload");
File uploadFile = new File(uploadPath);
if (!uploadFile.exists()){
uploadFile.mkdir(); //創建這個月錄
}
// 創建上傳文件的保存路徑,建議在WEB-INF路徑下,安全,用戶無法直接訪問上傳的文件
String tmpPath = this.getServletContext().getRealPath("/WEB-INF/tmp");
File file = new File(tmpPath);
if (!file.exists()) {
file.mkdir();//創建臨時目錄
}
// 處理上傳的文件,一般都需要通過流來獲取,我們可以使用 request, getInputstream(),原生態的文件上傳流獲取,十分麻煩
// 但是我們都建議使用 Apache的文件上傳組件來實現, common-fileupload,它需要旅 commons-io組件;
try {
// 創建DiskFileItemFactory對象,處理文件路徑或者大小限制
DiskFileItemFactory factory = getDiskFileItemFactory(file);
/* * //通過這個工廠設置一個緩沖區,當上傳的文件大於這個緩沖區的時候,將他放到臨時文件 factory.setSizeThreshold(1024 * * 1024); //緩存區大小為1M factory.setRepository (file);//臨時目錄的保存目錄,需要一個File */
// 2、獲取ServletFileUpload
ServletFileUpload upload = getServletFileUpload(factory);
// 3、處理上傳文件
// 把前端請求解析,封裝成FileItem對象,需要從ServletFileUpload對象中獲取
String msg = uploadParseRequest(upload, request, uploadPath);
// Servlet請求轉發消息
System.out.println(msg);
if(msg == "文件上傳成功!") {
// Servlet請求轉發消息
request.setAttribute("msg",msg);
request.getRequestDispatcher("success.jsp").forward(request, response);
}else {
msg ="請上傳文件";
request.setAttribute("msg",msg);
request.getRequestDispatcher("success.jsp").forward(request, response);
}
} catch (FileUploadException e) {
// TODO 自動生成的 catch 塊
e.printStackTrace();
}
}
public static DiskFileItemFactory getDiskFileItemFactory(File file) {
DiskFileItemFactory factory = new DiskFileItemFactory();
// 通過這個工廠設置一個緩沖區,當上傳的文件大於這個緩沖區的時候,將他放到臨時文件中;
factory.setSizeThreshold(1024 * 1024);// 緩沖區大小為1M
factory.setRepository(file);// 臨時目錄的保存目錄,需要一個file
return factory;
}
public static ServletFileUpload getServletFileUpload(DiskFileItemFactory factory) {
ServletFileUpload upload = new ServletFileUpload(factory);
// 監聽長傳進度
upload.setProgressListener(new ProgressListener() {
// pBYtesRead:已讀取到的文件大小
// pContextLength:文件大小
@Override
public void update(long pBytesRead, long pContentLength, int pItems) {
System.out.println("總大小:" + pContentLength + "已上傳:" + pBytesRead);
}
});
// 處理亂碼問題
upload.setHeaderEncoding("UTF-8");
// 設置單個文件的最大值
upload.setFileSizeMax(1024 * 1024 * 10);
// 設置總共能夠上傳文件的大小
// 1024 = 1kb * 1024 = 1M * 10 = 10м
return upload;
}
public static String uploadParseRequest(ServletFileUpload upload, HttpServletRequest request, String uploadPath)
throws FileUploadException, IOException {
String msg = "";
// 把前端請求解析,封裝成FileItem對象
List<FileItem> fileItems = upload.parseRequest(request);
for (FileItem fileItem : fileItems) {
if (fileItem.isFormField()) {// 判斷上傳的文件是普通的表單還是帶文件的表單
// getFieldName指的是前端表單控件的name;
String name = fileItem.getFieldName();
String value = fileItem.getString("UTF-8"); // 處理亂碼
System.out.println(name + ": " + value);
} else {// 判斷它是上傳的文件
// ============處理文件==============
// 拿到文件名
String uploadFileName = fileItem.getName();
System.out.println("上傳的文件名: " + uploadFileName);
if (uploadFileName.trim().equals("") || uploadFileName == null) {
continue;
}
// 獲得上傳的文件名/images/girl/paojie.png
String fileName = uploadFileName.substring(uploadFileName.lastIndexOf("/") + 1);
// 獲得文件的后綴名
String fileExtName = uploadFileName.substring(uploadFileName.lastIndexOf(".") + 1);
/* * 如果文件后綴名fileExtName不是我們所需要的 就直按return.不處理,告訴用戶文件類型不對。 */
System.out.println("文件信息[件名: " + fileName + " ---文件類型" + fileExtName + "]");
// 可以使用UID(唯一識別的通用碼),保證文件名唯
// 0UID. randomUUID(),隨機生一個唯一識別的通用碼;
String uuidPath = UUID.randomUUID().toString();
// ================處理文件完畢==============
// 存到哪? uploadPath
// 文件真實存在的路徑realPath
String realPath = uploadPath + "/" + uuidPath;
// 給每個文件創建一個對應的文件夾
File realPathFile = new File(realPath);
if (!realPathFile.exists()) {
realPathFile.mkdir();
}
// ==============存放地址完畢==============
// 獲得文件上傳的流
InputStream inputStream = fileItem.getInputStream();
// 創建一個文件輸出流
// realPath =真實的文件夾;
// 差了一個文件;加上翰出文件的名產"/"+uuidFileName
FileOutputStream fos = new FileOutputStream(realPath + "/" + fileName);
// 創建一個緩沖區
byte[] buffer = new byte[1024 * 1024];
// 判斷是否讀取完畢
int len = 0;
// 如果大於0說明還存在數據;
while ((len = inputStream.read(buffer)) > 0) {
fos.write(buffer, 0, len);
}
// 關閉流
fos.close();
inputStream.close();
msg = "文件上傳成功!";
fileItem.delete(); // 上傳成功,清除臨時文件
//=============文件傳輸完成=============
}
}
return msg;
}
}
- 運行后,如下情況:
- 在pom.xml中添加jar包的maven;
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
- 再次運行;
- 顯示成功,但文件在哪里???
17.郵件發送原理及實現
1.概述
傳輸協議
- SMTP協議
發送郵件:
我們通常把處理用戶smtp請求(郵件發送請求)的服務器稱之為SMTP服務器(郵件發送服務器)。 - POP3協議
接收郵件:
我們通常把處理用戶pop3請求(郵件接收請求)的服務器稱之為POP3服務器(郵件接收服務器)。
郵件收發原理
- 請參考:連接
使用Java實現郵件發送需要使用到的類
- 我們將用代碼完成郵件的發送。這在實際項目中應用的非常廣泛,比如注冊需要發送郵件進行賬號激活,再比如A項目中利用郵件進行任務提醒等等。
- 使用Java發送Emai分簡單,但是首先你應該准備 JavaMail API和 Java Activation framework。
- 得到兩個jar包:
導入jar包時,報錯:
Cannot resolve mail.jar:mail.jar:1.4
- 解決:maven重新導入如下。
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4.7</version>
</dependency>
-
JavaMail是sωn公司(現以被甲骨文收購)為方便Java開發人員在應用程序中實現郵件發送和接收功能而提供的一套標准開發包,它支持一些常用的郵件協議,如前面所講的SMTP,POP3,IMAP,還有MIME等。我們在使用 JavaMail!API編寫郵件時,無須考慮郵件的底層實現細節,只要調用 Javamail開發包中相應的API類就可以了。
-
我們可以先嘗試發送一封簡單的郵件,確保電腦可以連接網絡。
- 創建包含郵件服務器的網絡連接信息的Session對象。
- 創建代表郵件內容的Message對象;
- 創建Transport對象,連接服務器,發送Message,關閉連接;
-
主要有四個核心類,我們在編寫程序時,記住這四個核心類,就很容易編寫出Java郵件處理程序。
2.簡單郵件
郵件分類
- 簡單郵件:沒有除了文字以外的其他所有文件(包括附件和圖片、視頻等),即純文本郵件;
- 復雜郵件:除了傳統的文字信息外,還包括了一些非文字數據的郵件;
需要發送郵件首先就要我們的郵箱賬號支持POP3和SMTP協議,所以我們需要開啟郵箱的POP3+SMTP服務,然后我們需要復制下圖中的授權碼,這個授權碼就相當於你的QQ密碼,你可以使用你的郵箱賬號+授權碼來發送郵件,而SMTP服務器也就是使用這個來識別你的身份的。
package com.github.test;
import com.sun.mail.util.MailSSLSocketFactory;
import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.util.Properties;
/** * @Author: subei * @Description: 一封簡單的郵件 */
public class MailDemo01 {
public static void main(String[] args) throws Exception {
Properties prop = new Properties();
prop.setProperty("mail.host", "smtp.qq.com");
// 設置QQ郵件服務器
prop.setProperty("mail.transport.protocol", "smtp");
// 郵件發送協議
prop.setProperty("mail.smtp.auth", "true");
// 需要驗證用戶名密碼
// 關於QQ郵箱,還要設置SSL加密,加上以下代碼即可
MailSSLSocketFactory sf = new MailSSLSocketFactory();
sf.setTrustAllHosts(true);
prop.put("mail.smtp.ssl.enable", "true");
prop.put("mail.smtp.ssl.socketFactory", sf);
// 使用JavaMail發送郵件的5個步驟
// 1.創建定義整個應用程序所需的環境信息的 Session 對象
// 使用QQ郵箱的時候才需要,其他郵箱不需要這一段代碼
Session session = Session.getDefaultInstance(prop, new Authenticator() {
// 獲取和SMTP服務器的連接對象
@Override
public PasswordAuthentication getPasswordAuthentication() {
// 發件人郵件用戶名、授權碼
return new PasswordAuthentication("XXXX@qq.com", "授權碼");
}
});
// 開啟Session的debug模式,這樣就可以查看到程序發送Email的運行狀態
session.setDebug(true);
// 2.通過session得到transport對象
Transport ts = session.getTransport();
// 通過這一次和SMTP服務器的連接對象獲取發送郵件的傳輸對象
// 3.使用郵箱的用戶名和授權碼連上SMTP郵件服務器,即登陸
ts.connect("smtp.qq.com", "XXXX@qq.com", "授權碼");
// 4.創建郵件對象MimeMessage——點擊網頁上的寫信
// 創建一個郵件對象
MimeMessage message = new MimeMessage(session);
// 指明郵件的發件人——在網頁上填寫發件人
message.setFrom(new InternetAddress("XXXX@qq.com"));
// 設置發件人
// 指明郵件的收件人,現在發件人和收件人是一樣的,那就是自己給自己發——在網頁上填寫收件人
message.setRecipient(Message.RecipientType.TO, new InternetAddress("XXXX@qq.com"));
// 設置收件人
// 郵件的標題——在網頁上填寫郵件標題
message.setSubject("簡單郵件發送實現");
// 設置郵件主題
// 郵件的文本內容——在網頁上填寫郵件內容
message.setContent("<h2 style='color:red'>你好啊!</h2>", "text/html;charset=UTF-8");
// 設置郵件的具體內容
// 5.發送郵件——在網頁上點擊發送按鈕
ts.sendMessage(message, message.getAllRecipients());
// 6.關閉連接對象,即關閉服務器上的連接資源
ts.close();
}
}
- 運行測試
3.復雜郵件
-
復雜郵件就是非純文本的郵件,可能還包含了圖片和附件等資源。
-
MIME(多用途互聯網郵件擴展類型)
-
先認識兩個類一個名詞:
-
MimeBodyPart類
- javax.mail.internet.MimeBodyPart類表示的是一個MIME消息,它和MimeMessage類一樣都是從Part接口繼承過來。
- 即一個MIME消息對應一個MimeBodyPart對象,而MimeBodyPart對象就是我們寫的郵件內容中的元素。
-
MimeMultipart類
- javax.mail.internet.MimeMultipart是抽象類 Multipart的實現子類,它用來組合多個MIME消息。一個MimeMultipart對象可以包含多個代表MIME消息的MimeBodyPart對象。
- 即一個MimeMultipart對象表示多個MimeBodyPart的集合,而一個MimeMultipart表示的就是我們一封郵件的內容。
-
MimeMultipart對象的使用的時候需要設置setSubType()的屬性值,一共就下面3種取值:
- alternative:表明這個MimeMultipart對象中的MimeMessage對象的數據是純文本文件;
- related:表明這個MimeMultipart對象中的MimeMessage對象的數據包含非純文本文件;
- mixed:表明這個MimeMultipart對象中的MimeMessage對象的數據包含附件;
我們在使得的時候如果不知道使用哪一個,直接使用mixed即可,使用這個屬性值一定不會報錯。
- 相較於簡單郵件,復雜郵件變化的地方只是在於郵件內容本身會發送變化,而其他的步驟都是一樣的:
- 准備一些參數;
- 獲取session對象;
- 獲取傳輸對象;
- 登陸授權;
- 寫郵件 (和簡單郵件相區別);
- 發送郵件;
- 關閉服務器資源。
發送包含圖片的復雜郵件
package com.github.test;
import com.sun.mail.util.MailSSLSocketFactory;
import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import java.util.Properties;
/** * @Author: subei * @Description: 圖片的郵件 */
public class MailDemo01 {
public static void main(String[] args) throws Exception {
Properties prop = new Properties();
prop.setProperty("mail.host", "smtp.qq.com");
// 設置QQ郵件服務器
prop.setProperty("mail.transport.protocol", "smtp");
// 郵件發送協議
prop.setProperty("mail.smtp.auth", "true");
// 需要驗證用戶名密碼
// 關於QQ郵箱,還要設置SSL加密,加上以下代碼即可
MailSSLSocketFactory sf = new MailSSLSocketFactory();
sf.setTrustAllHosts(true);
prop.put("mail.smtp.ssl.enable", "true");
prop.put("mail.smtp.ssl.socketFactory", sf);
// 使用JavaMail發送郵件的5個步驟
// 1.創建定義整個應用程序所需的環境信息的 Session 對象
// 使用QQ郵箱的時候才需要,其他郵箱不需要這一段代碼
Session session = Session.getDefaultInstance(prop, new Authenticator() {
// 獲取和SMTP服務器的連接對象
@Override
public PasswordAuthentication getPasswordAuthentication() {
// 發件人郵件用戶名、授權碼
return new PasswordAuthentication("XXXX@qq.com", "授權碼");
}
});
// 開啟Session的debug模式,這樣就可以查看到程序發送Email的運行狀態
session.setDebug(true);
// 2.通過session得到transport對象
Transport ts = session.getTransport();
// 通過這一次和SMTP服務器的連接對象獲取發送郵件的傳輸對象
// 3.使用郵箱的用戶名和授權碼連上SMTP郵件服務器,即登陸
ts.connect("smtp.qq.com", "XXXX@qq.com", "授權碼");
// 4.創建郵件對象MimeMessage——點擊網頁上的寫信
// 創建一個郵件對象
MimeMessage message = new MimeMessage(session);
// 指明郵件的發件人——在網頁上填寫發件人
message.setFrom(new InternetAddress("XXXX@qq.com"));
// 設置發件人
// 指明郵件的收件人,現在發件人和收件人是一樣的,那就是自己給自己發——在網頁上填寫收件人
message.setRecipient(Message.RecipientType.TO, new InternetAddress("XXXX@qq.com"));
// 設置收件人
// 郵件的標題——在網頁上填寫郵件標題
message.setSubject("簡單郵件發送實現");
// 設置郵件主題
System.out.println("=============================復雜郵件的郵件內容設置==================================");
// 准備郵件數據
// 准備圖片數據
// 獲取一個圖片的MimeBodyPart對象
MimeBodyPart image = new MimeBodyPart();
// 由於圖片需要字符化才能傳輸,所以需要獲取一個DataHandler對象
DataHandler dh = new DataHandler(new FileDataSource("圖片的絕對地址"));
// 將圖片序列化
image.setDataHandler(dh);
// 為圖片的MimeBodyPart對象設置一個ID,我們在文字中就可以使用它了
image.setContentID("p6.jpg");
// 准備正文數據
MimeBodyPart text = new MimeBodyPart();
// 獲取一個文本的MimeBodyPart對象
text.setContent("這是一封郵件正文帶圖片<img src='cid:p6.jpg'>的郵件", "text/html;charset=UTF-8");
// 設置文本內容,注意在里面嵌入了<img src='cid:p6.jpg'>
// 描述數據關系
// 獲取MimeMultipart
MimeMultipart mm = new MimeMultipart();
// 將文本MimeBodyPart對象加入MimeMultipart中
mm.addBodyPart(text);
// 將圖片MimeBodyPart對象加入MimeMultipart中
mm.addBodyPart(image);
// 設置MimeMultipart對象的相對熟悉為related,即發送的數據為文本+非附件資源
mm.setSubType("related");
// 設置到消息中,保存修改
message.setContent(mm);
// 將MimeMultipart放入消息對象中
message.saveChanges();
// 保存上面的修改
System.out.println("===============================================================");
// 5.發送郵件——在網頁上點擊發送按鈕
ts.sendMessage(message, message.getAllRecipients());
// 6.關閉連接對象,即關閉服務器上的連接資源
ts.close();
}
}
- 只需修改第5步:
// 准備圖片數據
// 獲取一個圖片的MimeBodyPart對象
MimeBodyPart image = new MimeBodyPart();
// 由於圖片需要字符化才能傳輸,所以需要獲取一個DataHandler對象
DataHandler dh = new DataHandler(new FileDataSource("圖片的絕對地址"));
// 將圖片序列化
image.setDataHandler(dh);
// 為圖片的MimeBodyPart對象設置一個ID,我們在文字中就可以使用它了
image.setContentID("p6.jpg");
// 准備正文數據
MimeBodyPart text = new MimeBodyPart();
// 獲取一個文本的MimeBodyPart對象
text.setContent("這是一封郵件正文帶圖片<img src='cid:p6.jpg'>的郵件", "text/html;charset=UTF-8");
// 設置文本內容,注意在里面嵌入了<img src='cid:p6.jpg'>
// 描述數據關系
// 獲取MimeMultipart
MimeMultipart mm = new MimeMultipart();
// 將文本MimeBodyPart對象加入MimeMultipart中
mm.addBodyPart(text);
// 將圖片MimeBodyPart對象加入MimeMultipart中
mm.addBodyPart(image);
// 設置MimeMultipart對象的相對熟悉為related,即發送的數據為文本+非附件資源
mm.setSubType("related");
// 設置到消息中,保存修改
message.setContent(mm);
// 將MimeMultipart放入消息對象中
message.saveChanges();
- 測試運行:
發送包含附件的復雜郵件
- 需修改的代碼:
System.out.println("=============================復雜郵件的郵件內容設置==================================");
// 圖片
MimeBodyPart body1 = new MimeBodyPart();
body1.setDataHandler(new DataHandler(new FileDataSource("圖片的絕對地址")));
// 圖片設置ID
body1.setContentID("some.png");
// 文本
MimeBodyPart body2 = new MimeBodyPart();
body2.setContent("請注意,這是文本附件<img src='cid:test.png'>","text/html;charset=utf-8");
// 附件
MimeBodyPart body3 = new MimeBodyPart();
body3.setDataHandler(new DataHandler(new FileDataSource("附件1的絕對地址")));
// 附件設置名字
body3.setFileName("demo.c");
MimeBodyPart body4 = new MimeBodyPart();
body4.setDataHandler(new DataHandler(new FileDataSource("附件2的絕對地址")));
// 附件設置名字
body4.setFileName("demo.txt");
// 拼裝郵件正文內容
MimeMultipart multipart1 = new MimeMultipart();
multipart1.addBodyPart(body1);
multipart1.addBodyPart(body2);
// 1.文本和圖片內嵌成功!
multipart1.setSubType("related");
// new MimeBodyPart().setContent(multipart1);
// 將拼裝好的正文內容設置為主體
MimeBodyPart contentText = new MimeBodyPart();
contentText.setContent(multipart1);
// 拼接附件
MimeMultipart allFile =new MimeMultipart();
// 附件
allFile.addBodyPart(body3);
// 附件
allFile.addBodyPart(body4);
// 正文
allFile.addBodyPart(contentText);
// 正文和附件都存在郵件中,所有類型設置為mixed;
allFile.setSubType("mixed");
// 設置到消息中,保存修改
message.setContent(allFile);
// 將MimeMultipart放入消息對象中
message.saveChanges();
// 保存上面的修改
System.out.println("===============================================================");
- 測試運行:
4.網站注冊發送郵件功能實現
- 創建一個javaWeb項目;
- 環境搭建完成,測試通過。
- 導入maven依賴——pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<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>org.example</groupId>
<artifactId>mail</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!-- Servlet 依賴 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<!-- JSP 依賴 -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
</dependency>
<!-- JSTL表達式的依賴-->
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl-api</artifactId>
<version>1.2</version>
</dependency>
<!-- standard標簽庫-->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4.7</version>
</dependency>
</dependencies>
</project>
- 拷貝前端素材;
- index.jsp——注冊頁面;
- info.jsp——提示成功頁面;
<%@ page contentType="text/html;charset=UTF-8" %>
<%--注冊填寫郵箱的前端頁面--%>
<html>
<head>
<title>注冊</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/RegisterServlet.do" method="post">
用戶名:<input type="text" name="username"><br/>
密碼:<input type="password" name="password"><br/>
郵箱:<input type="text" name="email"><br/>
<input type="submit" value="注冊">
</form>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
<title>注冊成功</title>
</head>
<body>
<h2>🎉注冊成功🎉</h2>
${message}
</body>
</html>
- 編寫User.java
package com.github.pojo;
public class User {
private String username;
private String password;
private String email;
public User() {
}
public User(String username, String password, String email) {
this.username = username;
this.password = password;
this.email = email;
}
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;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", email='" + email + '\'' +
'}';
}
}
- 編寫工具類Sendmail.java
package com.github.util;
/** * 多線程實現郵件發送 * 使用多線程的原因就是提高用戶的體驗,一旦一個頁面3s及以上的時間白屏就可能被用戶關掉 * 所以我們在用戶提交表單之后,將費時的郵件發送工作使用一個子線程來完成,而主線程還是去完成它自己的事情 * 這么做就可以提高用戶體驗,不然用戶等待郵件發送的時間 */
import com.github.pojo.User;
import com.sun.mail.util.MailSSLSocketFactory;
import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.util.Properties;
/** * @author subei * 多線程這種處理就可以稱為異步處理 */
public class Sendmail extends Thread{
// 用於向客戶發送郵件的郵箱
private String from = "XXXX@qq.com";
// 用於登陸SMTP服務器的用戶名
private String username = "XXXX@qq.com";
// 授權碼
private String password = "授權碼";
// 發送郵件的地址
private String host = "smtp.qq.com";
private User user;
public Sendmail(User user) {
this.user = user;
}
@Override
public void run() {
try {
Properties prop = new Properties();
// 設置QQ郵件服務器
prop.setProperty("mail.host", host);
// 郵件發送協議
prop.setProperty("mail.transport.protocol", "smtp");
// 需要驗證用戶名密碼
prop.setProperty("mail.smtp.auth", "true");
// 關於QQ郵箱,還要設置SSL加密,加上以下代碼即可
MailSSLSocketFactory sf = new MailSSLSocketFactory();
sf.setTrustAllHosts(true);
prop.put("mail.smtp.ssl.enable", "true");
prop.put("mail.smtp.ssl.socketFactory", sf);
// 1.創建定義整個應用程序所需的環境信息的 Session 對象
// 使用QQ郵箱的時候才需要,其他郵箱不需要這一段代碼
// 獲取和SMTP服務器的連接對象
Session session = Session.getDefaultInstance(prop, new Authenticator() {
@Override
public PasswordAuthentication getPasswordAuthentication() {
// 發件人郵件用戶名、授權碼
return new PasswordAuthentication("XXXX@qq.com", "授權碼");
}
});
// 開啟Session的debug模式,這樣就可以查看到程序發送Email的運行狀態
session.setDebug(true);
// 2.通過session得到transport對象
Transport ts = session.getTransport();
// 3.使用郵箱的用戶名和授權碼連上SMTP郵件服務器,即登陸
ts.connect(host, username, password);
//4、創建郵件對象MimeMessage——點擊網頁上的寫信
MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress(username));
message.setRecipient(Message.RecipientType.TO, new InternetAddress(user.getEmail()));
message.setSubject("用戶注冊郵件!");
message.setContent("<p><h2>恭喜注冊成功!</h2></p>您的用戶名為: <h4>"+user.getUsername()+
"</h4><p>您的密碼:" + user.getPassword() +
"</p><p>請妥善保管您的密碼,如有問題請及時聯系網站客服,再次歡迎您的加入!!</p>", "text/html;charset=UTF-8");
// 5.發送郵件——在網頁上點擊發送按鈕
ts.sendMessage(message, message.getAllRecipients());
// 6.關閉連接對象,即關閉服務器上的連接資源
ts.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 編寫Servlet.java
package com.github.servlet;
import com.github.pojo.User;
import com.github.util.Sendmail;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class RegisterServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1.接收用戶填寫的表單數據
String username = req.getParameter("username");
String password = req.getParameter("password");
String email = req.getParameter("email");
System.out.println(username+password+email);
// 2.向用戶郵箱發送郵件,注意發送郵件很耗時,所以我們啟動一個子線程去做這件事,而用戶則是直接跳轉注冊成功頁面,以免降低用戶體驗
User user = new User(username,password,email);
// 獲取子線程對象
Sendmail sendmail = new Sendmail(user);
// 啟動子線程
new Thread(sendmail).start();
// 3.視圖跳轉
req.setAttribute("message","注冊成功!我們已經向您的郵箱發送了郵件,請您及時進行查收。由於網絡原因,您收到郵件的時間存在延遲,敬請諒解~");
req.getRequestDispatcher("info.jsp").forward(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
- web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0">
<servlet>
<servlet-name>RegisterServlet</servlet-name>
<servlet-class>com.github.servlet.RegisterServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>RegisterServlet</servlet-name>
<url-pattern>/RegisterServlet.do</url-pattern>
</servlet-mapping>
</web-app>
- 運行測試,遇到如下報錯。
- 檢查Artifacts;
- 再次運行,還是報錯500;
- 將activation-1.1.jar、mail-1.4.7.jar導入到Tomcat中的lib文件夾中。
- 再次運行。