- Java EE互聯(lián)網(wǎng)輕量級(jí)框架整合開(kāi)發(fā):SSM+Redis+Spring微服務(wù)(上下冊(cè))
- 楊開(kāi)振 劉家成
- 8516字
- 2021-08-13 19:40:06
第1章
Java EE基礎(chǔ)
本章目標(biāo)
1. 理解Java EE容器和組件的概念
2. 理解Web項(xiàng)目文件和目錄結(jié)構(gòu)
3. 掌握Servlet的應(yīng)用
Java EE(Java Platform Enterprise Edition)是一種企業(yè)級(jí)的Java版本,我們可以通過(guò)它構(gòu)建企業(yè)級(jí)應(yīng)用。嚴(yán)格講,它現(xiàn)在官方的名字應(yīng)該是Jakarta EE(Jakarta Enterprise Edition),不過(guò)在現(xiàn)實(shí)中,這個(gè)名字似乎沒(méi)有流行起來(lái),當(dāng)前還是普遍稱其為Java EE,所以本書(shū)依舊使用Java EE這個(gè)名稱。
1.1 Java EE概述
Java EE是SUN公司提出來(lái)的企業(yè)版Java開(kāi)發(fā)中間件,它主要用于企業(yè)級(jí)互聯(lián)網(wǎng)系統(tǒng)的搭建。Java語(yǔ)言憑借其平臺(tái)無(wú)關(guān)性、可移植性、健壯性、支持多線程和安全性等優(yōu)勢(shì)迅速成為構(gòu)建企業(yè)互聯(lián)網(wǎng)平臺(tái)的主流技術(shù)。隨著Java EE被廣泛使用,也衍生出了許多優(yōu)秀的框架,比如當(dāng)前最流行的Spring框架,還有Struts、Hibernate和MyBatis等,使得Java EE的開(kāi)發(fā)更加簡(jiǎn)單和快速。
Java EE的本質(zhì)是一種容器加組件技術(shù),這句話里包含了兩個(gè)概念——容器和組件。容器是用來(lái)管理組件行為的一個(gè)集合工具,組件的行為包括與外部環(huán)境的交互、組件的生命周期、組件之間的合作依賴關(guān)系和運(yùn)行等。組件是開(kāi)發(fā)者編寫(xiě)或者引入的第三方程序代碼,只要開(kāi)發(fā)者按照容器所定義的規(guī)范開(kāi)發(fā)組件,組件就可以在容器中運(yùn)行了。Java EE中的主要組件包括JSP、Servlet和EJB(Enterprise Java Bean)等,主要的開(kāi)發(fā)語(yǔ)言是Java。容器和組件的關(guān)系如圖1-1所示。

