JSP(上)
标签(空格分隔): JavaWeb
认识JSP
为什么需要JSP
我们在Servlet程序中可以通过PrintWrite进行输出,打印到web页面上。那么我们其实也可以通过这种方法打印html语句。但是这种方法是在是太麻烦了,也比较愚蠢。因为我们不但要在Servlet中写Java代码,还要通过这种方式写网页的静态样式,整个Servlet程序的代码将非常臃肿,编写和维护都将非常困难!
实际上在很多动态网页中,绝大部分内容都是固定不变的,只有局部内容需要动态产生和改变。并且对大量静态内容的美工设计和相关HTML语句的编写,未必是程序员所擅长的。因此为了弥补 Servlet 的缺陷,SUN公司在Servlet的基础上推出了JSP(Java Server Pages)技术作为解决方案。
什么是JSP
JSP是简化Servlet编写的一种技术,它将Java代码和HTML语句混合在同一个文件中编写,只对网页中的要动态产生的内容采用Java代码来编写,而对固定不变的静态内容采用普通静态HTML页面的方式编写。
JSP页面是由HTML语句和嵌套在其中的Java代码组成的一个普通文本文件,JSP 页面的文件扩展名必须为.jsp。JSP文件就像普通的HTML文件一样,它们可以放置在WEB应用程序中的除了WEB-INF及其子目录外的其他任何目录中,JSP页面的访问路径与普通HTML页面的访问路径形式也完全一样。
JSP运行原理
WEB容器(Servlet引擎)接收到以.jsp
为扩展名的URL的访问请求时,它将把该访问请求交给JSP引擎去处理。
每个JSP 页面在第一次被访问时,JSP引擎将它翻译成一个Servlet源程序,接着再把这个Servlet源程序编译成Servlet的class类文件,然后再由WEB容器(Servlet引擎)像调用普通Servlet程序一样的方式来装载和解释执行这个由JSP页面翻译成的Servlet程序。
可以在WEB应用程序正式发布之前,将其中的所有JSP页面预先编译成Servlet程序。
写一个JSP
新建一个 JSP 页面, 在 body 节点内的 <% %> 即可编写 Java 代码.
<body>
<%
Date date = new Date();
System.out.print(date);
%>
</body>
隐式对象
什么叫隐式对象
在<%%>
中,一般我们必须声明一个对象,然后才能使用。隐式对象就是不用声明也能使用的对象!
其实也不是没声明,而是在jsp生成的Servlet文件中默认声明好的对象。
public void _jspService(HttpServletRequest request, HttpServletResponse response)
throws java.io.IOException, ServletException {
PageContext pageContext = null;
HttpSession session = null;
ServletContext application = null;
ServletConfig config = null;
JspWriter out = null;
Object page = this;
//...
//使用 <% %> 编写的代码在此位置. 可以用到 request, response, pageContext, session
//application, config, out, page 这 8 个隐含对象. (实际上还可以使用一个叫 exception 的隐含对象)
}
9个隐式对象
①. request
: HttpServletRequest 的一个对象。
String name = request.getParameter("name");
②. response
: HttpServletResponse 的一个对象(在 JSP 页面中几乎不会调用 response 的任何方法)。
System.out.println(response instanceof HttpServletResponse);
③. pageContext
: 页面的上下文,是 PageContext 的一个对象。 可以从该对象中获取到其他 8 个隐含对象。也可以从中获取到当前
页面的其他信息。(学习自定义标签时使用它)
ServletRequest req = pageContext.getRequest();
System.out.println(req == request);
④. session
: 代表浏览器和服务器的一次会话,是 HttpSession 的一个对象. 后面详细学习。
System.out.println(session.getId);
⑤. application
: 代表当前 WEB 应用。是 ServletContext 对象。
System.out.println(application.getInitParameter("user"));
⑥. config
: 当前 JSP 对应的 Servlet 的 ServletConfig 对象(几乎不使用)。若需要访问当前 JSP 配置的初始化参数,需要通过映射的地址才可以。
映射 JSP:
<servlet>
<servlet-name>hellojsp</servlet-name>
<jsp-file>/hello.jsp</jsp-file>
<init-param>
<param-name>test</param-name>
<param-value>testValue</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>hellojsp</servlet-name>
<url-pattern>/hellojsp</url-pattern>
</servlet-mapping>
⑦. out
: JspWriter 对象。调用 out.println() 可以直接把字符串打印到浏览器上。JspWriter相当于一种带缓存功能的PrintWriter,设置JSP页面的page指令的buffer属性可以调整它的缓存大小,甚至关闭它的缓存。
⑧. page
: 指向当前 JSP 对应的 Servlet 对象的引用,但为 Object 类型,只能调用 Object 类的方法(几乎不使用)。
⑨. exception
: 在声明了 page 指令的isErrorPage="true"
时, 才可以使用。
<%@ page isErrorPage="true" %>
JSP的基本语法
JSP模版元素
在JSP页面中没有嵌套在<%
和%>
之间的的静态HTML内容被称之为JSP的模版元素。在静态的HTML内容之中可以嵌套JSP的其他各种元素来产生动态内容和执行业务逻辑。
JSP模版元素定义了网页的基本骨架,即定义了页面的结构和外观。
JSP表达式
JSP表达式(expression)提供了将一个java变量或表达式的计算结果输出到客户端的简化方式,它将要输出的变量或表达式直接封装在<%=
和 %>
之中。
Current time: <%= new java.util.Date() %>
JSP表达式中的变量或表达式的计算结果将被转换成一个字符串,然后被插入进整个JSP页面输出结果的相应位置处。
JSP表达式中的变量或表达式后面不能有分号(;),JSP表达式被翻译成Servlet程序中的一条out.print(…)语句。
JSP脚本片断
在JSP页面中编写的一条或多条Java代码需要嵌套在<%
和%>
中,嵌套在<%
和%>
之间的Java代码被称之为脚本片段(Scriptlets)
在JSP脚本片断中,可以定义变量、执行基本的程序运算、调用其他Java类、访问数据库、访问文件系统等普通Java程序所能实现的功能。
在JSP脚本片断可以直接使用JSP提供的隐式对象来完成WEB应用程序特有的功能。
JSP脚本片断中的Java代码将被原封不动地搬移进由JSP页面所翻译成的Servlet的_jspService方法中,所以,JSP脚本片断之中只能是符合Java语法要求的程序代码,除此之外的任何文本、HTML标记、其他JSP元素都必须在脚本片断之外编写。
在一个JSP页面中可以有多个脚本片断(每个脚本片断代码嵌套在各自独立的一对<% 和 %>之间),在两个或多个脚本片断之间可以嵌入文本、HTML标记和其他JSP元素。如:
<%
int x = 3;
%>
<p>这是一个HTML段落</p>
<%
out.println(x);
%>
并且,多个脚本片断中的代码可以相互访问,犹如将所有的代码放在一对<%%>之中的情况。
也就是说单个脚本片断中的Java语句可以是不完整的。但是!多个脚本片断组合后的结果必须是完整的Java语句!
脚本片断中的Java代码将被原封不动地搬移进由JSP页面所翻译成的Servlet的_jspService()
方法中,脚本片断之外的任何文本、HTML标记以及其他JSP元素也都会被转换成相应的Java程序代码插入进_jspService()
方法中,且脚本片断和其他JSP元素的插入位置与它们在JSP页面中的原始位置相对应。
因此可以写成下面这样:
<%
String ageStr = request.getParameter("age");
Integer age = Integer.parseInt(ageStr);
if(age >= 18){
%>
成人... //在脚本片段之外的模版元素,如果age>=18,会显示它
<%
}else{
%>
未成人... //在脚本片段之外的模版元素,如果age>=<18,会显示它
<%
}
%>
JSP声明
JSP声明Java代码封装在<%!
和 %>
之中,它里面的代码将被插入进Servlet的_jspService方法的外面。
(几乎不这么用,了解即可)
JSP中的属性
属性与参数
我们可以把attribute
(属性)理解为资源字典。
而把Parameter
(参数)理解为”由用户给服务器的一种东西”。
经常这样写:
XXXX.setAttribute("a", A);
XXXX.getAttribute("b");
把setAttribute理解为给资源字典XXXX增加一个条目
把getAttribute理解为从XXXX字典中取出一个键为”b”的条目的”值”
attribute
属性是WebApp内部各部分/组件之间交互信息使用的通信手段。jsp之间或与Servlet之间进行交互时,使用Attribute传递消息。通过getAttribute()和setAttribute()来共享request范围内的数据。attrubute中的数据是Object类型的,通过attribute传递的数据只会存在于web容器内部,仅仅是请求处理阶段。
Parameter,是用户和WebApp进行交互的时候,特别是用户要提交信息给WebApp的时候,传递信息的手段。,参数为页面提交的参数,包括:表单提交的参数、URL重写(就是xxx?id=1中的id)
传的参数等,因此这个并没有设置参数的方法(没有setParameter),而且接收参数返回的不是Object,而是String类型
request.getAttribute()方法返回request范围内存在的对象,request.getParameter()获取http请求提交过来的数据。
一般的Web应用,基本上是post方式的传递,用getParameter取值。对于自己控制的,可以通过request.setAttribute和getAttribute实现值的传递。
与属性相关的方法
public Object getAttribute(String name)
:获取指定的属性
public Enumeration getAttributeNames()
:获取所有的属性的名字组成的Enumeration对象
public void removeAttribute(String name)
:移除指定的属性
public void setAttribute(String name,Object value)
:设置属性
`
域对象和属性范围
pageContext,request,session,application对象都可以调用上面这些方法。
这四个对象也被称为域对象。
可以简单的理解为JSP中有四个字典:
范围 | 变量名 | 一般Servlet中我们对它的称呼 |
---|---|---|
当前JSP文件(页面)可见 | pageContext | 不存在的 |
同一服务器请求可见 | request | 请求 |
一次会话(一个人)可见 | session | session |
整个WebApp可见 | application | ServletContext上下文参数 |
pageContext
:
存储在pageContext对象中的属性仅可以被当前JSP页面的当前响应过程中调用的各个组件访问,例如,正在响应当前请求的JSP页面和它调用的各个自定义标签类。request
:
存储在request对象中的属性可以被属于同一个请求的所有Servlet和JSP页面访问,例如使用PageContext.forward和PageContext.include方法连接起来的多个Servlet和JSP页面。session
:
存储在session对象中的属性可以被属于同一个会话的所有Servlet和JSP页面访问。application
存储在application对象中的属性可以被同一个WEB应用程序中的所有Servlet和JSP页面访问(只要不移除)。
请求重定向与转发
语法
请求转发:
- 调用 HttpServletRequest 的 getRequestDispatcher() 方法获取 RequestDispatcher 对象调用 getRequestDispatcher() 需要传入要转发的地址
- 调用 HttpServletRequset 的 forward(request,response) 进行请求的转发
String path = "/testServlet";
RequestDispatcher requestDispatcher = request.getRequestDispatcher(path);
requestDispatcher.forward(request,response);
请求重定向:
- 直接调用response.sendRedirect(path)方法,path为要重定向的地址
String path = "/third/testServlet";
response.sendRedirect(path);
直观认识
test.html
写一个界面,点击连接,分别转到 ForwardServlet 和 RedirectServlet 上
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--这里一定注意地址的写法,加上当前项目地址-->
<a href="/third/ForwardServlet">Forward</a>
<br><br>
<a href="/third/RedirectServlet">Redirect</a>
</body>
</html>
ForwardServlet
创建ForwardServlet,当跳转到当前的ForwardServlet中,转发的方式跳转到指定的路径文件。注意如下:
- 在Servlet3.0中,可以在配置文件中进行映射,而是用注解的方式,标注注册名和路径
- 超链接是通过get请求的,因此在doGet方法中写。
//在Servlet3.0中,可以在配置文件中进行映射,而是用注解的方式
@javax.servlet.annotation.WebServlet(name="ForwardServlet",urlPatterns="/ForwardServlet")
public class ForwardServlet extends javax.servlet.http.HttpServlet {
protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
}
protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
//打印
System.out.println("ForwardServlet's doGet");
/**
* 请求的转发:
*
* 1. 调用 HttpServletRequest 的 getRequestDispatcher() 方法获取
* RequestDispatcher 对象调用 getRequestDispatcher() 需要传入要转发的地址
* 2. 调用 HttpServletRequset 的 forward(request,response) 进行请求的转发
*/
String path = "/testServlet";
RequestDispatcher requestDispatcher = request.getRequestDispatcher(path);
requestDispatcher.forward(request,response);
}
}
RedirectServlet
创建RedirectServlet,跳转到当前RedirectServlet后,以重定向的方式跳转的路径文件。
注意:
重定向相当于重新请求路径,因此要注意路径的格式,要么直接写文件名,不加”/”,要么加上路径”/项目名/文件名”
@WebServlet(name = "RedirectServlet",urlPatterns = "/RedirectServlet")
public class RedirectServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("RedirectServlet's doGet");
/**
* 执行请求的重定向
* 直接调用response.sendRedirect(path)方法,path为要重定向的地址
*/
String path = "/third/testServlet";
response.sendRedirect(path);
}
}
testServlet
@WebServlet(name = "testServlet",urlPatterns = "/testServlet")
public class testServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("testServlet's doGet");
}
}
结果
- 转发:
当我们点击Forward
的时候,web页面跳转到http://localhost:8080/third/ForwardServlet
页面,但是控制台输出为:
ForwardServlet's doGet
testServlet's doGet
- 重定向:
当我们点击Redirect
的时候,web页面跳转到http://localhost:8080/third/testServlet
页面,但是控制台的输出为:
RedirectServlet's doGet
testServlet's doGet
也就是说在请求转发时,页面是跳转到ForwardServlet中并获取了testServlet中的内容,而请求重定向中则是跳转到testServlet中。
本质区别
请求的转发是只发出了一次请求,而请求重定向则发出了两次请求。
举一个例子:
请求的转发:
小张最近手头紧,朝小A借500块钱,小A说“没问题,你等会”。但是小A其实也没有钱,小A自己管朋友小B借了500块钱,然后把钱借给小张。也就是说小A将小张的借钱请求直接转给了小B,对于小张来说,只借了一次钱
请求的重定向:
小张最近手头紧,朝小C借500块钱,小C说“不好意思,我也没钱”。于是小张又去找朋友小D,小D借给了小张500块钱。小张分别朝小C和小D两个人借钱,小C和小D没有产生关系
具体来说:
请求的转发:地址栏初次发出的请求的地址。
请求的重定向:地址栏不再是初次发出的请求地址。地址栏为最后响应的那个地址。请求转发:在最终的Servlet中,request对象和中转的request是同一个对象。也就是testServlet中可以获取到ForwardServlet中的属性Attribute
请求的重定向:在最终的Servlet中,request对象和中转的request是不同一个对象。testServlet中获取不到RedirectServlet中的属性Attribute,因为不是同一个请求对象了请求的转发:只能转发给当前Web应用的资源
请求重定向:可以重定向到任何资源请求的转发:
"/"
代表的是当前web应用的根目录
请求重定向:"/"
代表的是当前web站点的根目录