源 | 愚公要移山(ID:fdd_sxu_nwpu)
jsp技術(shù)作為曾經(jīng)一度火爆的技術(shù),在最近幾年確實使用率越來越低了,這篇文章帶你一塊探究一下jsp的從生到死。
jsp技術(shù)的誕生
在很久很久以前,那時候我們的開發(fā)都是通過servlet來完成的,這個servlet是什么呢?我們先來認(rèn)識一下:
servlet用Java語言編寫的服務(wù)器端程序。主要功能是和瀏覽器進行交互,生成頁面展示。
長下面這個樣子:
public class HelloWorld extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter;
out.println("<html>");
out.println("<head>");
out.println("<title>Hello World</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>Hello World!</h1>");
out.println("</body>");
out.println("</html>");
}
}
我們可以看到前端所展示的頁面,需要我們servlet去一個標(biāo)簽一個標(biāo)簽去生成,如果一個頁面超級復(fù)雜,動不動幾千行代碼,那這個servlet效率也就太低了。而且整個servlet代碼也會十分臃腫而且可讀性非常差。
這時候怎么辦呢?sun公司很早就意識到了這個問題,于是便倡導(dǎo)很多公司一塊來創(chuàng)建了一種能夠動態(tài)生成html的新技術(shù),不久之后jsp便誕生了。有效率的解決了上面servlet所出現(xiàn)的問題。
jsp的發(fā)展
既然jsp技術(shù)能夠解決剛剛servlet代碼里面所出現(xiàn)的技術(shù),我們來看一下是如何解決的:在這里我們舉一個小例子,就是前端jsp向服務(wù)器servlet發(fā)送請求圖書頁面的功能。
首先我們看一下servlet:
public class List_book extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//聲明一個ArrayList.用來存放Book類中的數(shù)據(jù)
ArrayList<Book> list = new ArrayList<Book>;
for(int i=0;i<10;i++){
Book book = new Book;
book.setName(res.getString("name"+i));
book.setAuthor(res.getString("author"+i));
list.add(book);
}
//將list數(shù)據(jù)發(fā)送到.jap文件中
request.getRequestDispatcher("ListBook.jsp").forward(request, response);
}
}
我們會發(fā)現(xiàn),現(xiàn)在的servlet沒有一點html代碼了。我們只需要把數(shù)據(jù)交給jsp。此時我們的頁面展示就交給jsp來做了。現(xiàn)在我們來看一下jsp長什么樣子:
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%String path = request.getContextPath;%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="description" content="This is my page">
<script></script>
</head>
<body>
<% ArrayList list = (ArrayList) request.getAttribute("list"); %>
<h2 align = "center">圖書列表</h2>
<table border = 1px align = "center">
<tr><th>圖書名稱</th><th>圖書作者</th></tr>
<!-- 繼續(xù)使用jsp語句 循環(huán)放入存放于list中的Book實體類中的數(shù)據(jù) -->
<%
for(int i = 0;i<list.size;i++){
Book book =(Book)
list.get(i);%>
<tr><th><%=book.getName %></th><th><%=book.getAuthor%></th><tr>
}
%>
</table>
</body>
</html>
這就是jsp,我們可以在html頁面中寫一些java代碼。對于我們程序員來說,在開發(fā)當(dāng)中靜態(tài)的頁面我們只需要用html和css寫一些標(biāo)簽來展示即可,對于那些動態(tài)的部分我們就可以使用java代碼。
那么jsp和servlet是不是就是這種協(xié)作的關(guān)系,本質(zhì)上有什么區(qū)別呢?
其實jsp只是servlet的一種特殊形式,每一個jsp頁面就是一個servlet實例,通俗一點的話來說:jsp就是servlet,只不過servlet把一些業(yè)務(wù)功能剝離開來交給了或者是形成了jsp。明白了吧。在我們的項目編譯的時候就是把jsp編譯成了servlet。
你們會發(fā)現(xiàn),這樣做其實挺好的,市場也驗證了一切,很快jsp技術(shù)流行開來,可是隨著時間的流逝,業(yè)務(wù)越來越復(fù)雜,jsp也開始跟不上時代了。
jsp的危機
我們先看一個對話場景:
java程序員:終于寫完了功能,是時候在界面上展示了。
前端程序員:你功能寫完了,我沒有數(shù)據(jù),在頁面什么沒法展示呀
java程序員:數(shù)據(jù)我寫好了,你在jsp中調(diào)用XX方法就能獲取了,
前端程序員:我已經(jīng)在jsp中寫好了這個方法,你為什么自己寫了?
于是乎,無窮無盡的爭吵還在繼續(xù)當(dāng)中。
這就是jsp的弊端,為什么呢?我們可以來總結(jié)一下:
(1)動態(tài)和靜態(tài)資源放在一起,一旦服務(wù)器出現(xiàn)狀況,前后臺一起玩完,用戶體驗極差。
(2)一旦jsp出現(xiàn)了問題,就需要前端后端發(fā)開人員一塊來分析解決,效率低。
(3)jsp無法使用nginx等。
(4)jsp頁面復(fù)雜,難以修改。
(5)第一次加載jsp需要編譯成servlet,時間久,而且業(yè)務(wù)量大的時候,jsp負擔(dān)太大。
(6)jsp對于開發(fā)人員簡直就是一個揮之不去的痛,太難了?。?!
鑒于以上缺點,于是另外一套機制橫空出世了,這就是前后端分離。什么是前后端分離呢?
前后端分離其實就是后端工程師只關(guān)注于后端頁面的開發(fā),不再處理前端問題。前端工程師只關(guān)注于自己的頁面開發(fā)。需要數(shù)據(jù)交互的時候,兩者會有一份接口文檔。
就這樣這種思想架構(gòu)很快的流行開來,這也就是為什么jsp落寞的真正原因。從此java從jsp轉(zhuǎn)向了restful結(jié)構(gòu),springMCV也開始流行開來,并逐漸占領(lǐng)了市場。前后端分離有什么優(yōu)點呢?我們來總結(jié)一下:
(1)動態(tài)和靜態(tài)資源分開存儲。
(2)出現(xiàn)bug能很快定位是前端還是后端。
(3)支持nginx。在高并發(fā)狀態(tài)下極其優(yōu)秀。
(4)直接請求頁面,不用編譯,速度效率都提上來了。
(5)從此前端和后端是相親相愛的一家人了?。。?!
jsp的落幕
又隨著時間的推移,jsp的時代基本上一去不復(fù)返了,因為我們又走入了移動互聯(lián)時代,這時候的客戶端可不是前端頁面了,還包括手機、汽車、電視等等各種設(shè)備,這種情況下,前后端必須要分離了。jsp基本上徹底告別了它的舞臺。
隨著容器技術(shù)(docker、k8s)以及微服務(wù)架構(gòu)逐步成熟和發(fā)展,這種強調(diào)后端分離思想讓jsp這個陪伴我近10年的頁面渲染技術(shù)宣告落幕了,有點傷感。。。
1、無法做到動靜分離
傳統(tǒng)java程序通過war包形式部署到tomcat,除了java代碼和jsp頁面,還包括css、js、圖片等靜態(tài)資源,一旦其中的某個jsp頁面出問題,會導(dǎo)致部分功能不可用,甚至服務(wù)器響應(yīng)阻塞,無法對外提供服務(wù)。
2、分工協(xié)調(diào)性差
jsp本質(zhì)上是一個java類,所以早期java開發(fā)人員是前后端開發(fā)任務(wù)全負責(zé),而UI設(shè)計師把設(shè)計好的html頁面給開發(fā)人員集成,這個需要雙方共同協(xié)調(diào)完成,效率低下,很難完成需求快速更新迭代,持續(xù)交付。
3、并發(fā)、吞吐量差
由于jsp本質(zhì)是java類,只能放在web服務(wù)器(如tomcat),所以jsp不能部署到并發(fā)性能更好的nginx或者apache下,這是很多人詬病java web性能不好的原因之一。
4、擴展性差
jstl內(nèi)置的一些tag標(biāo)簽耦合java代碼(類似于react中的component組件),很難做到只修改頁面而不用修改java代碼,擴展性很差。
5、頁面加載慢(同步機制)
如果一個頁面承載的內(nèi)容很多(如表單、表格、詳情),會導(dǎo)致頁面加載很慢。究其原因是jsp內(nèi)在特性決定的。
jsp初始化流程
首先,jsp頁面會初始化為servlet的class文件
其次,在servlet代碼中解析jsp tag標(biāo)簽,轉(zhuǎn)換成html網(wǎng)頁標(biāo)簽
最后,以流的方式輸出html網(wǎng)頁
這里有個要命的問題,從jsp轉(zhuǎn)換成html到瀏覽器渲染是一個同步過程。也就是說,如果數(shù)據(jù)加載很慢,會導(dǎo)致整個頁面出不來。
1、動靜分離,前端軟負載架構(gòu)
后端代碼(如java)和前端(html、js、css、圖片等)分離,單獨部署。
前端程序強調(diào)靜態(tài)資源,會單獨部署到抗壓能力更強的nginx下。而后端程序由于剝離了頁面、js、css、圖片,以接口形式對外提供服務(wù),
服務(wù)能力下沉(基礎(chǔ)平臺能力,sass服務(wù)化能力)。
2、分工明確
3、異步加載機制
如果頁面需要多次ajax調(diào)用,不需要同步進行,異步加載實現(xiàn)局部刷新。
4、組件化
以react、vue、angular為代表前端框架,提出組件化、框架化、復(fù)用性等工程化編程,使的前端也可以像后端那樣提供可復(fù)用性、可擴展性、高可用性的前端程序。
筆者認(rèn)為,前后端分離思想是一種趨勢,更深層次是技術(shù)更新很快,我們要與時俱進,時刻更新自己的知識庫。當(dāng)然對于之前不了解jsp的同學(xué)還是要去學(xué)習(xí)下,畢竟很多技術(shù)發(fā)展是有繼承性的。
如果覺得本文對您有幫助的話,記得關(guān)注、轉(zhuǎn)發(fā)哦,我會為大家持續(xù)提供原創(chuàng)干貨。需要資料,請關(guān)注、轉(zhuǎn)發(fā),私信“資料”面試+微服務(wù)+springboot資料免費贈送。
言:
為了后續(xù)的代碼審計一些常用的框架和技術(shù)都是有必要了解一下,在此重拾Spring Boot等開發(fā)知識內(nèi)容。
Thymeleaf是一個現(xiàn)代的服務(wù)器端Java模板引擎的web和獨立的環(huán)境,能夠處理HTML, XML, JavaScript, CSS,甚至純文本。
Thymeleaf的主要目標(biāo)是提供一種優(yōu)雅的和高度可維護的方式來創(chuàng)建模板。為了實現(xiàn)這一點,它構(gòu)建在自然模板的概念上,以不影響模板作為設(shè)計原型使用的方式將其邏輯注入模板文件。這改進了設(shè)計的交流,并在設(shè)計和開發(fā)團隊之間架起了橋梁。
Thymeleaf的設(shè)計從一開始就考慮了Web標(biāo)準(zhǔn),尤其是HTML5
Thymeleaf是一個非常可擴展的模板引擎(事實上它可以被稱為模板引擎框架),它允許你定義和自定義的方式,你的模板將被處理到一個精細的細節(jié)級別。
將一些邏輯應(yīng)用到標(biāo)記工件(標(biāo)記、一些文本、注釋,如果模板不是標(biāo)記,則僅僅是占位符)的對象稱為處理程序,這些處理程序的集合—加上一些額外的工件—通常是方言的組成部分。Thymeleaf的核心庫提供了一種稱為標(biāo)準(zhǔn)方言的方言,這對大多數(shù)用戶來說應(yīng)該足夠了。
這里主要以Srping Boot為主
<!--引入thymeleaf依賴-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
在項目的resources\templates目錄下創(chuàng)建HTML文件,這里注意導(dǎo)入thymeleaf的命名空間,否則無法進行模板的渲染。
<!doctype html>
<!--注意:引入thymeleaf的名稱空間-->
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<p th:text="'hello SpringBoot'">hello thymeleaf</p>
</body>
</html>
編寫Controller
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class IndexController {
@GetMapping("home")
public String index() {
return "index";
}
}
這里的注解需要使用@Controller,不能使用@RestController注解,否則會報錯.
<span th:text="${book.author.name}">
<li th:each="book : ${books}">
@{/order/list}
@{/order/details(id=${orderId})}
或者是
<form th:action="@{/createOrder}">
<a href="main.html" th:href="@{/main}">
#{main.title}
#{message.entrycreated(${entryId})}
<div th:object="${book}">
...
<span th:text="*{title}">...</span>
...
</div>
th標(biāo)簽屬性
1)th:text:文本替換;
2)th:utext:支持html的文本替換。
3)th:value:屬性賦值
4)th:each:遍歷循環(huán)元素
5)th:if:判斷條件,類似的還有th:unless,th:switch,th:case
6)th:insert:代碼塊引入,類似的還有th:replace,th:include,常用于公共代碼塊提取的場景
7)th:fragment:定義代碼塊,方便被th:insert引用
8)th:object:聲明變量,一般和*{}一起配合使用,達到偷懶的效果。
9)th:attr:設(shè)置標(biāo)簽屬性,多個屬性可以用逗號分隔
內(nèi)容比較簡單,主要作為記錄。
*請認(rèn)真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。