圖1-1 容器和組件的關(guān)系
1.1.1 Java EE容器
Java EE容器不是需要用戶開(kāi)發(fā)的內(nèi)容,它是依照J(rèn)ava EE規(guī)范提供的集合工具,只要滿足Java EE規(guī)范的組件都可以在該容器中運(yùn)行。從這句話中,可以知道組件是用戶開(kāi)發(fā)的,而容器是某個(gè)公司、組織或者個(gè)人依照J(rèn)ava EE規(guī)范提供的。Java EE容器可以分為Web容器、EJB容器和應(yīng)用容器,它們都依照J(rèn)ava EE制定的規(guī)范實(shí)現(xiàn)。下面來(lái)了解這三種容器。
? Web容器:它包含一個(gè)Servlet容器,Servlet容器可以運(yùn)行Java EE的核心組件Servlet,而實(shí)際上JSP最終會(huì)被Web容器翻譯為Servlet,再通過(guò)Servlet容器運(yùn)行。此外,Web容器還可以運(yùn)行HTML等文件。實(shí)現(xiàn)Web容器規(guī)范的服務(wù)器有多種,如Tomcat、Jetty、Wildfly(前身為JBoss)和GlassFish等。
? EJB容器:它是Java EE提出的一個(gè)企業(yè)級(jí)Java Bean的規(guī)范,能在它內(nèi)部運(yùn)行的組件是EJB,但是請(qǐng)注意,在默認(rèn)情況下,Tomcat只提供Web容器,不提供EJB容器,所以在Tomcat中無(wú)法運(yùn)行EJB,除非引入插件。Wildfly和GlassFish等服務(wù)器則提供了EJB容器,可以在它們當(dāng)中運(yùn)行EJB。但是EJB存在諸多的問(wèn)題,當(dāng)前已經(jīng)被大部分企業(yè)拋棄,基于實(shí)用原則,本書(shū)不再討論它。
? 其他Java應(yīng)用容器:解決某類問(wèn)題的一些廠商提供的容器,比如Java NIO,它是一種支持字節(jié)組件的容器。
從客觀的角度來(lái)看,在企業(yè)應(yīng)用中,Web容器是使用最廣泛的容器,本書(shū)主要討論這個(gè)容器。當(dāng)前,EJB已經(jīng)沒(méi)有太大的討論價(jià)值,其他的Java容器,雖然有比較廣泛的應(yīng)用,但是都是解決某一問(wèn)題的,針對(duì)性較強(qiáng)。后續(xù)我們會(huì)以Tomcat為例討論Web容器,其實(shí)Jetty、Wildfly和GlassFish都是類似的,因?yàn)樗鼈兌甲裱璊ava EE容器的規(guī)范。
1.1.2 Java EE組件
Java EE組件是運(yùn)行在Java EE容器中的程序片段,該程序以Java為主要的開(kāi)發(fā)語(yǔ)言,可以和Java的其他技術(shù)融合。在不同的Java EE容器中存在不同的組件,比如Web容器和EJB容器中的組件是不同的。在Web容器中,主要的組件是Servlet和JSP,由于當(dāng)前在企業(yè)中流行前端和后端分離,所以JSP技術(shù)已經(jīng)走向了被淘汰的邊緣,而Servlet是我們的研究重點(diǎn),從本質(zhì)來(lái)說(shuō),可以認(rèn)為JSP也是一種Servlet技術(shù),因?yàn)閃eb容器會(huì)先將JSP翻譯為Servlet,然后去執(zhí)行。而EJB容器中主要的組件是EJB,它們又可以分為會(huì)話Bean、實(shí)體Bean和消息驅(qū)動(dòng)Bean,只是它已經(jīng)是被淘汰的技術(shù),所以本書(shū)不再討論。其他Java應(yīng)用容器的組件,則需要根據(jù)用戶需要引入。
1.2 開(kāi)發(fā)環(huán)境簡(jiǎn)介
為了更好地進(jìn)行開(kāi)發(fā),本書(shū)在介紹Spring微服務(wù)之前會(huì)選用Eclipse作為開(kāi)發(fā)環(huán)境,在介紹Spring微服務(wù)時(shí)會(huì)選用IntelliJ IDEA作為開(kāi)發(fā)環(huán)境,它們都是現(xiàn)今最為流行的IDE。不過(guò)在此之前我們需要先介紹一下Tomcat、Maven和Java EE的Web項(xiàng)目。
1.2.1 Tomcat簡(jiǎn)介
Tomcat是Apache軟件基金會(huì)(Apache Software Foundation)的Jakarta項(xiàng)目中的一個(gè)核心部分,由Apache、Sun和其他公司及個(gè)人共同開(kāi)發(fā)。
Tomcat支持HTTP,并且支持Web容器規(guī)劃的實(shí)現(xiàn),它支持HTTP技術(shù),如HTML、CSS等,同時(shí)支持JSP和Servlet等Java EE技術(shù)。我們可以通過(guò)訪問(wèn)Apache官方網(wǎng)站,下載Tomcat,筆者的計(jì)算機(jī)系統(tǒng)是Windows 10的64位版本,因此選擇的Tomcat是64位的Tomcat 9.0.30。下載并解壓縮后,就準(zhǔn)備好了Tomcat。
為了啟動(dòng)Tomcat,我們需要配置好JDK環(huán)境變量(JAVA_HOME和PATH,這比較簡(jiǎn)單,網(wǎng)上也有很多教程,這里就不再討論了)。接著可以打開(kāi)Tomcat目錄下的bin文件夾,如果在Windows環(huán)境下可以看到startup.bat文件,則雙擊它啟動(dòng)Tomcat。啟動(dòng)完成后,在瀏覽器輸入訪問(wèn)地址http://localhost:8080,可以看到圖1-2所示的頁(yè)面。

圖1-2 Tomcat控制臺(tái)
當(dāng)看到這個(gè)頁(yè)面時(shí),說(shuō)明Tomcat已經(jīng)啟動(dòng)。這里有必要研究一下Tomcat目錄下的各個(gè)文件夾的作用,首先看它下面有哪些文件夾,如圖1-3所示。

圖1-3 Tomcat目錄下的文件夾
下面,我們通過(guò)表1-1進(jìn)行說(shuō)明。
表1-1 Tomcat目錄下的文件夾說(shuō)明

Tomcat的編碼經(jīng)常需要修改,在默認(rèn)情況下,如果使用Windows系統(tǒng),則可以在Tomcat的控制臺(tái)中看到日志存在很多亂碼,可以打開(kāi)conf文件夾下的配置文件logging.properites,然后修改其中的配置項(xiàng),如下:

這樣重啟Tomcat就不會(huì)有亂碼了。在運(yùn)行過(guò)程中,我們還可以設(shè)置Tomcat的運(yùn)行編碼,打開(kāi)conf目錄下的server.xml文件,找到<Connector>元素進(jìn)行修改,如下:

這樣可以讓Tomcat在UTF-8的環(huán)境下運(yùn)行,當(dāng)然也可以根據(jù)需要配置為GB2312等。
1.2.2 Maven
Maven是一種常見(jiàn)的構(gòu)建工具,它是可以通過(guò)一小段描述信息進(jìn)行項(xiàng)目的構(gòu)建、報(bào)告和文檔管理的工具軟件。我們首先從Apache網(wǎng)站上下載Maven壓縮包,然后解壓縮到本地,接下來(lái)設(shè)置環(huán)境變量MAVEN_HOME指向Maven目錄,最后在環(huán)境變量PATH中添加Maven的bin目錄。在默認(rèn)情況下,Maven會(huì)根據(jù)依賴從外國(guó)網(wǎng)站上下載對(duì)應(yīng)的依賴包,這個(gè)過(guò)程會(huì)十分緩慢。為了改變這種情況,可以在Maven目錄的conf文件下找到配置文件setting.xml,再找到<mirrors>元素,這是一個(gè)添加鏡像的元素,在它下面添加子元素,內(nèi)容如下:

將Maven的下載路徑指向阿里巴巴的鏡像后,Maven就會(huì)去國(guó)內(nèi)的阿里巴巴的鏡像下載對(duì)應(yīng)的依賴包,從而大大加快項(xiàng)目的構(gòu)建速度。我們可以使用Eclipse或者IntelliJ IDEA構(gòu)建Maven項(xiàng)目或者模塊,在此之前,最好將Maven的用戶設(shè)置指向修改過(guò)的setting.xml文件。這樣就可以構(gòu)建Maven項(xiàng)目了,注意,選擇骨架(maven-archetypes-webapp)構(gòu)建項(xiàng)目,會(huì)更方便我們構(gòu)建Web項(xiàng)目。
1.2.3 Web項(xiàng)目結(jié)構(gòu)
這里筆者用Eclipse構(gòu)建Maven的Web項(xiàng)目chapter1,然后觀察Web項(xiàng)目的目錄,如圖1-4所示。

圖1-4 Maven Web項(xiàng)目的目錄
這里的/src/main目錄下有三個(gè)文件夾:java、resources和webapp。其中java文件夾主要放置我們開(kāi)發(fā)的Java類;resources文件夾存放各種配置文件;webapp文件夾主要放置Web項(xiàng)目所需要的各類文件,如HTML、JSP和JavaScript文件等。/src/test目錄下的java文件夾則主要放置測(cè)試類。這里需要對(duì)/src/main目錄下的webapp文件夾進(jìn)行進(jìn)一步說(shuō)明,從圖1-4中可以看到在WEB-INF文件夾下存在一個(gè)web.xml文件,這是一個(gè)Java EE Web項(xiàng)目的配置文件,只是在新的Servlet 3.0以后的容器規(guī)范下,它不是必需的,而index.jsp是IDE為我們構(gòu)建的一個(gè)JSP樣例。
1.2.4 Web項(xiàng)目發(fā)布包
這里需要研究一下Web項(xiàng)目打包后的結(jié)構(gòu)。我們可以使用IDE將項(xiàng)目打包為war文件,然后解壓縮它,其目錄結(jié)構(gòu)如圖1-5所示。

圖1-5 Web項(xiàng)目包目錄結(jié)構(gòu)
在圖1-5中,MANIFEST.MF文件是一個(gè)說(shuō)明文檔,一般不常用。WEB-INF文件夾是核心,它下面還有classes和lib兩個(gè)文件夾,classes是Web項(xiàng)目開(kāi)發(fā)的Java文件,是經(jīng)過(guò)編譯生成的class文件,lib文件夾放置第三方依賴包,web.xml是Web項(xiàng)目的配置文件。index.jsp則是一個(gè)JSP頁(yè)面,是默認(rèn)的歡迎頁(yè)。把chapter1整個(gè)復(fù)制到Tomcat的webapps目錄下,就可以發(fā)布項(xiàng)目了。將war包直接放到Tomcat的webapps目錄下也可以發(fā)布項(xiàng)目,一般來(lái)說(shuō),這樣的方式適合在項(xiàng)目正式發(fā)布的時(shí)候使用。
1.3 Web容器的組件——Servlet
上述,我們通過(guò)Tomcat來(lái)獲取Web容器,而Web容器包含Servlet容器,那么我們肯定想在Tomcat中運(yùn)行我們開(kāi)發(fā)的程序。在Servlet容器中,Servlet是最基礎(chǔ)的組件,這里并沒(méi)有討論JSP(Java Server Page),這是因?yàn)閲?yán)格來(lái)說(shuō),也可以把JSP當(dāng)作Servlet,JSP存在的意義只是方便我們編寫(xiě)動(dòng)態(tài)頁(yè)面,使Java語(yǔ)言能和HTML相互結(jié)合。現(xiàn)在已經(jīng)很少直接使用JSP了,前后端分離已經(jīng)成為大勢(shì),基于實(shí)用原則,本書(shū)就不再介紹JSP頁(yè)面。本節(jié)的主要內(nèi)容是Servlet的應(yīng)用,它是理解Java EE的核心內(nèi)容,也是我們學(xué)習(xí)的核心內(nèi)容。按照Servlet 3.0以后的規(guī)范,組件都可以使用注解來(lái)配置,而不必使用web.xml配置,隨著Spring Boot的流行,使用注解開(kāi)發(fā)的方式成為主流,因此本章也會(huì)以注解為主,配置為輔介紹Servlet的應(yīng)用。注意,Servlet是運(yùn)行在Servlet容器中的組件,而Tomcat實(shí)現(xiàn)的Web容器包含Servlet容器。
1.3.1 Servlet入門(mén)實(shí)例
我們暫時(shí)不討論Servlet中復(fù)雜的內(nèi)容,先通過(guò)實(shí)例讓大家對(duì)Servlet有一個(gè)基本的認(rèn)識(shí),開(kāi)發(fā)Servlet一般需要繼承HttpServlet這個(gè)抽象類,實(shí)現(xiàn)一個(gè)服務(wù)方法。為此編寫(xiě)代碼清單1-1,定義MyServlet。
代碼清單1-1:自定義Servlet——MyServlet

注意標(biāo)識(shí)在類上面的@WebServlet,它標(biāo)識(shí)這個(gè)類是Servlet,將會(huì)被Servlet容器識(shí)別為Servlet,配置Servlet的名稱和攔截的路徑為/my。MyServlet則繼承了HttpServlet,且覆蓋了它定義的doGet方法。在IDE配置好Tomcat后,就可以運(yùn)行項(xiàng)目了。在瀏覽器中訪問(wèn)http://localhost:8080/chapter1/my,可以看到響應(yīng)的內(nèi)容,如圖1-6所示。

圖1-6 MyServlet的響應(yīng)內(nèi)容
圖1-6說(shuō)明MyServlet已經(jīng)裝配到Servlet容器中。這里首先需要認(rèn)識(shí)MyServlet中doGet方法的兩個(gè)參數(shù):
? 請(qǐng)求參數(shù)(HttpServletRequest):它代表請(qǐng)求,可以從請(qǐng)求中獲取對(duì)應(yīng)的參數(shù)和與之相關(guān)的信息。
? 響應(yīng)參數(shù)(HttpServletResponse):它代表如何響應(yīng)用戶的請(qǐng)求,我們可以通過(guò)它寫(xiě)入響應(yīng)信息,也可以設(shè)置應(yīng)答的類型和其他相關(guān)的信息,比如圖1-6中的Hello Servlet就是通過(guò)它寫(xiě)入的響應(yīng)信息。
關(guān)于這兩個(gè)參數(shù),在本章后續(xù)還會(huì)介紹,這里只需要有個(gè)印象。本章介紹的MyServlet比較簡(jiǎn)單,還遠(yuǎn)遠(yuǎn)沒(méi)有達(dá)到應(yīng)用的層級(jí),為此我們有必要進(jìn)行更深入的學(xué)習(xí)。
1.3.2 Servlet的生命周期
Servlet的生命周期是Servlet學(xué)習(xí)的核心內(nèi)容之一,也是初學(xué)者常常犯糊涂的地方。Servlet是Servlet容器中的一個(gè)接口,在它的基礎(chǔ)上,還會(huì)定義GenericServlet和HttpServlet兩個(gè)抽象類,它們的關(guān)系如圖1-7所示。

圖1-7 Servlet相關(guān)接口和類
這里的GenericServlet和HttpServlet就是我們關(guān)注的核心類,它們都是抽象類,定義了許多重要的方法,代碼清單1-2展示了HttpServlet的部分重要方法。
代碼清單1-2:HttpServlet的重要方法節(jié)選

這里的方法分為三大類:一是初始化方法,分別是兩個(gè)init方法,其中一個(gè)帶參數(shù),另一個(gè)不帶參數(shù);二是服務(wù)方法,這是Servlet的核心方法,包括service、doGet和doPost方法;三是銷(xiāo)毀方法,也就是destroy方法。這里的方法在注釋中都有清晰的說(shuō)明,請(qǐng)自行參考。
這里需要注意,Servlet容器是如何初始化和使用Servlet提供服務(wù)的,以及這些方法的調(diào)用順序等問(wèn)題,這些都是Servlet的核心內(nèi)容,請(qǐng)大家務(wù)必掌握好。Servlet的請(qǐng)求響應(yīng)過(guò)程如圖1-8所示。

圖1-8 Servlet的請(qǐng)求響應(yīng)過(guò)程
注意第一次請(qǐng)求和其他請(qǐng)求之間的不同。在第一次請(qǐng)求時(shí),Servlet容器會(huì)構(gòu)建Servlet實(shí)例,然后調(diào)用其初始化的init方法,接著調(diào)用服務(wù)方法(比如doGet、doPost等),從而響應(yīng)請(qǐng)求。第二次和之后的請(qǐng)求則是直接調(diào)用服務(wù)方法,不會(huì)再調(diào)用init方法初始化Servlet。可見(jiàn)在這個(gè)過(guò)程中,Servlet只有一個(gè)實(shí)例,而不是多個(gè)實(shí)例,而init方法也只會(huì)調(diào)用一次。那么,銷(xiāo)毀(destroy)方法又是如何呢?讓我們看圖1-9。

圖1-9 Servlet的銷(xiāo)毀方法
銷(xiāo)毀方法會(huì)在Servlet容器正常關(guān)閉或者在Servlet實(shí)例超時(shí)的時(shí)候被調(diào)用,這樣在Servlet容器中就會(huì)銷(xiāo)毀Servlet的實(shí)例了。
上述是Servlet的生命周期,初學(xué)者必須掌握好,有時(shí)候可以通過(guò)配置進(jìn)行改變。為了掌握好Servlet的生命周期,我們修改MyServlet的代碼進(jìn)行測(cè)試,如代碼清單1-3所示。
代碼清單1-3:測(cè)試Servlet生命周期

這里覆蓋了init、doGet和destroy方法,并且輸入了內(nèi)容,以方便我們監(jiān)測(cè),重啟項(xiàng)目,按照以下步驟測(cè)試。
在瀏覽器中訪問(wèn)http://localhost:8080/chapter1/my,可以看到日志打出:

這說(shuō)明Servlet容器開(kāi)始初始化MyServlet,并第一次執(zhí)行doGet方法。接著刷新請(qǐng)求兩次,可以看到日志打出:

可見(jiàn)init方法并未被調(diào)用,而是直接調(diào)用了doGet方法。最后正常關(guān)閉Tomcat,可以看到日志打出:

可見(jiàn)在Servlet容器正常關(guān)閉的時(shí)候,才會(huì)調(diào)用destroy方法銷(xiāo)毀Servlet實(shí)例。而整個(gè)過(guò)程是按照?qǐng)D1-8和圖1-9執(zhí)行的。此外我們可以使用代碼清單1-2中帶有參數(shù)的init方法傳遞參數(shù),我們需要學(xué)習(xí)Servlet的參數(shù)配置等內(nèi)容,如代碼清單1-4所示。
代碼清單1-4:配置Servlet


