JSP&Servlet6(二) --- JSP标准标签

时间:2022-02-10 17:17:48

什么是标准标签? 标准标签有什么作用?

简明扼要的来说, 标准标签就是一些以”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的设置可以是一个抽象类, 也可以是一个接口.