什么是标准标签? 标准标签有什么作用?
简明扼要的来说, 标准标签就是一些以”jsp:”作为前置的标签, 主要作用是为了减少JSP中的Java代码.
1. <jsp:include> <jsp:forward>标签
<jsp:include>
在前面曾经提到过include指示元素, 可以在JSP转译的时候将其他的JSP页面包括合并进来一起转译, 但是这样include进来的JSP页面是静态的, 我们无法给包括进来的JSP页面传递参数或对它进行动态调整, 只是将多个JSP页面合并成一个JSP页面再进行转译, 结果也只会生成一个Servlet.
但是使用标准标签<jsp:include>可以在运行时动态地将其他的JSP页面包括进来, 并传递参数等. 被包含的JSP页面也会自己独立生成一个Servlet类.
<jsp:include>在使用过程中实质上发生了什么呢?
前面在Servlet里面提到过请求的包含(include())与转发(forward()), 其实, 在JSP转译生成的Servlet中, 与处理Servlet中的请求的包含与转发是一样的, 也是会先取得RequestDispatcher对象, 然后在执行该对象的include()/forward()方法. 但由于使用<jsp:include>时, 被包含的JSP页面也会独立生成一个Servlet, 所以在转译后的Servlet执行RequestDispatcher对象的include()方法时, 会将请求转发给被包含的页面所独立生成的Servlet, 而后再回到目前的Servlet.
<jsp:include>使用范例:
下面的代码时将add.jsp包含进当前的JSP页面, 并使用<jsp:param>标签传递给add.jsp一些参数:
<jsp:include page = "add.jsp">
<jsp:param name = "a" value = "1">
<jsp:param name = "b" value = "2">
</jsp:include>
<jsp:forward>
<jsp:forward>与<jsp:include>的用法基本相似, 示例如下:
<jsp:forward page = "add.jsp">
<jsp:param name = "a" value = "1">
<jsp:param name = "b" value = "2">
</jsp:forward>
关于Servlet里转发与重定向的区别
既然讲到了JSP里面的转发请求, 那么就来简要的谈谈Servlet里面转发与重定向的区别吧!
例:转发
request.getRequestDispatcher("/test.view").forword(request, response);
例:重定向
response.sendRedirect("login.html");
转发: 转发的整个流程都是在服务端完成的, 而且是在同一个请求中完成的, 当前Servlet和转发的Servlet共享一个request, 在当前Servlet中放的东西, 在转发的Servlet中都能取得到, 整个过程是一个请求, 一个响应.
重定向: 客户发送一个请求到服务器, 服务器匹配Servlet, Servlet处理完之后调用response的sendRedirect()方法, 所以, 当当前的Servlet处理完之后, 会立即向客户端返回这个响应, 响应会告诉客户端必须要再发送一个请求去访问login.html, 于是客户端收到这个消息会立即发送一个新的请求去请求login.html, 这里的2个请求互不干扰, 互相独立. 当前的Servlet的request的setAttribute()里的任何东西, 在重定向的页面里都获取不到, 可见是2个请求, 2个响应.
2. <jsp:useBean>的使用和 <jsp:setProperty> /<jsp:getProperty>的简介
<jsp:useBean>
既然这个标签是<jsp:useBean>, 那就是use Bean, 那么我们先来了解一下”Bean”是什么.
Bean就是JavaBean, JavaBean是纯Java对象, 它具由以下几个特点:
- 首先必须实现java.io.Serializable接口;
- 没有public类型的类变量;
- 具有无参的构造器;
- 具有公开的getter和setter方法.
下面给出一个JavaBean的代码的例子:
public class User implements Serializable{
private String name;
private String passwd;
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public String getPasswd(){
return passwd;
}
public void setPasswd(String passwd){
this.passwd = passwd;
}
public boolean isValid(){
return "dela".equals(name) && "123456".equals(passwd);
}
}
我们先不考虑这是不是一个JavaBean, 就先把它当成一个普通的Java类来看, 那么在JSP中应用这个类大概会有如下代码:
<%@page import = "com.zhuyidi.chapter6.JSPDemo.*"
contentType = "text/html" pageEncoding = "UTF-8"%>
<%
User user = (User)request.getAttribute("user");
if(user == null){
user = new User();
request.setAttribute("user", user);
}
user.setName(request.getParameter("name"));
user.setPasswd(request.getParameter("passwd"));
%>
//略...
<body>
<%
if(user.isValid()){
%>
<h1><%= user.getName()%>登录成功</h1>
<%
}else{
%>
<h1>登录失败!</h1>
<%
}
%>
</body>
</html>
可以看出, 在JSP中直接编写Scriptlet(即Java代码)使用JavaBean非常麻烦, 在一开始也提到了, JSP的标准标签就是为了减少JSP中Java代码, 所以就有了<jsp:useBean>去使用JavaBean.
下面就来看以下使用<jsp:useBean>来操作User这个JavaBean, 代码如下:
<%@page contentType = "text/html" pageEncoding = "UTF-8"%>
<jsp:UseBean id = "bean_user" class = "com.zhuyidi.chapter6.JSPDemo.User" />
<jsp:setProperty name = "bean_user" property = "*" />
<html>
<head>
<meta http-equiv = "Content-Type" content = "text/html"; charset = "UTF-8">
<title>登录页面</title>
</head>
<body>
<%
if(bean_user.isValid()){
%>
<h1><jsp:getProperty name = "bean_user" property = "name" />登录成功</h1>
<%
}else{
%>
<h1>登录失败</h1>
<%
}
%>
</body>
</html>
可以看出, 使用<jsp:useBean>将会大大减少Scriptlet的编写, 那么下面我们对上述代码的一些关键字/标签进行解释一下吧.
- <jsp:useBean>标签: 用来取得或创建JavaBean.
- id属性: 指定JavaBean实例的名称. 之后在使用<jsp:setProperty>和<jsp:getProperty>的时候通过指定name属性, 来取得id与指定的name相同的JavaBean实例.
- class属性: 指定实例化哪一个类.
- scope属性: 用于指定JavaBean实例对象所存储的域范围. 其取值只能是page/request/session/application四个值中的一个,其默认值是page.
- <jsp:useBean>标签: 用于设置已经实例化的JavaBean的属性值.
- name属性: 用于指定要设置属性的JavaBean实例的id值.
- property属性: 用于给JavaBean实例赋值. 由于, property属性的赋值操作有好几种情况, 在此链接一篇非常详细的博客以便日后查阅: <jsp:setproperty>的用法
- <jsp:getProperty>标签: 用来取得已经实例化的JavaBean的属性值.
- name属性: 用于指定要取得属性值的JavaBean实例的id值.
- property属性: 用于取得JavaBean实例的属性值.
3. 深入<jsp:useBean>的使用和 <jsp:setProperty> /<jsp:getProperty>
<jsp:useBean>的scope属性
其实, JavaBean就是Java的一个类, 而JavaBean的实例, 就相当于这个Java类的一个对象. JSP终将转译成Servlet, 举个栗子, 有如下JSP代码:
<jsp:useBean id = "bean_user" class = "com.zhuyidi.chapter6.JSPDemo.User" />
这行代码转译为Servlet应该是这样的:
com.zhuyidi.chapter6.JSPDemo.User bean_user = null;
synchronized(request){
bean_user = (com.zhuyidi.chapter6.JSPDemo.User)_jspx_page_context.getAttribute("bean_user", PageContext.PAGE_SCOPE);
if(bean_user == null){
bean user = new com.zhuyidi.chapter6.JSPDemo.User();
_jspx_page_context.setAttribute("bean_user", bean_user, pageContext.PAGE_SCOPE);
}
}
这段代码就是说, 在使用<jsp:useBean>时, 会在属性范围查找是否存在以id属性为名称的Bean对象, 如果找到了就直接使用, 否则就创建. 其中, _jspx_page_context引用至pageContext对象.
在上面这段代码中, 我们注意到“PAGE_SCOPE”, 这个参数的意思就是, 查找以id属性为名称的Bean对象的范围是page. 而这个参数对应至JSP中的<jsp:useBean scope = “page” />.
下面就来详细了解一下scope属性吧.
前面提到: scope属性: 用于指定JavaBean实例对象所存储的域范围. 其取值只能是page/request/session/application四个值中的一个,其默认值是page. 这四个值都有什么区别呢? 我们以一个很直观的例子来验证一下.
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<jsp:useBean id="currentime" class="java.util.Date" scope="page" />
<html>
<head>
<title>当前时间</title>
</head>
<body>
<%= currentime.toString()%>
</body>
</html>
将上述代码的scope属性分别替换为: page/request/session/application, 页面上当前时间的显示的结果分别如下:
- 当scope = “page”时, 每刷新一次页面, 当前时间就更新一次. 这说明Bean实例只在当前页面有效, 离开当前页面就已经失效.
- 当scope = “request”时, 每刷新一次页面, 当前时间就更新一次. 这说明Bean实例只在本次请求有效, 当刷新一次页面必然不再是同一个请求, 若要进一步验证, 可以使用前面提过的转发(一次请求)和重定向(两次请求)来验证.
- 当scope = “session”时, 刷新页面时间不会更新, 但是重新打开一个浏览器请求该JSP页面时间就会得到更新. 在验证过程中, 首次使用chrome浏览器请求该JSP页面得到一个时间, 然后在chrome下再打开一个页面去请求得到的时间不变; 而当我使用firefox浏览器再去请求该JSP页面时, 时间得到了刷新, 然后在firefox下再打开一个页面, 时间又没有发生改变. 这说明, 一个打开一个新的浏览器就是重新建立session, 即重新建立一个Bean的实例.
- 当scope = “application”时, 不管是刷新页面还是重新打开浏览器, 时间都不会发生变化. 这说明Bean的实例在内存中只有一份, 只要不重启WEB服务, 时间都不会发生改变.
<jsp:useBean>的type属性和class属性的比较
关于type和class的区别, 有一篇博客给出了这样的回答, 但是由于博主有一点笔误, 所以我在这里再重新总结下.
<jsp:useBean id="myBean" class="package.MyBean" scope="request" />
<jsp:useBean id="myBean" type="package.MyBean" scope="request" />
以上两种用法, 当myBean不为空时, 在使用上两者没有什么区别. 但是当myBean被设为null后, 例如request.setAttribute(“myBean”,null), 两者在使用上就有区别了. 使用class=”package.MyBean”时不会抛出异常, 使用type=”package.MyBean”时会抛出异常. 原因是当使用class时, 首先在当前作用范围内查找是否存在myBean, 如果存在则直接使用现成的, 如果不存在则new一个. 而当使用type时, 如果当前范围内不存在myBean, 而且又没有使用class或beanName指定type时, 就会抛出异常. 并且class与beanName必须指定package(即引入了包), 而type可以不指定. 且type的设置可以是一个抽象类, 也可以是一个接口.