JSP
和Servlet技术一样,JSP也是SUN公司定义的一种开发动态web资源的技术,属于JavaEE技术之一。JSP实际上就是Servlet,它们在一起又称JSP/Servlet规范。
Servlet:写Java代码。在做HTML页面的输出时不方便(开发效率很低)
JSP:HTML+Java
1、JSP的原理及生命周期
当某个jsp页面第一次被请求时,会被Tomcat服务器的JSP引擎部分翻译成一个Servlet (☆),然后按照servlet的调用方式进行调用,过程如下:
当一个jsp页面被翻译成Servlet后,后面的过程,包括生命周期都和Servlet是类似的。所以说JSP就是Servlet,理解了JSP页面中的各个组成部分是如何被翻译成Servlet的,就掌握了JSP。学习JSP的过程中,应时常翻阅JSP对应的Servlet源码。
2、demo1_jsp.java类
上一小节中的demo1.jsp在访问时会被翻译成demo1_jsp.java,该类称为jsp的实现类(关于Tomcat中该类怎么命名不多说)。
demo1_jsp类继承自org.apache.jasper.runtime.HttpJspBase抽象类,该类除了继承HttpServlet类外,还实现了javax.servlet.jsp.HttpJspPage接口,在HttpJspPage接口中声明了一个_jspService()方法,它对应着jsp页面,绕晕了的看下图所示:
demo1.jsp中的几乎各种组成部分都能在demo1_jsp.java的_jspService()方法中找到(☆)。
3、JSP页面的组成部分
一个JSP页面可能会包含如下几个部分:JSP模板元素、Java脚本、JSP指令、JSP标签等等,这些部分都会被翻译到生成的Servlet中。
JSP模板元素即JSP页面中的HTML内容,翻译时直接放在out.write()方法中输出。
4、JSP中的JAVA脚本
1)JSP脚本片断:<% Java代码 %>
① JSP脚本片断中只能出现java代码,不能出现其它模板元素。JSP引擎在翻译JSP页面时,会将JSP脚本片断中的Java代码将被原封不动地放到Servlet的_jspService()方法中。
② 在一个JSP页面中可以有多个脚本片断,在两个或多个脚本片断之间可以嵌入文本、HTML标记和其他JSP元素。
③ 单个脚本片断中的Java语句可以是不完整的,但是,多个脚本片断组合后的结果必须是完整的Java语句。
<ul> <% for(int i = 0;i < 5; i ++){ %> <li>第<%=i %></li> <% } %> </ul> |
2)JSP脚本表达式:<%=Java变量或表达式 %>
JSP脚本表达式用于将程序数据输出到客户端。JSP引擎在翻译脚本表达式时,会在相应位置用out.print(…) 将表达式的值输给客户端(变量或表达式后面不能有分号!)。
3)JSP声明:<%! Java代码 %>
① JSP页面中编写的所有代码,默认会翻译到servlet的_jspService() 方法中,而JSP声明中的Java代码被翻译到_jspService方法的外面,所以JSP声明可用于定义JSP页面转换成的Servlet程序的成员变量和方法、静态代码块。
② JSP中九大隐式对象的作用范围仅限于_jspService()方法中,所以在JSP声明中不能使用这些隐式对象。
4)JSP注释:<%-- 注释信息 --%>
JSP引擎在将JSP页面翻译成Servlet程序时,忽略JSP页面中被注释的内容。
正规开发的JSP中应尽量避免出现Java脚本(可用标签封装)。
5、JSP指令
JSP指令是为JSP引擎而设计的,它们并不直接产生任何可见输出,而只是告诉引擎如何处理JSP页面中的其余部分。在JSP 2.0规范中共定义了三大指令(page、include、taglib)。
基本语法格式:<%@ 指令 属性=”值” …… %>,如果一个指令有多个属性,这多个属性可以写在一个指令中,也可以分开写。
1)page指令:<%@ page …… %>,有以下常用属性:
① import:告知JSP引擎,导入哪些包。JSP引擎会在生成的Servlet中默认导入javax.servlet.*、javax.servlet.http.*、javax.servlet.jsp.*。如:
<%@ page import="java.util.*,java.sql.*" %> 等价于:
<%@ page import="java.sql.*" %> <%@ page import="java.util.*" %>
② session:告知引擎是否在_jspService()代码中产生session对象,默认值true。
③ errorPage:告知引擎,如果当前页面出现了异常,应该转发到哪个页面上(“/”代表当前应用)。开发中一般会直接在web.xml中配置全局错误页面(两种写法):
<error-page> <exception-type>java.lang.Exception</exception-type> <location>/common/error.jsp</location> </error-page> <error-page> <error-code>404</error-code> <location>/common/404.jsp</location> </error-page> |
④ isErrorPage:告知引擎是否在_jspService()代码中产生exception对象,默认为false。利用该对象可以捕获页面中的异常,打印异常的详细信息。
⑤ contentType:☆ 告知引擎响应正文的MIME类型,在Servlet中被翻译成response.setContentType(xxx)。
⑥ pageEncoding:☆ 告知引擎,翻译JSP时(从磁盘上读JSP文件)所用的码表,同时还会设置响应正文的MIME类型。pageEncoding的默认值为“ISO-8859-1”。
如:pageEncoding=”UTF-8”相当于告知了引擎用UTF-8读jsp,并在servlet中response.setContentType(“text/html;charset=UTF-8”)
⑦ isELIgnored:告知引擎,是否忽略EL表达式。默认值是false,不忽略。
2)include指令:<%@ include file=”/demo2.jsp” %>,file属性以“/”开头代表当前应用。
3)taglib:<%@ taglib uri=”” prefix=”” %>,导入外部标签库,uri表示外部标签库的uri地址,好比名称空间,prefix相当于一个别名。
6、JSP标签
JSP标签也称之为Jsp Action(JSP动作)元素,它用于在jsp页面中提供业务逻辑功能,避免在jsp页面中直接编写java代码,造成jsp页面难以维护。
1)转发:
<jsp:forward page=”/demo2.jsp”>
<jsp:param name=”from” value=”demo1” />
</jsp:forward>
2)包含:
<jsp:include page=”/demo2.jsp” >
<jsp:param name=”from” value=”demo1” />
</jsp:include>
注意:
① JSP转发和包含标签实际上是对RequestDispatcher的两个方法(forward和include)的改写,且都可以包含<jsp:param>子标签,<jsp:param>中的name-value会以参数的形式加在page后。
② 动态包含和静态包含:除了include指令为静态包含,其余的全是动态包含,它们的区别如下图:
了解了上图后,对静态包含和动态包含的各种区别就很容易理解了,实际开发中,能用静态包含就不用动态包含!
3)JavaBean相关
JSP中提供了三个关于JavaBean组件的内置标签,分别为:
① <jsp:useBean>标签:
语法:<jsp:useBean id="beanName" class="package.class" scope="xxx"/>
属性:id属性用于指定JavaBean实例对象的引用名称和其存储在域范围中的名称,class属性用于指定JavaBean的完整类名,scope的取值为page、request、session、application四个中的一个,默认值为page属性。
作用:在指定的域范围内查找指定的JavaBean对象:
A. 如果存在则直接返回该JavaBean对象的引用。
B. 如果不存在则实例化一个新的JavaBean对象并将它以指定的名称存储到指定的域范围中。
② <jsp:setProperty>标签:
语法1:<jsp:setProperty name="beanName" property=”propertyName” value=”xxx” />
语法2:<jsp:setProperty name=”beanName” property=”propertyName | *” param=”xxx” />
属性:name指定JavaBean实例对象的引用名称,对应id属性;property 指定JavaBean的属性名,指定param时用请求参数设置JavaBean的属性。
③ <jsp:getProperty>标签:
语法:<jsp:getProperty name="beanName" property="propertyName" />
使用案例,注意事项都写在注释中了,直接看即可:
<jsp:useBean id="p" class="org.flyne.domain.Person" scope="page" /> <!-- 一定要注意value的写法:基本类型和字符串类型直接写,其他类型用表达式 --> <jsp:setProperty name="p" property="name" value="Flyne" /> <jsp:setProperty name="p" property="age" value="24" /> <jsp:setProperty name="p" property="regTime" value="<%=new Date() %>" /> name: <jsp:getProperty name="p" property="name"/><br /> age: <jsp:getProperty name="p" property="age"/><br /> regTime: <jsp:getProperty name="p" property="regTime"/><br /> |
另外还可以使用请求参数设置JavaBean的属性:
<jsp:setProperty name="p" property="name" param="name"/> <!-- 当JavaBean的属性名同请求参数名一致时还可以使用通配符 --> <jsp:setProperty name="p" property="*" /> |
7、九大隐含对象和四大域对象(☆)
在_jspService方法的开始部分定义了9大对象(exception对象需自行开启,通过isErrorPage指定),在JSP页面中可以直接使用这9大对象。
隐含对象名称 |
类型 |
备注 |
request |
javax.servlet.http.HttpServletRequest |
|
response |
javax.servlet.http.HttpResponse |
|
session |
javax.servlet.http.HttpSession |
page指令有开关:session |
application |
javax.servlet.ServletContext |
|
config |
javax.servlet.ServletConfig |
|
page |
javax.servlet.http.HttpServlet |
当前jsp对应Servlet的实例引用 |
exception |
java.lang.Throwable |
page指令有开关:isErrorPage |
out |
javax.servlet.jsp.JspWriter |
相当于带缓存功能的PrintWriter |
pageContext |
javax.servlet.jsp.PageContext |
很重要(☆) |
其中pageContext、request、session、application被称为四大域对象,又称属性范围,在实际开发中被用于存放数据(核心是把握其生命周期):
pageContext |
页面范围:存储的数据仅本页有效,开发中不用它来存储数据 |
request |
请求范围:一次请求范围内均有效,经常用 |
session |
会话范围:多次请求可共享数据,但不同的客户端不能共享,经常用 |
application |
应用范围:开发中尽量少用,用时要做同步处理 |
8、PageContext抽象类
1)利用PageContext可以获取其他8个JSP隐含对象。
使用场景:试想有这样一个函数,需要同时用到上述9个隐含对象(有点极端),设置9个参数?错,只需传入pageContext对象,再利用pageContext对象的getRequest()、getResponse()、getSession()、getServletContext()等方法获取其余8个对象。(自定义标签时会用到)
2)操作其他3个域对象中的属性:
① void setAttribute(String name, Object value, int scope)
Object getAttribute(String name, int scope)
void removeAttribute(String name, int scope)
scope参数可取如下常量值:PAGE_SCOPE(页面范围)、REQUEST_SCOPE(请求范围)、SESSION_SCOPE(会话范围)、APPLICATION_SCOPE(应用范围)。
② Object findAttribute(String name) 依次在四个范围中查找指定名称的属性:pageContext --> request --> session --> application。
3)提供转发和包含的简易方法(forward、include)
9、其他问题
1)JSP中的异常处理技巧
① 运行时异常:如1 / 0的运算,此时需查看与JSP文件对应的的Servlet的源码。
② 出现语法错误时,一般IDE会报错,在浏览器中也可以看到错误的详细信息,此时直接定位到JSP文件中查看就行。
2)使用page指令解决JSP中文乱码(☆)
出现中文乱码问题主要还是因为读和写所用的码表不一致造成的:
JSP开发人员可以采用各种字符集编码来编写JSP源文件(如UTF-8、GBK),JSP引擎将JSP源文件翻译成的Servlet源文件默认采用ISO8859-1编码,因此,JSP引擎将JSP源文件翻译成Servlet源文件时,需要进行字符编码转换,通过pageEncoding属性指定,两者需要保持一致。
3)JSP最佳实践
由于Servlet和JSP各自的特点,在长期的软件实践中,人们逐渐把servlet作为web应用中的控制器组件来使用,而把JSP技术作为数据显示模板来使用。
本文来源于:http://www.flyne.org/article/502