Java Web(一) Servlet詳解!!

    這篇文章到上一篇,距離的有點遙遠呀,隔了大概有兩個月把,中間在家過了個年,哈哈~ 如今從新開始拾起。html

                                        --WZYjava

 

1、什麼是servlet?web

    處理請求和發送響應的過程是由一種叫作Servlet的程序來完成的,而且Servlet是爲了解決實現動態頁面而衍生的東西。理解這個的前提是瞭解一些http協議的東西,而且知道B/S模式(瀏覽器/服務器)。apache

    B/S:瀏覽器/服務器。 瀏覽器經過網址來訪問服務器,好比訪問百度,在瀏覽器中輸入www.baidu.com,這個時候瀏覽器就會顯示百度的首頁,那麼這個具體的過程,步驟是怎樣的呢?這個就瞭解一下http請求和響應瀏覽器

                  

    請求,響應:經過給的連接應該能夠知道這兩個具體的內容tomcat

 

2、tomcat和servlet的關係安全

    Tomcat 是Web應用服務器,是一個Servlet/JSP容器. Tomcat 做爲Servlet容器,負責處理客戶請求,把請求傳送給Servlet,並將Servlet的響應傳送回給客戶.而Servlet是一種運行在支持Java語言的服務器上的組件. Servlet最多見的用途是擴展Java Web服務器功能,提供很是安全的,可移植的,易於使用的CGI替代品.服務器

    從http協議中的請求和響應能夠得知,瀏覽器發出的請求是一個請求文本,而瀏覽器接收到的也應該是一個響應文本。可是在上面這個圖中,並不知道是如何轉變的,只知道瀏覽器發送過來的請求也就是request,咱們響應回去的就用response。忽略了其中的細節,如今就來探究一下。app

              

      ①:Tomcat將http請求文本接收並解析,而後封裝成HttpServletRequest類型的request對象,全部的HTTP頭數據讀能夠經過request對象調用對應的方法查詢到。webapp

      ②:Tomcat同時會要響應的信息封裝爲HttpServletResponse類型的response對象,經過設置response屬性就能夠控制要輸出到瀏覽器的內容,而後將response交給tomcat,tomcat就會將其變成響應文本的格式發送給瀏覽器

 

    Java Servlet API 是Servlet容器(tomcat)和servlet之間的接口,它定義了serlvet的各類方法,還定義了Servlet容器傳送給Servlet的對象類,其中最重要的就是ServletRequest和ServletResponse。因此說咱們在編寫servlet時,須要實現Servlet接口,按照其規範進行操做。

 

