JAVA学习收藏Java

11. JSP(我的JavaEE笔记)

2016-04-20  本文已影响1800人  yjaal

1.什么是JSP

(1)jsp全称是Java Server Pages,它和Servlet技术一样都是sun公司定义的一种用于开发动态web资源的技术。
(2)jsp这门技术的最大的特点在于,写jsp就像在写html,但它相比html而言,html只能为用户提供静态数据,而jsp技术允许在页面中嵌套java代码,为用户提供动态数据。

2. jsp原理

其原理就是jsp引擎将jsp文件翻译成一个servlet,而其中的java代码将原封不动的复制到新的servlet中。其执行起来也和servlet一样。

3. jsp的最佳实践

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

其原因是,程序的数据通常要美化后再输出
(1)让jsp即用java代码产生动态数据,又做美化会导致页面难以维护。
(2)让servlet即产生数据,又在里面嵌套html代码美化数据,同样也会导致程序可读性差,难以维护。

4. jsp语法

(1)jsp模板元素

Jsp页面中的html内容称之为jsp模板元素。Jsp模板元素定义了网页的基本骨架,即定义了页面的结构和外观。

(2)jsp脚本表达式

Jsp脚本表达式用于将程序数据输出到客户端:
语法:<%=x%>
举例:当前时间:<%=new java.util.Date()%>

Jsp引擎在翻译脚本表达式时,会将程序数据转成字符串,然后在相应位置用out.print(...)将数据输出给客户端。

注意:jsp脚本表达式中的变量或表达式后面一定不能有分号!

(3)jsp脚本片断

Jsp脚本片断用于在jsp页面中编写多行java代码。
语法:

<%
    多行java代码
%>

注意:jsp脚本片断只能出现java代码,不能出现其它模板元素,jsp引擎在翻译jsp页面中,会将jsp脚本片断中的java代码将被原封不动地放到servlet中的_jspServlet方法中。Jsp脚本片断中的java代码必须严格遵循java代码,例如,每执行语句后面必须用分号结束。

在一个jsp页面中可以有多个脚本片断,在两个或多个脚本片断之间可以嵌入文本、html标记和其他jsp元素。多个脚本片断的代码可以相互访问,就像将所有的代码放在一对<%%>之中的情况。

单个脚本片断中的java语句可以是不完整的,但是,多个脚本片断组合后的结果必须是完整的java语句,例如:

<%
    for(int i = 0; i < 5; i++)
    {
%>
    <H1>www.itcast.org</H1>
<%
    }
%>

(4)jsp声明

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

<%!
    Java代码
%>

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

注意:不能在jsp中定义名字为 jspInit 或 jspDestroy 的方法,这样在将jsp翻译成servlet时会将原带的相关方法覆盖掉。

(5)jsp注释

Jsp中使用<%--注释信息--%>格式添加注释,但是注意:这个和html页面中的****不同的是,jsp中的注释信息不会被翻译到servlet中,这样就不会被显示在页面上,但是html中的注释是会被发送到浏览器中的。

5. jsp指令

(1)Jsp指令是为jsp引擎而设计的,它们并不直接产生任何可见输出,而只是告诉引擎如何处理jsp页面中的其余部分。一共有三个指令:page指令、include指令、taglib指令。这里只讲前两个,最后一个是用于自定义标签上的,以后会讲。

(2)jsp指令简介

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"%>

(3)page指令

  1. page指令用于定义jsp页面的各种属性,无论page指令出现在jsp页面中的什么地方,它作用的都是整个jsp页面,为了保持程序的可读性和遵循良好的编程习惯,page指令最好放在整个jsp页面的起始位置。
2. jsp2.0规范中定义的page指令完整语法:

language="java" 指定嵌入的语言

extends="package.class" 指明编译该jsp文件时继承哪个类。Jsp为servlet,因此当指明继承普通类时需要实现的servlet的init、destroy等方法。

