一、JSP介绍和原理

JSP全称是Java Server Pages,它和Servlet技术一样,都是SUN公司定义的一种用于开发动态web资源的技术。

JSP这门技术的最大的特点在于,写jsp就像在写html,但它相比html而言,html只能为用户提供静态数据,而Jsp技术允许在页面中嵌套java代码,为用户提供动态数据。JSP技术可以解决在Servlet中拼写html内容以及css、js内容十分不方便的问题。

具体来说,JSP和Servlet的区别是:

  1. JSP经编译后就变成了“类servlet”。JSP页面在第一次被访问到时会被JSP翻译引擎翻译成一个Servlet,从此对这个JSP页面的访问都是由这个Servlet执行后进行输出。
  2. JSP由HTML代码和JSP标签构成,更擅长页面显示;Servlet更擅长流程控制。
  3. JSP中嵌入JAVA代码,而Servlet中嵌入HTML代码。

二、JSP的执行过程

jsp技术包括springboot吗 jsp技术概述_java


在执行JSP 网页时,通常可分为两个时期:转译时期(Translation Time)和请求时期(Request Time) 。

  1. 首先,客户端发出请求(request ),请求访问JSP网页。
  2. 接着,JSP Container将要访问的.JSP文件转译成Servlet的源代码(.java文件)。
  3. 然后,将产生的Servlet的源代码(.java文件)经过编译,生成.class文件,并加载到内存执行。
  4. 最后把结果响应(response )给客户端。

JSP和Servlet的执行效率相差不大,只是第一次执行JSP页面时需要进行编译。一般人都会以为JSP 的执行性能会和Servlet 相差很多,其实执行性能上的差别只在第一次的执行。因为JSP 在执行第一次后,会被编译成Servlet 的类文件,即为XXX.class,当再重复调用执行时,就直接执行第一次所产生的Servlet,而不用再重新把JSP编译成Servlet。因此,除了第一次的编译会花较久的时间之外,之后JSP 和Servlet 的执行速度就几乎相同了。

简单入门示例:

<%--显示当前时间的JSP页面--%>
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
      <head>
      </head>
      <body>
      <%int num = 0; %>
        <font color="red">
            <%
                Date date = new Date();
                String dateStr = date.toLocaleString();
                out.write(dateStr);
             %>
             <%= new Date().toLocaleString() %>
         </font>
        <%
            for(int i=0;i<5;i++){
                num++;
        %>
                <font color="blue">
            <%
                out.write(num+"");
            %>
                </font>
        <%
            } 
        %> 

      </body>
    </html>

三、JSP的语法

1、JSP模版元素

jsp页面中书写的HTML内容称作JSP的模版元素,在翻译过来的Servlet中直接被out.write()输出到浏览器页面上了。JSP页面中的HTML内容称之为JSP模版元素。 JSP模版元素定义了网页的基本骨架,即定义了页面的结构和外观。

2、JSP脚本表达式

JSP脚本表达式(expression)用于将程序数据输出到客户端。

语法:<%= 变量或表达式 %>

举例:当前时间:<%= new java.util.Date() %>

JSP引擎在翻译脚本表达式时,会将程序数据转成字符串,然后在相应位置用out.print(…) 将数据输给客户端。注意:JSP脚本表达式中的变量或表达式后面不能有分号(;)。

3、JSP脚本片断

JSP脚本片断(scriptlet)用于在JSP页面中编写多行Java代码。

语法:

<% 
        多行java代码 
    %>

注意:JSP脚本片断中只能出现java代码,不能出现其它模板元素,JSP引擎在翻译JSP页面中,会将JSP脚本片断中的Java代码将被原封不动地放到Servlet的_jspService方法中。

  1. JSP脚本片断中的Java代码必须严格遵循Java语法,例如,每执行语句后面必须用分号(;)结束。
  2. 在一个JSP页面中可以有多个脚本片断,在两个或多个脚本片断之间可以嵌入文本、HTML标记和其他JSP元素。
    举例:
<%
    int x = 10;
    out.println(x);