3、編寫Servlet

    在前面,咱們已經知道了servlet是什麼,爲何須要servlet?(爲了實現動態網頁,而不是顯示靜態網頁,具體狀況能夠百度查查),tomcat和servlet的關係?等問題。如今來手動編寫一個Servlet。

    3.一、手動編寫servlet。

      一、建立一個MyServlet繼承HttpServlet,重寫doGet和doPost方法,也就是看請求的方式是get仍是post,而後用不一樣的處理方式來處理請求,

            

      二、在web.xml中配置MyServlet,爲何須要配置?讓瀏覽器發出的請求知道到達哪一個servlet,也就是讓tomcat將封裝好的request找到對應的servlet讓其使用。

            配置四個東西。

                

            配置以後,瀏覽器是如何經過咱們配置的信息來找到對應的servlet的。

                

            按照步驟,首先瀏覽器經過http://localhost:8080/test01/MyServlet來找到web.xml中的url-pattern,這就是第一步,匹配到了url-pattern後,就會找到第二步servlet的名字MyServlet,知道了名字,就能夠經過servlet-name找到第三步,到了第三步,也就可以知道servlet的位置了。而後到其中找到對應的處理方式進行處理。

       三、實驗,驗證上面配置成功。

               

            

    3.二、利用嚮導新建MyServlet

        這個就相對簡單了,web.xml不用咱們手動配置,工具直接幫咱們自動配置了

            一、右擊項目,在new選項中有直接新建servlet的選項

            二、配置MyServlet類中的信息

                

          三、配置web.xml中的servlet信息

                  

        四、查看MyServle01類中的代碼和web.xml,其中的配置跟手動的配置是同樣的,只是用圖形化界面,讓咱們更方便的建立servlet而產生的。

 

    3.三、詳解建立servlet的原理

        一、servlet的生命週期是什麼?

        二、爲何建立的servlet是繼承自httpServlet,而不是直接實現Servlet接口?

        三、servlet的生命週期中,能夠看出,執行的是service方法,爲何咱們就只須要寫doGet和doPost方法呢?

        等這一系列的問題,咱們都應該知道,而不該該就單純的知道如何配置和使用servlet?上面的問題,一一來解答。

        一、servlet的生命週期是什麼?

          服務器啓動時(web.xml中配置load-on-startup=1,默認爲0)或者第一次請求該servlet時,就會初始化一個Servlet對象,也就是會執行初始化方法init(ServletConfig conf)

          該servlet對象去處理全部客戶端請求,在service(ServletRequest req,ServletResponse res)方法中執行

          最後服務器關閉時,纔會銷燬這個servlet對象,執行destroy()方法。

              

        二、爲何建立的servlet是繼承自httpServlet,而不是直接實現Servlet接口?

        三、servlet的生命週期中,能夠看出,執行的是service方法,爲何咱們就只須要寫doGet和doPost方法呢?

          查看源碼,httpServlet的繼承結構。

             httpServlet繼承GenericServlet。懂的人立馬就應該知道,GenericServlet(通用Servlet)的做用是什麼?大概的就是將實現Servlet接口的方法,簡化編寫servlet的步驟。具體下面詳解

                

             GenericServlet的繼承結構,實現了Servlet接口和ServletConfig接口,

                    

             Servlet接口內容

                  

                從這裏能夠看到,Servlet生命週期的三個關鍵方法,init、service、destroy。還有另外兩個方法,一個getServletConfig()方法來獲取ServletConfig對象,ServletConfig對象能夠獲取到Servlet的一些信息,ServletName、ServletContext、InitParameter、InitParameterNames、經過查看ServletConfig這個接口就能夠知道

             ServletConfig接口內容

                 

                其中ServletContext對象是servlet上下文對象,功能有不少,得到了ServletContext對象,就能獲取大部分咱們須要的信息,好比獲取servlet的路徑,等方法。

               到此,就知道了Servlet接口中的內容和做用,總結起來就是,三個生命週期運行的方法,獲取ServletConfig,而經過ServletConfig又能夠獲取到ServletContext。而GenericServlet實現了Servlet接口後,也就說明咱們能夠直接繼承GenericServlet,就可使用上面咱們所介紹Servlet接口中的那幾個方法了,能拿到ServletConfig,也能夠拿到ServletContext,不過那樣太麻煩,不能直接獲取ServletContext,因此GenericServlet除了實現Servlet接口外,還實現了ServletConfig接口,那樣,就能夠直接獲取ServletContext了。

              GenericServlet類的內容詳解

                     

                看上圖,用紅色框框起來的就是實現Servlet和ServletConfig接口所實現的方法,有9個,這很正常,可是咱們能夠發現,init方法有兩個,一個是帶有參數ServletConfig的,一個有無參的方法,爲何這樣設計?這裏須要知道其中作了什麼事情,來看看這兩個方法分別作了什麼事?

                init(ServletConfig config)

                      

                init()

                      

                一個成員變量config

                      

                getServletConfig()

                      

                經過這幾個方法一塊兒來說解,首先看init(ServletConfig config)方法,由於只有init(ServletConfig config)中帶有ServletConfig對象,爲了方便可以在其餘地方也能直接使用ServletConfig對象,而不只僅侷限在init(ServletConfig config)方法中,因此建立一個私有的成員變量config,在init(ServletConfig config)方法中就將其賦值給config,而後經過getServletConfig()方法就可以獲取ServletConfig對象了,這個能夠理解,可是在init(ServletConfig config)中,158行,還調用了一個init()方法,而且這個init()方法是空的,什麼讀沒有,這是爲何呢?這個緣由是爲了防止一件事情,當咱們須要在init方法中作一點別的事情,咱們想到的方法就是繼承GenericServlet而且重寫了init(ServletConfig config)方法,這樣依賴,就破壞了本來在GenericServlet類中init(ServletConfig config)寫的代碼了,也就是在GenericServlet類中的成員變量config會一直是null,沒法獲得賦值,由於被重寫了,就不會在執行GenericServlet中init(ServletConfig config)方法中的代碼。要想賦值,就必須在重寫的init(ServletConfig config)方法中調用父類的init(ServletConfig config)方法,也就是super.init(ServletConfig config),這樣一來,就很不方便,怕有時候會忘了寫這句代碼,因此在GenericServlet類中增長一個init()方法,之後須要在init方法中須要初始化別的數據,只須要重寫init()這個方法,而不須要去覆蓋init(ServletConfig config)這個方法,這樣設計,就好不少,不用在管init(ServletConfig config)這個其中的內容了。也不用出現其餘的問題。

                service(ServletRequest req, ServletResponse res)

                      

                   一個抽象方法,說明在GenericServlet類中並無實現該內容,那麼咱們想到的是,在它上面確定還有一層,也就是還有一個子類繼承它,實現該方法,要是讓咱們本身寫的Servlet繼承GenericServlet,須要本身寫service方法,那豈不是累死,而且咱們能夠看到,service方法中的參數仍是ServletRequest,ServletResponse。並無跟http相關對象掛鉤,因此咱們接着往下面看。

           HttpServlet類詳解

               繼承了GenericServlet類,經過咱們上面的推測,這個類主要的功能確定是實現service方法的各類細節和設計。而且經過類名能夠知道,該類就跟http掛鉤了。

                     

                關注service(HttpServletRequest req, HttpServletResponse resp)方法和service(ServletRequest req, ServletResponse res)方法。

                  service(ServletRequest req, ServletResponse res)方法

                        

                    該方法中就作一件事情,就是將ServletRequest和ServletResponse這兩個對象強轉爲HttpServletRequest和HttpServletResponse對象。爲何能這樣轉?

                      首先要知道req、res是什麼類型,經過打印System.out.println(req),能夠知道,req實際上的類型是org.apache.catalina.connector.RequestFacade                         Tomcat中的源碼。

                        

                          

                    經過圖能夠得知,req的繼承結構:RequestFacade、httpServletRequest、ServletRequest,咱們知道自己req是ServletRequest,那麼從繼承結構上看,它也能夠當作HttpServletRequest,也能夠當作ServletRequest,因此強轉爲HttpServletRequest是能夠的,若是不明白,我舉個例子,ArrayList、List、Object 這個,Object obj = new ArrayList();  List list = new ArrayList();  一個ArrayList對象能夠當作List對象, 也能夠當作一個Object對象,如今obj是否是能夠堪稱List對象呢?答案是能夠的,由於obj就是ArrayList對象,既然是ArrayList對象,那麼就能夠當作是List對象。同樣的道理,RequestFacade 對應 ArrayList、httpServleRequest對應 List、 ServletRequest 對應 Object。

                    轉換爲httpServletRequest和HttpServletResponse對象以後,在調用service(HttpServletRequest req, HttpServletResponse resp)方法。

                service(HttpServletRequest req, HttpServletResponse resp)

                    這個方法就是判斷瀏覽器過來的請求方式是哪一種,每種的處理方式不同,咱們經常使用的就是get,post,而且,咱們處理的方式可能有不少的內容,因此,在該方法內會將get,post等其餘5種請求方式提取出來,變成單個的方法,而後咱們須要編寫servlet時,就能夠直接重寫doGet或者doPost方法就好了,而不是重寫service方法,更加有針對性。因此這裏就回到了咱們上面編寫servlet時的狀況,繼承httpServlet,而只要重寫兩個方法,一個doGet,一個doPost,其實就是service方法會調用這兩個方法中的一個(看請求方式)。因此也就解答了咱們一開始提的問題3。  

                     

