小白新手web開發簡單總結(三)-一個簡單的Spring專案
目錄
一 前言
經過小白新手web開發簡單總結(一)-什麼是Tomcat和小白新手web開發簡單總結(二)-什麼是web.xml,對一個web開發有了一點點認識,一個開發的流程應該大概是這個樣子:
- 1.一個web專案開發最後通過maven把專案程式碼(前端頁面展示+業務邏輯)打包成war包;
- 2.在伺服器上安裝Tomcat,那麼在安裝目錄下就會有一個webapps的資料夾,那麼我們就把第一步打包成的war包放到該目錄下。(從webapps這個命名方式上也可以看出,該Tomcat是可以啟動多個web應用);
- 3.進入到bin的目錄下,執行.startup.sh,啟動Tomcat,那麼就會讀取war包裡面的web.xml配置檔案,按照小白新手web開發簡單總結(二)-什麼是web.xml例項化裡面的類,那麼我們就可以通過在瀏覽器中輸入url來訪問web應用了。
上面的三點總結是基於我在本地電腦上的一個操作方式總結出來的,真正的在一個網站開發過程中的操作過程,後面再去研究,但我覺得應該差不多也是這個樣子。所以對於後面在學習一個web開發,我覺得需要學習下到底Spring、Spring MVC、Tomcat之間的關係,一個請求過來,到底是怎麼完成一次請求的?
二 web應用開發
1.什麼是web應用開發
web應用開發一般都會採用B/S架構。從開發角度來分,我覺得一個web應用開發和一個APP的開發一樣,可以劃分為包括的分成這幾部分:web頁面相關的業務邏輯+網路通訊+網路提供給業務層呼叫的功能。對於使用者來說,只需要瀏覽器,web應用的相關業務邏輯和資料都存在在服務端。瀏覽器通過Http協議與伺服器進行通訊,獲取web頁面的內容,通過瀏覽器顯示給使用者。
瀏覽器在整個過程中的角色:
(1)與伺服器建立TCP連線;
(2)傳送Http請求,在該請求中會標明GET/POST;
(3)接收Http響應,將伺服器返回的內容顯示在瀏覽器中。
在請求和響應都包括Http Header和Http Body,響應體根據設定的Content-Type返回對應的網頁資料(如text/html,則會返回<html></html>),瀏覽器將這些資訊顯示出來即可。瀏覽器獲取的第一個資源就是這些Html網頁,如果裡面還含有JavaScript、CSS、圖片等資源,瀏覽器會根據對應的資源url再一次向伺服器傳送請求。
伺服器在整個過程中的角色:
(1)與瀏覽器建立TCP連線;
(2)識別Http請求,接收和處理Http請求;
(3)將處理之後的Http請求返回給瀏覽器。
從角色分工中,可以看出,伺服器已經處理和解析Http請求等網路通訊的工作,瀏覽器負責傳送和接收Http請求,而web應用只需要放到web伺服器即可。在Java EE提供HttpServlet來處理Http請求,那麼只要該伺服器去實現HttpServlet,就可以實現網路通訊的工作。
2.一個簡單的web應用
我覺得可以把HttpSerlvet理解成一個處理客戶端發過來的Http請求的類,裡面有兩個方法:
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
其中引數HttpServletRequest req, HttpServletResponse resp就代表這請求和響應。在使用這個HttpSerlvet的時候,不需要關心怎麼與TCP互動,也不需要解析Http,例如在doGet()方法只需要:從req中接收到瀏覽器發過來的請求引數,在該方法中完成業務邏輯和讀寫資料庫的操作,最後將結果寫入到resp,那麼瀏覽器就可以接收到相關的資料,在res和resp中已經封裝了請求和響應的邏輯。
通過這個HttpSerlvet來完成一個簡單的web應用開發的例項:
(1)通過IDEA建立一個Maven工程
(2)配置pom.xml,引入Servlet
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
這裡注意要將該依賴作為<provided>引入,這樣僅會在編譯的時候使用,最後不會打包到.war包中,因為在後面用到的Tomcat伺服器中已經有相應的Servlet API。
(3)配置打包型別為.war,同時配置最後打包成.war的名字
<packaging>war</packaging>
<build>
<finalName>builder-spring</finalName>
</build>
(4)在java的目錄下建立一個FirstServlet的java檔案,並且編寫一個簡單的顯示一句話。
@WebServlet(urlPatterns = "/")
public class FirstServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html");
PrintWriter writer = resp.getWriter();
writer.write("<h1> Hello Web ,This is a simple web!</h1>");
//強制輸出
writer.flush();
}
}
當然也可以通過getOutputStream()
獲取寫入流。另外需要注意的是在寫入完畢之後,必須呼叫flush(),否則不能將這些資料及時返回給瀏覽器,而close(),是關閉的TCP連線,不能呼叫該方法。
(5)在專案工程的main/webapp/WEB-INF的目錄下,建立web應用的描述檔案web.xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>build spring</display-name>
</web-app>
(6)通過IDEA自帶的maven進行打包,依次執行clean->compile->package,然後在target的目錄下就會生成build-spring.war包
(7)將build-spring.war包拷貝到在Tomcat的安裝目錄webapps,在返回到bin目錄下,執行.startup.sh,在終端命令視窗顯示:
MacBook-Pro:bin j1$ ./startup.sh
Using CATALINA_BASE: /Users/j1/Documents/java/apache-tomcat-9.0.41
Using CATALINA_HOME: /Users/j1/Documents/java/apache-tomcat-9.0.41
Using CATALINA_TMPDIR: /Users/j1/Documents/java/apache-tomcat-9.0.41/temp
Using JRE_HOME: /Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home
Using CLASSPATH: /Users/j1/Documents/java/apache-tomcat-9.0.41/bin/bootstrap.jar:/Users/j1/Documents/java/apache-tomcat-9.0.41/bin/tomcat-juli.jar
Using CATALINA_OPTS:
Tomcat started.
(8)在瀏覽器中輸入http://localhost:8080/builder-spring/,就可以看到如下介面
當然也可以通過http://localhost:8080/builder-spring/?count=2來新增引數,那麼在FirstServlet可以直接通過HttpServletRequest讀取:
@WebServlet(urlPatterns = "/")
public class FirstServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html");
String count = req.getParameter("count");
PrintWriter writer = resp.getWriter();
writer.write(String.format("<h1> Hello Web %s ,This is a simple web!</h1>",count));
//強制輸出
writer.flush();
}
在瀏覽器中訪問如下:
通過上面的幾個步驟,成功的建立了一個簡單的web應用。當瀏覽器在訪問"http://localhost:8080/builder-spring/"的時候,就會發送請求,然後有Tomcat(前面的小白新手web開發簡單總結(一)-什麼是Tomcat也提到了Tomcat其實就是一個Servlet容器)會將該請求交給FirstServlet來處理(@WebServlet(urlPatterns = "/")提供了對映關係)。
另外對於這個url的組成其實就是我們在pom.xml配置的<finalName>,再加上FirstServlet中的@WebServlet(urlPatterns = "/")組成的。
上述程式碼已經上傳到github,github地址為:https://github.com/wenjing-bonnie/build-spring.git
3.實際的web應用
當然在一個web應用中,是有許多個Servlet組成的,每個Servlet都會對映到一個路徑。所以在開發一個完整的web應用,在web頁面相關的業務邏輯這一部分中,其實可以細分成幾個過程:
- (1)不同的路徑對映對應的不同的Servlet;
在每個Servlet處理業務邏輯的時候,其實也可以細分成過程:
1)從HttpServletRequest 中讀取請求引數;
2)處理複雜的業務邏輯:要有讀寫資料庫的公共操作類,要有資料庫的每張表對應著一些封裝類,要有針對這些表的操作的封裝類,更多的是在這一個Servlet可能涉及到多個數據庫表的操作,也就意味著需要引入多個表的操作類;
3)將響應資料寫入到HttpServletResponse,響應資料又可以看出是一些供瀏覽器顯示的資料(前面提到的例子中可能響應資料僅僅就是為了顯示一個字串,但在實際開發中會有更多跟頁面相關的渲染,例如jsp);
顯然在這個過程中會出現許多類例項的例項化和載入,以及這些類例項的生命週期的管理,並且在一個Servlet中還可以抽象成一種Model-View-Controller的設計模式。
- (2)web伺服器根據路徑對映規則,將不同路徑轉發到對應的Servlet,該轉發功能通常由服務端Dispatcher完成。
那麼網路通訊以及網路提供給業務層呼叫的功能就有Tomcat來完成。對比之前在小白新手web開發簡單總結(一)-什麼是Tomcat和小白新手web開發簡單總結(二)-什麼是web.xml,這些過程似乎都有了專門的對應:
- (1)Tomcat就是一個Servlet容器,用來例項化Servlet來進行處理請求和響應。Tomcat的Connector負責與瀏覽器來建立連線,接收瀏覽器傳送過的請求以及將響應返回給瀏覽器;
- (2)Tomcat中的Container中的Engine用來例項化Servlet,管理Servlet的生命週期,完成底層的網路通訊;
- (3)在web.xml中註冊了DispatcherSerlvet,用來將瀏覽器傳送過來的請求對映到對應路徑的處理類中。
現在看來,對於web應用開發的一些知識點開始明朗起來。
三 web應用開發的斷點除錯
從上面一個簡單的例子看到一個web應用開發,其實就是編寫Servlet來處理瀏覽器傳送過來的Http請求,當然我們需要將最終的這些Servlet按照既定的規則打包成war包,通過Tomcat來載入war包,並執行這些Servlet。
在一個簡單的web應用例項中提到的上面的第六步和第七步,我們需要通過IDEA打包成war包,並且還要通過本地安裝的Tomcat來啟動war包,那麼如果斷點除錯呢?其實我們可以Tomcat也是一個java程式,通過.startup.sh 來啟動Tomcat,其實就是啟動JVM並執行Tomcat的main()方法,並且載入war包之後初始化Servlet,從而對瀏覽器提供服務。其實我們完全可以通過程式碼的形式來完成一個Tomcat啟動web專案。
(1)還是上面的專案程式碼,我們將javax.servlet換成Tomcat的相關依賴:
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>${tomcat.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>${tomcat.version}</version>
</dependency>
(2)增加Tomcat例項化的main()方法
public class FirstServletTest {
public static void main(String[] args) throws LifecycleException {
//建立Tomcat
Tomcat tomcat = new Tomcat();
tomcat.setPort(Integer.getInteger("port", 8080));
tomcat.getConnector();
//建立webapp
Context context = tomcat.addWebapp("", new File("src/main/webapp").getAbsolutePath());
WebResourceRoot resources = new StandardRoot(context);
DirResourceSet set = new DirResourceSet(resources, "/WEB-INF/classes", new File("target/classes").getAbsolutePath(), "/");
resources.addPreResources(set);
context.setResources(resources);
//啟動Tomcat
tomcat.start();
tomcat.getServer().await();
}
}
(3)執行main()方法,然後在IDEA中下面就會顯示,表示嵌入式的Tomcat已經成功啟動,Tomcat會自動的將本工程放置到根目錄,可直接通過http://localhost:8080/?count=2進行訪問到對應的Servlet
二月 08, 2021 2:00:25 下午 org.apache.coyote.AbstractProtocol init
資訊: Initializing ProtocolHandler ["http-nio-8080"]
二月 08, 2021 2:00:25 下午 org.apache.catalina.core.StandardService startInternal
資訊: Starting service [Tomcat]
二月 08, 2021 2:00:25 下午 org.apache.catalina.core.StandardEngine startInternal
資訊: Starting Servlet engine: [Apache Tomcat/9.0.36]
二月 08, 2021 2:00:25 下午 org.apache.catalina.startup.ContextConfig getDefaultWebXmlFragment
資訊: No global web.xml found
二月 08, 2021 2:00:27 下午 org.apache.jasper.servlet.TldScanner scanJars
資訊: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
二月 08, 2021 2:00:27 下午 org.apache.coyote.AbstractProtocol start
資訊: Starting ProtocolHandler ["http-nio-8080"]
這樣就可以輕鬆的將一個專案通過 執行FirstServletTest的main()的方式來斷點除錯專案。但是在一個實際的web應用開發中,不知道是不是通過這種方式來斷點除錯,還需要自己再去學習與研究。
程式碼github地址為:https://github.com/wenjing-bonnie/build-spring.git
四 總結
通過這個簡單的web應用開發的例項,對比前面小白新手web開發簡單總結(一)-什麼是Tomcat和小白新手web開發簡單總結(二)-什麼是web.xml兩篇的總結,簡單在總結下自己現在清晰的東西:
- 1.一個web應用開發的專案通過maven打包成war包,執行在一個支援Servlet的web伺服器;
- 2.Tomcat就是一個支援Servlet的web伺服器,每次在一個web應用服務上線的時候,應該就是將war包放到安裝到伺服器的Tomcat的相關目錄下,並且將Tomcat啟動;
- 3.Tomcat啟動之後,就會讀取war包的web.xml檔案,然後將裡面對應的各個類例項化;
- 4.當有瀏覽器有請求傳送過來的時候,首先會與web伺服器建立TCP連線,有瀏覽器傳送請求;
- 5.Tomcat接收到請求之後,會有DispatcherServlet進行根據路徑匹配到對應的處理類;
- 6.處理完之後的響應傳送給瀏覽器,瀏覽器負責將資料進行顯示
- 7.HttpServlet不需要關心底層TCP等相關的網路邏輯,只需要從HttpServletRequest 取得請求引數,經過業務邏輯之後,將處理完之後的資料寫入到HttpServletResponse即可。
另外記錄一下關於Servlet的幾個知識點:在向HttpServletResponse寫入響應資料的時候,可以指定瀏覽器是重定向還是轉發
- 1.重定向
當瀏覽器請求一個url,伺服器在處理完資料,返回給瀏覽器的時候,會告訴瀏覽器地址發生變化,需要瀏覽器在重新發送請求,通過下面的程式碼實現重定向:
String redirectToUrl = "/xxxx";
// 傳送重定向響應:
resp.sendRedirect(redirectToUrl);
瀏覽器接收到如下響應:
HTTP/1.1 302 Found
Location: /xxxx
會根據Location返回的 url,重新發送一個新的請求。那麼在瀏覽器中看到是兩個請求。當然重定向又分為臨時重定向(302響應)和永久重定向(301響應)。兩者區別在於永久重定向,瀏覽器會換成Location傳送過來的url,在下一次在請求之前的url的時候,會直接傳送Location對應的url。在程式碼中通過
// HttpServletResponse.SC_MOVED_PERMANENTLY:301
//HttpServletResponse.SC_FOUND:302
resp.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
resp.setHeader("Location", "/xxxx");
- 2.轉發
轉發指的是內部轉發,當一個Servlet處理一個瀏覽器傳送過來的請求的時候,自己本身不去處理,而是轉發給另外一個Servlet來處理。程式碼中通過實現:
req.getRequestDispatcher("/xxxx").forward(req, resp);
後續請求處理實際是交給了"/xxxx"對應的Servlet來處理。
相比較於重定向而言,這個轉發僅僅是伺服器內部完成,對於瀏覽器來說,只會發出一個請求,本身不需要做任何處理,在瀏覽器中始終只有一個url。
這幾天每天都很充實,加油!!!!!