1.1、什么是JSP
JSP 全称是 Java Servlet Pages,它是和 servlet 技术一样,都是 SUN 公司定义的一种用于动态开发 web 资源的技术。
JSP 这门技术最大的特点在于,写 JSP就像在写 html ,但:它相比 html 而言,html 只能为用户提供静态数据,而 JSP 技术允许在页面中嵌套 java 代码,为用户提供动态数据。
jsp=html+css+javascript+java代码+jsp标签(servlet)
1.2、为什么需要JSP
我们在之前的学习中,经常这样写:
相比Servlet 而言,servlet 很难对数据进行排版(需要写很多的标签、换行等),而 JSP 出了可以用 java 代码产生动态数据的同时,也很容易对数据进行排版,JSP就是替代Servlet输出HTML的。
1.3、JSP的本质
jsp页面本质上是一个Servlet程序。
如果是第一次访问该 jsp 文件,web 服务器就会把 该 jsp 文件翻译成一个同名的 Servlet 文件,然后再把 class 加载进内存,如果是第二次(或者以后)访问,就直接访问内存中的实例,因此 jsp 也是单例,所以第一次访问 jsp 网站的速度比较慢。如果这个 jsp 文件被修改了,就相当于重新访问该 jsp 文件。
例如:新建一个a.jsp如下:
<%@ page contentType="text/html;charset=GBK" language="java" isErrorPage="true"%><!DOCTYPE html><html><head><title>a.jsp</title></head><body> <h1>我是a.jsp</h1> <% String s = "HelloWorld"; out.println(s); %></body></html>
当我第一次访问时,找到我们项目的部署位置:
然后找到Catalina目录:..\work\Catalina\localhost\JspWeb\org\apache\jsp
package org.apache.jsp;
import javax.servlet.*;import javax.servlet.http.*;import javax.servlet.jsp.*;
public final class a_jsp extends org.apache.jasper.runtime.HttpJspBase implements org.apache.jasper.runtime.JspSourceDependent, org.apache.jasper.runtime.JspSourceImports {
private static final javax.servlet.jsp.JspFactory _jspxFactory = javax.servlet.jsp.JspFactory.getDefaultFactory();
private static java.util.Map<java.lang.String,java.lang.Long> _jspx_dependants;
private static final java.util.Set<java.lang.String> _jspx_imports_packages;
private static final java.util.Set<java.lang.String> _jspx_imports_classes;
static { _jspx_imports_packages = new java.util.HashSet<>(); _jspx_imports_packages.add("javax.servlet"); _jspx_imports_packages.add("javax.servlet.http"); _jspx_imports_packages.add("javax.servlet.jsp"); _jspx_imports_classes = null; }
private volatile javax.el.ExpressionFactory _el_expressionfactory; private volatile org.apache.tomcat.InstanceManager _jsp_instancemanager;
public java.util.Map<java.lang.String,java.lang.Long> getDependants() { return _jspx_dependants; }
public java.util.Set<java.lang.String> getPackageImports() { return _jspx_imports_packages; }
public java.util.Set<java.lang.String> getClassImports() { return _jspx_imports_classes; }
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; } //JSP也是Servlet,运行时只有一个实例 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 {
//9大内置对象 final javax.servlet.jsp.PageContext pageContext; javax.servlet.http.HttpSession session = null; java.lang.Throwable exception = org.apache.jasper.runtime.JspRuntimeLibrary.getThrowable(request); //页面声明了isErrorPage="true"属性才会有 if (exception != null) { response.setStatus(javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } 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;charset=GBK"); 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("<!DOCTYPE html>\r\n"); out.write("<html>\r\n"); out.write("<head>\r\n"); out.write("<title>a.jsp</title>\r\n"); out.write("</head>\r\n"); out.write("<body>\r\n"); out.write(" <h1>我是a.jsp</h1>\r\n"); out.write(" ");
String s = "HelloWorld"; out.println(s); out.write("\r\n"); out.write("</body>\r\n"); out.write("</html>"); } 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); } }}
我们发现有个叫a_jsp.java的文件,跟踪原代码看到它它继承了HttpJspBase 类。HttpJspBase 类又继承了 HttpServlet 类。也就是说。jsp 翻译出来的 java 类, 它间接了继承了 HttpServlet 类。也就是说, 翻译出来的是一个 Servlet 程序 。细心的人可能发现,输出跟我们手写的很像,是的jsp帮我们完成了这部分复杂的工作。
JSP比Servlet更方便更简单的一个重要原因就是:内置了9个对象!内置对象有:out、session、response、request、config、page、application、pageContext、exception。
2、JSP的语法
2.1、JSP的指令
JSP指令用来声明JSP页面的相关属性,用于从 JSP 发送一个信息到容器,比如设置全局变量,文字编码,引入包等。常用的指令如下:
<%@ page language="java" import="java.util.*" pageEncoding="GBK"%><%@ include file=" " %><%@ taglib prefix=" " uri="" %>
2.1.1、jsp 头部的page指令
<%@ page contentType="text/html;charset=GBK" language="java" %>
page 指令中常用属性有:
2.1.2、include指令
注:被引入的 JSP 页面中,不能够存在与当前页面有冲突的变量。
语法:<%@ include file=”文件路径” %>案例:它会把b.jsp的内容都包含进来,再一起编译。<%@ include file=”b.jsp” %>
2.2、JSP常用脚本
脚本元素就是 java页面中的java代码,也叫片段的scriplet。JSP脚本有三种方式:
1)JSP声明:变量、方法和类的声明。语法:<%!声明变量、方法和类的代码%>
2)JSP中嵌入Java代码(片段)。语法:<% java代码 %>
3)JSP表达式。语法:<%=变量或可以返回值的方法或 Java 表达式%>
注:函数不能在<% %>(java片段)中定义
2.3、JSP文件的注释
三种方式:
1)<!-- 注释的内容 --> html的注释方法,可以在 JSP 中使用
2)<%-- 注释的内容 --%> jsp 专用的注释
3)//单行注释
/*多行注释*/
/**文档注释*/
java 注释
区别:
<!-- -->
会在 servlet 中生成对应的 out.write(“<!-- -->”)
,这样在返回给浏览器的静态页面中也有<!-- -->
,只是用户看不到而已;
<%-- --%>
则在 servlet 中没有任何输出,建议使用<%-- --%>
java 注释,会被翻译到java源代码中!
2.4、JSP的动作
JSP动作(JSP Actions)是一组JSP内置的标签,只书写少量的标记代码就能够使用JSP提供丰富的功能,JSP行为是对常用的JSP功能的抽象和封装。
JSP Actions是使用 xml 语法写的,是 jsp 规定的一些列标准动作,在容器处理 jsp 时,当容器遇到动作元素时,就执行相应的操作。可能看完概念还是很抽象。
常用的JSP动作有:
2.4.1、include动作
前面已经提及到了,include指令是静态包含,include动作是动态包含。其实include行为就是封装了request.getRequestDispatcher(String url).include(request,response)。
动态包含也可以像静态包含一样。把被包含的内容执行输出到包含位置。而include动作和include指令的区别:
1)属性不同
include 指令通过 file 属性来指定被包含的页面,该属性不支持任何表达式。
<jsp:include>
动作是通过 page 属性来指定被包含页面的,该属性支持 JSP 表达式。
2)处理方式不同
使用 include 指令包含文件时,被包含文件的内容会原封不动地插入到包含页中使用该指令的位置,然后 JSP 编译器再对这个合成的文件进行翻译,所以最终编译后的文件只有一个。(静态包含,被包含文件尽量不要使用<html></html>
、<body></body>
这种标记,还要避免变量冲突)
而使用<jsp:include>
动作包含文件时,只有当该标记被执行时,才会引入被包含的的页面进行编译,再将译之后的视图再引入到当前页面,然后继续执行后面的代码。所以被引入的页面即使有与当前相同的Java变量也没影响。
(如果被包含的文件改变,则静态包含的主页面需要重新编译。而动态包含只须重新编译被包含的文件即可。)
注:动态包含的原理(使用如下代码去调用被包含的 jsp 页面)
2.4.2、forward动作
前面我们学习servlet的时候,我们使用request.getRequestDispatcher(String url).forward(request,response),进行跳转,而使用forward动作其实是对他的封装。
使用案例1:
1)新建一个b.jsp
2)在a.jsp中加入
<%@ page contentType="text/html;charset=GBK" language="java"%><!DOCTYPE html><html><head><title>a.jsp</title></head><body> <h1>我是a.jsp</h1> <% String s = "HelloWorld"; out.println(s); %> <jsp:forward page="b.jsp"></jsp:forward></body></html>
3、JSP的内置对象
前面我们发现,不管是request也好、out也好,我们都能在jsp页面中直接使用。在前面我们也提到过JPS内置了9大对象: