目录
1、JSP简介
2、JSP体验
3、JSP运行原理
4、JSP基本语法
5、JSP指令
6、JSP内置九大对象
7、JSP标签
8、JSP配置
9、JSP排错
10、总结
在前面的Servlet学习中发现Servlet本质是一个java程序,因此Servlet更加擅长编写程序的业务逻辑,而如果要使用servlet输出一个页面(HTML+CSS+JavaScript)那么就是一个灾难。
因此SUN公司在随后的技术上做了调整,Servlet技术主要负责项目的业务逻辑,如果开发者需要编写动态页面,那么SUN提供了JSP技术。
多学一招:如何区分静态和动态页面?
页面 | 组成 | 源码形式 | 常见页面 |
静态页面 | HTML、CSS、JavaScript | 每次查看页面的源码保持不变 | index.html |
动态页面 | HTML、高级语言(Java) | 每次查看页面的源码保持可变 | index.jsp |
思考:http://www.test.cn/index.html 该链接对应的页面是动态还是静态?
以上的链接不一定是静态页面,因为可以使用servlet进行静态的映射。
1、JSP简介
JSP是SUN公司提供的一种用于快速编写动态页面的技术,全称是:Java Server Page。该技术其实比较简单,本质就是一个Servlet,只不过将Servlet技术做了改进使得程序员在开发页面的时候变得快速和简洁。
Servlet即java中包含html的页面,因此编写html比较繁琐,但是编写java比较方便。
JSP 即html中包含java,因此编写html比较方便,但是编写java比较繁琐。
2、JSP体验
体验程序:在页面中输出系统的当前时间?
<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
<html>
<head>
<title>this is first jsp page</title>
</head>
<body>
Current Date : <%= new java.util.Date()%>
</body>
</html>
发现JSP在编写页面的时候确实简单。
3、JSP运行原理
JSP的本质是Servlet,因此Tomcat在运行JSP页面的时候需要先将该访问的JSP页面进行翻译为为Servlet,翻译好之后还需要将该java程序进行编译,随后依照serlet的处理方式进行处理用户的请求。
具体可以通过%tomcat_home% \work\Catalina\localhost该目录查看翻译和编译好的文件数据。
具体图如下
4、JSP基本语法
JSP的基本语法我们主要以下面的几种模块去学习:
JSP模版 所有的HTML标签元素
JSP输出表达式 页面输出 out.write()
JSP脚本 基本的java程序
JSP声明 成员声明
JSP注释 程序的说明信息
4.1 JSP模版
在JSP中编写HTML等静态网页技术的语言时和基础班的页面编写是一样的,因此在这里不需要过多的阐述。
4.2 输出表达式(重点)
该表达式就是给页面进行输出数据。
语法:<%= 要输出的内容 %>
举例1:输出计算结果?
1+2=<%= 1+2 %><br/> //1+2=3
out=<%=out %> //out=org.apache.jasper.runtime.JspWriterImpl@10f0625
JSP页面如果修改了那么项目不需要重新部署,直接使用浏览器刷新页面即可。
JSP的输出表达式会默认在翻译的时候被翻译到指定类的_jspService()方法中。
使用细节:
JSP输出表达式不能加封号(;)。
4.3 JSP脚本(重点)
脚本:不能独立运行需要寄生在宿主环境中的语言即为脚本语言。
JSP中主要的是语言是HTML,偶尔会夹杂java语言,因此这里我们将java称之为JSP的脚本。
语法:<% java代码 %>
举例1:输出系统时间?
<%
java.util.Date date = new java.util.Date(); //JSP脚本
%>
Current Date : <%=date %><br/> //JSP输出表达式
举例2:循环输出 6个<h3>?
<%
for(int i = 0 ; i < 6; i++ ){
%>
<h3>this is h3</h3>
<%
}
%>
举例3:输出1~6级标题?
<%
for(int i = 1 ; i < 7; i++ ){
%>
<h<%=i %>>this is title</h<%=i %>>
<%
}
%>
举例4:out.print()和out.write()的区别?
<%
String str1 = "hello";
String str2 = null;
int a1 = 97;
%>
out.write("hello"):<% out.write(str1); %><br/> // hello
out.write(null):<% out.write(str2); %><br/> // 空白符
out.write(97):<% out.write(a1); %><br/> // a
out.print("hello"):<% out.print(str1); %><br/> // hello
out.print(null):<% out.print(str2); %><br/> // null
out.print(97):<% out.print(a1); %><br/> // 97
JSP在被tomcat翻译的时候,会默认将JSP脚本翻译到指定类的_jspService()中。
如果开发者在JSP页面中使用JSP脚本定义了一个变量,那么该变量属于_jspService()的局部变量。
思考:是否可以使用JSP脚本进行定义方法?
解答:一定不可以,因为方法中不能再有方法的声明。
4.4 JSP声明
Java中大家都学过变量的声明、方法的声明、类的声明。即定义。
语法:<%! 要声明的成员%>
举例1:声明一个成员变量?
<%!
int age = 10;
%>
age = <%= age %>
JSP声明默认被tomcat翻译到指定类的类非静态成员位置。因此可以直接使用JSP输出表达式直接访问。
举例2:声明一个方法?
<%!
public char change(int a){
return (char)a;
}
%>
<%= change(97) %> // a
举例3:声明一个类?
<%!
class Inner{
public String name = "jack";
public String showInfo(){
return this.name;
}
}
%>
<%= new Inner().name %><br/> // jack
<%= new Inner().showInfo() %><br/> //jack
举例4:重写JSP的生命周期方法?
<%!
static {
System.out.println("static block......."); // 类装载
}
public void jspInit() {
System.out.println("initializing jsp....."); // 初始化
}
public void jspDestroy() {
System.out.println("destroying jsp......."); // 服务器关闭
}
%>
<% System.out.println("_jspService()......"); %> // 响应用户请求
4.5 JSP注释
JSP注释主要是给开发人员看的,方便他们阅读程序的业务逻辑。
语法:<%-- 注释的了内容 --%>
举例1:在页面中添加html和jsp注释?
<!-- 我是HTML注释 --> //翻译demo1_jsp.java和浏览器查看源码都可见
<h3>我的三级标题</h3>
<%-- 我是JSP注释 --%> // 翻译demo1_jsp.java和浏览器查看源码都不可见
<%= "hello" %>
5、JSP指令
JSP指令主要用于控制页面的属性,如页面的编码方式、页面的类型、页面的脚本等。在JSP技术中常见的有三大指令:page指令、include指令、taglib指令。
如果要在JSP页面中使用指令,那么格式如下:
<%@ 指令名 属性名1=”属性值” 属性名2=”属性值”….. %>
5.1 page指令(重点)
举例1:详解page指令的所有属性?
<%@page
autoFlush="true" // 自动刷新缓冲区
contentType="text/html; charset=utf-8" // 通知浏览器数据类型和编码
buffer="8kb" // 设置缓冲区大小
errorPage="/error.jsp" // 指定当前页面的错误页面
import="java.util.Date" // 导包语句,多个使用,号分隔
info="我的页面" // 对页面的说明信息
isELIgnored="false" // 是否忽略EL表达式,默认false
isErrorPage="false" // 当前页面是否是错误页面
isThreadSafe="true" // 是否是线程安全
language="java" // JSP脚本是java语言
pageEncoding="utf-8" // 输出数据的编码方式
session="true" // 是否使用会话机制
extends="" // 指定翻译好的servlet继承的父类。不要指定该属性
%>
多学一招:如何修改JSP的编码方式?
MyEclispe à Window à Preferences à 搜索JSP à UTF-8 à Apply
多学一招:如何处理JSP的异常?
如果在JSP页面中需要处理异常,那么一般采用的是使用page指令指定错误的处理页面。
一般的如:File file = new File(“D:\\jnb.txt”); 默认在翻译的时候被翻译到指定类的_jspservice()方法中,而该方法使用try{}catch(Throwable e){}默认进行了处理,处理方式输出异常栈消息到页面。
5.2 include指令
该指令用于在当前页面中包含其他的页面。
举例1:在当前页面包含一个其他的JSP页面?
<%@include file="/included.jsp" %> // file属性指定包含的文件
以上的包含在翻译的时候Tomcat会将被包含的页面直接赋值进来形成一个翻译好的源文件,因此被包含页面中只要编写被包含的内容即可,无需再写其他的JSP模版元素(HTML)。
我们将这种翻译为一个源文件的包含称之为静态包含。
5.3 taglib指令(重点)
该指令主要用于引入JSTL标签库和自定义标签库。
举例1:引入JSTL核心标签库并使用?
<%@taglib uri="http://java.sun.com/jsp/jstl/core" // 指定标签库URL
prefix="c" %> // 指定标签库的前缀
<c:out value="hello jstl"></c:out>
总结:
JSP的三大指令中最常用的是page指令和taglib指令,而且这些指令都可以使用多次,而且可以出现在JSP页面中任意位置。
6、JSP内置九大对象
在学习JSP的page指令的时候我们学习了一个属性isErrorPage用于指定当前页面是否是处理异常的页面,如果设置为true那么直接可以在该页面中使用exception获取异常的对象并输出异常信息。
打开error.jsp页面:
public void _jspService(HttpServletRequest request,
HttpServletResponse response) throws java.io.IOException, ServletException {
PageContext pageContext = null;
HttpSession session = null;
Throwable exception = org.apache.jasper.runtime.JspRuntimeLibrary.getThrowable(request);
if (exception != null) {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
ServletContext application = null;
ServletConfig config = null;
JspWriter out = null;
Object page = this;
JspWriter _jspx_out = null;
PageContext _jspx_page_context = null;
.......
下面的语句其实可以访问该方法中前面定义还的局部变量。
}
以下就是JSP中内置的九大隐含对象
JSP内置对象 | JSP内置对象类型 | Servlet中的对象 |
request | HttpServletRequest | HttpServletRequest |
response | HttpServletResponse | HttpServletResponse |
config | ServletConfig | ServletConfig |
application | ServletContext | ServletContext |
pageContext | PageContext | 空 |
session | HttpSession | HttpSession |
exception | Throwable | Throwable |
out | JspWriter(print) | PrintWriter/OutputStream |
page | Object | Object |
大家发现JSP的九大内置对象中其实有很多我们在Servlet技术中学过了,因此在学习JSP的内置九大对象的时候我们只是学习没有学过的两个对象即:out和pageContex对象。
6.1 out对象
<%
out.write("out"); // JspWriter
response.getWriter().write("response.getWriter()");
%>
运行结果如下
response.getWriter() out
发现问题先输出的会后显示?
改变代码如下
<%
out.write("out"); // JspWriter
out.flush();
response.getWriter().write("response.getWriter()");
%>
运行结果如下
out response.getWriter()
从以上程序可以发现输出的顺序是由缓冲区决定的。
举例1:使用JSP下载图片?
<%@ page language="java" import="java.util.*,java.io.*" pageEncoding="UTF-8"%><%
// 获取路径
String path = this.getServletContext().getRealPath("/123.bmp");
File file = new File(path);
// 指定响应头
response.setHeader("content-disposition","attachment;filename="+file.getName());
// 输出图片
InputStream in = new FileInputStream(file);
ServletOutputStream res_out = response.getOutputStream();
byte[] b = new byte[1024];
int len = 0;
while( (len=in.read(b)) != -1 ){
// 输出数据
res_out.write(b,0,len);
}
// 释放资源
in.close();
%>
一定要将该文件中的普通的文本换行和HTML标签全部删除。否则会运行异常。
6.2 pageContext对象(重点)
该对象是PageContext类型,PageContext继承自JspContext可以帮助开发者提供一些上下文环境信息,即:获取其他的八大隐含对象。
举例1:使用pageContext获取其他JSP八大内置对象?
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"
isErrorPage="true"
%>
<%
out.write( ( pageContext.getRequest() == request ) + "<br/>" );
out.write( ( pageContext.getResponse() == response ) + "<br/>" );
out.write( ( pageContext.getServletConfig() == config ) + "<br/>" );
out.write( ( pageContext.getServletContext() == application )+"<br/>");
out.write( ( pageContext.getSession() == session ) + "<br/>" );
out.write( ( pageContext.getPage() == page ) + "<br/>" );
out.write( ( pageContext.getOut() == out ) + "<br/>" );
out.write( ( pageContext.getException() == exception ) + "<br/>" );
%>
运行结果如下:
true
true
true
true
true
truetrue
true
JSP中九大内置对象均为单例模式。
多学一招:如果只是获取其他的内置对象,那么PageContext类有什么意义?
只要在实际的开发环境中传递PageContext变量,那么接收该参数的类或方法就会具备给页面进行任意输出的功能。典型的应用在自定义标签技术中。
举例2:使用pageContext对象给指定的域中放置属性?
// 给pageContext添加和获取属性(默认是page域)
pageContext.setAttribute("name","jack");
String name = (String)pageContext.getAttribute("name");
out.write(name+"<br/>");
// 给request域对象添加和获取属性 pageContext.setAttribute("address","gz",PageContext.REQUEST_SCOPE);
String address = (String)request.getAttribute("address");
out.write(address+"<br/>");
// 给session域对象添加和获取属性
pageContext.setAttribute("age","30",PageContext.SESSION_SCOPE);
String age = (String)session.getAttribute("age");
out.write(age+"<br/>");
// 给application域对象添加和获取属性
pageContext.setAttribute("phone","123349574935",
PageContext.APPLICATION_SCOPE);
String phone =
(String)pageContext.getAttribute("phone",
PageContext.APPLICATION_SCOPE);
out.write(phone+"<br/>");
// 给page域对象添加和获取属性
pageContext.setAttribute("gender","male",PageContext.PAGE_SCOPE);
String gender = (String)pageContext.getAttribute("gender",PageContext.PAGE_SCOPE);
out.write(gender+"<br/>");
举例3:按照域的大小逐级查找属性?(重点)
pageContext.setAttribute("email","jnb@test.cn",PageContext.SESSION_SCOPE);
// 从每一个域中获取指定属性如果找到直接返回
String email = (String)pageContext.findAttribute("email");
out.write(email+"<br/>");
以上代码在查找域的时候默认是以从小到大的域顺序进行查找的
page - request - session - application
page域中的数据只有当前页面可以访问。
request域中的数据只能在同一请求中访问。
session域中的数据只能在同一会话中访问。
application域中的数据只能在同一网站中访问。
思考:pageContext.setAttribute("country","中国");该语句默认将属性添加给哪一个域?
解答:默认是page域,在当前页面使用该属性。
7、JSP标签
为了使得美工人员可以快速的阅读和修改JSP页面,那么开发人员应该尽力避免页面中出现过多的JSP脚本代码。因此SUN开发了JSP标签用于简化脚本的输出。
JSP标签也称之为JSP动作。
7.1 JSP标签语法
<jsp : 动作名 属性1= ‘属性值’ ></jsp : 动作名>
7.2 常见的JSP标签
<jsp:include>标签 à 包含指定的页面
<jsp:forward>标签 à 请求转发
<jsp:param>标签 à 在包含页面或请求转发的时候传递参数,不能单独使用。
举例1:使用JSP动作实现页面的包含?
<body>
下面是被包含页面
<br/>
<jsp:include page="/demo6.jsp" flush="true"></jsp:include>
<br/>
包含页面中的内容
</body>
该包含会将包含页面和被包含页面都翻译和编译为独立的文件,当用户请求服务器中的包含页面的时候,该页面会动态在包含的位置以流的方式将被包含页面翻译和编译好的响应结果返回给包含页面。因此该包含也称之为动态包含。
思考:动态包含和静态包含哪一个好用?
动态包含称之为懒汉式的加载。所以运行效率稍低。
静态包含称之为饿汉式的加载。所以运行效率稍高。
举例2:包含的时候给被包含页面传递参数?
包含页面
<body>
下面是被包含页面
<br/>
<jsp:include page="/demo6.jsp" flush="true">
<jsp:param name="name" value="test"/>
</jsp:include>
<br/>
包含页面中的内容
</body>
被包含页面
<body>
被包含页面中的内容是:<%= request.getParameter(("name")) %>
</body>
举例3:使用转发标签进行页面的转发并带参数?
<jsp:forward page="/demo6.jsp">
<jsp:param name="name" value="test"/>
</jsp:forward>
8、JSP配置
JSP本质是一个Servlet,那么我们在Servlet中学习的一些配置对于JSP也同样适用。接下来我们就来学习JSP的一些相关配置。
8.1 配置Servlet参数
<servlet>
<servlet-name>myjsp</servlet-name>
<jsp-file>/config.jsp</jsp-file>
<init-param>
<param-name>ip</param-name>
<param-value>192.168.10.1</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>myjsp</servlet-name>
<url-pattern>/index.html</url-pattern>
</servlet-mapping>
获取jsp配置的参数
<%
String ip = config.getInitParameter("ip");
out.write(ip);
%>
8.2 配置自启动JSP
1.直接网站的web.xml中的jsp配置中天以下语句
<load-on-startup>1</load-on-startup>
2.重写JSP的初始化方法
<%!
public void jspInit() {
System.out.println("init jsp...");
}
%>
3.启动服务器,在控制台查看输出信息
9、JSP排错
其实在编写JSP页面的时候一定或多或少会遇到问题,那么作为一个优秀的开发者,必须学会处理不同的错误的能力。
JSP整个运行会分为三个阶段:翻译阶段、编译阶段、运行阶段。那么错误的发生也就可能发生在这三个阶段中的一个,因此下面我们分析不同阶段的异常信息。
翻译阶段:发生错误后不会出现翻译好的java源码。
<body>
<%--
</body>
运行结果如下
org.apache.jasper.JasperException: /finderror.jsp(9,8) Unterminated <%-- tag
org.apache.jasper.compiler.DefaultErrorHandler.jspError(DefaultErrorHandler.java:40)
org.apache.jasper.compiler.ErrorDispatcher.dispatch(ErrorDispatcher.java:407)
org.apache.jasper.compiler.ErrorDispatcher.jspError(ErrorDispatcher.java:132)
如果发生该异常,那么需要看异常栈的第一行异常信息。
编译阶段:发生错误,那么不会出现class文件。
<body>
<%= "hello %>
</body>
运行结果如下
org.apache.jasper.JasperException: Unable to compile class for JSP: // 发生错误的整体描述
An error occurred at line: 9 in the jsp file: /finderror.jsp // 错误发生的具体行号
String literal is not properly closed by a double-quote
6: </head>
7:
8: <body>
9: <%= "hello %> // 错误发生的具体代码
10: </body>
11: </html>
运行阶段:发生错误,如何javase直接打印异常栈消息到页面。
<body>
<%=12/0 %>
</body>
运行结果如下
org.apache.jasper.JasperException: An exception occurred processing JSP page /finderror.jsp at line 9
6: </head>
7:
8: <body>
9: <%=12/0 %>
10: </body>
11: </html>
其实编程的时候难免遇到错误或异常,开发人员只需要冷静的分析错误信息即可。
10、总结
主要讲解了JSP的基本语法、指令和动作。但是大家应该更多的学习好JSP的基本语法和page指令并了解相关的JSP配置和动作。