Import="java.servlet.http" 指明需要导入的包,可以在一条指令的import属性中引入多个类或包,使用逗号分隔。也可以分开使用指令写。其中java.lang包,java.servlet包不需要导入。

session="true|false" 指明生成(true)或不生成(false)session,默认是生成,但是一般我们会将其置为false。

buffer="none|8kb|sizekb" 指明缓存的大小,设为none表明无缓存,默认是8kb缓存,可以自己设置大小,比如64kb。但是必须在autoFlush设为true时有效。

autoFlush="true|false" 是否运行缓存。默认为true(运行缓存)。

isThreadSafe="true|false" 指定是否线程安全。默认是false(非线程安全)。

info="text" 指明jsp的信息。该信息可以通过Servlet.getServletInfo()方法取到。

errorPage="relative_url" 指定某个jsp页面的相对路径。用于指明一个错误显示页面,然后我们在此页面碰到相关的异常时进行跳转。此属性指定的页面通常isErrorPage属性为true,且内置的exception对象为未捕捉的异常。注意:必须使用相对路径,如果以“/”斜杠开头,表示相对于当前web应用程序的根目录(注意:不是站点根目录),否则表示相对于当前页面。当然有时候我们的错误显示页面会有很多,所以我们一般在web.xml文件中使用<error-page>元素为整个web应用程序设置错误处理页面,其中的<exception-type>子元素指定异常类的完全限定名(java.lang.NoFoundException),而<error-code>指定出现什么错误,** <location>**元素指定以”/”开头的错误处理页面的路径。如果设置了某个jsp页面的errorPage属性,那么在web.xml文件中设置的错误处理将不对该页面起作用。例如一般我们会将所有的错误页面放在一个文件夹中,如下面的errors:

<error-page>
    <error-code>500</error-code>
    <location>/errors/500.jsp</location>
</error-page>

注意:在错误显示页面中输出的错误信息一定要大于1kb,不然浏览器不会显示出来。

isErrorPage="true|false" 指定该页面是否为错误处理页面,如果为true,则该jsp内置有一个Exception对象exception,可直接使用,否则没有。默认为false。注意:jsp中有九大隐式对象,其中有八个是默认会生成的。但是如果我们没有将此属性设置为true,那么exception隐式对象将看不到,可以通过exception.getCause()方法得到此隐式对象。

contentType="text/html;charset=UTF-8" 指明有效的文档类型。如html格式为text/html;纯文本格式为text/plain;JPG图像为image/jpeg;GIF图像为image/gif。Jsp引擎会根据page指令的此属性生成相应的调用ServletResponse.setContentType方法的语句。还具有说明jsp源文件的字符编码的作用。

pageEncoding="characterSet|ISO8859-1" 指定jsp引擎使用哪张码表。

isELIgnored="true|false" 指明是否支持EL表达式。true为不支持。一般都需要设置为false。

注意:默认是有缓存的,这就是jsp中的out.write()方法和Servlet中的write方法的区别,当我们在jsp中有如下内容:

out.write(“aaa”);
response.getWriter().write(“bbb”);

时bbb会先输出,因为aaa会先存在out对象的缓冲区中,当满足一些条件时才能将此缓冲区中的内容写到servlet的缓冲区,这样才能输出。而response.getWriter().write(“bbb”);会直接将bbb存在servlet的缓冲区中,于是可以直接输出。

使用page指令解决jsp中文乱码问题(在tomcat5.0中存在,高版本不存在了,同时在MyEclipse中也不存在此问题)
(1)jsp程序存在有与Servlet程序完全相同的中文乱码问题

输出响应正文时出现的中文乱码问题
读取浏览器传递的参数信息时出现中文乱码问题。

(2)jsp引擎将jsp页面翻译成Servlet源文件时也可能导致中文乱码问题