4、幾個重點的對象。ServletConfig、ServletContext,request、response

       講解四大類,ServletConfig對象,ServletContext對象、request對象,response對象

    ServletConfig對象

        獲取途徑:getServletConfig(); 

        功能:上面大概說起了一下,能獲得四個東西,

              

            getServletName();  //獲取servlet的名稱,也就是咱們在web.xml中配置的servlet-name

            getServletContext(); //獲取ServletContext對象,該對象的做用看下面講解

            getInitParameter(String); //獲取在servlet中初始化參數的值。這裏注意與全局初始化參數的區分。這個獲取的只是在該servlet下的初始化參數

                

             getInitParameterNames(); //獲取在Servlet中全部初始化參數的名字,也就是key值,能夠經過key值,來找到各個初始化參數的value值。注意返回的是枚舉類型

                

                

                

           注意:在上面咱們所分析的源碼過程當中,咱們就知道,其實能夠不用先得到ServletConfig,而後在獲取其各類參數,能夠直接使用其方法,好比上面咱們用的ServletConfig().getServletName();能夠直接寫成getServletName();而不用在先獲取ServletConfig();了,緣由就是在GenericServlet中,已經幫咱們獲取了這些數據,咱們只須要直接拿就行。

 

      ServletContext對象

           獲取途徑:getServletContext(); 、getServletConfig().getServletContext();  //這兩種獲取方式的區別就跟上面的解釋同樣,第一種是直接拿,在GenericServlet中已經幫咱們用getServletConfig().getServletContext();拿到了ServletContext。咱們只須要直接獲取就好了,第二種就至關於咱們本身在獲取一遍,兩種讀是同樣的。

           功能:tomcat爲每一個web項目都建立一個ServletContext實例,tomcat在啓動時建立,服務器關閉時銷燬,在一個web項目中共享數據,管理web項目資源,爲整個web配置公共信息等,通俗點講,就是一個web項目,就存在一個ServletContext實例,每一個Servlet讀能夠訪問到它。

              一、web項目中共享數據,getAttribute(String name)、setAttribute(String name, Object obj)、removeAttribute(String name)

                  setAttribute(String name, Object obj) 在web項目範圍內存放內容,以便讓在web項目中全部的servlet讀能訪問到

                  getAttribute(String name) 經過指定名稱得到內容

                  removeAttribute(String name) 經過指定名稱移除內容   

                   

                     

                    

              二、整個web項目初始化參數 //這個就是全局初始化參數,每一個Servlet中都能獲取到該初始化值

                  getInitPatameter(String name)  //經過指定名稱獲取初始化值

                  getInitParameterNames()  //得到枚舉類型

                   web.xml 配置 整個web項目的初始化

                      

                      

                      

              三、獲取web項目資源

                  3.1獲取web項目下指定資源的路徑:getServletContext().getRealPath("/WEB-INF/web.xml")

                    

                    

                  3.2獲取web項目下指定資源的內容,返回的是字節輸入流。InputStream getResourceAsStream(java.lang.String path)

                    前提知識:須要瞭解流。不知道的能夠去看看IO流總結的文章

                    

                    輸出內容截圖一部分

                    

 

               四、getResourcePaths(java.lang.String path)  指定路徑下的全部內容。

                    

                    

                    

                5還有不少別的方法,暫時用到的就這幾個了,之後須要在用的,就查看源碼,看API。

      request對象

          咱們知道,request就是將請求文本封裝而成的對象,因此經過request能得到請求文本中的全部內容,請求頭、請求體、請求行 。

                

            一、請求行內容的獲取。

              

              

                

            2請求頭的獲取

                隨便百度一個東西,而後查看的請求頭,包括如下這些內容,稍做了解。

                

                String getHeader(java.lang.String name) 得到指定頭內容String【】

                  

                  

                long getDateHeader(java.lang.String name) 得到指定頭內容Date

                int getIntHeader(java.lang.String name)  得到指定頭內容int

                Enumeration getHeaders(java.lang.String name) 得到指定名稱全部內容

            3請求體的獲取 -- 請求參數的獲取

               分兩種,一種get請求,一種post請求

               get請求參數:http://localhost:8080/test01/MyServlet?username=jack&password=1234

               post請求參數: <form method="post"><input type="text" name="username">

                String request.getParameter(String) 得到指定名稱,一個請求參數值。

                String[] request.getParameterValues(String) 得到指定名稱,全部請求參數值。例如:checkbox、select等

                Map<String , String[]> request.getParameterMap() 得到全部的請求參數  

    

            4請求轉發

                request.getRequestDispatcher(String path).forward(request,response);  //path:轉發後跳轉的頁面,這裏無論用不用"/"開頭,都是以web項目根開始,由於這是請求轉發,請求轉發只侷限與在同一個web項目下使用,因此這裏一直都是從web項目根下開始的,

                web項目根:

                    開發:G:\Workspaces\test01\WebRoot\..

                    運行時:D:\java\tomcat\apache-tomcat-7.0.53\webapps\test01\..

                 web站點根:

                    運行時:D:\java\tomcat\apache-tomcat-7.0.53\webapps\..

                從這裏能夠看出,web項目根就是從該web項目名開始,因此咱們請求轉發時,只須要接着項目名後面須要訪問的路徑寫就好了,

                特色:瀏覽器中url不會改變,也就是瀏覽器不知道服務器作了什麼,是服務器幫咱們跳轉頁面的,而且在轉發後的頁面,可以繼續使用原先的request,由於是原先的request,因此request域中的屬性均可以繼續獲取到。

                

      response對象

          

        經常使用的一個方法:response.setHeader(java.lang.String name, java.lang.String value) 設置指定的頭,通常經常使用。

             例如:設置每隔3秒就自動刷新一次,

              response.setHeader("Refresh",3);

              

              

              這樣能夠看到如今時間的秒數,會發現每隔三秒就會自動刷新一次頁面。

        這個最重要的一個就是重定向,其餘的一些操做都被封裝到response對象中了,重點講解重定向

          重定向(頁面跳轉)

            方式一:手動方案

                response.setStatus(302);  //狀態碼302就表明重定向

                response.setHeader("location","http://www.baidu.com");

            方式二:使用封裝好的,經過response.sendRedirect("http://www.baidu.com");

          特色:服務器告訴瀏覽器要跳轉的頁面,是瀏覽器主動去跳轉的頁面,瀏覽器知道,也瀏覽器的地址欄中url會變,是瀏覽器從新發起一個請求到另一個頁面,因此request是從新發起的,跟請求轉發不同。

            注意:response.sendRedirect(path);  //

            第一種:response.sendRedirect("/test01/MyServlet01");  //使用了"/"開頭,說明是從web站點根開始,因此須要寫test01/MyServlet01

            第二種:response.sendRedirect("MyServlet01");  //沒有使用"/"開頭,說明是從web項目根開始,那麼就無需寫test01了。

            重定向沒有任何侷限,能夠重定向web項目內的任何路徑,也能夠訪問別的web項目中的路徑,而且這裏就用"/"區分開來,若是使用了"/"開頭,就說明我要從新開始定位了,不訪問剛纔的web項目,本身寫項目名,若是沒有使用"/"開始,那麼就知道是訪問剛纔那個web項目下的servlet,就能夠省略項目名了。就是這樣來區別。

 

 

5、總結

      這一章節篇幅較長,不過理清不少知識點

          一、什麼是servlet?若是編寫servlet?

          二、分析了servlet的部分源碼,知道了其中的一些設計巧妙的東西,好比,原本編寫servlet是能看到其生命週期的,可是在其設計下,咱們只關注doGet和doPost方法,爲何能這樣呢?就能夠經過源碼中得知。

          三、servlet的生命週期,web.xml的配置

          四、servlet中的ServletConfig對象,ServletContext對象,request對象,response對象的詳細講解。包括其中的一些經常使用的方法。

          五、下一篇講解一下request、response的中文亂碼問題的解決

相關文章
相關標籤/搜索