java 服務(wù)器接口快速開發(fā)之servlet詳細(xì)教程
Servlet簡介
servlet是Server Applet的簡稱,翻譯過來就是服務(wù)程序.好吧,這么說你可能還是不太懂,簡單的講,這個(gè)servlet是運(yùn)行在服務(wù)器上的一個(gè)小程序,用來處理服務(wù)器請(qǐng)求的.進(jìn)一步講,我們知道,一般的網(wǎng)頁程序,是由我們通過瀏覽器訪問來實(shí)現(xiàn)的,在這個(gè)過程中,我們的瀏覽器發(fā)送訪問請(qǐng)求,服務(wù)器接收請(qǐng)求,并對(duì)瀏覽器的請(qǐng)求作出相應(yīng)的處理.這就是我們熟悉的B/S模型(瀏覽器-服務(wù)器模型).而servlet就是對(duì)請(qǐng)求作出處理的組件,運(yùn)行于支持Java的應(yīng)用服務(wù)器中.
Servlet的作用
在servlet剛剛出現(xiàn)的那個(gè)年代,servlet的作用十分復(fù)雜,既承擔(dān)著處理數(shù)據(jù)的作用,又承擔(dān)著展示頁面的作用,美工人員想要參與開發(fā),基本上是不太現(xiàn)實(shí)的,畢竟美工不可能再去花時(shí)間將頁面做好.隨著時(shí)間的推移,出現(xiàn)了MVC思想,也就是模型-界面-控制器思想,極大的簡便了開發(fā),也明確了servlet的作用.
根據(jù)上面這張圖,我們就能知道,servlet在其中承擔(dān)的作用是controller,控制器,起到對(duì)數(shù)據(jù)進(jìn)行操作的作用.
順便補(bǔ)充說明一下,最經(jīng)典的MVC模型就是JSP+JavaBean+Servlet開發(fā)的模式.
#Servlet處理的信息是什么?
我一直再講,servlet是對(duì)數(shù)據(jù)進(jìn)行處理的一個(gè)控制器,那么,你一定很好奇,servlet究竟處理的是什么數(shù)據(jù)?
這里你要知道,我之前在其他文章也講過,我們的web應(yīng)用完全是基于http協(xié)議的.http協(xié)議有請(qǐng)求報(bào)文(request)和響應(yīng)報(bào)文(request),請(qǐng)求報(bào)文就是瀏覽器向服務(wù)器發(fā)送的數(shù)據(jù)形成的數(shù)據(jù)對(duì)象,同理,相應(yīng)報(bào)文就是服務(wù)器向?yàn)g覽器發(fā)送的數(shù)據(jù)形成的信息,而http協(xié)議有兩個(gè)重要的方法,一個(gè)是POST,一個(gè)是GET,這兩個(gè)方法就是向?yàn)g覽器發(fā)送請(qǐng)求的方法.
你應(yīng)該知道這兩個(gè)方法在什么地方使用,沒錯(cuò),就是在前端的表單中使用,比如你登錄CSDN的時(shí)候,提交的用戶名和密碼,就是被http協(xié)議封裝成請(qǐng)求報(bào)文的形式發(fā)送到服務(wù)器的,這樣,servlet就能夠讀取請(qǐng)求報(bào)文的內(nèi)容,并對(duì)報(bào)文進(jìn)行處理了.
Servlet的開發(fā)流程
狹義上講,servlet是servlet是java語言實(shí)現(xiàn)的一個(gè)類,所以我們就要根據(jù)這個(gè)類進(jìn)行相應(yīng)的擴(kuò)展開發(fā).
開發(fā)流程如下:
- 編寫一個(gè)java類,繼承HttpServlet類
- 重寫HttpServlet類的doGet方法和doPost方法
- 配置web.xml文件,或者使用注解對(duì)servlet進(jìn)行配置
開發(fā)流程就是這個(gè)樣子,我們先來看一下最后一個(gè)步驟.
#對(duì)servlet進(jìn)行配置
你一定在想,如果我寫了好幾個(gè)servlet,但是前端發(fā)送請(qǐng)求的時(shí)候,究竟會(huì)把請(qǐng)求發(fā)送給哪個(gè)servlet呢?我在輸入某個(gè)地址的時(shí)候,究竟是由哪個(gè)servlet進(jìn)行響應(yīng)的呢?
這時(shí)候servlet的配置就顯得尤為重要.對(duì)servlet的配置指定了對(duì)前端請(qǐng)求處理究竟是通過哪個(gè)servlet.
配置servlet一共有兩種方式,一種是使用web.xml文件配置,另外一種就是使用注解配置,下面我們來詳解介紹這兩種配置方式
- 使用web.xml文件配置
注意,servlet的配置內(nèi)容要寫在webapp內(nèi)部
<webapp> <!-- 配置一個(gè)servlet --> <!-- servlet的配置 --> <servlet> <!-- servlet的內(nèi)部名稱,自定義。盡量有意義 --> <servlet-name>MyServlet</servlet-name> <!-- servlet的類全名: 包名+簡單類名 --> <servlet-class>cn.roobtyan.servlet.FirstServlet</servlet-class> </servlet> <!-- servlet的映射配置 --> <servlet-mapping> <!-- servlet的內(nèi)部名稱,一定要和上面的內(nèi)部名稱保持一致?。?--> <servlet-name>MyServlet</servlet-name> <!-- servlet的映射路徑(訪問servlet的名稱) --> <url-pattern>/first</url-pattern> </servlet-mapping> </webapp>
當(dāng)你訪問/first的時(shí)候,服務(wù)器自然就會(huì)把請(qǐng)求交給MyServlet進(jìn)行處理了.
- 使用@注解配置
新版本的servlet支持使用注解進(jìn)行配置,這樣極大的簡便了開發(fā).
注解配置如下:
@WebServlet(name = "LoginServlet",urlPatterns = {"/login"}) public class LoginServlet extends HttpServlet { }
然后,你在訪問/login的時(shí)候,服務(wù)器同樣就會(huì)將處理交由LoginServlet進(jìn)行處理了.
這樣是不是非常爽?(-)
實(shí)際上,注解的作用和web.xml的作用是相同的,一般都是推薦使用注解的方式進(jìn)行開發(fā),這樣十分簡便,可讀性也變的更加強(qiáng)大.
你一定會(huì)好奇,如下:
<url-pattern>/first<url-pattern>
和
@WebServlet(name = "LoginServlet",urlPatterns = {"/login"})
這里面的url可不可以不這么精確的配置,用一種模糊匹配的方式,就是我訪問某種規(guī)則的路徑的時(shí)候,統(tǒng)一調(diào)用一個(gè)servlet,這當(dāng)然是可以的了.
這就涉及到映射路徑的問題了
Servlet映射路徑的配置問題
- 精確匹配
精確匹配就是我們上面用的那種方式,使用固定的url來訪問這個(gè)servlet,這種沒什么需要說明的模糊匹配
- 模糊匹配
就是比較有意思的了,通過模糊匹配,我們可以讓好多路徑映射到同一個(gè)servlet,模糊匹配一般有如下格式
/* 任意路徑都映射到這個(gè)servlet /roobtyan/* /roobtyan下的任意路徑映射到該servlet *.(*.do *.action *.html) 是這樣的:/任意路徑.do/action/html
這里面有兩點(diǎn)是需要注意的,一是url要么以/開頭,要么以*開頭,其他的都是非法的
Servlet的生命周期
一般來講,servlet只會(huì)初始化一次,也就是整個(gè)過程中只存在一個(gè)servlet對(duì)象,即便是有多次訪問,依然只有一個(gè)對(duì)象,這個(gè)對(duì)象是可以復(fù)用的.我想你一定會(huì)好奇這個(gè)servlet究竟是在什么時(shí)候創(chuàng)建的,所以就來講一下servlet的生命周期,所謂的生命周期我們?cè)趈ava基礎(chǔ)知識(shí)中一定也了解過,就是servlet類究竟在什么時(shí)候創(chuàng)建,調(diào)用了何種方法,最后在什么時(shí)候被銷毀.我們之前學(xué)過的對(duì)象都是自己手動(dòng)創(chuàng)建,最后由JVM來銷毀的,而servlet的整個(gè)生命周期,都是由tomacat,也就是服務(wù)器控制的
我們以一張圖來了解一下:
可以看到,servlet共有三個(gè)關(guān)鍵的方法,分別是init(),service(),destroy().
- init方法只會(huì)調(diào)用一次,只是在創(chuàng)建servlet實(shí)例的時(shí)候才會(huì)創(chuàng)建
- service方法,是進(jìn)行數(shù)據(jù)處理的,只要接受了一次請(qǐng)求,就會(huì)被調(diào)用一次
- destroy方法,銷毀servlet對(duì)象的時(shí)候調(diào)用。停止服務(wù)器或者重新部署web應(yīng)用時(shí)銷毀servlet對(duì)象,同樣也是調(diào)用一次
#一個(gè)簡單的例子
好了,講了這么多,你一定是躍躍欲試了,我們就用一個(gè)登錄控制的例子來簡單的看一下servlet開發(fā)的步驟.
- 使用ide新建一個(gè)web項(xiàng)目
- 創(chuàng)建一個(gè)前端登錄表單login.jsp
- 創(chuàng)建一個(gè)登錄成功頁面
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>roobtyan登錄控制系統(tǒng)</title> </head> <body> <h1 align="center" style="color: red;">歡迎您登錄系統(tǒng)后臺(tái)</h1><hr/> <%--the form start--%> <div align="center"> <form method="post" action="/login"> Username:<input type="text" name="username"/><br/><br/> Password:<input type="password" name="password"/><br/><br/> <input type="submit" value="登錄"/> </form> </div> </body> </html>
同樣使用jsp頁面
welcome.jsp
- 創(chuàng)建LoginServlet.java
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>歡迎頁面</title> </head> <body> <h1 align="center" style="color: red">Welcome:</h1> <% out.println(session.getAttribute("user")); %> <hr/> <span style="align:center; color:yellow"> Time:<% out.println(new Date()); %> </span> </body> </html>
- 配置servlet
public class LoginServlet extends HttpServlet { public void service(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException { //設(shè)置字符編碼 request.setCharacterEncoding("utf8"); //從request對(duì)象中獲取username,password String username = request.getParameter("username"); String password = request.getParameter("password"); //判斷是否為管理員 if("administrator".equals(username)&&"123456".equals(password)){ //登錄成功,設(shè)置session HttpSession session = request.getSession(true); session.setAttribute("user", "管理員,歡迎你!"); }else { session.setAttribute("user","登錄信息錯(cuò)誤,請(qǐng)檢查用戶名或密碼"); } //將頁面轉(zhuǎn)發(fā)到歡迎頁面 requestDispatcher = request.getRequestDispatcher("/welcome.jsp"); requestDispatcher.forward(request,response); } }
這里對(duì)于servlet的配置,我們采取web.xml的方式,主要是因?yàn)檫@種方法相對(duì)麻煩,為了讓你有著更好的理解,就這樣做了.
<servlet> <servlet-name>LoginServlet</servlet-name> <servlet-class>com.roobtyan.cn.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>LoginServlet</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping>
這樣,我們的第一個(gè)servlet程序就做完了.我想如果你存在疑問的話,應(yīng)該是在jsp技術(shù)上,如果是這樣,那么請(qǐng)參照相關(guān)文章。
還有一個(gè)地方你可能存在疑惑,為什么使用request.getParameter方法可以獲取到提交的表單中的內(nèi)容呢?這個(gè)很好解釋,因?yàn)榍岸耸褂胮ost或者get方法將表單信息提交到servlet的時(shí)候,將表單信息封裝成了request對(duì)象,這樣就可以獲取到了.值得注意的是,表單中的name字段,就是我們獲取值的根據(jù).
最后一個(gè)可能存在疑問位置就是這里
//將頁面轉(zhuǎn)發(fā)到歡迎頁面 RequestDispatcher requestDispatcher = request.getRequestDispatcher("/welcome.jsp"); requestDispatcher.forward(request,response);
這段代碼我在最后會(huì)解釋,其實(shí)也挺簡單的上面的你都注意到了,那你非常厲害了.不過,有一個(gè)地方你可能注意不到,那就是這段代碼:
request.setCharacterEncoding("utf8");
設(shè)置字符編碼的這部分,如果不設(shè)置,會(huì)造成亂碼,這還是需要注意的.關(guān)于POST和GET亂碼的解決,請(qǐng)看我的文章:POST和GET亂碼的解決
#Servlet自動(dòng)加載
前面我們說了,servlet只有在第一次被訪問的時(shí)候才會(huì)加載,這肯定會(huì)造成第一個(gè)訪問的人訪問時(shí)間較長,因?yàn)樗枰却齭ervlet完成加載.那么,有沒有什么方法能夠使得servlet自動(dòng)加載呢,就是在啟動(dòng)服務(wù)器的時(shí)候就將servlet加載起來呢?答案是有的,同樣可以在web.xml中進(jìn)行配置
<servlet> <servlet-name>LoginServlet</servlet-name> <servlet-class>cn.roobtyan.LoginServlet</servlet-class> <!-- 讓servlet對(duì)象自動(dòng)加載 --> <load-on-startup>1</load-on-startup> </servlet>
就是使用的<login-on-startup></login-on-startup>
配置的,注意: 其中的整數(shù)值越大,創(chuàng)建優(yōu)先級(jí)越低!
Servlet多線程問題
前面我們講了,一個(gè)servlet在服務(wù)器中只會(huì)存在一個(gè)實(shí)例,不論是有多少訪問,都掉用的同一個(gè)實(shí)例,也就是單實(shí)例多線程的.這就存在著一定的線程安全問題,比如說,我在servlet中定義了一個(gè)全局變量,那么這個(gè)變量的值很有可能不是我期待的值,所以,在servlet中要盡量避免使用全局變量.
Servlet中重要的對(duì)象
在servlet中共有四個(gè)重要的對(duì)象:
HttpServletRequest 請(qǐng)求對(duì)象:獲取請(qǐng)求信息 HttpServletResponse 響應(yīng)對(duì)象: 設(shè)置響應(yīng)對(duì)象 ServletConfig對(duì)象 servlet配置對(duì)象 ServletContext對(duì)象 servlet的上下文對(duì)象
前兩個(gè)我們介紹的不少,這兩個(gè)的具體內(nèi)容我回單獨(dú)拿出來一章介紹,和HTTP協(xié)議一塊介紹,我覺得這樣看起來更能接受一些.那么我們現(xiàn)在就介紹后面兩個(gè)
ServletConfig對(duì)象
- 創(chuàng)建時(shí)間:在創(chuàng)建完servlet對(duì)象的時(shí)候,接著創(chuàng)建servletConfig對(duì)象.
- 如何得到對(duì)象:直接使用
ServletConfig config = this.getServletConfig();
- 簡單使用
這是web.xml的配置文件
<servlet> <servlet-name>LoginServlet</servlet-name> <servlet-class>cn.roobtyan.LoginServlet</servlet-class> <!-- 初始參數(shù): 這些參數(shù)會(huì)在加載web應(yīng)用的時(shí)候,封裝到ServletConfig對(duì)象中 --> <init-param> <param-name>location</param-name> <param-value>doom</param-value> </init-param> </servlet>
配置文件中的init-param
就是配置信息
這個(gè)ServletConfig對(duì)象共有如下的方法
java.lang.String getInitParameter(java.lang.String name) 根據(jù)參數(shù)名獲取參數(shù)值 java.util.Enumeration getInitParameterNames() 獲取所有參數(shù) ServletContext getServletContext() 得到servlet上下文對(duì)象 java.lang.String getServletName() 得到servlet的名稱
這個(gè)對(duì)象比較簡單,就不過多介紹,注意,這個(gè)對(duì)象只能在自己的servlet中使用,超出了范圍就不行了.
ServletContext對(duì)象
- 創(chuàng)建時(shí)間:加載web應(yīng)用時(shí)創(chuàng)建ServletContext對(duì)象
- 得到對(duì)象:從ServletConfig對(duì)象的getServletContext方法得到
這個(gè)對(duì)象又幾個(gè)比較重要的方法,我們來介紹一下.
- 作用:在一個(gè)web項(xiàng)目中共享數(shù)據(jù),管理web項(xiàng)目資源,為整個(gè)web配置公共信息等
java.lang.String getContextPath() --得到當(dāng)前web應(yīng)用的路徑 java.lang.String getInitParameter(java.lang.String name) --得到web應(yīng)用的初始化參數(shù) java.util.Enumeration getInitParameterNames() void setAttribute(java.lang.String name, java.lang.Object object) --域?qū)ο笥嘘P(guān)的方法 java.lang.Object getAttribute(java.lang.String name) void removeAttribute(java.lang.String name) RequestDispatcher getRequestDispatcher(java.lang.String path) --轉(zhuǎn)發(fā)(類似于重定向) java.lang.String getRealPath(java.lang.String path) --得到web應(yīng)用的資源文件 java.io.InputStream getResourceAsStream(java.lang.String path)
具體的方法使用就是這樣,按照API去用就可以了,我就不再過多介紹
轉(zhuǎn)發(fā)
轉(zhuǎn)發(fā)
剛才我們用到的
RequestDispatcher requestDispatcher = request.getRequestDispatcher("/welcome.jsp"); requestDispatcher.forward(request,response);
這個(gè)就是轉(zhuǎn)發(fā),按照這樣用就可以了
重定向
與轉(zhuǎn)發(fā)功能相似的是重定向,重定向的使用是這樣的:
response.sendRedirect("/welcome.jsp");
這樣也會(huì)訪問到welcome.jsp這個(gè)頁面.這就是之前的Respose對(duì)象,咱們先這樣用著,后面我回單獨(dú)寫一章博客來講解的.
##轉(zhuǎn)發(fā)和重定向的區(qū)別雖然二者最終實(shí)現(xiàn)的功能是相同的.但是還是有很大不同的.不同之處如下
- 地址欄變化
轉(zhuǎn)發(fā)不會(huì)改變地址欄中的URL,而重定向則會(huì)改變
- 跳轉(zhuǎn)范圍
轉(zhuǎn)發(fā)只能訪問到當(dāng)前web應(yīng)用中的內(nèi)容,而重定向則可以訪問到任意web應(yīng)用中的內(nèi)容
- request對(duì)象作用范圍
轉(zhuǎn)發(fā)后,在轉(zhuǎn)發(fā)后的頁面中仍然可以使用原來的request對(duì)象,而重定向,原來的request對(duì)象則失去作用.
所以,如果想要在多個(gè)頁面使用相同的request對(duì)象,那么只能使用轉(zhuǎn)發(fā),而不能使用重定向.好了,以上就是全部要介紹的內(nèi)容.servlet的生命周期是十分重要的,其他的只能靠動(dòng)手實(shí)踐才能很好的掌握,自己動(dòng)動(dòng)手敲出一個(gè)個(gè)好玩的例子吧!
總結(jié)
感謝您的閱讀,本篇文章就到這里了,希望您能夠習(xí)慣,也希望您可以多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
Java兩種方法計(jì)算出階乘尾部連續(xù)0的個(gè)數(shù)
這篇文章主要介紹了Java兩種方法計(jì)算出階乘尾部連續(xù)0的個(gè)數(shù),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03新手小白入門必學(xué)JAVA面向?qū)ο笾鄳B(tài)
說到多態(tài),一定離不開其它兩大特性:封裝和繼承,下面這篇文章主要給大家介紹了關(guān)于新手小白入門必學(xué)JAVA面向?qū)ο笾鄳B(tài)的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-02-02IDEA?服務(wù)器熱部署圖文詳解(On?Update?action/On?frame?deactivation)
這篇文章主要介紹了IDEA?服務(wù)器熱部署詳解(On?Update?action/On?frame?deactivation),本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-03-03SpringBoot整合Elasticsearch7.2.0的實(shí)現(xiàn)方法
這篇文章主要介紹了SpringBoot整合Elasticsearch7.2.0的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08Java LinkedHashSet集合的底層原理和TreeSet集合
LinkedHashSet保證元素有序且唯一,底層通過雙鏈表實(shí)現(xiàn),TreeSet元素不重復(fù)且可排序,底層使用紅黑樹實(shí)現(xiàn)排序,自定義類型排序可通過實(shí)現(xiàn)Comparable接口或提供Comparator來定義排序規(guī)則,適用于需要大量元素快速檢索的場(chǎng)景2024-10-10MybatisPlus自帶的queryWrapper實(shí)現(xiàn)時(shí)間倒序方式
這篇文章主要介紹了MybatisPlus自帶的queryWrapper實(shí)現(xiàn)時(shí)間倒序方式,具有很好的參考價(jià)值,希望對(duì)的有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01Java編程中快速排序算法的實(shí)現(xiàn)及相關(guān)算法優(yōu)化
這篇文章主要介紹了Java編程中快速排序算法的實(shí)現(xiàn)及相關(guān)算法優(yōu)化,快速排序算法的最差時(shí)間復(fù)雜度為(n^2),最優(yōu)時(shí)間復(fù)雜度為(n\log n),存在優(yōu)化的空間,需要的朋友可以參考下2016-05-05