版本歷史&代碼示例之Servelt、JSP、EL表達(dá)式
正文
Servlet
Servlet是一種基于Java的動(dòng)態(tài)Web資源動(dòng)態(tài)Web資源技術(shù),類似的技術(shù)還有ASP、PHP等。
- <!-- javax命名空間版本(Tomcat 9.x及以下版本支持) -->
- <dependency>
- <groupId>javax.servlet</groupId>
- <artifactId>javax.servlet-api</artifactId>
- <version>4.0.1</version>
- <scope>provided</scope>
- </dependency>
- <!-- jakarta命名空間版本(Tomcat 10.x及以上版本支持) -->
- <dependency>
- <groupId>jakarta.servlet</groupId>
- <artifactId>jakarta.servlet-api</artifactId>
- <version>5.0.0</version>
- <!-- <version>4.0.4</version> 此版本命名空間同javax -->
- <scope>provided</scope>
- </dependency>
版本歷史
Servlet規(guī)范由Sun Microsystems公司創(chuàng)建,1.0版于1997年6月完成。從2.3版開始,該規(guī)范是在JCP下開發(fā)。
版本 | 發(fā)布日期 | 隸屬于 | JSR版本 | 焦點(diǎn)說明 |
---|---|---|---|---|
1.0 | 1997.06 | - | - | 首個(gè)版本,由Sun公司發(fā)布 |
2.0 | 1997.08 | - | - | |
2.1 | 1998.11 | - | - | 新增了RequestDispatcher, ServletContext等 |
2.2 | 1999.08 | J2EE 1.2 | - | 成為J2EE的一部分。在.war文件中引入了self-contained Web applications的概念 |
2.3 | 2001.08 | J2EE 1.3 | JSR 53 | 增加了Filter,增加了關(guān)于Session的Listener(如HttpSessionListener) |
2.4 | 2003.08 | J2EE 1.4 | JSR 154 | 沒增加大的新內(nèi)容,對(duì)不嚴(yán)格的地方加了些校驗(yàn),如:對(duì)web.xml使用XML Schema |
2.5 | 2005.09 | Java EE 5 | JSR 154 | 最低要求JDK 5。注解支持(如@WebService、@WebMethod等,注意不是@WebServlet這種哦) |
3.0 | 2009.12 | Java EE 6 | JSR 315 | 史上最大變革。動(dòng)態(tài)鏈接庫(kù)和插件能力(Spring MVC利用此能力通過ServletContainerInitializer 進(jìn)行全注解驅(qū)動(dòng)開發(fā))、模塊化開發(fā)、異步Servlet、安全性、新的文件上傳API、支持WebSocket,新的注解(@WebServlet、@WebFilter、@WebListener),可脫離web.xml全注解驅(qū)動(dòng),此版本功能已經(jīng)很完整了,應(yīng)用的主流 |
3.1 | 2013.5 | Java EE 7 | JSR 340 | 新增非阻塞式IO。Spring的Web Flux若要運(yùn)行在Servlet容器,至少需要此版本,因?yàn)閺拇税姹酒鸩庞蟹亲钄噍斎胼敵龅闹С?/td> |
4.0 | 2017.09 | Java EE 8 | JSR 369 | 支持Http/2。從而支持服務(wù)器推技術(shù),新的映射發(fā)現(xiàn)接口HttpServletMapping可用來提高內(nèi)部的運(yùn)行效率 |
5.0 | 2020.11 | Jakarta EE 9 | JSR 369 | 同Servlet 4.0(只是命名空間從javax.* 變?yōu)榱?code style="margin: 0px; padding-top: 0px; padding-right: 5px !important; padding-bottom: 0px; padding-left: 5px !important; outline: 0px; max-width: 100%; box-sizing: border-box !important; word-wrap: break-word !important; background-color: rgb(248, 248, 248) !important; color: rgb(255, 131, 110) !important; display: inline !important; border-radius: 3px !important;">jakarta.*而已) |
Spring Boot相關(guān):
- 2.0.0.RELEASE版本(2018.05):正式內(nèi)置Servlet 3.1,畢竟Spring Web Flux從此版本開始(Spring 5)
- 2.1.0.RELEASE版本(2018.10):升級(jí)到Servlet 4.x,直到現(xiàn)在(2.6.x)也依舊是4.x版本
- 2.2.0.RELEASE版本(2019.10):開始支持jakarta.servlet這個(gè)GAV,(和javax.servlet)二者并行
- 2.5.0/2.6.0版本(2021.05):無變化
- 3.0.0版本(預(yù)計(jì)2022.12):基于Spring 6.x、Jakarta EE 9,基于GraalVM全面擁抱云原生的新一代框架
說明:Spring Boot 2.6和2.7都還會(huì)基于Spring Framework 5.3.x內(nèi)核。Spring Framework 6.0版本在2021年9月正式拉開序幕,將基于全新的Jakarta EE 9(命名空間為jakarta.*,不向下兼容)平臺(tái)開發(fā),相應(yīng)的Spring Boot 3也會(huì)基于此內(nèi)核
生存現(xiàn)狀
隨著Spring 5的發(fā)布推出WebFlux,Servlet技術(shù)從之前的必選項(xiàng)變?yōu)榭蛇x項(xiàng)。
但考慮到業(yè)務(wù)開發(fā)使用WebFlux收益甚微但開發(fā)調(diào)試成本均增加,因此實(shí)際情況是基于Servlet的Spring MVC技術(shù)依舊是主流,暫時(shí)地位不可撼動(dòng),依舊非常活躍。
實(shí)現(xiàn)(框架)
由于Servlet由Web容器負(fù)責(zé)創(chuàng)建并調(diào)用,因此只要實(shí)現(xiàn)了Servlet規(guī)范的Web容器均可作為它的實(shí)現(xiàn)(框架),如Tomcat、Jetty、Undertow、JBoss、Glassfish等。
代碼示例
導(dǎo)入依賴包:
scope一般provided即可,因?yàn)閃eb容器里會(huì)自帶此Jar
Spring Boot場(chǎng)景下無需顯示導(dǎo)入,因?yàn)門omcat已內(nèi)嵌(相關(guān)API)
- servlet-api的GAV
繼承HttpServlet寫一個(gè)用于處理Http請(qǐng)求的Servlet處理器
- /**
- * 在此處添加備注信息
- *
- * @author YourBatman. <a href=mailto:yourbatman@aliyun.com>Send email to me</a>
- * @site https://yourbatman.cn
- * @date 2021/9/12 06:23
- * @since 0.0.1
- */
- @WebServlet(urlPatterns = {"/hello"})
- public class HelloServlet extends HttpServlet {
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- resp.getWriter().write("hello servlet...");
- }
- }
IDEA添加(外置)Tomcat 9.x版本,以war包形式部署到Tomcat(小提示:
瀏覽器http://localhost:8080/hello即可完成正常訪問。
說明:自Servlet 3.0之后,web.xml部署描述符并非必須(全注解即可搞定)
工程源代碼:https://github.com/yourbatman/BATutopia-java-ee
JSP
Java Server Page的簡(jiǎn)稱。那么,有了Servlet為何還需要JSP?其實(shí)它倆都屬于動(dòng)態(tài)Web技術(shù),只是Servlet它用于輸出頁(yè)面簡(jiǎn)直太繁瑣了(每一句html都需要用resp.getWriter()逐字逐句的輸出),所以才出現(xiàn)了JSP技術(shù)來彌補(bǔ)其不足。
它使用JSP標(biāo)簽在HTML網(wǎng)頁(yè)中插入Java代碼。語法格式為:<% Java代碼 %>。它有九大內(nèi)置對(duì)象這么一說:
- 1、request:請(qǐng)求對(duì)象。javax.servlet.http.HttpServletRequest
- 2、response:響應(yīng)對(duì)象。javax.servlet.http.HttpServletResponse
- 3、session:會(huì)話對(duì)象。javax.servlet.http.HttpSession
- 4、application:應(yīng)用程序?qū)ο?。javax.servlet.ServletContext
- 5、config:配置對(duì)象。javax.servlet.ServletConfig
- 6、page:頁(yè)面對(duì)象。當(dāng)前jsp程序本身,相當(dāng)于this
- 7、pageContext:頁(yè)面上下文對(duì)象。javax.servlet.jsp.PageContext
- 8、out:輸出流對(duì)象,用于輸出內(nèi)容到瀏覽器。javax.servlet.jsp.jspWriter
- 9、exception:異常對(duì)象,只有在包含isErrorPage=”true”的頁(yè)面中才可以被使用。java.lang.Throwable
除了Servlet。與JSP 強(qiáng)相關(guān) 的技術(shù)還有EL表達(dá)式和JSP標(biāo)簽(JSTL),下面會(huì)接著介紹。
- <!-- javax命名空間版本(Tomcat 9.x及以下版本支持) -->
- <dependency>
- <groupId>javax.servlet.jsp</groupId>
- <artifactId>javax.servlet.jsp-api</artifactId>
- <version>2.3.3</version>
- <scope>provided</scope>
- </dependency>
- <!-- jakarta命名空間版本(Tomcat 10.x及以上版本支持) -->
- <dependency>
- <groupId>jakarta.servlet.jsp</groupId>
- <artifactId>jakarta.servlet.jsp-api</artifactId>
- <version>3.0.0</version>
- <!-- <version>2.3.6</version> 此版本命名空間同javax -->
- <scope>provided</scope>
- </dependency>
版本歷史
由于JSP的本質(zhì)就是Servlet,它的的版本號(hào)需要與Servlet對(duì)應(yīng)看待。
版本 | 發(fā)布日期 | JSR版本 | 對(duì)應(yīng)Servlet版本 |
---|---|---|---|
JSP 1.1 | 2000.07 | JSR 906 | Servlet 2.2 |
JSP 1.2 | 2002.06 | JSR 53 | Servlet 2.3 |
JSP 2.0 | 2003.11 | JSR 152 | Servlet 2.4 |
JSP 2.1 | 2005.09 | JSR 245 | Servlet 2.5 |
JSP 2.2 | 2009.12 | JSR 245(升級(jí)版) | Servlet 3.0 |
JSP 2.3 | 2013.05 | JSR 372(升級(jí)版) | Servlet 3.1 |
JSP 3.0 | 2020.11 | ----(Jakarta旗下) | Servlet 5.x |
Spring Boot相關(guān):Spring Boot從1.x版本開始就一直沒有“帶”JSP一起玩,若要Spring Boot支持JSP需要特殊開啟。
JSP 2.0是個(gè)重要版本,最重要的特性就是開始支持EL表達(dá)式了,可以用它來訪問應(yīng)用程序數(shù)據(jù)。JSP 2.3版本可斷定是最后一個(gè)版本,因?yàn)镴SP已走到盡頭,成為歷史。
生存現(xiàn)狀
JSP誕生之后,程序員寫頁(yè)面寫得確實(shí)很爽了。但是,它帶來了壞處:很多程序員同學(xué)將業(yè)務(wù)邏輯、頁(yè)面展示邏輯都往JSP塞,耦合在一起,導(dǎo)致JSP扛不住了,更重要的是程序員扛不住了,非常凌亂。
雖然后面出現(xiàn)了EL表達(dá)式和JSTL標(biāo)簽來幫助程序員不要在JSP里寫Java代碼,但只要不是強(qiáng)制的你能限制住自由的程序員么?然后呢,后來出現(xiàn)了Freemarker和Velocity這種模板引擎,使得程序員沒有辦法在頁(yè)面上寫Java代碼了,達(dá)到了分離的效果。
模板引擎出現(xiàn)后,JSP的地位已經(jīng)岌岌可危了。但真正殺死它的還是前端的崛起,從而進(jìn)入前后端完全分離的狀態(tài),至此基本可以宣布JSP(甚至包括模板引擎)的死亡。
所以JSP目前的生存狀態(tài)是:基本死亡狀態(tài)。你看,這不Spring Boot(默認(rèn))都不帶他玩了嘛~
實(shí)現(xiàn)(框架)
與Servlet相同的Web容器。
代碼示例
導(dǎo)包。由于我們不可能直接使用JSP的API,因此99.9999%情況下無需導(dǎo)包。
- 無需導(dǎo)包
創(chuàng)建webapp內(nèi)容文件夾。這點(diǎn)很重要,因?yàn)槭且獎(jiǎng)?chuàng)建一個(gè)web文件夾,以IDEA為例:在jsp-demo工程下添加web模塊圖片圖片完成后工程目錄結(jié)構(gòu)如下:
完成后工程目錄結(jié)構(gòu)如下:
值得一提的是:web目錄名稱叫什么無所謂(只是很多喜歡叫webapp、webroot等),重要的是要有這個(gè)小圓點(diǎn)。不乏聽見不少小伙伴說這個(gè)目錄名必須叫webapp,其實(shí)它名字叫什么、甚至位置放在哪都無所謂,重要是找得到就行。掌握原理,一通百通。
這里附上HelloJsp的內(nèi)容:
- /**
- * 在此處添加備注信息
- *
- * @author YourBatman. <a href=mailto:yourbatman@aliyun.com>Send email to me</a>
- * @site https://yourbatman.cn
- * @date 2021/9/12 06:26
- * @since 0.0.1
- */
- @WebServlet(urlPatterns = {"/hellojsp"})
- public class HelloJsp extends HttpServlet {
- @Override
- protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
- RequestDispatcher requestDispatcher = request.getRequestDispatcher("hello.jsp");
- // 放在WBE-INF下面的.jsp頁(yè)面必須通過Servlet轉(zhuǎn)發(fā)才能訪問到,更加安全
- // RequestDispatcher requestDispatcher = request.getRequestDispatcher("/WEB-INF/hello.jsp");
- requestDispatcher.forward(request, response);
- }
- }
以war包形式部署至Tomcat圖片瀏覽器訪問下面兩個(gè)路徑均可得到響應(yīng)結(jié)果:
- http://localhost:8080/hellojsp:請(qǐng)求 -> Servlet轉(zhuǎn)發(fā) -> jsp頁(yè)面(即使jsp頁(yè)面放到WEB-INF目錄下依舊可訪問)
- http://localhost:8080/hello.jsp:請(qǐng)求 -> jsp頁(yè)面(此直接方式只能訪問非WEB-INF目錄下的jsp文件)
頁(yè)面響應(yīng):
再?gòu)?qiáng)調(diào)一遍:自Servlet 3.0之后,web.xml部署描述符并非必須。即使有jsp頁(yè)面也是一樣~~~
工程源代碼:https://github.com/yourbatman/BATutopia-java-ee
EL表達(dá)式
Expression Language表達(dá)式語言。EL表達(dá)式語言的靈感來自于ECMAScript和XPath表達(dá)式語言(表達(dá)式語言當(dāng)然還有比較著名的Spring的SpEL,以及OGNL),它提供了在 JSP 中簡(jiǎn)化表達(dá)式的方法,目的是替代掉在Jsp里寫Java代碼,讓Jsp的代碼更加簡(jiǎn)化。
基本語法為:${EL表達(dá)式 },只能讀取數(shù)據(jù)不能設(shè)置數(shù)據(jù)(設(shè)置數(shù)據(jù)用JSP內(nèi)或者Servlet里的Java代碼均可)
請(qǐng)務(wù)必注意,基本語法中右邊的}的前面有個(gè)空格,使用時(shí)請(qǐng)務(wù)必注意
在EL中有四大域?qū)ο蠛?1大內(nèi)置對(duì)象這么一說:
- 請(qǐng)求參數(shù)
- 1、param 包含所有的參數(shù)的Map,可以獲取參數(shù)返回String。其底層實(shí)際調(diào)用request.getParameter()
- - name=${param.name }
- 2、paramValues 包含所有參數(shù)的Map,可以獲取參數(shù)的數(shù)組返回String[]。其底層實(shí)際調(diào)用request.getParameterValues()
- - hobby[0]=${paramValues.hobby[0] }
- 頭信息
- 3、header 包含所有的頭信息的Map,可以獲取頭信息返回String。
- - ${header.Connection }
- 4、headerValues 包含所有的頭信息的Map,可以獲取頭信息數(shù)組返回String[]。
- - ${headerValues["user-agent"][0] }
- Cookie
- 5、cookie包含所有cookie的Map,key為Cookie的name屬性值
- - ${cookie.JSESSIONID.name }
- 初始化參數(shù)
- 6、iniParam 包含所有的初始化參數(shù)(一般配在web.xml里)的Map,可以獲取初始化的參數(shù)
- - ${initParam.username} ${initParam.password}
- 四大作用域(重點(diǎn))
- 7、pageScope 包含page作用域內(nèi)的Map
- - ${pageScope.name }
- 8、requestScope 包含request作用域內(nèi)的Map
- - ${requestScope.name }
- 9、 包含session作用域內(nèi)的Map
- - ${sessionScope.name }
- 10、applicationScope 包含application作用域內(nèi)的Map
- - ${applicationScope.name }
- 頁(yè)面上下文
- 11、pageContext 包含頁(yè)面內(nèi)的變量的Map,可獲取JSP中的九大內(nèi)置對(duì)象
- - ${pageContext.request.scheme }
- - ${pageContext.session.id}
- <!-- javax命名空間版本(Tomcat 9.x及以下版本支持) -->
- <dependency>
- <groupId>javax.el</groupId>
- <artifactId>javax.el-api</artifactId>
- <version>3.0.0</version>
- </dependency>
- <!-- jakarta命名空間版本(Tomcat 10.x及以上版本支持) -->
- <dependency>
- <groupId>jakarta.el</groupId>
- <artifactId>jakarta.el-api</artifactId>
- <version>4.0.0</version>
- <!-- <version>3.0.3</version> 此版本命名空間同javax -->
- </dependency>
- 除此之外,還可以通過Tomcat的GAV直接導(dǎo)入,版本號(hào)同Tomcat
- <dependency>
- <groupId>org.apache.tomcat</groupId>
- <artifactId>tomcat-el-api</artifactId>
- <version>Tomcat版本號(hào)</version> <!-- 9.x版本是javax.*,10.x以及后面是jakarta.* -->
- </dependency>
- 嵌入式Tomcat提供的實(shí)現(xiàn)
- <dependency>
- <groupId>org.apache.tomcat.embed</groupId>
- <artifactId>tomcat-embed-el</artifactId>
- <version>Tomcat版本號(hào)</version> <!-- 9.x版本是javax.*,10.x以及后面是jakarta.* -->
- </dependency>
- 另外,還有二合一的GAV:3.x版本的API和impl實(shí)現(xiàn)都在一個(gè)jar里。
- 4.x使用jakarta.*命名空間,并且API分離(依賴于)jakarta.el-api
- <dependency>
- <groupId>org.glassfish</groupId>
- <artifactId>jakarta.el</artifactId>
- <version>4.0.2</version>
- <!-- <version>3.0.3</version> 此版本命名空間同javax -->
- </dependency>
值得注意的是,EL并非Web獨(dú)享而是可獨(dú)立使用,因此它的scope用默認(rèn)的即可。另外,這只是API,并非Impl實(shí)現(xiàn),是不能直接運(yùn)行的,否則會(huì)遇到類似如下異常:
- Caused by: javax.el.ELException: Provider com.sun.el.ExpressionFactoryImpl not found
- at javax.el.FactoryFinder.newInstance(FactoryFinder.java:101)
- ...
版本歷史
EL從JSP 2.0版本開始引入,用于在JSP頁(yè)面獲取數(shù)據(jù)的簡(jiǎn)單方式。因此它是隨著JSP的發(fā)展而出現(xiàn)的,只是可獨(dú)立使用而已。
版本 | 發(fā)布日期 | JSR版本 | 對(duì)應(yīng)JSP版本 | 對(duì)應(yīng)Servlet版本 |
---|---|---|---|---|
EL 2.0 | 2003.11 | JSR 152 | JSP 2.0 | Servlet 2.4 |
EL 2.2 | 2009.12 | JSR 245 | JSP 2.2 | Servlet 2.5 |
EL 3.0 | 2013.05 | JSR 341 | JSP 2.3 | Servlet 3.1 |
EL 4.0 | 2020.10 | 納入Jakarta | JSP 3.0 | Servlet 5.0 |
EL表達(dá)式3.0于2013年4月份發(fā)布(可認(rèn)為是最后一次功能升級(jí)),它的新特性包括:字符串拼接操作符、賦值(以前只能讀取,現(xiàn)在可以賦值啦)、分號(hào)操作符、對(duì)象方法調(diào)用(以前只能用JavaBean屬性導(dǎo)航)、Lambda表達(dá)式、靜態(tài)字段/方法調(diào)用、構(gòu)造器調(diào)用、Java8集合操作。具體就不一一舉例了,詳細(xì)情況可閱讀我收錄的JSR文檔。
生存現(xiàn)狀
隨著JSP的消亡,EL的存在感越來越弱。
好在它可以作為單獨(dú)的表達(dá)式語言使用,有Hibernate Validator對(duì)它是強(qiáng)依賴,所以生命力還行。但由于Hibernate Validator里使用得簡(jiǎn)單,所以EL并沒有必要再更新(動(dòng)力不足)。
實(shí)現(xiàn)(框架)
EL大部分情況下伴隨著JSP一起使用,所以交由Web容器去解析實(shí)現(xiàn)。
另外,EL作為一種表達(dá)式語言,也可以作為”工具“供以使用,比如著名的Hibernate Validator內(nèi)部就依賴于EL表達(dá)式語言來書寫校驗(yàn)規(guī)則(所以它在編譯期就強(qiáng)依賴于EL的API)。
代碼示例
在JSP中使用EL是由org.apache.tomcat:tomcat-jasper-el或者org.apache.tomcat.embed:tomcat-embed-jasper完成和JSP的整合,以及解析支持的。在JSP頁(yè)面里使用方式由于已經(jīng)過時(shí)(主要是使用示例一搜一大把),這里為了節(jié)約篇幅,就略了哈。
如果把EL當(dāng)做工具使用的話(比如Hibernate Validator用來錯(cuò)誤消息里插值用),需要了解一些API和常見用法,演示一下:
導(dǎo)包:
- 上面的GAV隨便選一個(gè)(記得太impl實(shí)現(xiàn),推薦org.glassfish:jakarta.el)
直接使用API書寫Demo
- /**
- * 在此處添加備注信息
- *
- * @author YourBatman. <a href=mailto:yourbatman@aliyun.com>Send email to me</a>
- * @site https://yourbatman.cn
- * @date 2021/9/12 10:12
- * @since 0.0.1
- */
- public class ElDemo {
- public static void main(String[] args) {
- ExpressionFactory factory = ELManager.getExpressionFactory();
- StandardELContext elContext = new StandardELContext(factory);
- // 將instance轉(zhuǎn)為對(duì)應(yīng)類型
- ValueExpression valueExpression = factory.createValueExpression("18", Integer.class);
- System.out.println(valueExpression.getValue(elContext));
- // 計(jì)算表達(dá)式的值
- valueExpression = factory.createValueExpression(elContext, "${1+1}", Integer.class);
- System.out.println(valueExpression.getValue(elContext));
- // 方法調(diào)用
- // MethodExpression methodExpression = factory.createMethodExpression(elContext, "${Math.addExact()}", Integer.class, new Class[]{Integer.class, Integer.class});
- // System.out.println(methodExpression.invoke(elContext, new Object[]{1, 2}));
- }
- }
- 運(yùn)行,結(jié)果輸出:
- 18
- 2
工程源代碼:https://github.com/yourbatman/BATutopia-java-ee
總結(jié)
現(xiàn)在越來越卷的IT行業(yè),衡量一個(gè)求職者的專業(yè)能力,深度往往比廣度更為重要。
正所謂這輩子聽過很多大道理,卻依舊過不好這一生;技術(shù)也一樣,聽過/知道過/使用過很多技術(shù),但依舊寫不出好的代碼。究其原因,就是理解不深刻。
自上而下的用,自底向上的學(xué),這是我個(gè)人一直秉承的一個(gè)觀念。知道一門技術(shù)、使用一門技術(shù)一般幾個(gè)小時(shí)or幾天就能大概搞定(畢竟如果一門技術(shù)入門很難的話也幾乎不太可能大眾化的流行起來),而理解一門技術(shù)的單位可能就是月、甚至是年了,這需要靜下心來學(xué)習(xí)和研究。