之前一直说的是Servlet,但是由于只是做一个Demo,并没有完全显示一个界面,所以它的两个缺点没有显示出来:
【javaweb|Java Web基础知识之JSP(穿上马甲我照样认识你)】
- 所有的HTML标签和文本都必须使用一个字符串形式通过ServletResponse的实例写出去;
- 所有的文本和样式必须得进行硬编码,也就是说如果你想稍微更改一下前端的显示的话,必须得重新编译Servlet实例;
- 当第一次请求JSP页面时,会将转换成一个页面实现类,转换的工作是由servlet容器完成的,所生成的servlet的名称由servlet容器决定,如果转换错误则会发出错误到客户端;如果转换成功则会进一步被servlet容器编译(此处是动态编译)成servlet类,之后容器加载和实例化该类,并执行相应的生命周期方法;
- 对该页面的后续请求,servlet容器也不傻,每次都转换它也累,所以它会检查该JSP页面自从上一次转换后是否发生修改,如果修改过则会重新转换、编译并执行,如果没有则执行内存中已经存在的JSP类实例。
- 配置应用程序启动时就转换和编译所有的JSP页面,而不是在接收第一次请求才做这些工作;
- 预先编译JSP页面,并将它们以servlet的形式进行部署;
- javax.servlet.jsp:包含核心的接口和类,servlet容器使用这些接口和类将JSP页面转换成servlet,最重要的莫过于JspPage和HttpJspPage;
- javax.servlet.jsp.el:包含支持JSP中的EL表达式的相关的类和接口
- javax.servlet.jsp.tagext:包含用于开发定制标签的类型,也就是如果你想自己写一个标签必须用到这里的API;
- javax.el:为Unified EL提供API;
文章图片
这样我们就可以相信了,JSP页面确实是一个Servlet(我没有胡诌!!)。为了直观一些,我们直接看JSP页面转换生成之后的servlet类,通过对比进一步解释JSP的本质,如下是一个JSP页面index.jsp:
Hello World!
可以看出这就是由普通的HTML标签组成的文本(你骗我,这不是JSP,这就是HTML),前面说过JSP页面其实就是由HTML元素和java代码组成的。 转换成对应的servlet,index_jsp.java如下,从名字可以看出jtomcat将jsp页面转换成servlet时的名称是由“名称_jsp”组成:
/*
* Generated by the Jasper component of Apache Tomcat
* Version: Apache Tomcat/7.0.68
* Generated at: 2016-04-16 12:01:36 UTC
* Note: The last modified time of this file was set to
*the last modified time of the source file after
*generation to assist with modification tracking.
*/
package org.apache.jsp;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent {private static final javax.servlet.jsp.JspFactory _jspxFactory =
javax.servlet.jsp.JspFactory.getDefaultFactory();
private static java.util.Map _jspx_dependants;
private volatile javax.el.ExpressionFactory _el_expressionfactory;
private volatile org.apache.tomcat.InstanceManager _jsp_instancemanager;
public java.util.Map getDependants() {
return _jspx_dependants;
}public javax.el.ExpressionFactory _jsp_getExpressionFactory() {
if (_el_expressionfactory == null) {
synchronized (this) {
if (_el_expressionfactory == null) {
_el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
}
}
}
return _el_expressionfactory;
}public org.apache.tomcat.InstanceManager _jsp_getInstanceManager() {
if (_jsp_instancemanager == null) {
synchronized (this) {
if (_jsp_instancemanager == null) {
_jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());
}
}
}
return _jsp_instancemanager;
}public void _jspInit() {
}public void _jspDestroy() {
}public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
throws java.io.IOException, javax.servlet.ServletException {final javax.servlet.jsp.PageContext pageContext;
javax.servlet.http.HttpSession session = null;
final javax.servlet.ServletContext application;
final javax.servlet.ServletConfig config;
javax.servlet.jsp.JspWriter out = null;
final java.lang.Object page = this;
javax.servlet.jsp.JspWriter _jspx_out = null;
javax.servlet.jsp.PageContext _jspx_page_context = null;
try {
response.setContentType("text/html");
pageContext = _jspxFactory.getPageContext(this, request, response,
null, true, 8192, true);
_jspx_page_context = pageContext;
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
_jspx_out = out;
out.write("\r\n");
out.write("\r\n");
out.write("Hello World!\r\n");
out.write("\r\n");
out.write("\r\n");
} catch (java.lang.Throwable t) {
if (!(t instanceof javax.servlet.jsp.SkipPageException)){
out = _jspx_out;
if (out != null && out.getBufferSize() != 0)
try {
if (response.isCommitted()) {
out.flush();
} else {
out.clearBuffer();
}
} catch (java.io.IOException e) {}
if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
else throw new ServletException(t);
}
} finally {
_jspxFactory.releasePageContext(_jspx_page_context);
}
}
}
从上面的源代码来看index_jsp继承了org.apache.jasper.runtime.HttpJspBase类,那么这个类是什么?从tomcat的javaDoc来看,如下:
文章图片
这个类继承了了HttpServlet并且还实现了JspPage和HttpJspPage接口。在JSP中的主体代码被转换成index_jsp类的_jspService()方法中的内容,并且这个方法是由Httpbase中的service()方法调用的,如下:
/**
* Entry point into service.
*/
@Override
public final void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
_jspService(request, response);
}@Override
public abstract void _jspService(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException;
当index_jsp继承了Httpbase时,则必须实现_jspService()方法,那么当响应来的时候就可以被httpbase实例调用。
三、 JSP隐式对象 其实之前每次看到这部分的时候,我都会很懵,因为不知道这些隐式对象是怎么起作用的(人类总是对自己无知的事情或者隐藏的事情充满了好奇和恐惧)。这次顺便一起说明一下,也是对自己的督促。我们知道每次servlet初始化或者接受请求的时候servlet容器都会传给生命周期方法一些对象,那么问题来了,JSP页面怎么接受这些对象?是在JSP页面转换成servlet的时候隐式地将这些对象加入到生成的servlet类中的,看上面由JSP页面生成的servlet类可以看出这一点,下面是隐式对象和类型之间的对应关系:
对象 | 类型 |
request | javax.servlet.http.HttpServletRequest |
response | javax.servlet.http.HttpServletResponse |
session | javax.servlet.http.HttpSession |
application | javax.servlet.ServletContext |
config | javax.servlet.ServletConfig |
exception | java.lang.Exception |
out | javax.servlet.jsp.JspWriter |
pageContext | javax.servlet.jsp.PageContext |
page | javax.servlet.jsp.HttpJspPage |
对于上述对象可以当做已经存在而直接使用,这里主要说三个对象:
- out:这个实例的类直接继承自java.io.Writer,所以可以直接将数据写出随响应而返回给客户端,就像我们在servlet中做的那样,response.getWriter().write();
- page:表示该页面,基本没什么用;
- pageContext:这个类表示这个页面的上下文环境,可以用来管理和存储在这个页面使用的属性,另外从上面生成的源代码来看,通过这个属性可以获得其他几个对象,如下;
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
另一个重要的作用对于pageContext来说,它可以存储各个作用范围的属性,由于它是直接继承自javax.servlet.jsp.JspContext,所以可以这样操作,有如下4个作用范围:
- APPLICATION_SCOPE
- SESSION_SCOPE
- REQUEST_SCOPE
- PAGE_SCOPE
abstract public void setAttribute(String name, Object value, int scope)
另外还可以通过设置只在该页使用的属性,即页面作用范围,如下:
abstract public void setAttribute(String name, Object value)
四、 JSP页面内容 JSP页面既包括模板数据,也包括句法元素,之前HTML标签和文本就是模板数据,而句法元素是由包含起来的内容,可以说除了句法元素之外全部是模板数据。关于模板数据我们这里不详细说明,不是我们的重点,这里着重说句法元素。句法元素主要有以下几种: 1、 指令
指令的作用是用来在JSP页面转换成servlet类的时候起到一种指导作用,说白了就是告诉JSP转换器怎么将一个JSP页面转换成servlet类,其中可以包含一些数据用来在转换的过程中加入类中,由于指令实在太多,所以这里我们说明两个比较重要的,page指令和include指令。 Page指令: 就如同这个指令的名字暗示的那样,这个指令是用来说明整个页面包含的信息的,我们怎么使用它呢?语法如下:
它有几个重要的属性,如下:
说明如下:
- contentType:这个大家就太熟悉了,就是http中的content-type,每次由ServletResponse的实例设置返回;
- pageEncoding:是该页面的页面编码,如果有中文的话就要小心这个值,如果没有可以直接默认(ISO-8859-1)
- isErrorPage:说明这个页面是否是一个在发生错误时返回的页面,可以用作进行异常处理时返回的页面;
- import:从名字就可看出来就是对应于java程序中的import关键字,导入依赖包;
- session:该属性用来说明该页面是否参与session管理,也就是说session在该页面是否起作用;
- buffer:指定out对象的缓冲区大小,单位kb,也可以为none,表示不使用缓存;
- autoFlush;说明缓冲区满的时候是否自动刷新,true时则会自动刷新,如果是false,则必须手动调用response.getWriter().flush()进行强制刷新,这个属性如果看源码的话是在PrintWriter中定义的;
- page指令可以放在页面的任何位置,但是注意,对于上面属性中涉及编码的必须放在最前面;
- page指令可以多次出现,但是如果在多个指令中出现同一属性则它们的值必须相同,但是import例外,它是进行累加的;
使用这个指令有以下两个问题要注意:
- 包含的文件的路径:如果file属性以"/"开头则说明,该文件的路径是以web应用程序在服务器上的根路径开始,也就是一个绝对路径;如果不是,则该路径被解释成相对于该页面的路径,也就是说被包含的页面文件必须和指令使用的页面在同一个文件夹下;
- 包含的文件的类型:可以是JSP文件,或者是jspf文件,还可以是静态文件html
脚本元素就是将java代码转成一个JSP页面,主要有三种形式:
- Scriptlet
注意:定义在其中的变量在之后的Scriptlet是可见的,也就是说可以直接使用。
- 表达式
Now is
- 声明
其实我们上边对脚本元素说了很多,在实际开发中并不常用,稍微懂一点web开发的都知道MVC模式,使用脚本元素明显违反了这种模式,鉴于之后又出现EL这种工具,所以更没有必要使用脚本元素了(不过自己做一些demo还是可以使用的)。 我们可以通过在部署描述符中配置来禁止使用脚本元素,如下:
*.jsp
true
在jsp-config中还可以配置很多属性,这里就不一一介绍了,如果有兴趣的可以去看部署描述符的schema文件。
3、 动作
动作会被编译成执行某个操作的Java代码,有点类似于通过文本的配置然后在接受请求时却会转换成可执行的代码。动作有很多种,它们是以标签的形式存在,除了标准的标准,还有一些定制的标签,这里就不说了,实在是太多了,如果想了解的话可以去查看JSTL相关的信息,也可以了解一下怎么编写自定义标签。 这里作为举例,说明两个即可: include: 该动作用于动态地包含另一个资源,这个可以是JSP页面,servlet或者是HTML静态页面。语法如下:
注意它和上述include指令之间的区别。主要如下:
- include指令是发生在JSP页面转换成servlet类的时候,因此include指令适合静态页面的包含,而include动作则是发生在每次请求的时候,因此可以使用include动作传递参数,同时还因为include动作是发生在接受请求的时候,每次的被包含页面的响应根据业务逻辑可以是最新的,但是由于include指令是在页面转换时作用的,所以如果被包含的页面不变化,那么返回的页面内容总是相同的;
- 如果想要使用include动作的这种在接受请求时返回最新响应的机制,则被包含的页面必须是以jsp结尾的;
这个我们之前在 Java Web基础知识之Filter:过滤一切你不想看到的事情中遇到过,通过它可以转向不同的资源,包括JSP和servlet,关于这个标签的底层实现是通过RequestDispatcher.forward()实现的,这个标签的使用如下:
在转发的时候可以添加一些新的请求参数如上所示,关于转发和重定向的区别见如下链接
推荐阅读
- Java|Java基础——数组
- 人工智能|干货!人体姿态估计与运动预测
- java简介|Java是什么(Java能用来干什么?)
- Java|规范的打印日志
- Linux|109 个实用 shell 脚本
- 程序员|【高级Java架构师系统学习】毕业一年萌新的Java大厂面经,最新整理
- javaweb|基于Servlet+jsp+mysql开发javaWeb学生成绩管理系统
- Spring注解驱动第十讲--@Autowired使用