請(qǐng)注意代碼中@WebServlet配置項(xiàng)的使用:其中,asyncSupported的默認(rèn)值為false,代表不使用異步線程運(yùn)行Servlet,這里配置為true,代表支持多線程異步;配置項(xiàng)loadOnStartup設(shè)置為1,如果這里設(shè)置為大于0,那么Servlet實(shí)例會(huì)在啟動(dòng)項(xiàng)目時(shí)就初始化到Servlet容器中,而不是在第一次請(qǐng)求時(shí)才初始化;配置項(xiàng)initParams設(shè)置Servlet的參數(shù)。這里編寫(xiě)了帶有參數(shù)的init方法,這個(gè)方法中的參數(shù)類型為ServletConfig,它代表Servlet的配置,在方法中,還獲取了這些參數(shù)。重啟Tomcat服務(wù)器,無(wú)須對(duì)MyServlet進(jìn)行請(qǐng)求,就可以看到init方法的調(diào)用和參數(shù)的打印了。
1.3.3 HttpServletRequest的應(yīng)用
應(yīng)該說(shuō),HttpServletRequest和HttpServletResponse這兩個(gè)類在Servlet的開(kāi)發(fā)中是最常用的。對(duì)于HttpServletRequest類,我們一般稱之為請(qǐng)求對(duì)象,為了學(xué)習(xí)它的使用方法,這里構(gòu)建一個(gè)RequesServlet,其內(nèi)容如代碼清單1-5所示。
代碼清單1-5:HttpServletRequest的使用


這里的代碼分為7段:第①段是獲取請(qǐng)求基本信息;第②段是獲取請(qǐng)求頭;第③段是獲取請(qǐng)求參數(shù);第④段是獲取請(qǐng)求上下文,并設(shè)置上下文屬性;第⑤段是設(shè)置請(qǐng)求屬性;第⑥段是設(shè)置Session屬性;第⑦段是跳轉(zhuǎn)到JSP頁(yè)面。每一段的注釋都有清晰的說(shuō)明,請(qǐng)自行參考。這段代碼涉及JSP頁(yè)面中三個(gè)重要內(nèi)置對(duì)象的使用:request、session和application,在使用它們的時(shí)候需要特別注意的是作用域。request請(qǐng)求對(duì)象的作用域是當(dāng)次用戶請(qǐng)求有效;session的作用域是瀏覽器和服務(wù)器會(huì)話期間有效;而application的作用域是Web項(xiàng)目在Servlet容器中存活期間有效。其中,session是服務(wù)器在和瀏覽器通信期間為了保存會(huì)話數(shù)據(jù)而開(kāi)辟的內(nèi)存空間,它可以記錄和瀏覽器之間的會(huì)話數(shù)據(jù),記錄和瀏覽器會(huì)通過(guò)一個(gè)sessionId進(jìn)行關(guān)聯(lián),我們還可以像代碼中那樣通過(guò)setMaxInactiveInterval方法設(shè)置超時(shí)時(shí)間,比如我們常見(jiàn)的購(gòu)物車(chē)往往就保存在session中。應(yīng)該說(shuō)JSP內(nèi)還有一個(gè)內(nèi)置對(duì)象page,只是這個(gè)對(duì)象只對(duì)當(dāng)前頁(yè)面有效,使用率很低,所以不再討論它的功能。在代碼的第⑦段,還會(huì)跳轉(zhuǎn)到一個(gè)JSP頁(yè)面,為此我們需要提供這個(gè)頁(yè)面,并且將其放在webapp目錄下,其內(nèi)容如代碼清單1-6所示。
代碼清單1-6:RequesServlet請(qǐng)求跳轉(zhuǎn)頁(yè)面(request-result.jsp)

這里使用了JSP的三個(gè)內(nèi)置對(duì)象request、session和application來(lái)獲取屬性值,需要注意它們的作用域。此時(shí)啟動(dòng)項(xiàng)目,在瀏覽器訪問(wèn)http://localhost:8080/chapter1/request/url?param1=value1¶m2=value2,就可以看到結(jié)果了,如圖1-10所示。

圖1-10 測(cè)試Servlet請(qǐng)求流程和JSP內(nèi)置對(duì)象
注意到瀏覽器中的地址并未顯示JSP,但是會(huì)展示JSP的內(nèi)容,同時(shí)各個(gè)JSP內(nèi)置對(duì)象也都可以正常工作了。此外還可以觀察Tomcat日志平臺(tái),它會(huì)打印出:

可見(jiàn)除了頁(yè)面,其他的信息也獲取成功了。
1.3.4 HttpServletResponse的應(yīng)用
HttpServletResponse也被我們稱為響應(yīng)對(duì)象,主要用于響應(yīng)請(qǐng)求。相對(duì)來(lái)說(shuō),它沒(méi)有HttpServletRequest那么復(fù)雜,它主要的作用是設(shè)置響應(yīng)頭和響應(yīng)體。在前后端分離為主要趨勢(shì)的今天,頁(yè)面主要通過(guò)AJAX(Asynchronous Javascript And XML)獲取數(shù)據(jù),而手機(jī)應(yīng)用也是接近的,所以后端更多的響應(yīng)的類型會(huì)以JSON數(shù)據(jù)集為主,這時(shí)需要通過(guò)HttpServletResponse設(shè)置響應(yīng)類型和編碼。下面舉例說(shuō)明,如代碼清單1-7所示。
代碼清單1-7:HttpServletResponse的使用