%>
<p>这是JSP页面文本</p>
<%
    int y = 20;
    out.println(y);
%>

多个脚本片断中的代码可以相互访问,犹如将所有的代码放在一对<%%>之中的情况。如:out.println(x);

  1. 单个脚本片断中的Java语句可以是不完整的,但是,多个脚本片断组合后的结果必须是完整的Java语句。例如:
<%
    for (int i=1; i<5; i++) 
    {
%>

    <H1>www.it315.org</H1>

<%
    }
%>

4、由于JSP脚本将转换成_jspService方法里的可执行代码,而Java语法不允许在方法里定义方法,所以JSP脚本里不能定义方法。

4、JSP声明
JSP页面中编写的所有代码,默认会翻译到servlet的service方法中, 而Jsp声明中的java代码被翻译到_jspService方法的外面。

语法:

<%! 
    java代码
%>

所以,JSP声明可用于定义JSP页面转换成的Servlet程序的静态代码块、成员变量和方法,与service方法同一级别。 多个静态代码块、变量和函数可以定义在一个JSP声明中,也可以分别单独定义在多个JSP声明中。JSP隐式对象的作用范围仅限于Servlet的_jspService方法,所以在JSP声明中不能使用这些隐式对象。

JSP声明部分定义的变量和方法可以使用private、public等访问控制符形式,也可使用static修饰将其变成类属性和类方法。但不能使用abstract修饰声明部分的方法,因为抽象方法将导致JSP对应Servlet变成抽象类,从而导致无法实例化。

示例:

<%!
static 
{ 
    System.out.println("loading Servlet!"); 
}
private int globalVar = 0;
public void jspInit()
{
    System.out.println("initializing jsp!");
}
%>
<%!
public void jspDestroy()
{
    System.out.println("destroying jsp!");
}
%>

5、JSP注释

JSP注释的语法:

<%-- 注释信息 --%>
  1. <%-- 注释的内容 --%> 被jsp注释注释掉的内容,在jsp翻译引擎将jsp翻译成Servlet的过程中会被丢弃,在翻译过来的Servlet中没有这些信息。
  2. <%//java注释%> java注释被当作jsp脚本片段被翻译到了Servlet中,在.java文件被翻译成.class文件的时候注释信息被丢弃。
  3. <!-- HTML注释 --> html注释被当作模版元素输出到了浏览器上,浏览器不会显示该内容,但是可以通过查看网页源代码看到。

6、 JSP的三个编译指令

JSP指令(directive)是为JSP引擎而设计的,它们并不直接产生任何可见输出,而只是告诉引擎如何处理JSP页面中的其余部分。在JSP 2.0规范中共定义了三个指令:page指令、Include指令、taglib指令。

page:该指令是针对当前页面的指令。
include:用于指定包含另一个页面。
taglib:用于定义和访问自定义标签。

JSP指令的语法:

<%@ 指令 属性名="值" %>
举例:<%@ page contentType="text/html;charset=gb2312"%>

如果一个指令有多个属性,这多个属性可以写在一个指令中,也可以分开写。
例如:

<%@ page contentType="text/html;charset=gb2312"%>
    <%@ page import="java.util.Date"%>
也可以写作:
    <%@ page contentType="text/html;charset=gb2312" import="java.util.Date"%>

(1)page指令

page指令用于定义JSP页面的各种属性,无论page指令出现在JSP页面中的什么地方,它作用的都是整个JSP页面,为了保持程序的可读性和遵循良好的编程习惯,page指令最好是放在整个JSP页面的起始位置

JSP 2.0规范中定义的page指令的完整语法:

<%@ page 
    [ language="java" ] 
    [ extends="package.class" ] 
    [ import="{package.class | package.*}, ..." ] 
    [ session="true | false" ] 
    [ buffer="none | 8kb | sizekb" ] 
    [ autoFlush="true | false" ] 
    [ isThreadSafe="true | false" ] 
    [ errorPage="relative_url" ] 
    [ isErrorPage="true | false" ] 
    [ contentType="mimeType [ ;charset=characterSet ]" | "text/html ; charset=ISO-8859-1" ] 
    [ pageEncoding="characterSet | ISO-8859-1" ] 
    [ isELIgnored="true | false" ] 
