一. 为什么会出现JSP技术?
JSP的本质是Servlet,当用户向指定的Servlet发送请求时,Servlet利用输出流动态生成HTML页面.包括每一个静态的HTML标签和所有在HTML页面中出现的内容.
包括大量的HTML标签,大量的静态文本及格式等,导致Servlet的开发效率极为底下.所有的表现的逻辑,包括布局,色彩以及图像等,都必须耦合在Java代码中. 这时候JSP出现了.弥补了这种不足.JSP通过在标准的HTML页面中嵌入Java代码.其静态的部分无需Java程序控制,只有那些需要从数据库读取或需要动态生成的页面内容,才使用Java脚本控制.
JSP页面的内容由两部分组成
- 静态部分:标准的HTML,静态的页面内容,这些内容与静态的HTML页面相同
- 动态部分:受Java程序控制的内容,这些内容由Java程序来动态生成.
看下形式,前台有一个jsp文件,经过web容器编译后就生成了.java文件和.class文件,其中.java文件就是其对应的servlet文件,servlet再负责响应请求:
这个servlet类里面主要有3个主要的方法
- init()方法: 初始化JSP/Servlet的方法
- destroy()方法:销毁JSP/Servlet之前的方法
- service()方法:对用户的请求进行响应的方法
这些都是Web容器负责生产管理的,后面会介绍servlet的一些细节,为什么web容器会编译成这样的文件.还有补充一些HTTP等网络方面的知识.希望可以对整个流程清楚.
点这里,这里是jsp预处理,编译成servlet后,再响应到页面的流程
二. JSP注释
1. JSP注释用于标注在程序开发过程中的开发提示,不会被发送到客户端.
2. 注释格式
<%--注释内容-- %>
和HTML注释规划相似,下面是HTML的注释格式
<!--注释内容-->
注意区分
还有Java中一种注释是使用 '//'(引号内的标示)
3.JSP声明
JSP声明用于变量和方法.
比如在jsp文件的<body>标签上面,</head>标签下面,声明一个整形变量和方法
<%!
//声明一个整形变量(在java代码注释写法)
public int conut;
//声明一个方法
public String info(){
return "hello JSP";
}
%>
你可以在<body></body>标签中,输出这个变量和方法
比如
<body>
<% out.println(count++);
out.println(info());
%>
</body>
这给人一种假象,好像在jsp中声明的java变量和方法是可以独立存在的,其实是一种假象,可以看一下这个jsp编译成servlet后的代码
这表明JSP声明部分将转换成对应的Serlvet的成员变量和方法.
三.输出JSP表达式
- 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>
<%!
public int count;
public String info()
{
return "hello";
}
%>
<body>
<!-- 使用表达式输出变量值 -->
<%=count++%>
<br/>
<!-- 使用表达式输出方法返回值 -->
<%=info()%>
</body>
</html>
四.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>
<table bgcolor="#9999dd" border="1" width="300px">
<!-- Java脚本,这些脚本会对HTML的标签产生作用 -->
<%
for(int i = 0 ; i < 10 ; i++)
{
%>
<!-- 上面的循环将控制<tr>标签循环 -->
<tr>
<td>循环值:</td>
<td><%=i%></td>
</tr>
<%
}
%>
<table>
</body>
</html>
这个代码经过web容器编译后,会生成servlet类,里面的输出语句会生成一个10行的表格.并在表格中输出表达式的值.
但是有一点,JSP脚本里不能定义方法,j因为Java语法不允许在方法里定义方法.
五. JSP的三个编译指令
- page:该指令是针对当前页面的指令
- include:该指令可以包含另一个界面;
taglib:用于定义个访问自定义标签
使用编译指令的格式:
<%@ 编译指令名 属性名="属性值"...%>
page制定通常位于JSP页面的顶端位置,一个JSP页面也以包含多条page指令.
- - language:声明当前JSP界面使用的脚本语言,默认是Java.
- - extends: 指定JSP页面编译所产生的Java类所继承的父类或所实现的接口
- - import: 用来导入包.下面几个包是默认自动导入的,不需要显示导入.导入的包有:java.lang.,javax.servlet.,javax.servlet.jsp.,javax.servlet.http.;
- - session:设定这个JSP页面是否需要HTTP Session.
- - buffer: 制定输出缓冲区的大小.输出缓冲区的JSP内置对象;out用于缓存JSP页面对客户浏览器的输出,默认值为8KB,可以设置为none,也可以设置为其他的值,单位为Kb.
- - autoFlush:当前缓冲区即将溢出时,是否需要强制输出缓冲器的内容.设置为true时为正常输出,如果设置为false,则会在buffer溢出时产生一个异常
- - info:设置该JSP程序的信息,也可以看成是其说明,可以通过Servlet.getServletinfo()方法获取该值. 如果在JSP界面中,可以直接调用getServletinfo()方法获取该值,因为JSP页面的实质就是Servlet.
- - errorPage:指定错误处理界面.如果本页面产生了异常或者错误.而该JSP页面没有对应的处理代码.则会自动调用高属性所指定的JSP页面
- - isErrorPage:设置本JSP界面是否为错误处理程序.如果该页面本身已是错误处理页面.则通常不需要指定errorPage属性
- - contentType:用于设定生成网页的文件格式和编码字符集,即MIME类型和页面字符集类型,默认的MIME类型是text/html;默认的字符集类型为ISO-8859-1;
- - pageEncoding:指定生成网页的编码字符集
例如:
<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
<%@ page import="java.sql.*" %>include指令
- - 使用include指令,可以将一个外部文件嵌入到当前JSP文件中,同时解析这个页面中的JSP语句(如果有的话).这是个静态的include语句,它会把目标页面的其他编译指定也包含进来,但动态include则不会.
include既可以包含静态的文本,也可以包含动态的JSP页面.静态的include编译指令会被包含的页面加入本页面.融合成一个页面.因此被包含的页面甚至不要是一个完整的页面.
include编译指令的语法如下:
<%@include file="relativeURLSpec"%>
六. 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实例的属性值.
1. forward指令
用于将响应转发到另外的页面.既可以转发到静态的HTML页面,也可以转发到动态的JSP页面,或者转发到容器中Servlet.
JSP的forward指令格式如下:
<jsp:forward page="{relativeURL|<%=expression%>}">
{<jsp;param.../>}
</jsp:forward>
对于第二种语法用于再转发时增加额外的请求参数.增加的请求参数的值可以通过HttpServletRequest类的getParameter()方法取得.
下面页面使用了forward动作指令来转发用户请求.
<%@ 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界面包含了title信息,页面中也包含了简单的文本内容,页面的jsp代码段则将客户端请求转发到forward-result.jsp页面,转发请求时增加了一个请求参数:参数名为age,参数值为29.
在forward-result.jsp页面中,使用request内置对象(request内置对象是HttpServletRequest的实例)来获取增加的请求参数值.
forward-result.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>forward结果页</title>
<meta name="website" content="http://www.crazyit.org" />
</head>
<body>
<!-- 使用request内置对象获取age参数的值 -->
<%=request.getParameter("age")%>
<!-- 输出username请求参数的值 -->
<%=request.getParameter("username")%>
</body>
</html>
执行forward指令时你会发现,用户请求的地址依然内有发生改变,但是页面内容却完全变为被forward目标页的内容.而且执行forward指令转发请求时,客户端的请求参数不会丢失.
看下面表单提交页面的例子,该页面没有任何动态的内容,只是一个静态的表单页,作用是将请求参数提交到forward-result.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页,增加输出表单参数的代码,也就是在forward-result.jsp页面上增加如下代码:
<!--输出username请求参数的值-->
<%=request.getParameter("username")%>
在表单提交页面中的文本框中输入任意字符串后提交该表单,既可看到age的值被输出来了.
总结: 以上例子表明,执行forward时不会丢失请求参数.
2. include指令
include指令是一个动态include指令,也用于包含某个页面,但仅仅将被导入页面的body内容插入本页面.注意和编译指令中的include指令区别.
语法格式和forward差不多,可以看下下面的例子.
<%@ 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>
其中jsp代码段使用了动态导入语法导入了script.jsp.表面上看,该页面的执行效果与使用静态include导入的页面并没有什么不同.但查看jsp-include.jsp页面生成Servlet的源码,可以看到下面的片段.
这表明动态导入只是使用一个include方法来插入目标页面的内容,而不是将目标页面完全融入本页面中.
归纳起来,静态导入和动态导入有如下三点区别:
& 静态导入是将被导入页面代码完全融入,两个页面融合成一个整体Servlet;而动态导入则在Servlet中使用include方法来导入被导入页面的内容.
& 静态导入时被导入页面的编译指令会起作用;而动态导入时被导入页面的编译指令则会失去作用,只是插入被导入的body内容
& 动态包含还可以增加额外的参数.
总结: forward 拿目标页面代替原有页面,而include则拿目标页面插入原有界面.
3.useBean,setProperty,getProperty指令
其中useBean指令用于在JSP页面中初始化一个Java实例; setProperty指令用于为JavaBean实例的属性值设置值;getProperty指令用于输出JavaBean.
useBean语法格式:
<jsp: userBean id="name" class="classname" scope="page|request|session|application">
其中id是JavaBean的实例名,class属性确定JavaBean的实现类.scope属性拥有指定JavaBean实例的作用范围,该值有以下4个值:
page : 该JavaBean实例仅在有效页面有效.
request:该JavaBean实例在本次请求有效
session:该JavaBean实例在本次session内有效
application:该JavaBean实例在本应用内一直有效
setProperty指令的语法格式如下:
<jsp: setProperty name="BeanName" property="propertyName" value="value"/>
name属性确定需要设定JavaBean的实例名;Property属性确定需要设定的属性名;value属性则确定需要设置的属性值.
getProperty的语法格式如下:
<jsp:getProperty name="BeanName" property="propertyName">
name为确定需要输出的JavaBean的实例名;Property属性确定需要输出的属性名.
下面JSP页面示范了如何使用这3个动作指令来错做JavaBean.
<%@ 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="lee.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>
事实上,当页面使用setProperty和getProperty标签时,系统底层就是调用setName()和getName()方法来操作Person实例的属性值.
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;
}
}
4. plugin指令
plugin指令主要用于下载服务器段的JavaBean或Applet到客户端执行.
5. param指令
param指令用于设置参数值,这个指令本身不能单独使用,因为单独的param指令没有实例意义.param指令可以与一下三个指令结合使用.
jsp:include
jsp:forward
jsp:plugin
当与include指令结合使用时,param指令用于将参数值传入被导入的页面;当与forward指令结合使用时,param指令用于将参数值传入被转向的页面;当与plugin指令结合使用时,则用于将参数传入页面中的JavaBean实例或者Applet实例.
param指令的语法格式:
<jsp:param name="paramName" value="paramValue">
七. JSP脚本中的9个内置对象
JSP脚本中包含9个内置对象,这9个内置对象都是Servlet API的实例,只是JSP规范对他们进行了默认初始化(由JSP页面对应Servlet的_jspServlet()方法来创建这些实例).也就是说,他们已经是对象,可以直接使用.9个内置对象一次如下:
1.application对象:
- javax.servlet.ServletContext的实例,该实例代表JSP所属的Web应用本身,可用于JSP页面.或者在Servlet之间交换信息.常用的方法getAttribute(StringattName),setAttribute(String paramName)和getInitParameter(String paramName)等2.config对象:
- javax.servlet.ServletConfig的实例,该实例代表该JSP的配置信息.常用的方法有getInitParameter(String paramName)和getInitParameternames()等方法.事实上,JSP页面通常不需要配置,也就不存在配置信息.因此,该对象更多在Servlet中有效.3.exception对象:
- java.lang.Throwable的实例,该实例代表其其他页面中的异常和错误.只有当页面是错误处理页面,即编译指令page的isErrorPage属性为true时,该对象才可以使用.常用的方法有getMessage()和printStackTrace()等.4.out对象:
- javax.servlet.jsp.JspWriter的是实例.该实例代表JSP页面的输出流,用于输出内容,形成HTML页面.
5.page对象:
- 代表该页面本身,通常没有太大用处,也就是servlet中的this,其类型就是生成的servlet类,能用page的地方就可以用this.
6.pageContext对象:
- javax.servlet.jsp.PageContext的实例.该对象代表该JSP页面上下文,使用该对象可以访问页面中的共享数据.常用的方法有getServletContext()和getServletConfig()等.
7.request对象:
- javax.servlet.http.HttpServletRequest的实例.该对象封装了一次请求,客户端的请求参数都被封装在该对象里.这是一个常用的对象,获取客户端请求参数必须使用该对象.常用的方法有getParameter(String paramName),getParameterValues(String paramName),setAttribute(String attrName, Object attrValue),getAttribute(String attrName)和setCharacterEncoding(String env)等.
- 9. response对象:
- javax.servlet.http.HttpServletResponse的实例,代表服务器对客户端的响应.通常很少使用该对象直接响应,而是使用out对象,除非需要生成非字符串响应.而response对象常用于重定向,常用的方法有getOutputStream(),sendRedirect(java.lang.String.location)等
- 10.session对象:
- javax.servlet.http.HttpSession的实例,该对象代表一次会话,当客户端浏览器与站点建立连接时,会话开始;当客户端关闭浏览器时,回话结束.常用的方法有:getAttribute(String attrName),setAttribute(String attrName,Object attrValue)等.
看一下tomcat中一个jsp编译成servlet的源码.
/*
- Generated by the Jasper component of Apache Tomcat
- Version: Apache Tomcat/7.0.47
- Generated at: 2017-01-24 15:04:40 UTC
- Note: The last modified time of this file was set to
- the last modified time of the source file after
- generation to assist with modification tracking.
*/
package org.apache.jsp;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent {
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 javax.el.ExpressionFactory _el_expressionfactory;
private org.apache.tomcat.InstanceManager _jsp_instancemanager;
public java.util.Map<java.lang.String,java.lang.Long> getDependants() {
return _jspx_dependants;
}
public void _jspInit() {
_el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
_jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());
}
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 {
//看这里,这里可以看到几个内置对象
final javax.servlet.jsp.PageContext pageContext;
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");
pageContext = _jspxFactory.getPageContext(this, request, response,
null, false, 8192, true);
_jspx_page_context = pageContext;
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
out = pageContext.getOut();
_jspx_out = out;
out.write("\n");
out.write("<!DOCTYPE html>\n");
out.write("\n");
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy");
request.setAttribute("year", sdf.format(new java.util.Date()));
request.setAttribute("tomcat7Url", "http://tomcat.apache.org/");
request.setAttribute("tomcat7DocUrl", "/docs/");
request.setAttribute("tomcat7ExamplesUrl", "/examples/");
out.write("\n");
out.write("<html lang=\"en\">\n");
out.write(" <head>\n");
out.write(" <title>");
out.print(request.getServletContext().getServerInfo() );
out.write("</title>\n");
out.write(" <link href=\"favicon.ico\" rel=\"icon\" type=\"image/x-icon\" />\n");
out.write(" <link href=\"favicon.ico\" rel=\"shortcut icon\" type=\"image/x-icon\" />\n");
out.write(" <link href=\"tomcat.css\" rel=\"stylesheet\" type=\"text/css\" />\n");
out.write(" </head>\n");
out.write("\n");
out.write(" <body>\n");
out.write(" <div id=\"wrapper\">\n");
out.write(" <div id=\"navigation\" class=\"curved container\">\n");
out.write(" <span id=\"nav-home\"><a href=\"");
out.write("\">Home</a></span>\n");
out.write(" <span id=\"nav-hosts\"><a href=\"");
out.write((java.lang.String) org.apache.jasper.runtime.PageContextImpl.proprietaryEvaluate("${tomcat7DocUrl}", java.lang.String.class, (javax.servlet.jsp.PageContext)_jspx_page_context, null, false));
out.write("\">Documentation</a></span>\n");
out.write(" <span id=\"nav-config\"><a href=\"");
out.write((java.lang.String) org.apache.jasper.runtime.PageContextImpl.proprietaryEvaluate("${tomcat7DocUrl}", java.lang.String.class, (javax.servlet.jsp.PageContext)_jspx_page_context, null, false));
out.write("config/\">Configuration</a></span>\n");
out.write(" <span id=\"nav-examples\"><a href=\"");
out.write((java.lang.String)
out.write(" Apache Software Foundation. All Rights Reserved</p>\n");
out.write(" </div>\n");
out.write(" </body>\n");
out.write("\n");
out.write("</html>\n");
} catch (java.lang.Throwable t) {
if (!(t instanceof javax.servlet.jsp.SkipPageException)){
out = _jspx_out;
if (out != null && out.getBufferSize() != 0)
try { 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页面编译后Servlet类都有如上所示的结构,上面servlet类的代码表明。request,response两个对象是_jspService()方法的形参,当Tomcat调用该方法时会初始化这两个对象。而page,pageContext,application,config,session,out都是_jspService()方法的局部变量,由该方法完成初始化。
- 通过上面的代码不难看出JSP内置对象的实质:他们要么是_jspService()方法的形式参数,要么是_jspService()方法的局部变量,所以我们直接在JSP脚本(脚本将对应于Servlet的_jspService()方法部分)中调用这些对象,无需创建它们。
总结:当编写JSP页面时,一定不要仅停留在JSP页面本身来看问题,这样可能导致许多误解,导致无法理解JSP运行方式。.