這里首先獲取兩個(gè)請(qǐng)求參數(shù),然后將其放到一個(gè)Map結(jié)構(gòu)中,接著將其轉(zhuǎn)換為JSON。之后通過(guò)HttpServletResponse對(duì)象設(shè)置響應(yīng)類型、編碼、響應(yīng)頭等內(nèi)容,最后獲取PrintWriter對(duì)象,輸出JSON信息。啟動(dòng)Tomcat,在瀏覽器中訪問(wèn)http://localhost:8080/chapter1/response/my?param1=value1¶m2=value2,就可以看到圖1-11所示的內(nèi)容了。

圖1-11 使用JSON數(shù)據(jù)集作為Servlet的請(qǐng)求響應(yīng)
可見(jiàn)請(qǐng)求體也以JSON數(shù)據(jù)集的形式展示了出來(lái)。我們可以再查看響應(yīng)頭,筆者使用的是Chrome瀏覽器,點(diǎn)擊F12鍵,就可以看到控制臺(tái)了,圖1-12就是筆者監(jiān)控的結(jié)果。

圖1-12 監(jiān)控請(qǐng)求頭
從圖1-12中可以看到,設(shè)置的響應(yīng)碼和響應(yīng)頭都成功了。當(dāng)然有時(shí)候,我們也需要跳轉(zhuǎn)到JSP,使用HttpServletResponse對(duì)象也是可以的,下面通過(guò)代碼清單1-8進(jìn)行舉例。
代碼清單1-8:使用HttpServletResponse進(jìn)行跳轉(zhuǎn)

注意到代碼中設(shè)置了請(qǐng)求屬性、會(huì)話(Session)屬性和ServletContext屬性,之后通過(guò)sendRedirect方法跳轉(zhuǎn),這里的跳轉(zhuǎn)需要給出相對(duì)全路徑。由于跳轉(zhuǎn)到response-result.jsp,所以下面需要編寫(xiě)它,并放在webapp目錄下,其內(nèi)容如代碼清單1-9所示。
代碼清單1-9:Servlet響應(yīng)JSP頁(yè)面(response-result.jsp)

啟動(dòng)項(xiàng)目,在瀏覽器中訪問(wèn)http://localhost:8080/chapter1/resp/jsp/my?param1= value1¶m2=value2¶ms3=value3,可以看到圖1-13所示的內(nèi)容。

圖1-13 使用JSP作為Servlet的響應(yīng)頁(yè)面
這里需要注意兩點(diǎn):第一,我們請(qǐng)求的路徑和瀏覽器中顯示的最終路徑并不相同,瀏覽器顯示的是一個(gè)JSP路徑,這點(diǎn)和HttpServletRequest的跳轉(zhuǎn)不一樣;第二,更重要的是可以看到param1這個(gè)參數(shù)為null,因?yàn)镠ttpServletResponse的跳轉(zhuǎn)并不傳遞請(qǐng)求上下文,所以不能讀取Servlet中HttpServletRequest設(shè)置的屬性,只能查看Session的屬性和ServletContext的屬性。
1.3.5 過(guò)濾器的使用
過(guò)濾器(Filter)的作用是在Servlet執(zhí)行的過(guò)程前后執(zhí)行一些邏輯,比如可以控制對(duì)Servlet的訪問(wèn)權(quán)限控制。在Servlet規(guī)范中,需要使用注解@WebFilter標(biāo)識(shí)過(guò)濾器,同時(shí)需要實(shí)現(xiàn)Filter(javax.servlet.Filter)接口,為此我們先來(lái)了解一下Filter接口的定義,如代碼清單1-10所示。
代碼清單1-10:Filter接口

和Servlet類似,F(xiàn)ilter的init方法是一個(gè)初始化方法,它會(huì)在項(xiàng)目啟動(dòng)時(shí)先于Servlet的init方法執(zhí)行,對(duì)過(guò)濾器進(jìn)行初始化,在接口中有了默認(rèn)的空實(shí)現(xiàn);doFilter是過(guò)濾器的邏輯方法,它存在一個(gè)filterChain的參數(shù),通過(guò)它的doFilter方法可以放行請(qǐng)求;而destroy方法是銷(xiāo)毀過(guò)濾器時(shí)執(zhí)行的方法,它在過(guò)濾器超時(shí)或者Servlet容器正常關(guān)閉時(shí)會(huì)調(diào)用,但是它會(huì)在Servlet的destroy方法之后執(zhí)行,和init方法一樣默認(rèn)是空實(shí)現(xiàn)。
下面我們通過(guò)代碼清單1-11來(lái)學(xué)習(xí)過(guò)濾器的使用。
代碼清單1-11:過(guò)濾器實(shí)例


先看過(guò)濾器上標(biāo)注的@WebFilter,它標(biāo)識(shí)這個(gè)類是一個(gè)過(guò)濾器,同時(shí),配置項(xiàng)filterName是過(guò)濾器名稱,urlPatterns是限制攔截的路徑,而servletNames是攔截的Servlet名稱,initParams則是過(guò)濾器的配置參數(shù)。init方法從配置中讀取配置參數(shù)。Filter的destroy方法在Servlet容器關(guān)閉時(shí),會(huì)在Servlet的destroy方法之后執(zhí)行。doFilter是過(guò)濾器的核心邏輯,在代碼中的主要作用是獲取參數(shù)param1,如果為空,則輸出攔截請(qǐng)求的信息,然后直接返回不再執(zhí)行下面的邏輯,否則就使用:

這行代碼表示放行請(qǐng)求到具體的Servlet或者JSP上去。
啟動(dòng)項(xiàng)目,然后在瀏覽器訪問(wèn)http://localhost:8080/chapter1/request/url,可以看到圖1-14所示的內(nèi)容。

圖1-14 測(cè)試過(guò)濾器
可見(jiàn),沒(méi)有參數(shù)param1,請(qǐng)求被過(guò)濾器攔截了。訪問(wèn)http://localhost:8080/chapter1/request/url?param1=value1¶m2=value2,可以看到圖1-15所示的內(nèi)容。

圖1-15 測(cè)試過(guò)濾器放行請(qǐng)求
此時(shí)因?yàn)榇嬖趨?shù)param1,所以過(guò)濾器放行了請(qǐng)求。最后正常關(guān)閉Tomcat,可以看到這樣的日志順序。


日志的注釋是筆者自己加的,為的是讓大家更好地理解過(guò)濾器和Servlet的執(zhí)行順序。從日志可以看到過(guò)濾器的init方法會(huì)先于Servlet的init方法執(zhí)行;過(guò)濾器的邏輯也會(huì)先于Servlet的邏輯執(zhí)行;但是過(guò)濾器的destroy方法會(huì)在Servlet的destroy方法之后執(zhí)行。
1.3.6 監(jiān)聽(tīng)
在Servlet的規(guī)范中存在多種監(jiān)聽(tīng)(Listener),比如監(jiān)聽(tīng)Servlet上下文屬性的ServletContextAttributeListener、監(jiān)聽(tīng)請(qǐng)求的ServletRequestListener和監(jiān)聽(tīng)Session屬性操作的HttpSessionAttributeListener等,最常用的當(dāng)屬ServletContextListener,所以本節(jié)就用它舉例說(shuō)明監(jiān)聽(tīng)的使用。ServletContextListener是Web項(xiàng)目在Servlet容器中的監(jiān)聽(tīng)器,允許我們?cè)赪eb項(xiàng)目啟動(dòng)之前和之后的上下文織入自己的邏輯。先來(lái)看ServletContextListener接口定義,如代碼清單1-12所示。
代碼清單1-12:ServletContextListener接口定義

contextInitialized方法會(huì)在Servlet上下文初始化后執(zhí)行,而contextDestroyed會(huì)在Servlet上下文銷(xiāo)毀之后執(zhí)行,我們可以在Servlet上下文初始化之前構(gòu)建一些資源,或者在Servlet上下文銷(xiāo)毀之后釋放一些資源。
下面,我們開(kāi)發(fā)一個(gè)監(jiān)聽(tīng)器,來(lái)展示它的使用方法,如代碼清單1-13所示。
代碼清單1-13:監(jiān)聽(tīng)器實(shí)例

重啟項(xiàng)目,可以看到contextInitialized方法在過(guò)濾器的init方法之前執(zhí)行。如果正常關(guān)閉Servlet容器,則contextDestroyed方法在過(guò)濾器的destroy方法之后執(zhí)行。
1.3.7 Servlet容器初始化器
在我們開(kāi)發(fā)的過(guò)程中,往往會(huì)引入第三方包,而當(dāng)中有些類可能是我們需要使用的。此時(shí)Servlet容器初始化器(ServletContainerInitializer)允許我們將一些第三方的類加載到Servlet容器中,具體需要加載哪些類型,可以通過(guò)注解@HandlesTypes來(lái)指定。
我們假設(shè)存在第三方的一個(gè)類——OuterServiceImpl,它是OuterService接口的實(shí)現(xiàn)類,如代碼清單1-14所示。
代碼清單1-14:第三方類OuterServiceImpl

此時(shí)我們可以通過(guò)實(shí)現(xiàn)抽象類ServletContainerInitializer的onStartup方法,并且通過(guò)@HandlesTypes指定引入的類型,這樣就可以使用第三方的類了,如代碼清單1-15所示。
代碼清單1-15:通過(guò)Serlvet容器初始化器加載第三方類

這個(gè)Servlet初始化器上標(biāo)注了@HandlesTypes,并且指定了加載類型為OuterService,同時(shí)初始化器實(shí)現(xiàn)了ServletContainerInitializer的onStartup方法,該方法會(huì)在Servlet上下文構(gòu)建時(shí)執(zhí)行。onStartup方法有兩個(gè)參數(shù):一個(gè)是set,它是一個(gè)@HandlesTypes所指定類型的集合,該集合包含所指定類型的實(shí)現(xiàn)類或者子類;另一個(gè)是servletContext,它是Servlet的上下文。方法的邏輯在代碼注釋中也寫(xiě)清楚了,請(qǐng)自行參考。
有了這個(gè)初始化器,還需要在項(xiàng)目的resources目錄下構(gòu)建子目錄/META-INF/servics,然后在其下面構(gòu)建文件javax.servlet.ServletContainerInitializer,以文本形式打開(kāi)它,編寫(xiě)其內(nèi)容如下:

顯然這就指向了我們自己開(kāi)發(fā)的Servlet初始化器。
啟動(dòng)項(xiàng)目,可以看到日志在所有監(jiān)聽(tīng)器、過(guò)濾器和Servlet初始化之前打印出:

可見(jiàn)我們開(kāi)發(fā)的Servlet容器初始化器(WebContainerInitializer)已經(jīng)正常工作了。
1.3.8 使用Cookie
Cookie是服務(wù)器寫(xiě)入用戶本地瀏覽器的數(shù)據(jù),因?yàn)橛脩艨梢越没蛘邉h除Cookie,所以使用Cookie并非十分可靠。下面我們通過(guò)CookieServlet來(lái)學(xué)習(xí)Cookie的使用,如代碼清單1-16所示。
代碼清單1-16:使用Cookie


這段代碼中的writeCookie方法是將Cookie寫(xiě)入瀏覽器,而showCookie方法是顯示Cookie。具體執(zhí)行哪個(gè)方法是通過(guò)參數(shù)action控制的,當(dāng)其為write時(shí),就會(huì)執(zhí)行writeCookie方法,當(dāng)其為show時(shí),就會(huì)執(zhí)行showCookie方法。啟動(dòng)項(xiàng)目后,在瀏覽器中先訪問(wèn)http://localhost:8080/chapter1/cookie/test?action=write,再訪問(wèn)http://localhost:8080/chapter1/cookie/test?action=show,就可以看到圖1-16所示的內(nèi)容了。

圖1-16 Cookie的使用
從圖1-16可見(jiàn),Cookie寫(xiě)入和顯示都已經(jīng)成功了。
1.3.9 提交表單
上述談到Servlet的doGet方法,而實(shí)際上,還可以使用doPost、doPut等方法,其中最常用的是doPost方法。一般來(lái)說(shuō),GET請(qǐng)求從服務(wù)端獲取信息,它的安全性較差,且對(duì)提交數(shù)據(jù)的類型和長(zhǎng)度有所限制,同時(shí),參數(shù)在URL是可見(jiàn)的,它的優(yōu)勢(shì)在于速度較快。POST請(qǐng)求是瀏覽器向服務(wù)端提交數(shù)據(jù),數(shù)據(jù)類型和長(zhǎng)度都不受限,同時(shí)參數(shù)可以放在表單中,不在URL中顯示出來(lái),安全度也較高,但是性能相對(duì)低一些。由于表單涉及商業(yè)數(shù)據(jù),比較重要,因此提交表單的操作一般會(huì)使用POST請(qǐng)求。
下面我們開(kāi)發(fā)一張JSP,通過(guò)它來(lái)提交表單,如代碼清單1-17所示。
代碼清單1-17:JSP表單

這里注意<form>的屬性配置,因?yàn)閍ction配置的是提交到的地址,而method默認(rèn)為get,所以這里修改為post,意味著將表單以POST請(qǐng)求的方式提交到服務(wù)端。接著開(kāi)發(fā)后端處理表單的Servlet,如代碼清單1-17所示。
代碼清單1-18:開(kāi)發(fā)Servlet接收POST請(qǐng)求

這里的PostServlet與之前開(kāi)發(fā)的Servlet不同的是,不再覆蓋doGet方法,而是覆蓋doPost方法,這就意味著它只能接收POST類型的請(qǐng)求。這里將請(qǐng)求匹配地址設(shè)置為/post,也就是會(huì)接收表單的提交。
啟動(dòng)項(xiàng)目后,訪問(wèn)http://localhost:8080/chapter1/form.jsp,可以看到圖1-17的測(cè)試POST請(qǐng)求內(nèi)容。

圖1-17 測(cè)試POST請(qǐng)求
在圖1-17中,填寫(xiě)了表單的內(nèi)容,點(diǎn)擊“提交”按鈕,可以看到圖1-18所示的內(nèi)容。

圖1-18 提交表單后的結(jié)果
從圖1-18中可以看到,我們已經(jīng)成功地通過(guò)POST請(qǐng)求,將表單提交到Servlet中處理。
1.3.10 使用web.xml
除了可以使用Servlet 3.0規(guī)范給出的各種注解,我們還可以使用web.xml配置Servlet、監(jiān)聽(tīng)器和過(guò)濾器等內(nèi)容。在此之前,我們把開(kāi)發(fā)過(guò)的WebContextListener上標(biāo)注的@WebListener、ServletFilter上標(biāo)注的@WebFilter和MyServlet上標(biāo)注的@WebServlet刪除或注釋掉,這樣Servlet容器就不能識(shí)別它們了。接著我們可以通過(guò)web.xml配置它們,如代碼清單1-19所示。
代碼清單1-19:使用web.xml配置Web項(xiàng)目


這段代碼有點(diǎn)長(zhǎng),不過(guò)結(jié)果還算清晰,具體的含義已經(jīng)在注釋中進(jìn)行了說(shuō)明,請(qǐng)大家自行參考。
- Google Apps Script for Beginners
- Visual Basic .NET程序設(shè)計(jì)(第3版)
- 數(shù)字媒體應(yīng)用教程
- 軟件架構(gòu)設(shè)計(jì):大型網(wǎng)站技術(shù)架構(gòu)與業(yè)務(wù)架構(gòu)融合之道
- Azure IoT Development Cookbook
- Java編程指南:基礎(chǔ)知識(shí)、類庫(kù)應(yīng)用及案例設(shè)計(jì)
- Bulma必知必會(huì)
- INSTANT MinGW Starter
- PLC編程與調(diào)試技術(shù)(松下系列)
- Scientific Computing with Scala
- ExtJS高級(jí)程序設(shè)計(jì)
- Instant PHP Web Scraping
- 零基礎(chǔ)輕松學(xué)C++:青少年趣味編程(全彩版)
- OpenCV Android開(kāi)發(fā)實(shí)戰(zhàn)
- 實(shí)驗(yàn)編程:PsychoPy從入門(mén)到精通