本文中源码工程下载地址:https://download.csdn.net/download/hancoder/
请先学Servlet
引入:
问题:
在学习了 Servlet 之后,使用 Servlet 进行页面的展现,代码书写过于麻烦。极大的影响开发的效率,那么有没有一种方式可以让我们像以前写网页一样来进行网页的编程工作呢?
解决:
使用 JSP 技术
概念:
JSP 全名为 Java Server Pages,中文名叫 java 服务器页面,其根本是一个简化的 Servlet 设计,它 [1] 是由 Sun Microsystems 公司倡导、许多公司参与一起建立的一种动态网页技术标准。
特点:
本质上还是 Servlet
跨平台,一次编写处处运行
组件跨平台
健壮性和安全性
一、 JSP的基本原理
1.1 jsp是什么
JSP通过在标准的HTML页面中嵌入Java代码,其静态的部分无须Java程序控制,只有那些需要从数据库读取或需要动态生成的夜间内容,才使用Java脚本控制。
浏览器发起请求,请求JSP,请求被 Tomcat 服务器接收,执行JspServlet将请求的JSP文件转义成为对应的java文件 (也是Servlet),然后执行转义好的 java 文件。
<html>
<head>
<title>HTML示例</title>
</head>
<body>
<% out.println(new java.util.Date());
//f放在\<%标签内代表S JAVA脚本,而不是静态内容。%>
</body>
</html>
如果把上述JSP直接拖到浏览器中,显示的是代码内容。说明JSP页面必须放在Web应用中才生效。
从表面上看,JSP页面已经不再需要Java类,似乎完全脱离了Java面向对象的特征。事实上,JSP的本质依然是Servlet(一个特殊的Java类),【每个JSP页面就是一个Servlet实例】—JSP页面由系统编译成Servlet,Servlet再负责相应用户请求。也就是说,JSP其实也是Servlet的一种简化,使用JSP时,其实还是使用Servlet,因为Web应用中的每个JSP页面都会由Servlet容器生成对应的Servlet。对于Tomcat而言,JSP页面生成的Servlet放在work路径对于的Web应用下。
1.2 JSP与servlet比较
Servlet是一个特殊的Java程序,它运行于服务器的JVM中,能够依靠服务器的支持向浏览器提供显示内容。
JSP本质上是Servlet的一种简易形式, JSP会被服务器处理成一个类似于Servlet的Java程序,可以简化页面内容的生成。
Servlet和JSP最主要的不同点在于,Servlet 的应用逻辑是在Java 文件中,并且完全从表示层中的HTML分离开来。而JSP的情况是Java和HTML可以组合成一个扩展名为.jsp 的文件(有人说,Servlet就是在Java中写HTML,而JSP就是在HTML中写Java代码,当然,这个说法还是很片面的)。
JSP侧重于视图,Servlet更侧重于控制逻辑,在MVC模式中,JSP适合充当视图(view)而Servlet适合充当控制器(controller)。
1.3 helloworld
jsp文件
//jsp脚本2,如"hello2.jsp"
<%@ page language="java" contentType="text/html; charset=utf8" %>
<!DOCTYPE html>
<html>
<head>
<title>Insert title here</title>
</head>
<body>
<%
for(int i =0;i<7;i++){
out.println("<font size ='" +i+"'>");%>
哈哈哈</font>
<br/>
<%}%>
</body>
</html>
JSP生成java文件:
- 原文件:在
eclipse工作目录下有\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\work\Catalina\localhost\day2项目镜像名\org\apache\jsp
, - 生成文件:该文件夹下有
hello2_jsp.java和hello2_jsp.class
。这两个文件都是Tomcat生成的,Tomcat根据HSP页面生成对应Servlet的Java文件和class文件。
打开对应的java文件,可以看到一个特殊的类,是一个Servlet类。
package org.apache.jsp;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
//继承HttpJspBase子类,该类其实是Servlet的子类。
public final class index_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;
}
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;
}
// init
public void _jspInit() {
}
// Destroy
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 {
if (!javax.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) {
final java.lang.String _jspx_method = request.getMethod();
if ("OPTIONS".equals(_jspx_method)) {
response.setHeader("Allow","GET, HEAD, POST, OPTIONS");
return;
}
if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method)) {
response.setHeader("Allow","GET, HEAD, POST, OPTIONS");
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSP 只允许 GET、POST 或 HEAD。Jasper 还允许 OPTIONS");
return;
}
}
final javax.servlet.jsp.PageContext pageContext;
javax.servlet.http.HttpSession session = null;
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=utf8");
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>Insert title here</title>\r\n");
out.write("</head>\r\n");
out.write("\r\n");
out.write("<body>\r\n");
out.write(" ");
for(int i =0;i<7;i++){
out.println("<font size ='" +i+"'>");
out.write("\r\n");
out.write(" \t哈哈哈</font>\r\n");
out.write("\t\t<br/>\r\n");
out.write("\t");
}
out.write("\r\n");
out.write(" \r\n");
out.write("</body>\r\n");
out.write("</html>\r\n");
out.write("\r\n");
} 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);
}
}
}
输出图:
可以看出,JSP页面里的Java代码不仅仅可以输出动态内容,还可以动态控制页面里的静态内容。如图输出了7个哈哈哈。
总结
-
可以看出原来jsp里html格式的内容被按行改写为
out.write("\r\n");
,他其实是我们原来的response.getWriter.write()
-
jsp文件生成了一个java文件,类的关键字:final、extends、implements
-
方法包括:
_jspInit()
:初始化JSP/Servlet的方法,_jspService()
:对用户请求生成响应的方法,_jspDestroy()
:销毁JSP/Servlet之前的方法 -
jsp中的内容除了第一行
<%@ page language="java" contentType="text/html; charset=utf8" %>
解析掉了,剩下的语句均会用java排好格式后以out.write("")
方法输出,
1.4 为什么是JspService()而不是Service()
在Servlet中,Tomcat只认识Service方法,而不认识JSP中的JspService()方法,因为JSP程序中没有Service()方法,所以Tomcat会去父类HttpJspBase中调用父类中的Service()方法。而有趣的是,父类中正好有Service()方法,而该Serivce()方法的大括号内调用了_JspService(request,response)
。
所以_JspService(request,response)
。实际上就是Service()
1.5 JSP的流程:浏览器与服务器:
- 对于用户:用户发出请求,然后JSP Server返回标准的html页面。
- 对于JSP Server:如果该JSP页面第一次收到请求,先编译JSP文件,生成对应的Servlt(所以第一个访问的人响应时间长);如果该JSP页面已有对应的Servlet,则根据请求动态生成标准的html页面。每次都会根据请求动态生成html文档。
结论:
- JSP文件必须在JSP服务器内运行。
- JSP文件必须生成Servlet才能执行。
- 每个JSP页面的第一个访问者速度很慢,因为必须等待JSP编译成Servlet。
- JSP页面的访问者无须安装任何客户端,甚至不需要运行Java的运行环节,因为JSP页面输送到客户端的是标准HTML页面。,
JSP常用语法:
-
java脚本:<% 这里写java内容 %>
<%! //声明一个整形变量 public int count; //声明一个方法 public String info(){ return "hello"; } %> 说明:看起来很特别,似乎不需要定义类就可直接定义方法,方法似乎可以脱离类存在。实际上,JSP声明将会转换成对应Servlet的成员变量或成员方法,因此JSP声明依然符合java语法
-
定义变量/方法:<%! 声明变量或者方法 %>
-
注释:<%-- jsp注释讲究一个对称 --%>
<%-- jsp注释讲究一个对称 --%> <!-- HTML注释 --> HTML的源码注释可以通过浏览器上源代码查看到,但JSP的注释无法通过源代码查看到
-
输出:<%=java代码输出表达式 %> ,相当于把结果输出到html页面上
相当于 out.println(count++);
类与对象
下面的代码说明,在JSP服务器端由一个Servlet实例,如果代码中出现了static静态变量,那么每访问一次页面,返回的值就+1.
hello3.jsp
-------------------------------------------
<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title> 声明示例 </title>
<meta name="website" content="http://www.crazyit.org" />
</head>
<!-- 下面是JSP声明部分 -->
<%!
//声明一个整形变量
public int count;
//声明一个方法
public String info(){
return "hello";
}
%>
<body>
<%
//将count的值输出后再加1
out.println(count++);
%>
<br/>
<%
//输出info()方法的返回值
out.println(info());
%>
</body>
</html>
在浏览器中测试该页面时,可以看到正常输出了count值,但没刷新一次(即使不同浏览器),count值就会加1。上面的代码中有个count成员和一个info()方法,看似不属于任何类,但这只是一个假象。打开对应的hello3_jsp.java文件,可以看到这两个东西呗包含在了hellO3_jsp
中。
结论:JSP中的内容都会转换成对应的Servlet成员变量和方法,每个Servlet在容器中只有一个实例。
注:JSP中的方法与变量对应Servlet类的方法和成员,所以可以用private,public,static,但不可以用abstract,因为会导致Servlet变成抽象类,从而无法实例化。
hello4.jsp实例:<%=表达式%>等价于out.println(表达式)。下面页面的执行效果与hello.jsp一样。
------------------------
<html>
<%!
public int count;
public String info(){
return "hello";
}
%>
<body>
<!-- 使用表达式输出变量值 -->
<%=count++%>
<br/>
<!-- 使用表达式输出方法返回值 -->
<%=info()%>
</body>
</html>
输出JSP表达式:<%=%>
JSP提供了输出表达式的简单方法:
<%=java代码输出表达式 %>
相当于原来的out.prinln()
<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title> 小脚本测试 </title>
</head>
<body>
<%
for(int i=0;i<10;i++){
%>
<tr>
<td>循环制:</td>
<td><%=i%></td>
</tr>
<%
}
%>
<br/>
<%
//输出info()方法的返回值
out.println(info());
%>
</body>
</html>
二、 JSP的3个编译命令
解析:
<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
JSP的编译指令是通知JSP引擎的消息,他不直接生成输出。编译指令都有的默认值,所以无需都指定。
格式:<%@ 编译指令名 属性名="值"...>
常见的三个编译指令:
- page:该指定是指针当前页面的指令
- INCLUDE:用于指定包含另一个界面。
- taglib:用于定义和访问自定义标签。
2.1 page指令
属性 | 定义 | 示例 |
---|---|---|
language =“语言” | 主要指定JSP 容器 要用什么语言来编译JSP页。JSP 1.2 规范中指出,眼下仅仅能够使用Java 语言。只是未来不排除添加其他语言。如C、C++、Perl 等等。默认值为Java | <%@page lanuage=“java”%> |
extends = “基类名” | 主要定义此JSP 网页产生的Servlet 是继承哪个父类 | |
import= “包名” | 定义此JSP 网页能够使用哪些Java类库,默认导入了几个包。如果有的包没导入,将报错:...cannot be resolved | <%@page import=java.util.*"%> |
session=”true或false” | 决定此JSP 网页能否够使用session 对象。默认值为true | |
buffer=”none或size in kb” | 决定输出流(output stream) 是否有缓冲区。默认值为8KB 的缓冲区 | |
autoFlush=”true 或false” | 决定输出流的缓冲区是否要自己主动清除,缓冲区满了会产生异常(Exception)。默认值为true | |
isThreadSafe=”true 或 false” | 告诉JSP 容器,此JSP 网页能否同一时候处理多个请求。默认值为true。假设此值设为false。 转义生成的Servlet会实现SingleThreadModel接口。 | |
info =”text” | 表示此JSP 网页的相关信息 | |
errorPage=”error_url.jsp” | 实质是JSP的异常处理机制。表示假设发生异常错误时。网页会被又一次指向指定的URL。如果页面异常或错误,而该JSP没有对应的处理代码,就会自动调用该属性所指定的JSP页面。如果没有指定错误界面,系统则直接把异常信息呈现给客户端浏览器。错误界面需要指定isErrorPage,如下条 | |
isErrorPage=”true或false” | 表示此JSP Page 是否为专门处理错误和异常的网页。在上个标签中指定了的错误界面会调到这里显示错误信息。 | |
contentType = “ctinfo” | 表示MIME 类型和JSP 网页的编码方式,其作用相当于HttpServletResponse接口的setContentType()方法 |
<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
<%@ page import="java.sql.*" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title> 小脚本测试 </title>
<meta name="website" content="http://www.crazyit.org" />
</head>
<body>
<%
//注册数据库驱动
Class.forName("com.mysql.jdbc.Driver");
//获取数据库连接
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/javaee","root","32147");
//创建Statement
Statement stmt = conn.createStatement();
//执行查询
ResultSet rs = stmt.executeQuery("select * from news_inf");
%>
<table bgcolor="#9999dd" border="1" width="300">
<%
//遍历结果集
while(rs.next()){
%>
<tr>
<!-- 输出结果集 -->
<td><%=rs.getString(1)%></td>
<td><%=rs.getString(2)%></td>
</tr>
<%}%>
<table>
</body>
</html>
如上程序中,使用了两条page指令
<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
<%@ page import="java.sql.*" %>
其中第二条指令用于导入本页面中使用的类,如果没有通过page指令的import属性导入这些类,则需在脚本中使用全类名(全限定类名)--即必须带报名。课件,此处的import属性类似于java程序中的import关键字的作用
删除第二条page会出现Connection cannot be resolved to a type
2.2 include指令
和C++中的include差不多,如C++预编译的时候会将include的文件夹全部写到主程序中。
这是个静态的include语句,它会把模板页面的其他编译指令也包含进来,但动态include则不会。
include既可以包含静态的文本,也可以包含动态的JSP页面。静态的编译指令会将被包含的页面加入本页面,融合成一个页面,因此被包含页面甚至不需要是一个完整的页面。
include指令能够将一个外部文件嵌入到当前jsp文件里,同时解析这个页面中的jsp语句。include命令既能够包括jsp页面也能够包括静态文本。
编译指令语法例如以下:
<%@ include file="要导入的jsp页面或文本文件" %>
如:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>include</title>
</head>
<body>
<%@ include file="header.jsp"%>
<%@ include file="text.txt"%>
<%@ include file="footer.jsp"%>
</body>
</html>
使用include指令注意事项:
1.file名不能为依变量。以下的样例是错误的:
<%! String url="header.jsp" %>
<%@ include="<%=url %>"
2.file名后面不能传入參数。以下的样例是错误的:
<%@ include file="header.jsp?username=abc" %>
3.file的路径是相对于此jsp的路径。
2.3 taglib指令
taglib指令用于引入一些特定的标签库,语法格式:
<%@ taglib prefix="标签前缀tagPrefix" uri="标签库tagLibraryURI" %>
如使用struts标签库:
<%@ taglib prefix="s" taglib="/struts-tags" %>
prefix=”tagPrefix “用于定制标记的前缀,jsp, jspx, java, javax, servlet, sun, 和sunw已被sun公司保留。uri=”tagLibraryURI”统一资源标记库,制定一个特定的标记库,能够是相对路径,也能够是绝对路径。
三、jsp的7个动作指令
动作指令与编译指令不同,编译指定是通知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 实例的属性值。
3.1 forward指令
forward指令用于将页面响应转发到另外的页面,能够是html页面、jsp页面、容器中的servlet.
语法例如以下:
对于JSP1.0使用语法 :
<jsp:forward page="目标页面地址">
</jsp:forward>
对于JSP1.1以上规范,快乐使用如下语法:
<jsp:forward page="login.jsp">
<jsp:param name="username" value="yaopan" />
</jsp:forward>
假设转发的时候须要传递參数能够使用<jsp:param></jsp:param>
指令进行设置。
第二种语法用于转发时增加额外的请求参数。增加的请求参数的值可以通过HttpServletRequest
类的getParameter()
方法获取。
------------下面是jsp-forward.jsp----
传递了一个参数,key为"age",值为29-----------------------------
<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title> forward的原始页 </title>
<meta name="website" content="http://www.crazyit.org" />
</head>
<body>
<h3>forward的原始页</h3>
<jsp:forward page="forward-result.jsp">
<jsp:param name="age" value="29"/>
</jsp:forward>
</body>
</html>
这个jSP界面包含了简单的h3信息,然后将客户端请求转发到forward-result.jsp页面,转发请求时增加了一个请求参数:参数名为age,参数值为29
----------下面是forward-result.jsp-----在结果页上显示了29------------------
在forward-result.jsp页面中,使用了request内置对象来获取增加的请求参数值。
<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>forward结果页</title>
<meta name="website" content="http://www.crazyit.org" />
</head>
<body>
<!-- 使用request内置对象获取age参数的值 -->
<%=request.getParameter("age")%>
</body>
</html>
-------------下面是form.jsp------
---------------------------
<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title> 提交 </title>
<meta name="website" content="http://www.crazyit.org" />
</head>
<body>
<!-- 表单提交页面 -->
<form id="login" method="post" action="jsp-forward.jsp">
<input type="text" name="username">
<input type="submit" value="login">
</form>
</body>
</html>
修改forward-result.jsp中的内容,大体样式变成这样,然后重新提交form
<body>
<!-- 使用request内置对象获取age参数的值 -->
<%=request.getParameter("age")%>
<!-- 输出username请求参数的值 -->
<%=request.getParameter("username")%>
</body>
可以看出,result显示的界面中,不仅有正常传过来的age,还有从form那里传过来的username。
结论:
result中不仅可以输出forward指令增加的请求参数,还可以看到表单里username表单域对应的请求参数,这表明执行forward时不会丢失请求参数。
page="{relativeURL|<%expression%>}"
3.2 include指令
include指令是一个动态的include指令,也用于包括某个页面。但不会导入被include原页面的编译指令,仅仅将被导入页面的body内容插入本页面。
导入方式:
<jsp:include page="{relativeURL|<%expression%>}" flush="true"/>
或者,能够通过param指令传递參数,在被导入页面中加入额外的请求参数:
<jsp:include page="{relativeURL|<%expression%>}" flush="true"/>
<jsp::param name="" value=""/>
</jsp:include>
flush属性用于指定输出缓存是否转移到被导入到文件中。如果指定为true,则包含在被导入文件中;如果指定为false,则包含在原文件中。对于JSP1.1旧版本,只能设置为false。
-------------------jsp-include.jsp-----------------------
<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title> jsp-include测试 </title>
<meta name="website" content="http://www.crazyit.org" />
</head>
<body>
<!-- 使用动态include指令导入页面 -->
<jsp:include page="scriptlet.jsp" />
</body>
</html>
在Servlet上执行后,生成的Servlet源代码有如下部分内容:
out.write("<!-- 使用动态include指令导入页面 -->\r\n");
org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "scriptlet.jsp", out, false);
out.write("\r\n");
out.write("</body>\r\n");
out.write("</html>");
可以看出:动态导入只是使用一个include方法来插入模板页面的内容【的索引】,而不是将模板页面完全融入到本页面中。
总结:
<%@include>
指令,(静态包含)被包含的文件被原封不动的插入到包含页面中使用该指令的位置,然后JSP编译器再对这个合成的文件进行编译,【最终编译后的文件只有一个】。 是在翻译阶段执行。
<jsp:include>
(动态包含)包含文件时,当该动作标识执行后,JSP程序会将【请求转发】到(注意不是重定向)被包含页面,并【将执行结果输出到浏览器中】,然后返回页面【继续执行后面的代码】,以为web容器执行的两个文件,所以JSP编译器会分别对这两个文件进行编译。 在请求处理阶段执行.
说明:
翻译阶段:
Jsp容器将jsp页面转化成servlet(称为jsp页面实现类—JSP Page implementation class),并编译这个servlet。这两步就组成了翻译阶段。
请求处理阶段:
Jsp容器除将jsp页面转化成servlet外,还调用jsp页面实现类以处理每个请求并产生应答。这个阶段我们就称为请求处理阶段。请求处理阶段仅执行类文档
动态导入和静态导入的三点 区别:
- 静态导入是将被导入页面的代码完全融入,两个页面融合成一个整体Servlet;而动态导入则在Servlet中使用include方法来引入被导入页面的内容。
- 静态导入时被导入页面的编译指令会起作用;而动态导入时被导入页面的编译指令则失去作用,只是插入被导入页面的body内容。
- 动态导入还可以增加额外的参数。即如下代码方式:
-------------- jsp-include2.jsp -----------------------
<body>
<jsp:include page="forward-result.jsp" >
<jsp:param name="age" value="32"/>
</jsp:include>
</body>
--------------forward-result.jsp----------------
<body>
<!-- 使用request内置对象获取age参数的值 -->
<%=request.getParameter("age")%>
<!-- 输出username请求参数的值 -->
<%=request.getParameter("username")%>
</body>
----------------输出结果------------------
32 null
3.3 useBean、setProperty、getProperty指令
这三个指令与javaBean相关,
- useBean用于在jsp页面中初始化一个Java实例;
- setProperty为javaBean实例的属性设置值;
- getProperty属性用于输出javaBean实例的属性。
假设jsp页面中须要反复使用某段代码,就可以把这段代码定义成java方法,然后多个jsp页面调用该方法达到页面复用的效果。
useBean的语法格式例如以下:
<jsp:useBean id="beanID实例名" class="java包名.类名" scope="page|request|session|application"/>
或
<jsp:useBean id="beanID" class="java包名.类名" scope="page|request|session|application"></jsp:useBean>
其中,id属性是javaBean的实例名,class属性确定javaBean的实现类。scope属性用于指定JavaBean实例的作用范围,该范围有一下4个值:
- page:该JavaBean实例仅在该页面有效。
- request:该JavaBean实例在本次请求有效。
- session:该JavaBean实例在本次session内有效。
- application:该JavaBean实例在本应用内一直有效。
setProperty
setProperty语法格式如下:
<jsp:setProperty property="ProtertyName" name="BeanName" value="value"/>
name属性是需要设定JavaBean的实例名;property属性确定需要设置的属性名;value属性则确定需要设置的属性值。
getProperty
getProperty语法格式如下:
<jsp:getProperty property="ProtertyName" name="BeanName" />
name属性确定需要输出的JavaBean的实例名,property属性确定需要输出的属性名。
结合使用实例:
--------beanTest.JSP----------------
<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title> Java Bean测试 </title>
<meta name="website" content="http://www.crazyit.org" />
</head>
<body>
<!-- 创建lee.Person的实例,该实例的实例名为p1 -->
<jsp:useBean id="p1" class="Person" scope="page">
<!-- 设置p1的name属性值 -->
<jsp:setProperty name="p1" property="name" value="wawa"/>
<!-- 设置p1的age属性值 -->
<jsp:setProperty name="p1" property="age" value="23"/>
<!-- 输出p1的name属性值 -->
<jsp:getProperty name="p1" property="name"/><br/>
<!-- 输出p1的age属性值 -->
<jsp:getProperty name="p1" property="age"/>
</body>
</html>
//----------在WEB-INF/src/lee/Person.java有Person这个类----Person.java-----
// -----------编译得到的文件在WEB-INF/classes/lee/Person.class
package lee;
public class Person
{
private String name;
private int age;
//无参数的构造器
public Person()
{
}
//初始化全部属性的构造器
public Person(String name , int age)
{
this.name = name;
this.age = age;
}
//name属性的setter和getter方法
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return this.name;
}
//age属性的setter和getter方法
public void setAge(int age)
{
this.age = age;
}
public int getAge()
{
return this.age;
}
}
对于上面JSP页面中的<jsp:setProperty />和<jsp:getProperty />
标签而言,他们都要求根据属性名来操作JavaBean的属性。实际上<jsp:setProperty />和<jsp:getProperty />
要求的属性名,与Java类中定义的属性有一定的区别,例如<jsp:setProperty />和<jsp:getProperty />
需要使用name属性,但JavaBean中是否真正定义了name属性并不重要,重要的是在JavaBean中提供了setName()和getName()
方法即可。事实上,当页面使用setName()和getName()
标签时,系统底层就是调用setName()和getName()
方法来操作Person实例的属性的。(方法中Name的首字母大写会与成员变量name不一致,是因为我们命名时遵守了驼峰命名法,但是JSP还是能够有效关联大小写,找到name)。
当我们为Web应用提供新的class文件后,必须重新该Web应用,让他可以重新加载这些新的class文件。
注:
java只是源文件,我们放在web应用的WEB-INF/src路径下,实际上java源代码对web应用不起作用,所以我们会使用Ant来编译它,并将编译得到的二进制文件放入WEB-INF/classes路径下。而且,当我们为web应用提供了新的class文件后,必须重启该web应用,让他可以重新加载这些新的class文件。
上面三个标签完全可以不适应,使用java代码的new代码代替是一样的。
3.4 plugin指令
plugin主要用于下载server端的javaBean或applet到client运行。如今非常少使用。
3.5 param指令
<jsp:forward page="login.jsp">
<jsp:param name="username" value="yaopan" />
</jsp:forward>
param指令用于设置参数值,该指令本身不能单独使用,单独的param指令没有意义,配合以下三个指令使用:
- jsp:include
- jsp:forward
- jsp:plugin
当与include指令结合时,param指令用于将参数值传入被导入的页面;
当与forward指令结合使用时,param指令用于将参数值传入被转向的页面;
当与plugin指令结合使用时,则用于将参数传入页面中的JavaBean实例或Applet实例。
四:
虽然把基于Web应用成为B/S(Browser/Server)架构的应用,但其实Web’应用一样是C/S(Client/Server)结构的应用,只是这种应用的服务器是Web服务器,而客户端是浏览器。
对于大部分浏览器而言,它通常负责完成三件事情:
- 向远程服务器发送请求
- 读取远程服务器返回的字符串数据
- 负责根据字符串数据渲染出一个丰富多彩的页面
web服务器则负责接收客户端的请求,每当接收到客户端连接请求之后,web服务器应该使用单独的线程为该客户端提供服务:接收请求数据、送回响应数据。所以称为“请求/响应”架构。
web服务器大致需要完成如下几个步骤:
- 1 启动单独的线程
- 2 使用I/O流读取用户的请求数据
- 3 从请求数据中解析参数
- 4 处理用户请求
- 5 生成响应数据
- 6 使用I/O流向客户端发送请求数据。
126步是通用的,345步存在差异:因为不同请求里包含的请求参数不同,处理用户请求的方式也不同,所生成的响应自然也不同。
实际上,web服务器会调用servlet的_jspService()
方法来完成345步。而web服务器负责完成多线程、网络通信等底层功能。
HttpServletRequest、HttpServletResonse
web服务器执行了第3步后,得到请求参数,需要通过这些请求参数来创建HttpServletRequest、HttpServletResonse等对象,作为调用_jspService()
方法的参数,实际上一个web服务器必须为ServletAIP中绝大部分接口提供实现类。
从上面的介绍可以看出,web应用里的jSP页面、Servlet等程序都将由web服务器来调用,JSP、Servlet之间通常不会相互调用,这就产生一个问题:JSP、Servlet之间如何交换数据?
JSP、Servlet之间如何交换数据?
为了解决这个问题,几乎所有的web服务器(包括java、APS、PHP、Ruby等)都会提供4个类似Map的结构,分配是application、session、request、page,并允许JSP、Servlet将数据放入这4个类似Map的结构中,并允许从这4个Map结构中取出数据。这4个Map结构的区别是范围不同。
- Application:对于整个web应用有效,一旦JSP、Servlet将数据放入application中,该数据将可以被该应用下的其他所有的JSP、Servlet访问。
- session:仅对一次会话有效,一旦JSP、Servlet将数据放入session中,该数据将可以被本次会话的其他所有的JSP、Servlet访问。
- request:仅对本次请求有效,一旦JSP、Servlet将数据放入request中,该数据将可以被该次请求的其他JSP、Servlet访问。
- page:仅对当前页面有效,一旦JSP、Servlet将数据放入page中,该数据只可以被当前页面的JSP脚本、声明部分访问。
就像现实中有两个人,想交换钱,但不能直接相互接触,那么A去银行存钱,B从银行取钱。因此上面4者扮演的类似银行的角色。
JSP中的application、session、request、pageContext 4个内置对象分配用于操作application、session、request、page范围中的数据。
4.3 编码问题
详见另外一个文档
JSP中的编码:
1 pageEncoding:
<%@ page pageEncoding="UTF-8"%>
jsp页面编码: jsp文件本身的编码
2 contentType:
<%@ page contentType="text/html; charset=UTF-8"%>
web页面显示编码:jsp的输出流在浏览器中显示的编码
3 html页面charset:
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
web页面输入编码: 输入框输入的字体编码
4 setCharacterEncoding: request.setCharacterEncoding(),response.setCharacterEncoding()
web服务器输入的请求流: web Server响应浏览器的请求数据
5 setContentType: response.setContentType()
web服务器输出的响应流: web Server响应浏览器的输出数据
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
1. pageEncoding: 只是指明了 JSP 页面本身的编码格式,跟页面显示的编码没有关系;
容器在读取(文件)或者(数据库)或者(字符串常量)时将起转化为内部使用的 Unicode,而页面显示的时候将
内部的Unicode转换为contentType指定的编码后显示页面内容;
如果pageEncoding属性存在,那么JSP页面的字符编码方式就由pageEncoding决定,
否则就由contentType属性中的charset决定,如果charset也不存在,JSP页面的字符编码方式就采用
默认的ISO-8859-1。
2. contentType: 指定了MIME类型和JSP页面响应时的字符编码方式。
如contentType="text/html; charset=UTF-8"
MIME类型的默认值是“text/html”;
字符编码方式的默认值是“ISO-8859-1”.
MIME类型和字符编码方式由分号隔开;
3. pageEncoding和contentType的关系:
1. pageEncoding的内容只是用于jsp输出时的编码,不会作为header发出去的; 是告诉web Server
jsp页面按照什么编码输出,即web服务器输出的响应流的编码;
2. 第一阶段是(第一次访问时)web容器将jsp编译成.java文件,它会根据pageEncoding的设定读取jsp,结果是由指定的编码方案翻译成统一的UTF-8 JAVA源码(即.java,也就是Servlet).
3. 第二阶段是由JAVAC的JAVA源码至java byteCode字节码的编译,不论JSP编写时候用的是什么编码方案,
经过这个阶段的结果全部是UTF-8的encoding的java源码(JVM读取java会默认认为java的编码格式是utf8),JAVAC用UTF-8的encoding读取java源码,结果当然是编译成了UTF-8 encoding的二进制码文件(即.class),这是JVM对常数字串在二进制码(java encoding)内表达的规范.
4. 第三阶段是Tomcat(或其的application container)载入和执行阶段二的来的JAVA二进制码,
输出的结果,也就是在客户端见到的,这时隐藏在阶段一和阶段二的参数contentType就发挥了功效。(这一阶段的理解:class文件在tomcat运行就产生了类似html的页面,这个页面放入请求报文的请求体中,目标url是浏览器,而contentType是JSP中设置的ContextType)
4. 和contentType效果一样的设置方式还有
- html页面charset,
- response.setCharacterEncoding(),
- response.setContentType(),
- response.setHeader();
其中,
response.setContentType(),response.setHeader();优先级最好,
其次是response.setCharacterEncoding();
再者是 <%@page contentType="text/html; chareset=gbk"%>,
最后是<meta http-equiv="content-type" content="text/html; charset=gb2312" />.
5. web页面输入编码: 在设置页面编码<%@page contentType="text/html; chareset=gbk"%>的同时,也就指定了页面的输入编码;如果页面的显示被设置为UTF-8,那么用户所有的页面输入都会按照 UTF-8 编码; 服务器端程序在读 取表单输入之前要设定输入编码; 表单被提交后,浏览器会将表单【字段值】转换为指定字符集对应的【字节值】,然后根据 HTTP 标准 URL编码方案对结果字节进行编码.但是页面需要告诉服务器当前页面的编码方式;
request.setCharacterEncoding(),能修改Serverlet获取请求的编码,
response.setCharacterEncoding(),能修改Serverlet返回结果的编码.
4.3.1 JSP编译过程中JSP源文件转码
(1)原因分析
JSP源文件编译时,JSP引用应用JSP转换工具(jspc)搜索JSP文件中的<%@page contentType="text/html; charset=<JSPcharset>"%>
语句来制定字符集编码形式,如果在JSP文件中未指定charset=<JSPcharset>
,则取JVM中的默认设置file.encoding,一般情况下,这个值是ISO-88591。在简体中文系统中可直接用 file.coding格式将含有中文的JSP源文件转码为UNICODE格式,但是在英文平台或其他操作系统中就会出现乱码。
(2)解决方案
在JSP源文件开头写入<%@ page contentType="GBK"%>
,它表示JSP编译器应该将当前的JSP源文件从GBK格式转换为 UNICODE格式,而不是从所在操作系统的file.encoding格式转换为 UNICODE格式,从而可以避免因含有中文的JSP源文件的移植而造成的乱码问题。
2.JVM对客户端表单提交的参数转码问题(设置请求编码)
(1)原因分析
Java在网络传输中使用的编码是ISO-8859-1,如果在JSP源文件中没有通过<% request.setCharacterEncoding("gbk");%>
指定输入的编码格式,那么直接从 request.getParameter()方式获得的字符串均是ISO-8859-1格式的,中文字符串按照ISO-8859-1格式编码后直接输出,在Web页上看到的必将是一些乱码
2)解决方案
可以采用下述两种方法对问题进行解决
- 利用 request. getParameter()方法获取含有中文字符串参数之前,在JSP源文件中加入语句
<% request.setCharacterEncoding("gbk");%>
,这样指定了输入的编码格式为GBK,JVM将接收到的输入从ISO-8859-1转换为GBK格式后再输出,可以很好地解决乱码问题。
- 每次在JSP源文件中用 request. getParameter()方法获取中文参数后用
String(parameter.getBytes("ISO-8859-1")."GBK")
对其进行转码,这样中文输入即可正确地显示在Web页面上。
3 JWM运行临时 Servlet类文件后产生输出的转码问题(响应编码)
(1) 原因分析
客户端读取服务器端的输出和服务器端读取客户端的输入时, Servlet需要将HTM页面内容转换为浏览器可接受的 encoding(用 contentType指定)内容发送出去。通过web提交时输入的 ServletRequest输出时的 ServletResponse,默认情况下都是利用ISO-8859-1进行编码解码如果不指定输出编码格式,则将输出从 UNICODE格式转为Iso-8591格式后,浏览器直接按照默认的ISO-8859-1格式输出中文,这样浏览器看到的就是乱码了。
(2) 解决方案
可在JP源文件开头处写入如下语句
<%@ page contentType="text/html; charset=GBK"%>
这样浏览器在接收到来自服务器端的、以ISO-8859-1格式编码的数据后就知道应该用GBK格式来显示,从而将含有中文的数据正确地显示在客户端的浏览器上。
4.JSP源文件在数据库的入口和出口处字符编码的转换问题
(1)原因分析
大部分数据库默认支持的编码格式为ISO-8859-1,当JSP文件向数据库插入中文数据时,必须先将数据从GBK/GB2312格式转为ISO-8859-1格式;同理,当JSP文件从数据库读出数据时,须先将数据从ISO-8859-1格式转为 GBK/GB2312格式。如果在数据库入口和出口处没有进行正确的字符编码转换,就会在读取数据库数据时导致汉字信息失真,web页面显示的中文字符就出现乱码。
(2)解决方案
可以采用下述两种方法
- 如果需要处理的数据量比较小时,可以在数据库入口处对含有中文的数据进行强制编码格式转换,下面介绍具体方法
入库: String str1-希赛网培训
String str2= new String(strl.getBytes("GBK"),"ISO-8859-1"):
出库: String st3= result.getstring(1)
String str4= new String(str3.getBytes("ISO-8859-1"),"GBK");
- 如果需要处理的数据量比较大时,可以将数据库默认支持的编码格式直接改为GBK或者GB2312。在 MySQL中,可以通过修改 MySQL安装目录下的配置文件my.ini实现。将 my.ini文件中[MySQL]和[client]两个区中的
default-character-set=***
处的域名***
都改为GBK,然后重启 MySQL服务即可,这样JSP就可以直接存取数据库中的中文数据而不会出现乱码问题
总结
<%@ page pageEncoding="UTF-8"%>
页面的编码
<%@ page contentType="GBK"%>
JSP转到java文件的编码
<% request.setCharacterEncoding("gbk");%>
指定获取的表单数据编码。
<%@ page contentType=text/html; charset=GBK"%>
浏览器显示的编码
html页面charset:<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
web页面输入编码: 输入框输入的字体编码
五、jsp脚本中的9个内置对象
JSP脚本中包含9个内置对象,这9个内置对象都servlet API接口的实例,只是JSP规范对它们进行了默认初始化(由JSP页面对应servlet的_JSPServlet()
方法来创建这些实例)。也就是说,它们已经是对象,可以直接使用。
在编译后的jspservlet中我们可以在_JSPServlet
方法中看到创建九大对象的过程
名称 | 类型 | 含义 | 获取方式 |
---|---|---|---|
request | HttpSevletRequest | 封装所有 请求 信息 | 方法参数 |
response | HttpServletResp onse | 封装所有 响应 信息 | 方法参数 |
session | HttpSession | 封装所有 会话 信息 | req.getSession() |
application | ServletContext | 所有信息 | getServletContext(); request.getServletContext(); |
out | PrintWriter | 输出对象 | response.getWriter() |
exception | Exception | 异常对象 | |
page | Object | 当前页面对象 | |
pageContext | PageContext | 获取其他对象 | |
config | ServletConfig | 配置信息 |
四大作用域
page:在当前页面不会重新实例化.
request:在一次请求中同一个对象,下次请求重新实例化一个request 对象.
session:一次会话。只要客户端 Cookie 中传递的 Jsessionid 不变,Session 不会重新实力会(不超过默认时间.)
实际有效时间:
1 浏览器关闭.Cookie 失效.
2 默认时间.在时间范围内无任何交互。通过在 tomcat 的web.xml 中配置
<session-config>
<session-timeout>30</session-timeout>
</session-config>
application: 只有在 tomcat 启动项目时菜实例化.关闭 tomcat 时销毁application
5.1 application对象
javax.servlet.ServletContext
的实例。代表JSP所属的web应用本身,可用于JSP页面,或者在Servlet之间交换信息。常用的是方法有
getAttribute(String attName)
setAttribute(String attName,String attValue)
getInitParameter(String paramName)
jsp、servlet之间不能相互调用。为了解决jsp和servlet之间数据交换的问题,webserver提供了四个对象,各自是application、session、request、page。
放入application对象中的数据对整个Web应用有效,一旦jsp、servlet将数据放入application对象之中,该数据能够被该应用下其他jsp、servle访问。
application对象代表web应用本身,因此使用application来操作web相关数据。application对象通常有如下两个作用:
- 在整个web应用的多个JSP、Servlet之间贡献数据
- 访问web应用的配置参数
5.1.1 让多个JSP、Servlet共享数据-setAttribute
application通过setAttribute(String attrName,Object value)
方法将一个值设置成application的attrName属性,该属性的值对整个web应用有效,因此该web应用的每个jsp页面或Servlet都可以访问该属性买房问该属性的方法是getAttribute(String attrName)
。
将i值自加后放入application的变量内 ,映射了属性名和变量
--------put-application.jsp-------------------
<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>application测试</title>
<meta name="website" content="http://www.crazyit.org" />
</head>
<body>
<!-- JSP声明 -->
<%!
int i;
%>
<!-- 将i值自加后放入application的变量内 -->
<%
application.setAttribute("counter",String.valueOf(++i));
%>
<!-- 输出i值,每次刷新页面都+1 -->
<%=i%>
</body>
</html>
访问上面的counter属性:
--------------get-application.jsp----------
<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>application测试</title>
<meta name="website" content="http://www.crazyit.org" />
</head>
<body>
<!-- 直接输出application 变量值 -->
<%=application.getAttribute("counter")%>
</body>
</html>
application不仅可以用于JSP页面之间共享数据,还可以用于Servlet和JSP之间共享数据。我们可以把application理解成一个map对象,任何JSP、Servlet都可以把某个变量放入application中保存,并为之指定一个属性名;而该应用里的其他JSP、Servlet就可以根据该属性名来得到这个变量。
Servlet中的访问方法是:
ServletContext sc = getServletConfig().getServletContext();
sc.getAttribute("counter");
具体案例:
-------GetApplication.java-----放在src下,而jsp放在应用下下--------
演示如何在servlet中访问application里的变量----
@WebServlet(name="get-application",
urlPatterns={"/get-application"})
public class GetApplication extends HttpServlet
{
public void service(HttpServletRequest request,
HttpServletResponse response)throws IOException
{
response.setContentType("text/html;charset=gb2312");
PrintWriter out = response.getWriter();
out.println("<html><head><title>");
out.println("测试application");
out.println("</title></head><body>");
ServletContext sc = getServletConfig().getServletContext();
out.print("application中当前的counter值为:");
out.println(sc.getAttribute("counter"));
out.println("</body></html>");
}
}
由于在Servlet中并没有application内置对象,所以上面程序先获取了该web应用的servletContext实例,每个web应用只有一个ServletContext实例,在JSP页面中可通过application内置对象访问该实例。
5.1.2 获得web应用配置参数-getInitParameter
application还有一个重要用处:可用于获取web应用的配置参数。如下JSP页面,访问数据库所使用的信息(驱动、URL、用户名、密码)都在web.xml中给出。
----------getWebParam.jsp-------
<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
<%@ page import="java.sql.*" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>application测试</title>
<meta name="website" content="http://www.crazyit.org" />
</head>
<body>
<%
//从配置参数中获取驱动
String driver = application.getInitParameter("driver");
//从配置参数中获取数据库url
String url = application.getInitParameter("url");
//从配置参数中获取用户名
String user = application.getInitParameter("user");
//从配置参数中获取密码
String pass = application.getInitParameter("passwd");
//注册驱动
Class.forName(driver);
//获取数据库连接
Connection conn = DriverManager.getConnection(url,user,pass);
//创建Statement对象
Statement stmt = conn.createStatement();
//执行查询
ResultSet rs = stmt.executeQuery("select * from news_inf");
%>
<table bgcolor="#9999dd" border="1" width="480">
<%
//遍历结果集
while(rs.next())
{
%>
<tr>
<td><%=rs.getString(1)%></td>
<td><%=rs.getString(2)%></td>
</tr>
<%
}
%>
<table>
</body>
</html>
其中application.getInitParameter(String paramName);
来获取web应用配置参数,这些配置参数应该在web.xml文件中使用context-param元素配置,每个元素配置一个参数,该元素有如下两个子元素:
- param-name:配置web参数名
- param-value:配置web参数值
<context-param/>
web.xml文件中使用元素配置的参数对整个web应用有效,所以也被称为web应用的配置参数。
-------web.xml---------
<?xml version="1.0" encoding="GBK"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<!-- 配置第一个参数:driver -->
<context-param>
<param-name>driver</param-name>
<param-value>com.mysql.jdbc.Driver</param-value>
</context-param>
<!-- 配置第二个参数:url -->
<context-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost:3306/javaee</param-value>
</context-param>
<!-- 配置第三个参数:user -->
<context-param>
<param-name>user</param-name>
<param-value>root</param-value>
</context-param>
<!-- 配置第四个参数:pass -->
<context-param>
<param-name>passwd</param-name>
<param-value>32147</param-value>
</context-param>
</web-app>
在浏览器中浏览getWebParam.jsp页面时,可看到数据库、数据查询完全成功。课件,使用application可以访问web应用的配置参数。
getInitParameter — — —
5.2 config对象
javax.servlet.ServletConfig
的实例
config对象代表当前jsp配置信息,但jsp页面通常无需配置,也就不存在配置信息。该对象在jsp页面中较少使用。在servlet中使用较多,因为servlet需要在web.xml文件中机芯配置,可以指定配置参数。
常用的方法有
getInitParameter(String paramName)
getInitParameternames()
下面程序中使用了config.getServletName()
。输出了
-----------configTest.jsp-----------------
<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>测试config内置对象</title>
<meta name="website" content="http://www.crazyit.org" />
</head>
<body>
<!-- 直接输出config的getServletName的值 -->
<%=config.getServletName()%>
</body>
</html>
-------web.xml---------
<?xml version="1.0" encoding="GBK"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<servlet>
<!-- 指定Servlet名字 -->
<servlet-name>config</servlet-name>
<!-- 指定将哪个JSP页面配置成Servlet -->
<jsp-file>/configTest2.jsp</jsp-file>
<!-- 配置名为name的参数,值为yeeku -->
<init-param>
<param-name>name</param-name>
<param-value>yeeku</param-value>
</init-param>
<!-- 配置名为age的参数,值为30 -->
<init-param>
<param-name>age</param-name>
<param-value>30</param-value>
</init-param>
</servlet>
<servlet-mapping>
<!-- 指定将config Servlet配置到/config URL-->
<servlet-name>config</servlet-name>
<url-pattern>/config</url-pattern>
</servlet-mapping>
</web-app>
5.3 exception对象
exception对象是javax.servlet.Throwable
的实例。代表其他页面中的异常和错误。只有当页面是错误处理页面,即把page指令的isErrorPage属性设置为true时,该对象才可以使用。
常用的方法有
getMessage();
printStackTrace();
异常类型是:<%=exception.getClass()%><br/>
异常信息是:<%=exception.getMessage()%><br/>
打开JSP生成的Servlet类,可以发现如下代码
public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
throws java.io.IOException, javax.servlet.ServletException {
try {
response.setContentType("text/html; charset=utf8");
....
} catch (java.lang.Throwable t) {
if (_jspx_page_context != null)
_jspx_page_context.handlePageException(t);
} finally {
_jspxFactory.releasePageContext(_jspx_page_context);
}
因为jsp脚本和静态HTML部分都将转换成jspService()方法,这就是JSP脚本无需处理异常的原因。一旦try块捕捉到JSP脚本的异常,并且_jspx_page_context
不为null,就会由该对象来处理该异常。
_jspx_page_context
对异常的处理也非常简单:如果该页面的page指令指定了一个errorPage属性,则将请求forward到errorPage属性指定的页面,否则使用系统页面来输出异常信息。
如下程序,注意指定看page:errorPage属性
<!-- 通过errorPage属性指定异常处理页面 -->
<%@ page contentType="text/html; charset=GBK" language="java" errorPage="error.jsp" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title> JSP脚本的异常机制 </title>
<meta name="website" content="http://www.crazyit.org" />
</head>
<body>
<%
int a = 6;
int c = a / 0;//错误
%>
</body>
</html>
转发到的错误界面:
<%@ page contentType="text/html; charset=GBK" language="java" isErrorPage="true" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title> 异常处理页面 </title>
<meta name="website" content="http://www.crazyit.org" />
</head>
<body>
异常类型是:<%=exception.getClass()%><br/>
异常信息是:<%=exception.getMessage()%><br/>
</body>
</html>
此时地址栏仍然是抛出异常的界面。
5.4 out对象
out对象是javax.servlet.jsp.JspWriter
的实例,代表jsp的输出流,永于输出内容形成html页面。
一般使用输出表达式的地方,都可以使用out对象来达到统一的效果。
out对象一些方法:
- out.println():输出完毕后会换行,换行效果在页面中不显示仅仅在源代码中显示
- out.print():输出后不换行
- out.getBufferSize() :输出缓冲区大小
- out.getRemaining() :输出剩余缓冲区大小
- out.clear():清空缓冲区,不将数据发送至client
- out.clearBuffer():将数据发送至client,再清空缓冲区
- out.flush():输出缓冲区内容
- out.close():关闭输出流
5.5page对象
代表该页面本身。通常没有太大用处。也就是Servlet中的this,其类型是生成的Servlet类,能用page的地方就能用this。
5.6 pageContext对象
代表页面上下文,主要用于jsp之间的共享数据,使用pageContext对象能够訪问page、request、session、application范围的变量。
常用方法:getServletContex() getServletConfig()
pageContext.setAttribute(String name);
pageContext.setAttribute(String name,int scopte);
SCOPE取值有下面4个值
PageContext.REQUEST_SCOPE :对应page范围
PageContext.REQUEST_SCOPE :对应request范围
PageContext.SESSION_SCOPE :对应session范围
PageContext.APPLICATION_SCOPE :对应application范围
<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title> pageContext测试 </title>
<meta name="website" content="http://www.crazyit.org" />
</head>
<body>
<%
//使用pageContext设置属性,该属性默认在page范围内
pageContext.setAttribute("page","hello");
//使用request设置属性,该属性默认在request范围内
request.setAttribute("request","hello");
//使用pageContext将属性设置在request范围中
pageContext.setAttribute("request2","hello"
, pageContext.REQUEST_SCOPE);
//使用session将属性设置在session范围中
session.setAttribute("session","hello");
//使用pageContext将属性设置在session范围中
pageContext.setAttribute("session2","hello"
, pageContext.SESSION_SCOPE);
//使用application将属性设置在application范围中
application.setAttribute("app","hello");
//使用pageContext将属性设置在application范围中
pageContext.setAttribute("app2","hello"
, pageContext.APPLICATION_SCOPE);
//下面获取各属性所在的范围:
out.println("page变量所在范围:" +
pageContext.getAttributesScope("page") + "<br/>");
out.println("request变量所在范围:" +
pageContext.getAttributesScope("request") + "<br/>");
out.println("request2变量所在范围:"+
pageContext.getAttributesScope("request2") + "<br/>");
out.println("session变量所在范围:" +
pageContext.getAttributesScope("session") + "<br/>");
out.println("session2变量所在范围:" +
pageContext.getAttributesScope("session2") + "<br/>");
out.println("app变量所在范围:" +
pageContext.getAttributesScope("app") + "<br/>");
out.println("app2变量所在范围:" +
pageContext.getAttributesScope("app2") + "<br/>");
%>
</body>
</html>
5.7 request对象
javax.servlet.http.HttpServletRequest
的实例。
request对象是jsp中的重要对象,每一个对象封装着一次用户请求,客户端的请求参数都被封装都改对象中。这是一个常用的对象,获取客户端请求参数必须使用该对象。而且所有对请求參数都被封装在request对象中,request对象是获取用户请求参数的重要途径。
GET方式的请求:from表单没有设置method的时候默认是get方式。
POST方式的请求:通常使用提交表单from元素的method属性为post。请求参数的大小不受限制,但往往取决于服务器的限制。
5.7.1 获取请求头/请求参数
request对象的一些方法:(这里的内容看Servlet的第4部分吧)
请求头是get,请求体是post
- request.getMethod():获取请求的方法名。如get,post.
- request.getRequestURI():获取请求资源名.如请求訪问:/jspstudy/08/request.jsp
- request.getProtocol():获取请求使用的协议.如:HTTP/1.1
- request.getServerName():获取请求使用的server,如:localhost.
- request.getServerPort():获取请求使用的serverport。如:8080
- request.getRemoteAddr():获取client的ip地址.
- request.getRemoteHost():获取client主机.
- request.getParameter():获取传递过来的參数值.
- request.getParameterValues():请求參数有多个值时。该方法返回多个值组成的数组.
- request.setAttribute():
-----------form.jsp--------
<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
<!DOCtype html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title> 收集参数的表单页 </title>
<meta name="website" content="http://www.crazyit.org" />
</head>
<body>
<form id="form1" method="post" action="request1.jsp">
用户名:<br/>
<input type="text" name="name"><hr/>
性别:<br/>
男:<input type="radio" name="gender" value="男">
女:<input type="radio" name="gender" value="女"><hr/>
喜欢的颜色:<br/>
红:<input type="checkbox" name="color" value="红">
绿:<input type="checkbox" name="color" value="绿">
蓝:<input type="checkbox" name="color" value="蓝"><hr/>
来自的国家:<br/>
<select name="country">
<option value="中国">中国</option>
<option value="美国">美国</option>
<option value="俄罗斯">俄罗斯</option>
</select><hr/>
<input type="submit" value="提交">
<input type="reset" value="重置">
</form>
</body>
</html>
---------request1.jsp---------------
<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
<%@ page import="java.util.*" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title> 获取请求头/请求参数 </title>
<meta name="website" content="http://www.crazyit.org" />
</head>
<body>
<%
//获取所有请求头的名称
Enumeration<String> headerNames = request.getHeaderNames();
while(headerNames.hasMoreElements())
{
String headerName = headerNames.nextElement();
//获取每个请求、及其对应的值
out.println(
headerName + "-->" + request.getHeader(headerName) + "<br/>");
}
out.println("<hr/>");
//设置解码方式,对于简体中文,使用gb2312解码
request.setCharacterEncoding("gb2312");
//下面依次获取表单域的值
String name = request.getParameter("name");
String gender = request.getParameter("gender");
//如果某个请求参数有多个值,将使用该方法获取多个值
String[] color = request.getParameterValues("color");
String national = request.getParameter("country");
%>
<!-- 下面依次输出表单域的值 -->
您的名字:<%=name%><hr/>
您的性别:<%=gender%><hr/>
<!-- 输出复选框获取的数组值 -->
您喜欢的颜色:<%for(String c : color)
{out.println(c + " ");}%><hr/>
您来自的国家:<%=national%><hr/>
</body>
</html>
测试代码:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>request方法</title>
</head>
<body>
<form action="" method="post">
username:<input type="text" name="username"><br/>
<input type="submit">
请求方法名:<%=request.getMethod() %><br/>
请求的资源:<%=request.getRequestURI() %><br/>
请求使用的协议:<%=request.getProtocol() %><br/>
请求使用的server:<%=request.getServerName() %><br/>
请求使用的serverport:<%=request.getServerPort() %><br/>
client的IP地址:<%=request.getRemoteAddr() %><br/>
client主机:<%=request.getRemoteHost() %><br/>
表单提交过来的值:<%=request.getParameter("username") %><br/>
</form>
</body>
</html>
输出:
请求方法名:POST
请求的资源:/jspstudy/08/request.jsp
请求使用的协议:HTTP/1.1
请求使用的server:localhost
请求使用的serverport:8080
client的IP地址:0:0:0:0:0:0:0:1
client主机:0:0:0:0:0:0:0:1
表单提交过来的值:test
5.7.2 操作request范围的属性
HttpServletRequest还包含如下两个方法,用于设置和获取request范围的属性
setAttribute(String attName,Object attValue) 将attValue设置成request范围的属性
Object getAttribute(String attName) 获取request范围的属性
在jsp中的html部分获取方式是RequestScope.变量名
5.7.3 执行forward或include
request还有一个功能就是执行forward和include,也就是代替JSP所提供的forward和include动作指令。前面我们需要forward都是
5.8 response对象
response对象用于server对client的响应,大部分时候使用out对象向页面输出。可是out对象时JspWriter对象的实例,JspWriter时Writer的字类。Writer是字符流,因此out对象无法输出非字符内容。jsp页面须要输出图片、pdf文档、excel等非字符对象时须要使用response作为响应输出.response还能够用于重定向。向client添加cookie
5.9 session对象
session对象代表一次用户会话。一次用户会话的含义是:从client连接server開始。到client与server断开为止,这个过程就是一次会话。
session通经常使用于跟踪用户的会话信息,如推断用户是否登录。购物车中跟踪用户购买的商品。
session范围的属性能够在多个页面点跳转之间共享。一旦关闭浏览器,session结束。session范围内的属性就会所有丢失。
其他
路径问题
方法1:使用相对路径
<img src="../imgs/1.png">
如果层级过多,不方便
方法2:使用绝对路径
需要事先知道项目部署的地址,不实用
方法3:base
<!-- 添加jsp脚本-->
<%
String base = request.getContextPath()+"/";
String url = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+base;
%>
<!--在head中添加base标签-->
<base href="<%=url%>">
<body>
<!-- 默认base中的相对项目路径-->
<img src="imgs/1.png">
</body>
另外,如果页面转向某个Servlet,而Servlet里又是forward到的某个jsp页面,如果这时写相对路径就应该先找到Servlet的路径,也就是web.xml中配置的url-pattern中的路径,如:假设有个x.jsp放在webapplication根目录下,而主页index.jsp是提交到servlet上去的,由Serlet来分发forward到x.jsp,Servlet的url配置如下:
<url-pattern>/servlet/TestServlet</url-pattern>
那么Servlet完成forward转向后,如果没有
<base href="<%=basePath%>">
<script type="text/javascript" src="script/check.js"></script>
就会失效,因为Servlet的访问路径为http://localhost/webapp/servlet/TestServlet那么web服务器会到http://localhost/webapp/servlet/script/下去找check.js此时这里肯定是没有这个文件的,所以,如果遇到这样的情况建议使用绝对路径就不会有错