Java Web之JSP使用Java Bean
write:2022-4-21
前文我们学习了java的会话:Java Web之Session,这节课我们学习java bean,如果在java文件中嵌入大量的java程序片段,会降低jsp的可读性并使jsp的调试跟踪维护变得复杂,因此需要将这些java片段放在java bean中,只需在jsp中通过标签访问这些java bean即可。
1)JavaBean是一种可重复使用、且跨平台的软件组件。
2)JavaBean可分为两种:
一种是有用户界面(UI,User Interface)的JavaBean;
还有一种是没有用户界面,主要负责表示业务数据或者处理事务(如数据运算,操纵数据库)的JavaBean。
3)JSP通常访问的是后一种JavaBean。
一个标准的JavaBean有以下几个特性:
JavaBean是一个公共的(public)类
JavaBean有一个不带参数的构造方法
JavaBean通过setXXX方法设置属性,通过getXXX方法获取属性。如果属性名为“xyz”,那么get方法名为“getXyz”,set方法名为“setXyz”,属性名中的第一个字母在方法名中改为大写。
为什么要有上述的规范:因为Java Bean的出现就是为了使它能够像标准零件一样具有广泛的通用性,都遵守一样得规范,才能被jsp页面以同样的方式访问。
eg:
package mypack;
public class CounterBean{
private int count=0; //定义count属性
public CounterBean(){} //不带参数的构造方法
public int getCount(){ //读取count属性
return count;
}
public void setCount(int count){ //设置count属性
this.count=count;
}
}
假定把CounterBean类发布到helloapp应用中,它的存放位置是:
helloapp/WEB-INF/classes/mypack/CounterBean.class
通过<%@ page import>指令导入JavaBean类,例如: <%@ pageimport=“mypack.CounterBean” %>
(注意:名字必须完整,因此包名也必须包含进来)
jsp:useBean标签用来声明JavaBean对象,例如:
id:为所声明的Java Bean对象所提供的唯一的标识符(也就是存放范围内的属性名);scope:Java Bean存在的范围(有四个可选值:page(默认值),request,session,application)
以上jsp:useBean标签和以下Java程序片段的作用是等价的:
<%
mypack.CounterBean myBean = null; //定义myBean局部变量
//试图从会话范围内读取myBean属性
myBean = (mypack.CounterBean) session.getAttribute("myBean");
if (myBean == null){ //如果会话范围内不存在myBean属性
myBean = new mypack.CounterBean();
session.setAttribute("myBean", myBean);
} %>
显然使用jsp:useBean标签代码更简洁。
page(默认值)页面范围
request 请求范围
session 会话范围
application 应用范围
同一个Java Bean可以被多个web组件访问,但因被访问的范围不同有不同的生命周期。
客户每次请求访问JSP页面时,都会创建一个JavaBean对象。JavaBean对象的有效范围是客户请求访问的当前JSP网页。
JavaBean对象在以下两种情况下都会结束生命期:
客户请求访问的当前JSP网页通过jsp:forward标记将请求转发到另一个文件
客户请求访问的当前JSP页面执行完毕并向客户端发回响应。
eg:
pageCounter.jsp
<%@ page import="mypack.CounterBean" %>
<html><head><title>Counter</title></head><body>
<jsp:useBean id="myPageBean" scope="page"class="mypack.CounterBean" />
<%-- 把myPageBean的count属性的值加1 --%>
<jsp:setProperty name="myPageBean" property="count"
value="<%=myPageBean.getCount()+1 %>" />
<%-- 打印myPageBean的count属性 --%>
Current count value is :<jsp:getProperty name="myPageBean" property="count" />
<%
String scopeNames[]={"No scope","page","request","session","application"};
//寻找myPageBean的范围
int scope=pageContext.getAttributesScope("myPageBean"); //getAttributesScope()方法获得某个Java Bean的范围
%>
<%--打印myPageBean的范围 --%>
<p>scope=<%=scopeNames[scope]%></p>
</body></html>
运行:
(1)通过浏览器第一次访问
http://localhost:8080/helloapp/pageCounter.jsp,将会看到网页上的内容如下:
Current count value is :1
scope=page
(2)通过浏览器多次访问pageCounter.jsp,会看到CounterBean对象的count属性的值始终为1。(因为每次访问jsp:useBean标签都创建一个新的CounterBean对象,count属性初始值为0)
客户每次请求访问JSP页面时,都会创建新的JavaBean对象。
JavaBean对象的有效范围为:
客户请求访问的当前JSP网页;
和当前JSP网页共享同一个客户请求的网页,即当前JSP网页中<%@ include>指令以及jsp:forward标签包含的其他JSP文件
当所有共享同一个客户请求的JSP页面执行完毕并向客户端发回响应时,JavaBean对象结束生命周期。
JavaBean对象作为属性保存在HttpServletRequest对象中,属性名为jsp:useBean标签中JavaBean的id,属性值为JavaBean对象。相当于执行以下语句:request.setAttribute(“myBean”,mybean);
JSP还可以通过HttpServletRequest.getAttribute()方法
取得JavaBean对象,例如:
<%CounterBean obj=(CounterBean)request.getAttribute(“myBean”);%>
eg:requestCounter1.jsp
<%@ page import="mypack.CounterBean" %>
<html><head><title>Counter</title></head><body>
<jsp:useBean id="myRequestBean" scope="request" class="mypack.CounterBean" />
<jsp:setProperty name="myRequestBean" property="count"
value="<%=myRequestBean.getCount()+1 %>" />
Current count value is :<jsp:getProperty name="myRequestBean" property="count" />
<%
String scopeNames[]={"No scope","page","request","session","application"};
int scope=pageContext.getAttributesScope("myRequestBean");
%>
<p>scope=<%=scopeNames[scope]%></p>
</body></html>
运行:
(1)通过浏览器第一次访问
http://localhost:8080/helloapp/requestCounter1.jsp,将会看到网页上的内容如下:
Current count value is :1
scope=request
(2)通过浏览器多次访问requestCounter1.jsp,会看到CounterBean对象的count属性的值始终为1。
接下来
修改requestCounter1.jsp:
<p>scope=<%=scopeNames[scope]%></p>
<jsp:forward page="requestCounter2.jsp" />
requestCounter2.jsp:
<%@ page import="mypack.CounterBean" %>
<html>
<head>
<title>
Counter
</title>
</head>
<jsp:useBean id="myRequestBean" scope="request" class="mypack.CounterBean" />
<body>
Current count value is :<jsp:getProperty name="myRequestBean" property="count" />
</body>
</html>
运行:
(1)通过浏览器第一次访问
http://localhost:8080/helloapp/requestCounter1.jsp,将会看到网页上的内容如下:
Current count value is :1
scope=request
执行requestCounter1.jsp,Servlet容器创建名为myRequestBean的Java Bean对象,count值为1,请求转发到requestCounter2.jsp,发现已经有名为myRequestBean的Java Bean对象,因此Servlet容器不再创建新的Java Bean对象,因为requestCounter1.jsp与requestCounter2.jsp共享HttpServletRequest对象,也共享存放在HttpServletRequest里的myRequestBean以及属性,因此count值为1,当requestCounter2.jsp响应结果回到客户端,因此本次请求完全结束,HttpServletRequest对象结束生命周期,存放在其中的myRequestBean也结束生命周期。
(2)通过浏览器多次访问requestCounter1.jsp,会看到CounterBean对象的count属性的值始终为1。
<%@ page import="mypack.CounterBean" %>
<html><head><title>Counter</title></head>
<body>
<jsp:useBean id="mySessionBean" scope="session" class="mypack.CounterBean" />
<jsp:setProperty name="mySessionBean" property="count"
value="<%=mySessionBean.getCount()+1 %>" />
Current count value is :<jsp:getProperty name="mySessionBean" property="count" />
<%
String scopeNames[]={"No scope","page","request","session","application"};
int scope=pageContext.getAttributesScope("mySessionBean");
%>
<p>scope=<%=scopeNames[scope]%></p>
</body></html>
运行:
1)通过浏览器第一次访问
http://localhost:8080/helloapp/sessionCounter.jsp,将会看到网页上的内容如下:
Current count value is :1
scope=session
(2)通过当前浏览器多次访问sessionCounter.jsp,会看到CounterBean对象的count属性的值不断递增。
(3)再打开一个新的浏览器,通过它多次访问sessionCounter.jsp,会看到CounterBean对象的count属性的值从1开始不断递增。(新的浏览器开始新的会话,两个会话独立的,拥有各自的Java Bean对象,不过名字都叫mysessionBean)
<%@ page import="mypack.CounterBean" %>
<html><head><title>Counter</title></head><body>
<jsp:useBean id="myApplicationBean" scope="application" class="mypack.CounterBean" />
<jsp:setProperty name="myApplicationBean" property="count"
value="<%=myApplicationBean.getCount()+1 %>" />
Current count value is :<jsp:getProperty name="myApplicationBean" property="count" />
<%
String scopeNames[]={"No scope","page","request","session","application"};
int scope=pageContext.getAttributesScope("myApplicationBean");
%>
<p>scope=<%=scopeNames[scope]%></p>
</body></html>
运行:
(1)通过浏览器第一次访问
http://localhost:8080/helloapp/applicationCounter.jsp,将会看到网页上的内容如下:
Current count value is :1
scope=application
(2)通过当前浏览器多次访问applicationCounter.jsp,会看到CounterBean对象的count属性的值不断递增。(当前应用范围内只有一个名为myApplicationBean的对象)
(3)再打开一个新的浏览器,通过它多次访问applicationCounter.jsp,会看到CounterBean对象的count属性的值在第一个浏览器中看到的最后值的基础上不断递增。(不同的浏览器访问的始终是同一个myApplicationBean对象)
(4)重新启动helloapp应用,再通过浏览器多次访问applicationCounter.jsp,会看到CounterBean对象的count属性的值又从1开始不断递增。(上次一关闭应用时,与它关联的JavaBean对象也就终止生命周期,重新启动,Servlet容器会创建新的JavaBean对象)
1)使得HTML与Java程序分离,这样便于维护代码。如果把所有的程序代码都写到JSP网页中,会使得代码繁杂,难以维护
2)可以降低开发JSP网页人员对Java编程能力的要求
3)JSP侧重于生成动态网页,事务处理由JavaBean来完成,这样可以充分利用JavaBean组件的可重用性特点,提高开发网站的效率
答案:A,B
注意:application.getAttribute()方法的返回类型是object类型,需要进行强制类型转换,转换为CounterBean类型
问题:mypack.CounterBean类的类文件应该发布到helloapp应用的什么目录下?
选项:
(A) /helloapp
(B) /helloapp/WEB-INF/
© /helloapp/WEB-INF/classes
(D) /helloapp/WEB-INF/classes/mypack
答案:D
问题:在JSP页面中通过jsp:useBean标签来访问JavaBean,而不是通过<% …%>Java程序片段来访问JavaBean,有什么好处?
答案:把程序代码与JSP网页分离,使JSP网页更加简洁,可维护。
问题:为什么JavaBean必须遵守特定的规范,比如对于CounterBean的count属性,其get和set方法应该为getCount()和setCount()方法,而不能随心所欲的定义为getCOunt()、insertcount()等等其他名字?
答案:当所有JavaBean遵守相同的规范,jsp:getProperty和jsp:setProperty标签就能自动根据JavaBean的属性来推断出它的get和set访问方法。(因为如果JavaBean遵守相同的规范,那么使用Java Bean的系统就可以按照统一的方式来访问Java Bean)