Jsp引擎将jsp源文件翻译成Servlet源文件默认采用UTF-8编码,而jsp开发人员可以采用各种字符集编码来编写jsp源文件,这样就会造成乱码。如果jsp文件中没有说明它采用的字符集编码,jsp引擎将把它当作默认的ISO8859-1字符集编码处理。

乱码产生过程:
在tomcat5.0中,若我们使用记事本编写jsp程序,若jsp程序中有汉字,此时在中文环境下会使用gb2312保存,但是在将其翻译成Servlet程序时会,jsp引擎会使用ISO8859-1进行翻译,这时就会出现乱码,如果我们在Servlet中不对编码作任何设置,那么Response对象也将会使用ISO8859-1将乱码显示在浏览器中,而此时浏览器会使用ISO8859-1进行显示,这样我们看到的就是乱码,但是如果我们在浏览器中选择使用bg2312显示还是可以将乱码转换成正确的汉字。但是如果我们在Servlet中对编码进行设置,只要设置的不是ISO8859-1,那么我们在浏览器中就不能将乱码还原成正确的汉字了,即使我们最好使用bg2312进行显示。但是我们不能让用户每次都这样选择,所以我们使用pageEncoding进行设置,比如设置为UTF-8,这样就改变了引擎将jsp程序翻译成Servlet时使用的码表。但是如果是使用记事本写的jsp程序,还是会出现乱码,因为记事本在中文环境下是使用gb2312保存的,这里我们需要手工改变保存时使用的码表。但是在MyEclipse中不需要,它会根据pageEncoding设置的码表保存相关的jsp程序。

注意:tomcat5.0中还有一个问题是,在高版本的tomcat中会将webapps中的工程自动发布成web应用,但是5.0不会,在文件夹中必须要有一个WEB-INF的文件夹,同时还必须要有web.xml文件才行。

如何解决
通过page指令的contentType属性说明jsp源文件的字符集编码。
page指令的pageEncoding属性说明jsp源文件的字符集编码

(4) include指令

  1. include指令用于引入其他jsp页面,如果使用include指令引入了其它jsp页面,那么jsp引擎将把这两个jsp翻页成一个Servlet。所以include指令引入通常称为静态导入。

语法:

<%@ include file="relativeURL"%>

其中的file属性用于指定被引入文件的路径。路径以”/”开头,表示当前web应用。

细节:
被引入的文件必须遵循jsp语法。被引入的文件可以使用任意的扩展名是html,jsp引擎也会按照处理jsp页面的方式处理它里面的内容,为了见名知意,jsp规范建议使用.jsp作为静态引入文件的扩展名。

由于使用include指令将会涉及到2个页面,并会把jsp翻译成一个Servlet,所以这2个jsp页面的指令不能冲突。同时多个jsp程序中最好只有一个jsp程序里面带有html这类全局标签,但是不能将标识jsp文件的头删掉。
例:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
    这是网页头

但是有些指令可以冲突,比如pageEncoding、import。

以上是静态包含,也可以使用动态包含:

<%
    Request.getRequestDispatcher("/public/head.jsp").include(request,response);
%>

这就是动态包含,但是这种包含效率较低,因为它会将多个jsp翻译成多个servlet。

6. jsp运行原理和九大隐式对象

(1)每个jsp页面在第一次被访问时,web容器都会把请求交给jsp引擎(一个java程序)去处理。Jsp引擎先将jsp翻译成一个_jspServlet(实质上也是一个Servlet),然后按照Servlet的调用方式进行调用。

(2)由于jsp第一次访问时会翻译成Servlet,所以第一次访问通常会比较慢,但后面的访问时,jsp引擎如果发现jsp没有变化,就不再翻译,而是直接调用,所以程序的执行效率不会收到影响。

(3)jsp引擎在调用jsp对应的_jspServlet时,会传递或创建九个与web开发相关的对象供_jspServlet使用。Jsp技术的设计者为了便于开发人员在编写jsp页面时获得这些web对象的引用,特意定义了九个相应的变量,开发人员在jsp页面中通过这些变量就可以快速获得这九个对象的引用。

