工具好,也要用得巧。 本文已被https://yourbatman.cn收录;女娲Knife-Initializr工程可公开访问啦;程序员专用网盘https://wangpan.yourbatman.cn;
你好,我是方向盘(YourBatman、方哥)。笔者的公号(Java方向盘)是保留地,只分享原创,不转载、不发商务广告!!!
✍前言
若你还不太清楚Java EE是什么,可先移步这里:什么是Java EE?
最近一段时间,将Java EE/Jakarta EE进行了科普,almost也许可能大概是全网最全的,颇受好评。本专栏全部内容可点击上方专栏标题查看,这里做简单陈列:
- 【方同学】Java EE几十种技术,“活着的”还剩几何(Web应用技术篇)
- 【方同学】Java EE几十种技术,“活着的”还剩几何(企业应用技术篇)
- 【方同学】Java EE几十种技术,“活着的”还剩几何(服务/安全/Java SE篇)
本以为科普到这就差不多了告一段落的,直到有小伙伴私信我说:看了这么多但依旧不知道Java EE如何使用?当头棒喝,作为一名Javaer,其实天天都在使用着Java EE。只是在Spring框架的襁褓之下,它优秀的抽象能力甚至让开发者感知不到底层技术的存在。
本着管生管养,管杀管埋的初心,决定再续写几篇,针对每一项(主流)Java技术给出示例,主要包括这几个方面:
- ✌版本历史
- ✌生存现状
- ✌实现(框架)
- ✌代码示例
所属专栏
- BATutopia-Java EE
相关下载
- 工程源代码:https://github.com/yourbatman/FXP-java-ee
- 【女娲Knife-Initializr工程】访问地址:http://152.136.106.14:8761
- Java开发软件包(Mac):https://wangpan.yourbatman.cn/s/rEH0 提取码:javakit
- 程序员专用网盘上线啦,开放注册送1G超小容量,帮你实践做减法:https://wangpan.yourbatman.cn
版本约定
- Java EE:6、7、8
- Jakarta EE:8、9、9.1
✍正文
Servlet
Servlet是一种基于Java的动态Web资源动态Web资源技术,类似的技术还有ASP、PHP等。
代码语言:javascript复制<!-- 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规范由Sun Microsystems公司创建,1.0版于1997年6月完成。从2.3版开始,该规范是在JCP下开发。
版本 | 发布日期 | 隶属于 | JSR版本 | 焦点说明 |
---|---|---|---|---|
1.0 | 1997.06 | - | - | 首个版本,由Sun公司发布 |
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,增加了关于Session的Listener(如HttpSessionListener) |
2.4 | 2003.08 | J2EE 1.4 | JSR 154 | 没增加大的新内容,对不严格的地方加了些校验,如:对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 | 史上最大变革。动态链接库和插件能力(Spring MVC利用此能力通过ServletContainerInitializer进行全注解驱动开发)、模块化开发、异步Servlet、安全性、新的文件上传API、支持WebSocket,新的注解(@WebServlet、@WebFilter、@WebListener),可脱离web.xml全注解驱动,此版本功能已经很完整了,应用的主流 |
3.1 | 2013.5 | Java EE 7 | JSR 340 | 新增非阻塞式IO。Spring的Web Flux若要运行在Servlet容器,至少需要此版本,因为从此版本起才有非阻断输入输出的支持 |
4.0 | 2017.09 | Java EE 8 | JSR 369 | 支持Http/2。从而支持服务器推技术,新的映射发现接口HttpServletMapping可用来提高内部的运行效率 |
5.0 | 2020.11 | Jakarta EE 9 | JSR 369 | 同Servlet 4.0(只是命名空间从javax.*变为了jakarta.*而已) |
Spring Boot相关:
- 2.0.0.RELEASE版本(2018.05):正式内置Servlet 3.1,毕竟Spring Web Flux从此版本开始(Spring 5)
- 2.1.0.RELEASE版本(2018.10):升级到Servlet 4.x,直到现在(2.6.x)也依旧是4.x版本
- 2.2.0.RELEASE版本(2019.10):开始支持jakarta.servlet这个GAV,(和javax.servlet)二者并行
- 2.5.0/2.6.0版本(2021.05):无变化
- 3.0.0版本(预计2022.12):基于Spring 6.x、Jakarta EE 9,基于GraalVM全面拥抱云原生的新一代框架
说明:Spring Boot 2.6和2.7都还会基于Spring Framework 5.3.x内核。Spring Framework 6.0版本在2021年9月正式拉开序幕,将基于全新的Jakarta EE 9(命名空间为jakarta.*,不向下兼容)平台开发,相应的Spring Boot 3也会基于此内核
✌生存现状
随着Spring 5的发布推出WebFlux,Servlet技术从之前的必选项变为可选项。
但考虑到业务开发使用WebFlux收益甚微但开发调试成本均增加,因此实际情况是基于Servlet的Spring MVC技术依旧是主流,暂时地位不可撼动,依旧非常活跃。
✌实现(框架)
由于Servlet由Web容器负责创建并调用,因此只要实现了Servlet规范的Web容器均可作为它的实现(框架),如Tomcat、Jetty、Undertow、JBoss、Glassfish等。
✌代码示例
导入依赖包:
- scope一般provided即可,因为Web容器里会自带此Jar
- Spring Boot场景下无需显示导入,因为Tomcat已内嵌(相关API)
servlet-api的GAV
继承HttpServlet写一个用于处理Http请求的Servlet处理器
代码语言:javascript复制/**
* 在此处添加备注信息
*
* @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(小提示:<packaging>war</packaging>
),并启动Tomcat
浏览器http://localhost:8080/hello
即可完成正常访问。
说明:自Servlet 3.0之后,web.xml部署描述符并非必须(全注解即可搞定)
工程源代码:https://github.com/yourbatman/FXP-java-ee
JSP
Java Server Page的简称。那么,有了Servlet为何还需要JSP?其实它俩都属于动态Web技术,只是Servlet它用于输出页面简直太繁琐了(每一句html都需要用resp.getWriter()逐字逐句的输出),所以才出现了JSP技术来弥补其不足。
它使用JSP标签在HTML网页中插入Java代码。语法格式为:<% Java代码 %>
。它有九大内置对象这么一说:
1、request:请求对象。javax.servlet.http.HttpServletRequest
2、response:响应对象。javax.servlet.http.HttpServletResponse
3、session:会话对象。javax.servlet.http.HttpSession
4、application:应用程序对象。javax.servlet.ServletContext
5、config:配置对象。javax.servlet.ServletConfig
6、page:页面对象。当前jsp程序本身,相当于this
7、pageContext:页面上下文对象。javax.servlet.jsp.PageContext
8、out:输出流对象,用于输出内容到浏览器。javax.servlet.jsp.jspWriter
9、exception:异常对象,只有在包含isErrorPage=”true”的页面中才可以被使用。java.lang.Throwable
除了Servlet。与JSP 强相关 的技术还有EL表达式和JSP标签(JSTL),下面会接着介绍。
代码语言:javascript复制<!-- 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的本质就是Servlet,它的的版本号需要与Servlet对应看待。
版本 | 发布日期 | JSR版本 | 对应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(升级版) | Servlet 3.0 |
JSP 2.3 | 2013.05 | JSR 372(升级版) | Servlet 3.1 |
JSP 3.0 | 2020.11 | ----(Jakarta旗下) | Servlet 5.x |
Spring Boot相关:Spring Boot从1.x版本开始就一直没有“带”JSP一起玩,若要Spring Boot支持JSP需要特殊开启。
JSP 2.0是个重要版本,最重要的特性就是开始支持EL表达式了,可以用它来访问应用程序数据。JSP 2.3版本可断定是最后一个版本,因为JSP已走到尽头,成为历史。
✌生存现状
JSP诞生之后,程序员写页面写得确实很爽了。但是,它带来了坏处:很多程序员同学将业务逻辑、页面展示逻辑都往JSP塞,耦合在一起,导致JSP扛不住了,更重要的是程序员扛不住了,非常凌乱。
虽然后面出现了EL表达式和JSTL标签来帮助程序员不要在JSP里写Java代码,但只要不是强制的你能限制住自由的程序员么?然后呢,后来出现了Freemarker和Velocity这种模板引擎,使得程序员没有办法在页面上写Java代码了,达到了分离的效果。
模板引擎出现后,JSP的地位已经岌岌可危了。但真正杀死它的还是前端的崛起,从而进入前后端完全分离的状态,至此基本可以宣布JSP(甚至包括模板引擎)的死亡。
所以JSP目前的生存状态是:基本死亡状态。你看,这不Spring Boot(默认)都不带他玩了嘛~
✌实现(框架)
与Servlet相同的Web容器。
✌代码示例
导包。由于我们不可能直接使用JSP的API,因此99.9999%情况下无需导包。
代码语言:javascript复制无需导包
创建webapp内容文件夹。这点很重要,因为是要创建一个web文件夹,以IDEA为例:在jsp-demo工程下添加web模块
完成后工程目录结构如下:
值得一提的是:web目录名称叫什么无所谓(只是很多喜欢叫webapp、webroot等),重要的是要有这个小圆点。不乏听见不少小伙伴说这个目录名必须叫webapp,其实它名字叫什么、甚至位置放在哪都无所谓,重要是找得到就行。掌握原理,一通百通。
这里附上HelloJsp的内容:
代码语言:javascript复制/**
* 在此处添加备注信息
*
* @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页面必须通过Servlet转发才能访问到,更加安全
// RequestDispatcher requestDispatcher = request.getRequestDispatcher("/WEB-INF/hello.jsp");
requestDispatcher.forward(request, response);
}
}
以war包形式部署至Tomcat
浏览器访问下面两个路径均可得到响应结果:
http://localhost:8080/hellojsp
:请求 -> Servlet转发 -> jsp页面(即使jsp页面放到WEB-INF目录下依旧可访问)http://localhost:8080/hello.jsp
:请求 -> jsp页面(此直接方式只能访问非WEB-INF目录下的jsp文件)
页面响应:
再强调一遍:自Servlet 3.0之后,web.xml部署描述符并非必须。即使有jsp页面也是一样~~~
工程源代码:https://github.com/yourbatman/FXP-java-ee
EL表达式
Expression Language表达式语言。EL表达式语言的灵感来自于ECMAScript和XPath表达式语言(表达式语言当然还有比较著名的Spring的SpEL,以及OGNL),它提供了在 JSP 中简化表达式的方法,目的是替代掉在Jsp里写Java代码,让Jsp的代码更加简化。
基本语法为:${EL表达式 }
,只能读取数据不能设置数据(设置数据用JSP内或者Servlet里的Java代码均可)
请务必注意,基本语法中右边的
}
的前面有个空格,使用时请务必注意
在EL中有四大域对象和11大内置对象这么一说:
- 请求参数
1、param 包含所有的参数的Map,可以获取参数返回String。其底层实际调用request.getParameter()
- name=${param.name }
2、paramValues 包含所有参数的Map,可以获取参数的数组返回String[]。其底层实际调用request.getParameterValues()
- hobby[0]=${paramValues.hobby[0] }
- 头信息
3、header 包含所有的头信息的Map,可以获取头信息返回String。
- ${header.Connection }
4、headerValues 包含所有的头信息的Map,可以获取头信息数组返回String[]。
- ${headerValues["user-agent"][0] }
- Cookie
5、cookie包含所有cookie的Map,key为Cookie的name属性值
- ${cookie.JSESSIONID.name }
- 初始化参数
6、iniParam 包含所有的初始化参数(一般配在web.xml里)的Map,可以获取初始化的参数
- ${initParam.username} ${initParam.password}
- 四大作用域(重点)
7、pageScope 包含page作用域内的Map
- ${pageScope.name }
8、requestScope 包含request作用域内的Map
- ${requestScope.name }
9、 包含session作用域内的Map
- ${sessionScope.name }
10、applicationScope 包含application作用域内的Map
- ${applicationScope.name }
- 页面上下文
11、pageContext 包含页面内的变量的Map,可获取JSP中的九大内置对象
- ${pageContext.request.scheme }
- ${pageContext.session.id}
代码语言:javascript复制<!-- 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直接导入,版本号同Tomcat
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-el-api</artifactId>
<version>Tomcat版本号</version> <!-- 9.x版本是javax.*,10.x以及后面是jakarta.* -->
</dependency>
嵌入式Tomcat提供的实现
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-el</artifactId>
<version>Tomcat版本号</version> <!-- 9.x版本是javax.*,10.x以及后面是jakarta.* -->
</dependency>
另外,还有二合一的GAV:3.x版本的API和impl实现都在一个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独享而是可独立使用,因此它的scope用默认的即可。另外,这只是API,并非Impl实现,是不能直接运行的,否则会遇到类似如下异常:
代码语言:javascript复制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页面获取数据的简单方式。因此它是随着JSP的发展而出现的,只是可独立使用而已。
版本 | 发布日期 | JSR版本 | 对应JSP版本 | 对应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表达式3.0于2013年4月份发布(可认为是最后一次功能升级),它的新特性包括:字符串拼接操作符、赋值(以前只能读取,现在可以赋值啦)、分号操作符、对象方法调用(以前只能用JavaBean属性导航)、Lambda表达式、静态字段/方法调用、构造器调用、Java8集合操作。具体就不一一举例了,详细情况可阅读我收录的JSR文档。
✌生存现状
随着JSP的消亡,EL的存在感越来越弱。
好在它可以作为单独的表达式语言使用,有Hibernate Validator对它是强依赖,所以生命力还行。但由于Hibernate Validator里使用得简单,所以EL并没有必要再更新(动力不足)。
✌实现(框架)
EL大部分情况下伴随着JSP一起使用,所以交由Web容器去解析实现。
另外,EL作为一种表达式语言,也可以作为”工具“供以使用,比如著名的Hibernate Validator
内部就依赖于EL表达式语言来书写校验规则(所以它在编译期就强依赖于EL的API)。
✌代码示例
在JSP中使用EL是由org.apache.tomcat:tomcat-jasper-el
或者org.apache.tomcat.embed:tomcat-embed-jasper
完成和JSP的整合,以及解析支持的。在JSP页面里使用方式由于已经过时(主要是使用示例一搜一大把),这里为了节约篇幅,就略了哈。
如果把EL当做工具使用的话(比如Hibernate Validator
用来错误消息里插值用),需要了解一些API和常见用法,演示一下:
导包:
代码语言:javascript复制上面的GAV随便选一个(记得太impl实现,推荐org.glassfish:jakarta.el)
直接使用API书写Demo
代码语言:javascript复制/**
* 在此处添加备注信息
*
* @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转为对应类型
ValueExpression valueExpression = factory.createValueExpression("18", Integer.class);
System.out.println(valueExpression.getValue(elContext));
// 计算表达式的值
valueExpression = factory.createValueExpression(elContext, "${1 1}", Integer.class);
System.out.println(valueExpression.getValue(elContext));
// 方法调用
// 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}));
}
}
运行,结果输出:
18
2
工程源代码:https://github.com/yourbatman/FXP-java-ee
✍总结
现在越来越卷的IT行业,衡量一个求职者的专业能力,深度往往比广度更为重要。
正所谓这辈子听过很多大道理,却依旧过不好这一生;技术也一样,听过/知道过/使用过很多技术,但依旧写不出好的代码。究其原因,就是理解不深刻。
自上而下的用,自底向上的学,这是我个人一直秉承的一个观念。知道一门技术、使用一门技术一般几个小时or几天就能大概搞定(毕竟如果一门技术入门很难的话也几乎不太可能大众化的流行起来),而理解一门技术的单位可能就是月、甚至是年了,这需要静下心来学习和研究。
本专栏文章
- 【方同学】Java EE几十种技术,“活着的”还剩几何(Web应用技术篇)
- 【方同学】Java EE几十种技术,“活着的”还剩几何(企业应用技术篇)
- 【方同学】Java EE几十种技术,“活着的”还剩几何(服务/安全/Java SE篇)
我是方向盘(YourBatman):前25年不会写Hallo World、早已毕业的大龄程序员。高中时期《梦幻西游》骨灰玩家,网瘾失足、清考、延期毕业、房产中介、保险销售、送外卖…是我不可抹灭的黑标签