%>

说明:

1、language表明语言,默认是java,可以省略不写。

2、extends表明继承哪些类,或所实现的接口,必须是一个servlet类。

3、import表明导入哪些包或类,其中JSP引擎自动导入的包有:

java.lang.*
javax.servlet.*
javax.servlet.jsp.*
javax.servlet.http.*

可以在一条page指令的import属性中引入多个类或包,其中的每个包或类之间使用逗号分隔:

<%@ page import="java.util.Date,java.sql.*,java.io.*"%>

上面的语句也可以改写为使用多条page指令的import属性来分别引入各个包或类:

<%@ page import="java.util.Date"%>
<%@ page import="java.sql.*"%>
<%@ page import="java.io.*"%>

4、session默认值为true,这时候JSP翻译成Servlet默认包含session对象。如果我们只需要一些静态的表单,不需要session对象,可以将其值置为false,默认就不会自动创建session对象,减少空间浪费。当然我们即使将值设为false,我们仍然可以手动创建session对象。

5、buffer:指定输出缓冲区的大小。输出缓冲区的JSP内部对象:out用于缓存JSP页面对客户浏览器的输出,默认值为8KB,可以设置为none,也可以设置为其他的值,单位为KB。

6、autoflush:当输出缓冲区即将溢出时,是否需要强制输出缓冲区的内容。设置为true时为正常输出,如果设置为false,则会在buffer溢出时产生一个异常。

7、isThreadSafe表示是否线程安全,用来控制它是否实现SingleThreadModel接口。注意:当其值设为false的时候才会实现SingleThreadModel接口,但是这个接口已经废弃了,所以这个属性基本不使用。而且即使实现了这个接口,也不能保证是线程安全的。

8、 JSP页面也是java程序,它肯定会出现抛出异常的情况,而抛出500异常等界面对用户来说是否不友好,那么errorPage属性就可以设置代替异常提示的界面。errorPage属性的设置值必须使用相对路径,如果以“/”开头,表示相对于当前WEB应用程序的根目录(注意不是站点根目录),否则,表示相对于当前页面。可以在web.xml文件中使用元素为整个WEB应用程序设置错误处理页面,其中的子元素指定异常类的完全限定名,元素指定以“/”开头的错误处理页面的路径。 如果设置了某个JSP页面的errorPage属性,那么在web.xml文件中设置的错误处理将不对该页面起作用。

9、isErrorPage默认值为false,如果将其设为true,那么翻译的servlet中会自动生成一个exception对象,就是前一个出错页面抛出的异常对象。

10、JSP引擎会根据page指令的contentType属性生成相应的调用ServletResponse.setContentType方法的语句。page指令的contentType属性还具有说明JSP源文件的字符编码的作用。它和pageEncoding属性都是用来控制JSP乱码的。JSP中文乱码问题会在后面进行详细描述。

11、pageEncoding是用来告诉JSP翻译引擎读取JSP文件的编码方式。

12、isELIgnored是用来控制当前页面是否允许使用EL表达式的。

使用page指令解决JSP中文乱码:

乱码情况1:当JSP页面第一次被访问时,JSP翻译引擎将jsp文件翻译成Servlet,此时如果不明确指定,则翻译引擎在读取jsp文件时将使用utf-8码表,如果当初存JSP时使用的码表和翻译时读取文件使用的码表不同,则在翻译的过程中就会产生乱码。

解决方法:在JSP的page指令中有一个pageEncoding属性,用来通知JSP翻译引擎在翻译时以什么编码读取JSP文件,想要解决第一种乱码,只要将pageEncoding值设置为当初保存JSP时使用的编码,这样存储和读取时使用的码表一致就不会出现乱码了。

