第5章JSP与JavaBean
本章导读
主要内容
编写和使用JavaBean
获取和修改bean的属性
bean的辅助类
JSP与bean结合的简单例子
难点
获取和修改bean的属性
关键实践
记忆测试
在谈论组件之前让我们看一个常见的事——组装电视机。组装一台电视机时,人们可以选择多个组件,例如电阻、电容、显像管等,一个组装电视机的人不必关心显像管是怎么研制的,只要根据说明书了解其属性和功能就可以了。不同的电视机可以安装相同的显像管,显像管的功能完全相同。如果一台电视机的显像管发生了故障,并不会影响其他的电视机;
如果两台电视机安装了一个共享的组件——天线,当天线发生了故障,两台电视机就会受到同样的影响。
按照Sun公司的定义,JavaBean是一个可重复使用的软件组件。实际上JavaBean是一种Java类,通过封装属性和方法成为具有某种功能或者处理某个业务的对象,简称bean。由于JavaBean是基于Java语言的,因此JavaBean不依赖平台,具有以下特点:
可以实现代码的重复利用。
易编写、易维护、易使用。
可以在任何安装了Java运行环境的平台上的使用,而无须重新编译。
JSP页面可以将数据的处理过程指派给一个或几个bean来完成,即JSP页面调用这些bean完成数据的处理,并将有关处理结果存放到bean中,然后JSP页面负责显示bean中的数据。例如使用Java程序片或某些JSP指令标记显示bean中的数据(见5.2节),即JSP页面的主要工作是显示数据,不负责数据的逻辑业务处理,如图5.1所示。
图5.1JSP+JavaBean
本章在webapps目录下新建一个Web服务目录ch5,除非特别约定,本章例子中涉及的JSP页面均保存在ch5目录中。创建bean的类的字节码文件须按要求存放,因此要在ch5目录下建立目录结构\ch5\WEBINF\classes(WEBINF字母大写)。
5.1编写和使用JavaBean
5.1.1编写JavaBean
视频讲解
编写JavaBean就是编写一个Java的类,所以只要会写类就能编写一个JavaBean。这个类创建的一个对象称为一个JavaBean,简称bean,分配给bean的变量(成员变量),也称bean的属性。为了能让使用bean的应用程序构建工具(比如Tomcat服务器)使用JSP动作标记知道bean的属性和方法,在类的命名上需要遵守以下规则:
(1) 如果类的成员变量(也称bean的属性)的名字是xxx,那么为了获取或更改bean的属性的值,类中必须提供两个方法:
getXxx(),用来获取属性xxx。
setXxx(),用来修改属性xxx。
也就是方法的名字用get或set为前缀,后缀是将属性(成员变量)名字的首字母大写的字符序列。
(2) 类中定义的方法的访问权限都必须是public的。
(3) 类中必须有一个构造方法是public、无参数的。
下面我们编写一个创建bean的Java类,并说明在JSP中怎样使用这个类创建一个bean。要求创建bean的类带有包名,即Java源文件须使用package语句给出包名,例如:
package gping;
或
package tom.jiafei;
以下是用来创建bean的Java源文件。
Circle.java(负责创建bean)
package tom.jiafei;
public class Circle {
double radius;
public Circle() {
radius=1;
}
public double getRadius() {
return radius;
}
public void setRadius(double newRadius) {
radius=newRadius;
}
public double circleArea() {
return Math.PI*radius*radius;
}
public double circleLength() {
return 2.0*Math.PI*radius;
}
}
将上述Java文件保存为Circle.java。注意,保存Java源文件时,“保存类型”选择为“所有文件”,将“编码”选择为“ANSI”。
5.1.2保存bean的字节码
视频讲解
为了使JSP页面使用bean,Tomcat服务器必须使用相应的字节码文件创建一个对象,即创建一个bean。为了让Tomcat服务器能找到字节码文件,字节码文件必须保存在特定的目录中。
ch5 \WEBINF\classes目录下,根据包名对应的路径,在classes目录下再建立相应的子目录。例如,包名tom.jiafei对应的路径是tom\jiafei,那么在classes目录下建立子目录结构tom\jiafei,如图5.2所示。
图5.2字节码文件的存放位置
将创建bean的字节码文件,例如Circle.class,复制到\WEBINF\classes\tom\jiafei中。为了调试程序方便,可以直接按照bean的包名将bean的源文件(例如Circle.java)保存在\WEBINF\classes\tom\jiafei目录中,然后用命令行进入tom\jiafei的父目录classes(不要进入tom或jiafei目录)编译Circle.java:
classes>javac tom\jiafei\Circle.java
视频讲解
5.1.3创建与使用bean
使用bean
使用JSP动作标记useBean加载使用bean,语法格式是:
或
例如:
需要特别注意的是,其中的“创建bean的类”要带有包名,例如:
class="tom.jiafei.Circle"
bean的加载原理
当JSP页面使用JSP动作标记useBean加载一个bean时,Tomcat服务器首先根据JSP动作标记useBean中id给出的bean名字以及scope给出的使用范围(bean生命周期),在Tomcat服务器管理的pageContent内置对象中查找是否含有这样的bean(对象)。如果这样的bean(对象)存在,Tomcat服务器就复制这个bean(对象)给JSP页面,就是常说的Tomcat服务器分配这样的bean给JSP页面。如果在pageContent中没有查找到JSP动作标记要求的bean,就根据class指定的类创建一个bean,并将所创建的bean添加到pageContent中。通过Tomcat服务器创建bean的过程可以看出,首次创建一个新的bean需要用相应类的字节码文件创建对象,当某些JSP页面再需要同样的bean时,Tomcat服务器直接将pageContent中已经有的bean分配给JSP页面,从而提高JSP页面
bean的使用效率。
注: 如果修改了字节码文件,必须重新启动Tomcat服务器才能使用新的字节码文件。
bean的有效范围和生命周期
scope的取值范围给出了bean的生命周期(存活时间),即scope取值决定了Tomcat服务器分配给用户的bean的有效范围和生命周期,因此需要理解scope取值的具体意义。下面就JSP动作标记useBean中scope取值的不同情况进行说明。
(1) page bean。
scope取值为page的bean称为page bean,page bean的有效范围是用户访问的当前页面,存活时间直到当前页面执行完毕。Tomcat服务器分配给每个JSP页面的page bean是互不相同的。也就是说,尽管每个JSP页面的page bean的功能相同,但它们占有不同的内存空间。page bean的有效范围是当前页面,当页面执行完毕,Tomcat服务器取消分配的page bean,即释放page bean所占有的内存空间。需要注意的是,不同用户(浏览器)的page bean也是互不相同的。也就是说,当两个用户同时访问同一个JSP页面时,一个用户对自己page bean的属性的改变,不会影响到另一个用户。
(2) session bean。
scope取值为session的bean称为session bean,session bean的有效范围是用户访问的Web服务目录下的各个页面,存活时间是用户的会话期(session)间,直到用户的会话消失(session对象达到了最大生存时间或用户关闭自己的浏览器以及服务器关闭,见4.3.1节)。如果用户访问Web服务目录多个页面,那么每个页面id相同的session bean是同一个bean(占有相同的内存空间)。因此,用户在某个页面更改了这个session bean的属性值,其他页面的这个session bean的属性值也将发生同样的变化。当用户的会话(session)消失,Tomcat服务器取消所分配的session bean,即释放session bean所占有的内存空间。需要注意的是,不同用户(浏览器)的session bean是互不相同的(占有不同的内存空间)。也就是说,当两个用户同时访问同一个Web服务目录,一个用户对自己session bean属性的改变,不会影响到另一个用户(一个用户在不同Web服务目录的session bean互不相同)。
(3) request bean。
scope取值为request的bean称为request bean, request bean的有效范围是用户请求的当前页面,存活时间是从用户的请求
产生到请求结束。Tomcat服务器分配给每个JSP页面的request bean是互不相同的。Tomcat服务器对请求作出响应之后,取消分配给这个JSP页面的request bean。简单地说,request bean只在当前页面有效,直到响应结束。request bean存活时间略长于page bean的存活时间,原因是Tomcat服务器认为页面执行完毕后,响应才算结束。需要注意的是,不同用户的request bean的也是互不相同的。也就是说,当两个用户同时请求同一个JSP页面时,一个用户对自己request bean属性的改变,不会影响到另一个用户。
(4) application bean。
scope取值为application的bean称为application bean,application bean的有效范围是当前Web服务目录下的各个页面,存活时间直到Tomcat服务器关闭。Tomcat服务器为访问Web服务目录的所有用户分配一个共享的bean,即不同用户的application bean也都是相同的一个。也就是说,任何一个用户对自己application bean属性的改变,都会影响到其他用户(不同Web服务目录的application bean互不相同)。
图5.3是bean的有效期的示意图,图5.4是有效范围示意图。
图5.3bean的有效期
图5.4bean的有效范围
注: 当使用session bean时,要保证用户端支持Cookie。
例5_1中负责创建page bean的类是上述的Circle类,page bean的名字是circle。
例5_1
example5_1.jsp(效果如图5.5所示)
<%@ page contentType="text/html"%>
<%@ page pageEncoding="utf-8"%>
<%--通过useBean标记,获得名字是circle的page bean --%>
圆的初始半径是: <%=circle.getRadius()%>
<%double newRadius =100;
circle.setRadius(newRadius);//修改半径
%>
修改半径为<%= newRadius %>
圆的半径是: <%=circle.getRadius()%>
圆的周长是: <%=circle.circleLength()%>
圆的面积是: <%=circle.circleArea()%>
图5.5page bean
例5_2使用id为girl的session bean,创建session bean的类仍然是上述的Circle.class。在例5_2的example5_2_a.jsp页面中,session bean的半径radius的值是1(如图5.6(a)所示),然后链接到example5_2_b.jsp页面,显示session bean的半径radius的值,然后将session bean的半径radius的值更改为1.618(如图5.6(b)所示)。用户再刷新example5_2_a.jsp或example5_2_b.jsp时看到的session bean的radius的值就都是1.618了(如图5.6(c)所示)。
图5.6session bean
例5_2
example5_2_a.jsp(效果如图5.6(a)(c)所示)
<%@ page contentType="text/html"%>
<%@ page pageEncoding="utf-8"%>
<%--通过JSP标记,用户获得一个id是girl的session bean: --%>
这里是example5_2_a.jsp页面。
圆的半径是<%=girl.getRadius()%>
单击超链接,到其他页面看圆的半径。
example5_2_b.jsp
example5_2_b.jsp(效果如图5.6(b)所示)
<%@ page contentType="text/html"%>
<%@ page pageEncoding="utf-8"%>
<%--用户的id是girl的session bean: --%>
这里是example5_2_b.jsp页面
当前圆的半径是: <%=girl.getRadius()%>
<% girl.setRadius(1.618);
%>
修改后的圆的半径是<%=girl.getRadius()%>
单击超链接,到其他页面看圆的半径。
example5_2_a.jsp
例5_3中使用了id为boy的application bean。当第一个用户访问这个页面时,显示application bean的radius的值,然后把application bean的radius的值更改为2.718(如图5.7(a)所示)。当其他用户访问这个页面时,看到的application bean的radius的值都是2.718(如图5.7(b)所示)。
图5.7application bean
例5_3
example5_3.jsp(效果如图5.7(a)(b)所示)
<%@ page contentType="text/html"%>
<%@ page pageEncoding="utf-8"%>
圆的初始半径是:<%=boy.getRadius()%>
<% boy.setRadius(2.718);
%>
修改后的圆的半径是:<%=boy.getRadius()%>
5.2获取和修改bean的属性
使用useBean动作标记获得一个bean后,在Java程序片或表达式中bean就可以调用方法产生行为,如前面的例5_1~例5_3所示,这种情况下,不要求创建bean的类遵守setXxx和getXxx等规则(见5.1.1节)。获取或修改bean的属性还可以使用JSP动作标记getProperty、setProperty,这种情况下,要求创建bean的类遵守setXxx和getXxx等规则,当JSP页面使用getProperty、setProperty标记获取或修改属性xxx时,必须保证bean有相应的getXxx和setXxx方法,即对方法名字的命名有特殊的要求。下面讲述怎样使用JSP的动作标记getProperty、setProperty去获取和修改bean的属性。
5.2.1getProperty动作标记
视频讲解
使用getProperty动作标记可以获得bean的属性值,并将这个值用串的形式发送给用户的浏览器。使用getProperty动作标记之前,必须使用useBean动作标记获得相应的bean。
getProperty动作标记的语法格式是:
或
其中,name取值是bean的id,用来指定要获取哪个bean的属性的值,property取值是该bean的一个属性的名字。
注:
让request调用setCharacterEncoding方法设置编码为UTF8,以避免显示bean的属性值出现乱码现象。
5.2.2setProperty动作标记
视频讲解
使用setProperty动作标记可以设置bean的属性值。使用这个标记之前,必须使用useBean标记得到一个相应的bean。
setProperty动作标记可以通过两种方式设置bean属性值。
(1) 将bean属性值设置为一个表达式的值或字符序列。
value给出的值的类型要和bean的属性的类型一致。
(2) 通过HTTP表单的参数值来设置bean的相应属性值。
① 用form表单的所有参数值设置bean相对应属性值的使用格式如下:
在setProperty标记的上述用法中不具体指定bean属性值将对应form表单中哪个参数指定的值,系统会自动根据名字进行匹配对应,但要求bean属性的名字必须在form表单中有名称相同的参数名字相对应,Tomcat服务器会自动将参数的字符串值转换为bean相对应的属性值
② 用form表单的某个参数的值设置bean的某个属性值的使用格式如下:
setProperty标记的上述用法具体指定了bean属性值将对应表单中哪个参数名(param)指定的值,这种设置bean的属性值的方法,不要求property给出的bean属性的名字和param给出的参数名一致,即不要求bean属性的名字必须和表单中某个参数名字相同。
当把字符序列设置为beans的属性值时,这个字符序列会自动被转化为bean的属性类型。Java语言将字符序列转化为其他数值类型的方法如下。
转化到int: Integer.parseInt(Sting s)
转化到long: Long.parseLong(Sting s)
转化到float: Float.parseFloat(Sting s)
转化到double: Double.parseDouble(Sting s)
这些方法都可能发生NumberFormatException异常,例如,当试图将字符序列ab23转化为int型数据时就发生了NumberFormatException。
注: 用form表单设置bean的属性值时,只有提交了表单,对应的setProperty标记才会被执行。
例5_4使用Goods类创建request bean。example5_4_a.jsp通过form表单指定example5_4_a.jsp和example5_4_b.jsp中的request bean的name和price属性值。
example5_4_a.jsp和example5_4_b.jsp使用getProperty动作标记以及bean调用方法两种方式显示request bean的name和price属性值。
例5_4
JavaBean
将Goods.java保存在\ch5\WEBINF\classes\tom\jiafei中,用命令行进入tom\jiafei的父录classes,
按如下格式编译Goods.java:
classes>javac tom\jiafei\Goods.java
Goods.java(负责创建bean)
package tom.jiafei;
public class Goods{
String name="无名";
double price=0;
public String getName() {
return name;
}
public void setName(String newName){
name=newName;
}
public double getPrice() {
return price;
}
public void setPrice(double newPrice) {
price=newPrice;
}
}
JSP页面
example5_4_a.jsp(效果如图5.8(a)所示)
<%@ page contentType="text/html"%>
<%@ page pageEncoding="utf-8"%>
<% request.setCharacterEncoding("utf-8");
%>
名称:
名称: <%= phone.getName()%>
价格:
价格: <%=phone.getPrice()%>
example5_4_b.jsp(效果如图5.8(b)所示)
<%@ page contentType="text/html"%>
<%@ page pageEncoding="utf-8"%>
<% request.setCharacterEncoding("utf-8");
%>
图5.8设置与获取bean的属性值
名称:
名称: <%= phone.getName()%>
价格:
价格: <%=phone.getPrice()%>
注:
setProperty和getProperty动作标记适合在处理数据和显示数据不是很复杂的情况下使用,如果业务逻辑或显示数据比较复杂,可能在Java程序片中用bean调用方法更加方便(创建bean的类也不用遵循方法命名的setXXX和getXXX规则)。
5.3bean的辅助类
视频讲解
在写一个创建bean的类时,除了需要用import语句引入JDK提供的类,可能还需要自己编写一些其他的类,只要将这样类的包名和bean类的包名一致即可(也可以和创建bean的类写在一个Java源文件中)。
在下面的例5_5中,使用一个bean列出Tomcat服务器驻留的计算机上某目录中特定扩展名的文件。创建bean的ListFile类,需要一个实现FilenameFilter接口的辅助类FileExtendName,该类可以帮助bean列出指定扩展名的文件(把ListFile.java编译生成的字节码ListFile.class和FileExtendName.class复制到\ch4\WEBINF\classes\tom\jiafei中)。
例5_5使用ListFile类创建request bean。例5_5中用户通过表单设置request bean的extendsName属性值,request bean列出目录中由extendsName属性值指定的扩展名的文件。
例5_5
JavaBean
用命令行进入tom\jiafei的父目录classes,编译ListFile.java(约定见5.1.2节):
classes>javac tom\jiafei\ListFile.java
ListFile.java(负责创建bean)
package tom.jiafei;
import java.io.*;
class FileExtendName implements FilenameFilter {
String str=null;
FileExtendName (String s) {
str="."+s;
}
public boolean accept(File dir,String name) {
return name.endsWith(str);
}
}
public class ListFile {
String extendsName=null;
String [] allFileName=null;
String dir=null;
public void setDir(String dir) {
this.dir =dir;
}
public String getDir() {
return dir;
}
public void setExtendsName(String s) {
extendsName=s;
}
public String getExtendsName() {
return extendsName;
}
public String [] getAllFileName() {
if(dir!=null) {
File mulu=new File(dir);
FileExtendName help=new FileExtendName(extendsName);
allFileName=mulu.list(help);
}
return allFileName;
}
}
JSP页面
example5_5.jsp(效果如图5.9所示)
<%@ page contentType="text/html"%>
<%@ page pageEncoding="utf-8"%>
<% request.setCharacterEncoding("utf-8");
%>
目录 中
扩展名是 的文件有:
<% String [] fileName=file.getAllFileName();
if(fileName!=null) {
for(int i=0;i"+fileName[i]);
}
}
%>
图5.9特定扩展名的文件
5.4JSP与bean结合的简单例子
JSP页面中调用bean可以将数据的处理从页面中分离出来,实现代码复用,以便更有效地维护一个Web应用。本节将结合一些实际问题,进一步熟悉掌握bean的使用方法。在本节中,创建bean类的包名都是red.star,使用的Web服务目录仍然是ch5
,因此,需要在ch5目录下建立目录结构:
ch5\WEBINF\classes\red\star,将
创建bean的字节码文件都保存在该目录中。为了调试程序方便,可以直接按照创建bean类的包名将相应的Java源文件保存在Web服务目录的相应目录中,例如将Java源文件保存在Web服务目录ch5的WEBINF\classes\red\star目录中,然后使用MSDOS命令行进入\red\star的父目录classes,按如下格式编译Java源文件:
classes>javac red\star\源文件名
5.4.1三角形bean
视频讲解
例5_6使用request bean(Triangle类负责创建)完成三角形的有关数据的处理。例子中的JSP页面提供一个form表单,用户可以通过form表单将三角形三边的长度提交给该页面。用户提交form表单后,JSP页面将计算三角形面积的任务交给一个request bean去完成。
例5_6
JavaBean
用命令行进入red\star的父目录classes,编译Triangle.java(约定见5.1.2节):
classes>javac red\star\Triangle.java
Triangle.java(负责创建request bean)
package red.star;
public class Triangle {
double sideA=-1,sideB=-1,sideC=-1;
String area;
boolean isTriangle;
public void setSideA(double a) {
sideA=a;
}
public double getSideA() {
return sideA;
}
public void setSideB(double b) {
sideB=b;
}
public double getSideB() {
return sideB;
}
public void setSideC(double c) {
sideC=c;
}
public double getSideC() {
return sideC;
}
public String getArea() {
double p=(sideA+sideB+sideC)/2.0;
if(isTriangle){
double result= Math.sqrt(p*(p-sideA)*(p-sideB)*(p-sideC));
area=String.format("%.2f",result); //保留2位小数
}
return area;
}
public boolean getIsTriangle(){
if(sideA
<%@ page pageEncoding="utf-8"%>
<% request.setCharacterEncoding("utf-8");
%>
三角形的三边是:
,
,
.
这三个边能构成一个三角形吗?
面积是:
图5.10用bean计算三角形面积
视频讲解
5.4.2四则运算bean
例5_7使用session bean(ComputerBean类负责创建)完成四则运算。例子中的JSP页面提供一个form表单,用户可以通过form表单输入两个数,选择四则运算符号提交给该页面。用户提交form表单后,JSP页面将计算任务交给session bean去完成。
例5_7
JavaBean
用命令行进入red\star的父目录classes,编译ComputerBean.java(约定见5.1.2节):
javac red\star\ComputerBean.java
ComputerBean.java(负责创建session bean)
package red.star;
public class ComputerBean {
double numberOne,numberTwo,result;
String operator="+";
public void setNumberOne(double n) {
numberOne=n;
}
public double getNumberOne() {
return numberOne;
}
public void setNumberTwo(double n) {
numberTwo=n;
}
public double getNumberTwo() {
return numberTwo;
}
public void setOperator(String s) {
operator=s.trim();;
}
public String getOperator() {
return operator;
}
public double getResult() {
if(operator.equals("+"))
result=numberOne+numberTwo;
else if(operator.equals("-"))
result=numberOne-numberTwo;
else if(operator.equals("*"))
result=numberOne*numberTwo;
else if(operator.equals("/"))
result=numberOne/numberTwo;
return result;
}
}
JSP页面
example5_7.jsp(效果如图5.11所示)
<%@ page contentType="text/html"%>
<%@ page pageEncoding="utf-8"%>
<% request.setCharacterEncoding("utf-8");
%>
=
图5.11用bean完成四则运算
视频讲解
5.4.3浏览图像bean
例5_8中的JSP页面通过单击“下一张”或“上一张”超链接浏览图像,JSP页面将获取图像名字的任务交给session bean去完成,JSP页面根据bean获得的图像名字显示图像。例子中使用的图像文件保存在当前Web服务目录的子目录image中(图像文件的名字中不能含有空格)。
例5_8
JavaBean
用命令行进入red\star的父目录classes,编译Play.java(约定见5.1.2节):
classes>javac red\star\Play.java
Play.java(负责创建session bean)
package red.star;
import java.io.*;
public class Play {
String pictureName[];//存放全部图片文件名字的数组
String showImage;//存放当前要显示的图片
String webDir="";//Web服务目录的名字,例如ch5
String tomcatDir;//Tomcat的安装目录,例如apache-tomcat-9.0.26
int index=0;//存放图片文件的序号
public Play() {
File f= new File(""); //该文件认为在Tomcat服务器启动的目录中,即bin目录中
String path=f.getAbsolutePath();
int index=path.indexOf("bin"); //bin是Tomcat的安装目录下的子目录
tomcatDir=path.substring(0,index); //得到Tomcat的安装目录的名字
}
public void setWebDir(String s) {
webDir=s;
File dirImage=new File(tomcatDir+"/webapps/"+webDir+"/image");
pictureName=dirImage.list();
}
public String getShowImage() {
showImage=pictureName[index];
return showImage;
}
public void setIndex(int i) {
index=i;
if(index>=pictureName.length)
index=0;
if(index < 0)
index=pictureName.length-1;
}
public int getIndex() {
returnindex ;
}
}
JSP页面
example5_8.jsp(效果如图5.12所示)
<%@ page contentType="text/html"%>
<%@ page pageEncoding="utf-8"%>
<% request.setCharacterEncoding("utf-8");
%>
<%
String webDir=request.getContextPath(); //获取当前Web服务目录的名称
webDir=webDir.substring(1); //去掉名称前面的目录符号: /
%>
width =300 height =200>
下一张
上一张
图5.12浏览图像
视频讲解
5.4.4日历bean
例5_9使用session bean(Calendar类负责创建)显示某月的日历。用户单击“下一月”“上一月”超链接可以翻阅日历。也可以输入年份,选择月份,单击 form表单中的提交键查看日历。
例5_9
JavaBean
用命令行进入red\star的父目录classes,编译Calendar.java(约定见5.1.2节):
classes>javac red\star\Calendar.java
Calendar.java(负责创建session bean)
package red.star;
import java.time.LocalDate;
import java.time.DayOfWeek;
public class Calendar {
int year ,month ;
String saveCalender;//存放日历
public Calendar(){
year=LocalDate.now().getYear();
month=LocalDate.now().getMonthValue();
}
public void setYear(int y){
year=y;
}
public int getYear(){
return year;
}
public void setMonth(int m){
month=m;
if(month>12){
year++;
month=1;
}
if(month<1){
month=12;
year--;
}
}
public int getMonth(){
return month;
}
public String getSaveCalender(){
LocalDate date=LocalDate.of(year,month,1);
int days=date.lengthOfMonth(); //得到该月有多少天
int space=0;//存放空白字符的个数
DayOfWeek dayOfWeek=date.getDayOfWeek(); //得到1号是星期几
switch(dayOfWeek) {
case SUNDAY:space=0;
break;
case MONDAY:space=1;
break;
case TUESDAY:space=2;
break;
case WEDNESDAY: space=3;
break;
case THURSDAY:space=4;
break;
case FRIDAY:space=5;
break;
case SATURDAY:space=6;
break;
}
String [] c=new String[space+days];
for(int i=0;i星期四星期五 | 星期六 | ";
StringBuffer buffer=new StringBuffer();
buffer.append("");
buffer.append(head);
int n=0;
while(n");
int increment=Math.min(7,c.length-n);
for(int i=n;i"+c[i]+"");
}
buffer.append("");
n=n+increment;
}
buffer.append("
");
saveCalender=new String(buffer);
return saveCalender;
}
}
JSP页面
example5_9.jsp(效果如图5.13所示)
<%@ page contentType="text/html"%>
<%@ page pageEncoding="utf-8"%>
<% request.setCharacterEncoding("utf-8");
%>
年
月的日历:
下一月
上一月
图5.13查看日历
视频讲解
5.4.5计数器bean
例5_10使用application bean(ComputerCount类负责创建)记录Web服务目录(通常所说的网站)被访问的次数。只要用户第1次访问(用户的session被创建)Web服务目录,那么当前Web服务目录的访问计数就增加1。如果用户的session没有消失,用户再访问当前Web服务目录,访问的计数不再增1。
例5_10
JavaBean
用命令行进入red\star的父目录classes,编译ComputerCount.java(约定见5.1.2节):
classes>javac red\star\ComputerCount.java
ComputerCount.java(负责创建application bean)
package red.star;
import java.io.*;
public class ComputerCount {
int number=0;
public synchronized void addCount() {
number++;
}
public int getNumber(){
return number;
}
}
import java.time.DayOfWeek;
JSP页面
example5_10_a.jsp(效果如图5.14(a)所示)
<%@ page contentType="text/html"%>
<%@ page pageEncoding="utf-8"%>
<% if(session.isNew()) {
count.addCount();
}
%>
这是网站的example5_10_a.jsp页面。
网站访问量:
欢迎去example5_10_b.jsp参观
example5_10_b.jsp(效果如图5.14(b)所示)
<%@ page contentType="text/html"%>
<%@ page pageEncoding="utf-8"%>
<% if(session.isNew()) {
count.addCount();
}
%>
这是网站的example5_10_b.jsp页面
网站访问量:
欢迎去example5_10_a.jsp参观
图5.14两个用户访问Web服务目录
视频讲解
5.5上机实验
提供了详细的实验步骤要求,按步骤完成,提升学习效果,积累经验,不断提高Web设计能力。
5.5.1实验1小数表示为分数
实验目的
掌握怎样使用 request bean。
实验要求
(1) 编写inputNumber.jsp,该页面提供一个form表单,该form表单提供一个text文本框,用于用户输入一个纯小数(例如0.618)
。用户在form表单中输入纯小数后,单击submit提交键将纯小数提交给getFraction.jsp页面。
(2) getFraction.jsp使用request bean,并使用setProperty动作标记让request bean将纯小数转换为分数,把分子和分母存放在request bean的名字是numerator 和denominator 的属性(变量)中。然后getFraction.jsp使用getProperty动作标记获取request bean的numerator 和denominator的属性值。
(3) 在Tomcat服务器的webapps目录下(例如D:\apachetomcat9.0.26\webapps)新建一个名字是ch5_practice_one的Web服务目录。把JSP页面都保存到ch5_practice_one目录中。在ch5_practice_one下建立子目录WEBINF(字母大写),然后在WEBINF下再建立子目录classes。将创建request bean的类的Java源文件保存在classes的相应子目录中(见5.1.2节)。
(4) 用浏览器访问JSP页面inputNumber.jsp。
参考代码
参考代码运行效果如图5.15所示。
图5.15把纯小数表示成分数
JavaBean用命令行进入sea\water的父目录classes,编译Fraction.java(约定见5.1.2节):
classes>javac sea\water\Fraction.java
Fraction.java(负责创建request bean)
package sea.water;
public class Fraction {
public double number ;//存放小数
public long numerator ;//存放分子
public long denominator; //存放分母
public double getNumber(){
String numberString=String.valueOf(number);
String xiaoshuPart =
numberString.substring(numberString.indexOf(".")+1); //得到纯小数部分
return Double.parseDouble("0."+xiaoshuPart);
}
public long getNumerator(){
return numerator;
}
public long getDenominator(){
return denominator;
}
public void setNumber(double number){
this.number=number;
String numberString=String.valueOf(number);
String xiaoshuPart =
numberString.substring(numberString.indexOf(".")+1); //得到小数部分
int m=xiaoshuPart.length(); //m的值就是小数的小数位数
numerator=Long.parseLong(xiaoshuPart);//分子
denominator=(long)Math.pow(10,m);//分母
long greatCommonDivisor=f(numerator,denominator) ; //最大公约数
numerator=numerator/greatCommonDivisor;
denominator=denominator/greatCommonDivisor;
}
private long f(long a,long b) { //求a和b的最大公约数
if(a==0) return 1;
if(a
<%@ page pageEncoding="utf-8"%>
getFraction.jsp (效果如图5.15(b)所示)
<%@ page contentType="text/html"%>
<%@ page pageEncoding="utf-8"%>
表示成分数是:
5.5.2实验2记忆测试
实验目的
掌握使用session bean存储用户的数据,和4.6.5节的记忆测试实验进行比对,体会使用bean的方便和好处。
实验要求
(1) 编写choiceGrage.jsp,该页面中的form表单中使用radio标记选择记忆测试级别: 初级、中级和高级。初级需要记忆一个长度为5个字符的字符序列(例如★■★▲●),中级需要记忆一个长度为7个字符的字符序列,高级需要记忆一个长度为10个字符的字符序列。在choiceGrage.jsp页面选择级别后,单击form表单的提交键提交给giveTest.jsp页面。
(2) 编写giveTest.jsp页面,该页面获取choiceGrage.jsp页面提交的级别后,使用session bean显示testString
的属性值(例如属性值是长度为7个字符的字符序列),然后提示用户在5秒内记住这个字符序列。5秒后,该页面将自动定向到answerTest.jsp页面。
(3) 编写answerTest.jsp页面,该页面的form表单提供用户给出答案的界面,即使用radio标记让用户选择字符序列中的各个字符,以此代表用户认为自己记住的字符序列。单击提交键,将选择提交给judgeAnswer.jsp页面。
(4) 编写judgeAnswer.jsp页面,该页面负责判断有户是否记住了giveTest.jsp页面给出的字符序列。
(5) 在Tomcat服务器的webapps目录下新建名字是ch5_practice_two的目录,即新建Web服务目录ch5_practice_two。把JSP页面都保存到ch5_practice_two目录中。在ch5_practice_two目录下建立子目录WEBINF(字母大写),然后在WEBINF目录下再建立子目录classes,即在ch5_practice_two目录下建立\WEBINF\classes目录结构。将创建session bean类的Java源文件按
照包名保存在classes的相应子目录中(见5.1.2节)。
(6) 用浏览器访问JSP页面choiceGrage.jsp。
(7) 将本实验与4.6.5节的实验进行对比,感受使用session bean的好处。
参考代码
请比较4.6.5节的记忆测试实验,体会使用bean的方便和好处。参考代码运行效果如图5.16所示。
图5.16记忆测试
JavaBean
用命令行进入sea\water的父目录classes,编译Memory.java(约定见5.1.2节):
classes>javac sea\water\Memory.java
Memory.java(负责创建session bean)
package sea.water;
import java.util.ArrayList;
import java.util.Random;
public class Memory {
static ArrayList list=new ArrayList();
static {
list.add("★");
list.add("●");
list.add("▲");
list.add("■");
list.add("◆");
}
int grade=5 ;//存放级别,例如初级grade存放的值是5,中级是7,高级是10
String testString;//存放需要记忆的字符序列,例如,★■★▲●
boolean isGivenTestString=false; //存放是否已经给了测试题目
public void setGrade(int n){
grade=n;
}
public int getGrade(){
return grade;
}
public void giveTestString(){
StringBuffer buffer=new StringBuffer();
Random random=new Random();
for(int i=0;i
<%@ page pageEncoding="utf-8"%>
giveTest.jsp(效果如图5.16(b)所示)
<%@ page contentType="text/html"%>
<%@ page pageEncoding="utf-8"%>
<%@ page import="java.util.ArrayList"%>
<%@ page import="java.util.Random"%>
<%String grade=request.getParameter("grade");
String testString =""; //存放测试题目,例如★■★▲●
if(grade == null){
memory.setGrade(memory.getGrade());
}
else {
memory.setGrade(Integer.parseInt(grade));
}
if(memory.getIsGivenTestString()== false) {
memory.giveTestString();
testString=memory.getTestString(); //得到测试的题目
memory.setIsGivenTestString(true);
}
else if(memory.getIsGivenTestString()== true){
response.sendRedirect("answerTest.jsp"); //定向到答题页面
}
%>
给5秒记住您看到的字符序列:
<%= testString %>
5秒后,将转到答题页。
<%response.setHeader("refresh","5");
%>
answerTest.jsp(效果如图5.16(c)所示)
<%@ page contentType="text/html"%>
<%@ page pageEncoding="utf-8"%>
judgeAnswer.jsp(效果如图5.16(d)所示)
<%@ page contentType="text/html"%>
<%@ page pageEncoding="utf-8"%>
<%memory.setIsGivenTestString(false);
request.setCharacterEncoding("utf-8");
int n=memory.getGrade();
StringBuffer buffer=new StringBuffer();
for(int i=1;i<=n;i++){
buffer.append(request.getParameter("R"+i)); //获取radio提交的值
out.print(""+request.getParameter("R"+i));
}
String userAnswer=new String(buffer);
String testString=memory.getTestString(); //得到测试的题目
if(testString.equals(userAnswer)){
out.print("您记忆不错");
}
else {
out.print("您没记忆住!答案是:
"+testString);
}
%>
返回,继续练习记忆
重新选择级别
5.5.3实验3成语接龙
实验目的
掌握使用application bean存储所有用户共享的数据。
实验要求
(1) 编写inputIdioms.jsp,该页面使用application bean显示目前成语接龙的信息。提供form表单,用户根据目前成语接龙的信息输入一个成语,单击提交键提交给当前页面,如果输入成语符合成语接龙规则(例如输入的成语的首字和成语接龙中的最后一个成语的末字相同),application bean就将用户输入的成语添加到成语接龙中。
(2) 在Tomcat服务器的webapps目录下新建名字是ch5_practice_three的Web服务目录。把JSP页面都保存到ch5_practice_three目录中。在ch5_practice_three下建立子目录WEBINF(字母大写),然后在WEBINF目录下再建立子目录classes,即在ch5_practice_three目录下建立\WEBINF\classes目录结构。将创建application bean的类的Java源文件按照包名保存在classes的相应子目录中(见5.1.2节)。
(3) 用浏览器访问JSP页面inputIdioms.jsp。
参考代码
参考代码运行效果如图5.17所示。
图5.17成语接龙
JavaBean
用命令行进入sea\water的父目录classes,编译ContinueIdioms.java(约定见5.1.2节):
classes>javac sea\water\ ContinueIdioms.java
ContinueIdioms.java(负责创建application bean)
package sea.water;
import java.util.LinkedList;
import java.util.Iterator;
import java.util.NoSuchElementException;
public class ContinueIdioms {
LinkedList listIdioms ; //存放成语的链表
public String nowIdioms;//当前参与接龙的成语
public ContinueIdioms(){
listIdioms=new LinkedList();
}
public synchronized void setNowIdioms(String s){
nowIdioms=s;
try{
String previous=listIdioms.getLast(); //得到上次添加的成语
//上一个成语的最后一个字符
char endChar=previous.charAt(previous.length()-1);
char startChar=nowIdioms.charAt(0); //当前成语的第一个字符
if(startChar == endChar)
listIdioms.add(nowIdioms);
}
catch(NoSuchElementException exp){
listIdioms.add(nowIdioms);
System.out.println(exp);
}
}
public String getAllIdioms(){
StringBuffer buffer=new StringBuffer();
Iterator iterator =listIdioms.iterator();
if(iterator.hasNext() == false)
buffer.append("→");
while(iterator.hasNext()){
buffer.append(iterator.next()+"→");
}
return new String(buffer);
}
}
JSP页面
inputIdioms.jsp (效果如图5.17(a)(b)所示)
<%@ page contentType="text/html"%>
<%@ page pageEncoding="utf-8"%>
<% request.setCharacterEncoding("utf-8");
%>
目前的接龙情景:
继续接龙,输入成语:
5.6小结
JavaBean是一个可重复使用的软件组件,是遵循一定标准、用Java语言编写的一个类,该类的一个实例称作一个JavaBean。
一个JSP页面可以将数据的处理过程指派给一个或几个bean来完成,我们在JSP页面中调用这些bean即可。在JSP页面中调用bean可以将数据的处理代码从页面中分离出来,实现代码复用,更有效地维护一个Web应用。
bean的生命周期分为page、request、session和application。
习 题 5
1. 假设Web服务目录mymoon中的JSP页面要使用一个bean,该bean的包名为blue.sky。请说明,应当怎样保存bean的字节码文件。
2. 假设Web服务目录是mymoon,star是mymoon的一个子目录,JSP页面a.jsp保存在star中,并准备使用一个bean,该bean的包名为tom.jiafei。下列哪个叙述是正确的?
A. 创建bean的字节码文件保存在\mymoon\WEBINF\classes\tom\jiafei中
B. 创建bean的字节码文件保存在\mymoon\star\WEBINF\classes\tom\jiafei中
C. 创建bean的字节码文件保存在\mymoon\WEBINF\star\classes\tom\jiafei中
D. 创建bean的字节码文件保存在\mymoon\WEBINF\classes\start\tom\jiafei中
3. tom.jiafei.Circle是创建bean的类,下列哪个标记是正确创建session bean的标记?
A.
B.
C.
D.
4. 假设创建bean的类有一个int型的属性number,下列哪个方法是设置该属性值的正确方法?
A. public void setNumber(int n){B. void setNumber(int n){
number=n;number=n;
} }
C. public void SetNumber(int n) {D. public void Setnumber(int n){
number=n;number=n;
}}
5. 假设JSP页面使用标记
创建了一个名字为moon的bean,该bean有一个String类型、名字为number的属性。如果创建moon的Java类AAA没有提供public String getNumber()方法,JSP页面是否允许使用如下getProperty标记获取moon的number属性值?
6. 编写一个JSP页面,该页面提供一个表单,用户可以通过表单输入梯形的上底、下底和高的值,并提交给本JSP页面,该JSP页面将计算梯形面积的任务交给一个page bean去完成。JSP页面使用getProperty动作标记显示page bean中的数据,例如梯形的面积。
7. 编写两个JSP页面a.jsp和b.jsp,a.jsp页面提供一个表单,用户可以通过表单输入矩形的两个边长提交给b.jsp页面,b.jsp调用一个request bean去完成计算矩形面积的任务。b.jsp页面使用getProperty动作标记显示矩形的面积。