(4)九个隐式对象

Request、Response、session、Application(servletContext)、config(servletConfig)、page(this)、exception、out(jspWriter)、pageContext。其中只有最后一个是jsp独有的,其余的都是在servlet中学过的。

1.out隐式对象

Out隐式对象用于向客户端发送文本数据。Out对象是通过调用pageContext对象的getOut方法返回的,其作用和用法与servletResponse.getWriter方法返回的PrintWriter对象非常相似,但是不同。

Jsp页面中的out隐式对象的类型为jspWriter,jspWriter相当于一种带缓存功能的PrintWriter,设置jsp页面的page指令的buffer属性可以调整它的缓存大小,甚至关闭它的缓存。

只有向out对象中写入了内容,且满足如下任何一个条件时,out对象才去调用servletResponse.getWriter方法,并通过该方法返回的PrintWriter对象将out对象的缓冲区中的内容真正写到servlet引擎提供的缓冲区中:

  1. 设置page指令的buffer属性关闭了out对象的缓存功能;
  2. out对象的缓冲区已满
  3. 整个jsp页面结束
    注意:这里有两个缓冲区。

2.用jsp实现文件下载

<%@page import="java.io.OutputStream"%><%@page import="java.io.FileInputStream"%><%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><%
    String path = application.getRealPath("1.jpg");
    String filename = path.substring(path.lastIndexOf("\\") + 1);
    response.setHeader("content-disposition", "attachment;filename="+filename);
    FileInputStream in = new FileInputStream(path);
    int len = 0;
    byte[] buffer = new byte[1024];
    OutputStream sout = response.getOutputStream();
    while((len = in.read(buffer)) > 0){
        sout.write(buffer, 0, len);
    }
    in.close();
 %>


注意:这里我们必须将所有的模板内容都删除干净,因为只要有模板内容那么就会生成out对象,但是我们可以看到java代码中我们自己创建了一个字节流,那如果服务器又生成一个out的字符流就会产生异常,字符流是不能输出图片的。只有当模板代码没有时才不会生成out隐式对象,即使生成了也不会输出。而删除模板代码要将空格都删除干净,每个<% %>之间不能有空格,而最后的%>后面也不能有空格。我们可以看到,这和我们在servlet中写是一样的。

3. pageContext对象

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

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

既然pageContext内置其他八大对象,那么如果将此对象传递给一个普通的java对象,那么这个java对象就相当于一个servlet。

(2)pageContext作为域对象

pageContext对象的方法
public abstract void setAttribute(String name,Object value)
public abstract Object getAttribute(String name)
public abstract void removeAttribute(String name)

pageContext对象中还封装了访问其它域的方法
public abstract Object getAttribute(String name,int scope)
public abstract void setAttribute(String name,
Object value,int scope)
public abstract void removeAttribute(String name, int scope)

代表各个域的常量
PageContext. APPLICATION_SCOPE
PageContext. SESSION_SCOPE
PageContext. REQUEST_SCOPE
PageContext. PAGE_SCOPE

例子:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>My JSP '2.jsp' starting page</title>
  </head>
  <body>
    <%  
        //往session中存入一个数据
        pageContext.setAttribute("data", "xxx", PageContext.SESSION_SCOPE);
        //从session中取数据
        /* String data = (String)session.getAttribute("data");
        String data = (String)pageContext.getAttribute("data"); */
        
        String data = (String)pageContext.findAttribute("data");
     %>
     <%=data %>
  </body>
</html>

注意:findAttribute这个方法重点掌握,用于查找各个域中的属性。

(3)引入和跳转到其他资源

pageContext类中定义了一个forward方法和两个include方法来分别简化和替代RequestDispatcher.forward方法和include方法。方法接收的资源如果以“/”开头,“/”表示当前的web应用。当然这都是动态引用。