乱码情况2:在翻译过来的Servlet中如果要输出中文到浏览器,因为out相当于response.getWriter,如果不指定编码,服务器发送数据时将字符转成字节使用的是ISO8859-1导致乱码。另外,除了控制服务器以什么码表发送以外还要控制浏览器以什么码表打开才能解决乱码。

解决方法:在JSP的page指令中有一个ContentType属性,这属性相当于在翻译过来的servlet中写入response.setContentType()从而可以解决第二种乱码问题。

注意:其实在JSP中只要设定过pageEncoding属性,自动就会设置Cotent-Type属性。

(2)include指令

include指令用于引入其它JSP页面,如果使用include指令引入了其它JSP页面,那么JSP引擎将把这两个JSP翻译成一个servlet。所以include指令引入通常也称之为静态引入。只有include指令进行的包含是静态包含,其他的都是动态包含。

语法:

<%@ include file="relativeURL"%>
其中的file属性用于指定被引入文件的路径。路径以“/”开头,表示代表当前web应用。

一些细节:

  1. 被引入的文件必须遵循JSP语法。
  2. 被引入的文件可以使用任意的扩展名,即使其扩展名是html,JSP引擎也会按照处理jsp页面的方式处理它里面的内容,为了见名知意,JSP规范建议使用.jspf(JSP fragments)作为静态引入文件的扩展名。
  3. 由于使用include指令将会涉及到2个JSP页面,并会把2个JSP翻译成一个servlet,所以这2个JSP页面的指令不能冲突(除了pageEncoding和导包除外)。
  4. 如果被嵌入的文件经常需要改变,建议使用<jsp:include>操作指令,因为它是动态的include语句。

(3)taglib指令

taglib指令用于在JSP页面中导入标签库。
7、JSP的动作指令
动作指令与编译指令不同,编译指令是通知Servlet引擎的处理消息,而动作指令只是运行时的动作。编译指令在将JSP编译成Servlet时起作用;而处理指令通常可替换成JSP脚本,它只是JSP脚本的标准化写法。

JSP动作指令主要有7个:
jsp:forward 执行页面转向,将请求的处理转发到下一个页面
jsp:param 用于传递参数,必须与其他支持参数的标签一起作用
jsp:include 用于动态引入一个JSP页面
jsp:plugin 用于下载JavaBean或Applet到客户端执行
jsp:useBean 创建一个JavaBean的实例
jsp:setProperty 设置JavaBean实例的属性值
jsp:getProperty 输出JavaBean实例的属性值

8、JSP内置对象

JSP内置对象也叫做JSP隐式对象,JSP总共有九大隐式对象。在JSP引擎翻译过来的Servlet中Service方法自动帮我们前置定义这九个对象,可以在JSP页面中直接使用。这九大隐式对象分别是:request、response、config、application、exception、Session、page、out、pageContext。

其中page相当于Servlet,config相当于ServletConfig,application相当于ServletContext。

重点讲一下out和pageContext对象。

(1) out对象

out相当于是response.getWriter得到的PrintWriter对象。但是Out和response.getWriter获取到的流不同在于,这个out对象本身就具有一个缓冲区,利用out写出的内容会先缓存在out缓冲区中,直到out缓冲区满了或者整个页面结束时out缓冲区中的内容才会被写出到response缓冲区中,最终可以带到浏览器页面进行展示。out缓冲区的工作原理如下图所示:

jsp技术包括springboot吗 jsp技术概述_java-web_02

page指令中的buffer和autoFlush属性与out缓冲区有关。buffer可以用来禁用out缓冲区或者设置缓冲区大小,默认是8kb。autoFulsh用来设置当out缓冲区满了以后如果再写入数据时out如何处理,如果是true,则先将满了的数据谢大哦response中后再接受新数据。如果是false,则满了再写入数据直接抛出异常。

注意:在JSP页面中需要进行数据输出时,不要自己获取response.getWriter而是要使用out进行输出,防止既用out又用response.getWriter而导致输出顺序错乱的问题。

(2) pageContext对象

