本系列文章旨在记录和总结自己在Java Web开发之路上的知识点、经验、问题和思考,希望能帮助更多(Java)码农和想成为(Java)码农的人。
目录
- 介绍
- Servlet要生成HTML
- JSP核心原理
- JSP基本语法
- 这样更好理解
- 总结
介绍
前面的文章使用Servlet技术实现了一个简单的租房网平台。
但是,它有很多不足,最直观的感受应该就是:
- 在Java代码里输出HTML代码实在是太难看、太繁琐、太容易出错了;
- 如果想改变一下HTML呈现效果就必须修改HTML代码,所以还必须重新编译Java代码。
所以,JSP技术应运而生了。
JSP的全称是JavaServer Page,截止到目前最新版本好像是2.3,但在JCP官网(https://jcp.org/en/home/index)又只能搜到2.1版本(关于JCP上如何下载规范可以参考这篇文章)。如果有读者知道如何下载JSP 2.3版本的规范(或者没有),请告知一下。
Servlet要生成HTML
对于Web应用来说,HTML页面往往很重要,数量也比较多,每个页面中的HTML代码也比较多。
而动态生成的HTML页面也是如此,大部分展示结构/效果/元素都是相同的,仅仅只是数据不一样。
可以这么说,Servlet代码中大部分是HTML代码。
所以,我们是否能够运用逆向思维:在HTML页面中通过某种语法嵌入少量的Java代码,然后让工具自动生成Servlet的代码?
JSP核心原理
答案当然是可行的,JSP正是这样的技术。
这种在HTML页面中嵌入Java代码的页面就叫JSP页面。我们编写JSP页面,由Servlet容器转换成Servlet并编译成Java字节码文件,当第一个该JSP页面的请求到来时Servlet容器就会加载对应的Servlet并生成Servlet对象,后面的处理就跟前面所介绍的Servlet技术相同了。
所以,第一次请求JSP页面的时候,总会比较慢,因为需要转换成Servlet并编译,再加载Servlet类文件并生成Servlet实例。
当然,我们(就是Servlet容器的开发者)就必须改造Servlet容器,让它能够识别、转换JSP页面,然后能够自动的编译转换成的Servlet代码。这样的Servlet容器就叫做JSP容器,所以合起来叫做Servlet/JSP容器。
Servlet/JSP容器甚至能够自动检测到JSP页面的修改并重新编译和加载,所以,我们修改JSP页面后并不需要重新启动Servlet/JSP容器。
可以说,JSP的核心原理/本质就是Servlet。
JSP基本语法
既然是HTML代码中嵌入Java代码,那么JSP页面的主体当然就是HTML,这部分就使用HTML的语法编写。
那么Servlet/JSP容器是如何识别嵌入的Java代码呢?
JSP使用符号“”和“%>”来将Java代码或与Java代码相关的东西包围住,比如:
- “”和“--%>”及其中间的内容是JSP的注释,它不会发送到浏览器端,区别于HTML的注释“”。
- “”和“%>”及其中间的的内容是JSP的指令,用来设置页面的一些参数,import依赖的Java包,包含其他JSP页面等等。
- “”和“%>”中间可以直接写Java代码,这叫JSP的脚本。
- “”和“%>”及其中间的的内容是JSP的表达式,用来直接输出表达式的计算结果。
- “”和“%>”及其中间的的内容是JSP的声明,用来声明该页面使用的变量和方法。
所以,Servlet/JSP容器一旦遇到上述的符号,它就知道该如何转换成Java代码了。
JSP其他技术
事实上,JSP技术在不断的演化(任何技术都是这样,演化就是为了进步,发现不足,解决不足)。
JSP其他的相关技术有:
- JSP的动作,它很像HTML或XML的标签,我猜测它就是使用下面的JSP的标签技术实现的,比如:
- JSP的标签,这包括JSP自带的标准标签JSTL(JSP Standard Tag Library,即JSP标准标签库)、可以扩展自己所需要标签的自定义标签技术和标签文件技术。它的使用很像XML标签,当然,扩展自己的标签就比较复杂了。
- 表达式语言(Expression Language,简称EL),似乎此技术与JSP技术是相互独立的,但JSP里面用它还是相当频繁的,不知道当时开发EL是否主要就是为了在JSP中使用,我没有去深究了。
- JSP页面片段,主要是为了消除重复,提高复用。
可以明确的是,目前JSP的主要用途就是起到一个模板的作用,并尽量采用EL来访问应用数据,而不再使用JSP声明、JSP表达式和JSP脚本来实现业务逻辑,业务逻辑交给独立的Servlet来实现(当然,后来出现的MVC模式中Servlet仅用来充当控制器)。
这样更好理解
我们把JSP页面的内容分成两大类:
- 一类就是HTML代码,这个就像编写普通的HTML页面那样编写就行,我们把这类代码叫做模板元素。
- 另一类就是JSP相关的部分,这是通过规定的某种符号来识别的,这类代码就叫做JSP语法元素。
无论如何,一个JSP页面都会转换成一个Servlet,即不管是模板元素还是JSP语法元素,它最终都会转换成Java代码。
所以,转换成的这些Java代码必然在一个Servlet类的源代码中,而Servlet接口中最重要的方法就是那个service()方法。
这样看来,转换成的这些Java代码可以看作是service()方法中的内容,比如:
- 模板元素其实转换成Java代码就是:writer.println("")等类似这样的。
- JSP脚本则直接拷贝即可。
- JSP隐式对象实际上是Servlet/JSP容器生成并传进来的参数,比如request就是HttpServletRequest实例、application就是ServletContext实例、config就是ServletConfig实例、pageContext就是javax.servlet.jsp.PageContext实例、out是javax.servlet.jsp.JspWriter实例(类似从ServletResponse.getWriter()获得的Writer)等等。
- JSP表达式就转换成:writer.print("表达式的计算结果")。
- JSP动作和标签肯定就转换成对应的处理类的调用代码。
- EL则转成访问附加到某个隐式对象的属性中的数据,即getAttribute("xxx")名为xxx的属性对象(它其实是你的业务实体类对象),然后就可以访问xxx中的数据。
还有一些则转换成Servlet类文件的其他内容,比如:
- JSP的page指令导入Java包的部分。
- JSP声明可以声明该页面使用的变量和方法,即该页面转换成的Servlet类的成员变量和成员方法。
总结
现在,我们就可以使用JSP技术来改造前面的租房网应用了。
- 我们要善于使用逆向思维;
- 一旦你觉得写代码很繁琐,很多重复的地方,那么就表示哪里有问题,或者应该改进,甚至有可能抽象出来一种新技术;
- JSP的本质就是Servlet,因为它需要被转换成Servlet;
- JSP目前一般用来当HTML模板,仅使用EL来访问数据,而不在其中编写Java脚本了。