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