pageContext对象是JSP技术中最重要的一个对象,它代表JSP页面的运行环境,这个对象不仅封装了对其它8大隐式对象的引用,它自身还是一个域对象,可以用来保存数据。并且,这个对象还封装了web开发中经常涉及到的一些常用操作,例如引入和跳转其它资源、检索其它域对象中的属性等。

1、pageContext可以作为入口对象获取其他八大隐式对象的引用。

getException方法返回exception隐式对象 
getPage方法返回page隐式对象
getRequest方法返回request隐式对象 
getResponse方法返回response隐式对象 
getServletConfig方法返回config隐式对象
getServletContext方法返回application隐式对象
getSession方法返回session隐式对象 
getOut方法返回out隐式对象

2、pageContext是一个域对象。它是四大作用域的入口,可以操作四大作用域中的域属性。

作用范围:当前JSP页面

生命周期:当对JSP页面的访问开始时,创建代表当前JSP的PageContext,当对当前jsp页面访问结束时销毁代表当前jsp的pageContext。

作用:在当前jsp中共享数据。

//pageContext对象的方法 
public void setAttribute(java.lang.String name,java.lang.Object value)
public java.lang.Object getAttribute(java.lang.String name)
public void removeAttribute(java.lang.String name)
//pageContext对象中还封装了访问其它域的方法
public java.lang.Object getAttribute(java.lang.String name,int scope)
public void setAttribute(java.lang.String name, java.lang.Object value,int scope)
public void removeAttribute(java.lang.String name,int scope)
//代表各个域的常量
PageContext.APPLICATION_SCOPE
PageContext.SESSION_SCOPE
PageContext.REQUEST_SCOPE
PageContext.PAGE_SCOPE 
//搜寻四大作用域中的属性,如果找到则返回该值,如果都找不到则返回null
//搜寻的顺序是从最小的域开始向最大的域查找
findAttribute方法

3、提供了请求转发和请求包含的快捷方法
PageContext类中定义了一个forward方法和两个include方法来分别简化和替代RequestDispatcher.forward方法和include方法。
方法接收的资源如果以“/”开头, “/”代表当前web应用。

8、 JSP标签

JSP标签也称之为Jsp Action(JSP动作)元素,它用于在Jsp页面中提供业务逻辑功能,避免在JSP页面中直接编写java代码,造成jsp页面难以维护。具体标签的使用在JSP标签技术一文中详细阐述。

四、JSP使用注意细节

1、JSP映射

<servlet>
    <servlet-name>SimpleJspServlet</servlet-name>
    <jsp-file>/jsp/simple.jsp</jsp-file>
    <load-on-startup>1</load-on-startup >
</servlet>
    ……
<servlet-mapping>
    <servlet-name>SimpleJspServlet</servlet-name>
    <url-pattern>/xxx/yyy.html</url-pattern>
</servlet-mapping>

2、JSP最佳实践

不管是JSP还是Servlet,虽然都可以用于开发动态web资源。但由于这2门技术各自的特点,在长期的软件实践中,人们逐渐把servlet作为web应用中的控制器组件来使用,而把JSP技术作为数据显示模板来使用。

其原因为,程序的数据通常要美化后再输出:
让jsp既用java代码产生动态数据,又做美化会导致页面难以维护。
让servlet既产生数据,又在里面嵌套html代码美化数据,同样也会导致程序可读性差,难以维护。
因此最好的办法就是根据这两门技术的特点,让它们各自负责各的,servlet只负责响应请求产生数据,并把数据通过转发技术带给jsp,数据的显示jsp来做。

3、四大域对象

pageContext(称之为page域) 
request(称之为request域)
session(称之为session域)
servletContext(称之为application域)

如果一个数据只在当前jsp页面使用,可以使用pageContext域存储。

如果一个数据,除了在当前Servlet中使用,还要在请求转发时带到其他Servlet处理或jsp中显示,这个时候用request域。

如果一个数据,除了现在我自己要用,过一会我自己还要用,存在session域。

如果一个数据,除了现在我自己要用过一会其他人也要用,存在ServletContext域中。