(4)jsp标签

jsp标签也称之为Jsp Action(jsp动作)元素,它用于在jsp页面中提供业务逻辑功能,避免在jsp页面中直接编写java代码,造成jsp页面难以维护。

(5)jsp常用标签

<jsp:include>动态包含标签:

<jsp:include page="<%=url%>"/>

<jsp:forward><jsp:param>跳转标签:

<jsp:forward page="1.jsp">
  <jsp:param name="xxx" value="yyy">
</jsp:forward>

相当于:

<jsp:forward page="1.jsp?xxx=yyy"/>

语法:

<jsp:include page="1.jsp|<%=url%>" flush="true|false"/>

Page属性用于指定被引入资源的相对路径,它也可以通过执行一个表达式来获得;flush属性指定在插入其他资源的输出内容时,是否先将当前jsp页面的已输出的内容刷新到客户端。

<jsp:include>与include指令的比较
(1) <jsp:include>标签是动态引入,涉及到两个jsp页面会被翻译成2个servlet,这两个servlet的内容在执行时进行合并。

(2) 而include指令是静态引入,涉及到的2个jsp页面会被翻译成一个servlet,其内容是在源文件级别进行合并。

(3) 都会把两个jsp页面的内容合并输出,所以这两个页面不要出现重复非html全局架构标签,否则输出给客户端的内容将会是一个格式混乱的html文档。

<jsp:forward>标签
page属性用于指定请求转发到的资源的相对路径,它也可以通过执行一个表达式来获得。

<jsp:param>标签
用于传入参数。可以将多个参数写在一个标签中,也可以分开写。

映射jsp

<servlet>
    <servlet-name></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>

这是我们如果直接访问yyy.html就相当于访问simple.jsp。

如何查找jsp页面中的错误
(1) jsp页面中的jsp语法格式有问题,导致其不能被翻译成servlet源文件,jsp引擎将提示这类错误发生在jsp页面中的位置以及相关信息。

(2) jsp页面中的jsp语法格式没有问题,但被翻译成的servlet源文件中出现了java语法问题,导致jsp页面翻译成的servlet不能通过编译,jsp引擎也将提示这类错误发生在jsp页面中的位置以及相关信息。

(3) jsp页面翻译成的servlet在运行时出现异常,这与普通的java错误一样,会提示相关信息。

(4) 一个难以理解的错误:有时候访问jsp正确,之后访问又出现错误,也就是时好时坏。这是因为如果第一次访问时正确,那么就有一个正确的servlet存在硬盘中了,如果这时jsp发生改动产生了错误,那么第二次访问会出错,而第三次访问时服务器发现刚才编译过,此时不会再次编译,会直接从硬盘中取得之前正确的文件,这时就正确了。但是如果间隔时间长了,那么又会再次编译。

(6)三个容器

request:客户机像服务器发出请求,用户看完之后就没用了,比如新闻,这种数据一般存在此容器中。

session:客户机像服务器发出请求,用户看完之后,“过一会儿”之后还有用,比如购物,这种数据一般存在此容器中。

servletContext:客户机像服务器发出请求,产生的数据用户看完之后还要给其它用户使用,比如聊天数据。这类数据一般存在此容器中。
最后,如果使用较小的容器能完成的任务就不要使用较大的容器。

补:九大隐式对象和四大作用域总结

名称 描述 作用域
request 请求对象 request
response 响应对象 Page
pageContext 页面上下文对象 Page
session 会话对象 Session
application(servletContext) 应用程序对象 Application
out(jspWriter) 输出对象 Page
config(servletConfig) 配置对象 Page
page(this) 页面对象 Page
exception 异常对象 Page

详细说明:

四大作用域的区别:

最后:上面的作用域依次增大。

上一篇 下一篇

猜你喜欢

热点阅读