第5 章 JSP中使用JavaBean JSP最强有力的一个方面就是能够使用JavaBean组件。JavaBean是一个可重复使 用的软件组件。JavaBean是一种Java类,通过封装属性和方法成为具有某种功能或者能 够处理某些业务的对象,简称Bean。 一个基本的JSP 页面由静态的HTML 标签和Java脚本组成,如果Java脚本和 HTML标签大量掺杂在一起,就显得页面混杂,不易维护。JSP页面可以将数据的处理 过程指派给一个或者几个JavaBean来完成,而在JSP页面中调用JavaBean。不提倡大量 的数据处理都用页面中的Java脚本来完成。JSP页面中调用JavaBean,可有效地分离静 态工作部分和动态工作部分,实现业务逻辑和表现形式的分离。JavaBean负责业务逻辑 的处理,JSP负责页面的展示,如图5-1所示。 图5-1 JSP+JavaBean模型 5.1 JavaBean介绍 JavaBean体系结构是全面基于组件的标准模型。JavaBean是Java描述的软件组件 模型,有点类似于Microsoft的COM 组件概念。JavaBean组件是Java类,这些类遵循特 定的接口格式,以便于容器按照标准的方式使用方法命名、底层行为来构造和访问 JavaBean对象。 5.1.1 JavaBean 简介 1.JavaBean的特点 . 可以实现代码的重复利用。 . 易编写、易维护、易使用。 . 可以在任何支持Java的平台上工作,而不需要重新编译。 . 可以通过网络传输。 . 可以与其他Java类同时使用。 1 16 JSP 程序设计(第2 版) 2.JavaBean的应用范围 JavaBean传统的应用在于可视化领域,如AWT(抽象窗口工具集)和Swing下的应 用。现在,JavaBean更多的应用在于非可视化领域,它在服务器端应用方面表现出了越 来越强的生命力。非可视化的JavaBean和可视化的JavaBean同样使用属性和事件。非 可视化的JavaBean在JSP程序中常用来封装业务逻辑、数据库操作等,可以很好地实现 业务逻辑和前台页面的分离,使得系统具有更好的健壮性和灵活性。 注意:JavaBean和EJB(EnterpriseJavaBean)的概念是完全不同的。EJB分为三类: 会话Bean(SessionBean)、实体Bean(EntityBean)、消息驱动Bean(MessageDriven Bean)。EJB很复杂,而JavaBean是非常简单朴素的。 5.1.2 编写JavaBean 遵循的原则 编写JavaBean就是编写一个Java的类,所以只要学会写类就能编写一个JavaBean, 这个类创建的一个对象称之为JavaBean。为了让使用JavaBean的应用程序构建工具(比 如JSP引擎)知道这个Bean的属性和方法,JavaBean的类需要遵守以下规则: . 必须具备一个零参数的构造方法,显式地定义这样一个无参构造方法或者省略所 有的构造方法都能满足这项要求。 . 成员变量也称为属性,JavaBean不应该有公开的成员变量,需使用存取方法读取 和修改属性,不允许对字段直接访问。属性的名字建议以小写英文字母开头。 . 属性的值通过getXxx()和setXxx()方法来访问。例如,如果类有String类型的 属性title,读取title的方法是返回String的getTitle(),修改title的方法是 setTitle(Stringtitle)。 . 布尔型属性的读取方法可以使用getXxx(),也可以使用isXxx()。 .JavaBean需要定义在包里面,package保留字给出类所在的包。 具有两个属性的JavaBean:User类 package cn.edu.uibe.model; public class User { private String userName; private String password; public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } } 【技巧】 Eclipse可以帮助我们生成Getter和Setter方法,依次选择Eclipse→ Source,打开GenerateGettersandSetters对话框,如图5-2所示。 5.1.3 JavaBean 的属性 属性是JavaBean组件内部状态的表示,JavaBean的属性可以分为以下4类: 第5 章 JSP 中使用JavaBean 1 17 图5-2 使用Eclipse生成Getter和Setter方法 . Simple(简单属性)。 .Indexed(索引属性)。 . Bound(绑定属性)。 . Constrained(约束属性)。 可以在JSP页面中使用的属性是简单属性和索引属性,而绑定属性和约束属性是在 图形界面开发时才会用到,下面介绍简单属性和索引属性。 1.简单属性 package cn.edu.uibe.model; public class SimpleAttrBean { private String attr ; //JavaBean 的属性 public SimpleAttrBean(){ } //零参数的构造方法 public String getAttr(){ //读取属性的方法 return attr; } public void setAttr(String attr){ //设置属性的方法 this.attr = attr; } } 2.索引属性 索引属性是一个数组,读取和写入属性也是使用Getter方法和Setter方法。 package cn.edu.uibe.model; public class IndexedAttrBean { private int[] dataSet = {1,2,3,4,5}; //索引属性 public IndexedAttrBean(){} //零参数构造方法 public int[] getDataSet(){ //返回整个数组 return dataSet; } public int getDataSet(int index){ //返回数组中的一个元素 return dataSet[index]; } public void setDataSet(int[] x){ //设置整个数组 dataSet = x; } 1 18 JSP 程序设计(第2 版) public void setDataSet(int index, int x){ //设置数组中的一个值 dataSet[index] = x; } } 5.2 在JSP页面中 动作元素可以使用JavaBean对象,这样就可以将大部 分业务处理逻辑封装在JavaBean里面。本节介绍 的基本语法、JavaBean 的条件化操作、JavaBean的存放位置和JavaBean的作用范围。 5.2.1 的基本语法 动作元素用来在JSP页面中获取或者创建一个JavaBean对象,并指 定它的名字和作用范围。JSP容器确保JavaBean对象在指定的范围内可以使用。 的语法格式如下: //仅当JavaBean 实例化时才执行 ... 当服务器上某个含有 动作元素的JSP页面被加载执行时,JSP引擎首 先根据ID给出的JavaBean的名字(beanName),在scope范围对应的JSP内部对象上查 找是否有这个名字的属性(Attribute)。如果在指定范围内找到了与beanName同名的属 性,JSP引擎返回属性对应的对象引用。如果在指定的范围内没有找到与beanName同 名的属性,JSP引擎根据class属性给出的包名和类名,创建一个该类的对象,并将该对象 作为属性名为beanName的属性值,绑定到scope范围对应的JSP 内部对象上。创建 JavaBean对象时,JSP引擎使用无参数的构造方法。由于pageContext对象可以操作各 个范围的属性,实际上JavaBean的查找和绑定都是通过pageContext对象来完成的。如 果没有指定JavaBean的范围,默认范围是page。 的含义可以通过下面的代码来理解: Object beanName = pageContext.getAttribute("beanName", SCOPE); if(beanName==null){ Object bean = new package.BeanClassName(); 第5 章 JSP 中使用JavaBean 1 19 pageContext.setAttribute("beanName", bean, SCOPE); } 在pageContext类的定义中,SCOPE 的取值有:PAGE_SCOPE、REQUEST_ SCOPE、SESSION_SCOPE 和APPLICATION_SCOPE,对应的JSP 内部对象分别是 pageContext、request、session和application。 例如,将User.java中定义的类cn.edu.uibe.model.User定义为名字为user,范围是 request的JavaBean。 或者: 5.2.2 JavaBean 的条件化操作 使用 在JSP页面中操作JavaBean对象时,bean对象不一定是新创建 的, 之间的语句也不一定执行。 1.bean的条件化创建 . 仅当找不到相同id和scope的bean时, 才会引发bean新实例的 创建。 . 如果找到相同id和scope的bean,则仅仅是将已经存在的bean赋值给由id指定 的变量。 HelloBean.java中定义了一个JavaBean的类HelloBean,它只有一个属性message, bean提供了getMessage()和setMessage()方法来访问属性message。 package cn.edu.uibe.model; public class HelloBean { private String message; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; System.out.println(message); } } request_bean1.jsp 中使用 动作元素创建了一个HelloBean 类的 JavaBean对象,范围是request,bean 的名字为hello。页面的小脚本中的调用hello. setMessage()方法将message属性值设置为字符串"HelloJavaBean!"。页面又使用 动作元素包含了另外一个JSP页面request_bean2.jsp。这两个JSP页面对 1 20 JSP 程序设计(第2 版) 应的是同一个request对象,可以共享范围是request的JavaBean对象。 <%@ page contentType="text/html; charset=GB18030" %> Bean 的条件化创建 <% //可以在脚本中直接使用JavaBean 对象 hello.setMessage("Hello JavaBean!"); %> 在被包含的JSP页面request_bean2.jsp中,也有一个 动作元素,id为 hello,class为cn.edu.uibe.model.HelloBean,scope为request,但这并不会导致重新实例 化一个JavaBean对象,因为request范围内已经有了一个名字为hello的JavaBean,这里 仅仅是引用那个JavaBean对象而已。页面中调用hello.getMessage()将得到在第一个页 面中设置的属性值,即"HelloJavaBean!"。如果在第二个页面中是重新实例化的 JavaBean对象,那么getMessage()的输出将是空值null。 <%@ page contentType="text/html; charset=GB18030" %> <% String message = hello.getMessage(); %>

<%=message%>

使用浏览器访问request_bean1.jsp,运行结果如图5-3所示。 图5-3 bean的条件化创建 2.bean属性的条件化设置 . 将 替换为
。 . 标签内部的语句仅当创建新的bean时才执行,如果找到已有的bean,则不执行。 request_bean3.jsp 中使用 动作元素创建了一个HelloBean 类的 JavaBean对象,范围为request,bean的名字为hello,属性message的值设置为字符串"好 消息!"。页面又使用 动作元素包含了另外一个JSP 页面request_ bean4.jsp。 第5 章 JSP 中使用JavaBean 1 21 <%@ page contentType="text/html; charset=GB18030" %> Bean 的条件化创建 <% hello.setMessage("好消息!"); %> 在被包含的JSP页面request_bean4.jsp中,也有一个 动作元素,id为 hello,class为cn.edu.uibe.model.HelloBean,scope为request。在 之间,使用hello.setMessage()重新设置属性的值为"坏消息!"。请读者 思考:这行语句会执行吗? 输出是“好消息!”还是“坏消息!”? <%@ page contentType="text/html; charset=GB18030" %> <% hello.setMessage("坏消息!"); %>

<%=hello.getMessage()%>

使用浏览器访问request_bean3.jsp,运行结果如图5-4所示。 图5-4 bean属性的条件化设置 5.2.3 JavaBean 存放的位置 为了在JSP页面中使用JavaBean,应用服务器需要使用字节码文件(扩展名为.class) 来创建Java对象,这就要求应用服务器能找到字节码,因此字节码文件需要位于特定的 目录中。 1.零散存放 Java的每个类都对应一个.class文件,零散存放是指将这些.class文件存放在Web 应用的/WEB-INF/classes目录下包名对应的子目录中。例如,在JSP页面中有: 1 22 JSP 程序设计(第2 版) 这个bean的名字是hello,类是cn.edu.uibe.model.HelloBean,生成的字节码文件是 HelloBean.class,它存放的位置是: /WEB-INF/classes/cn/edu/uibe/model/HelloBean.class 2.打包存放 Java允许将多个.class文件打包成一个扩展名为.jar的压缩文件,实际上采用的压缩 格式就是ZIP格式。多个JavaBean可以打包成一个.jar文件,然后将打包后的.jar文件 存放在Web应用的/WEB-INF/lib目录下。 Eclipse提供了将.class文件打包的工具:依次单击Eclipse→File→Export→Java→ JARFile,导出时选择要导出哪些package并给出生成的jar文件的文件名。在压缩文件 内部,扩展名为.class的文件存放在包名对应的子目录中。 例如将cn.edu.uibe.model.HelloBean的字节码文件HelloBean.class打包在文件名 是hello.jar的压缩文件中,那么hello.jar压缩文件内部的目录结构是cn/edu/uibe/ model/HelloBean.class,而文件hello.jar存放的位置是Web应用的/WEB-INF/lib目录。 5.2.4 JavaBean 的作用范围 JSP中使用JavaBean实际上是将JavaBean对象作为一个属性(Attribute)绑定到了 pageContext对象、request对象、session对象或者application对象上。scope对应的取值 分别为page、request、session和application,JavaBean的作用范围分别对应一个页面、一 次请求、一个用户的会话和一个Web应用。 1.page范围 . 指定为page范围的JavaBean仅在定义的JSP页面内有效。 . page范围的JavaBean实际上是绑定在pageContext对象上的一个属性,可以通 过pageContext.getAttribute("beanName")来得到这个JavaBean对象。 . 如果没有指定JavaBean的范围,默认范围是page。 2.request范围 . 指定为request范围的JavaBean在客户端的一次请求期间有效。 .request范围的JavaBean实际上是绑定在request对象上的一个属性,可以通过 request.getAttribute("beanName")来得到这个JavaBean对象。 .request范围的JavaBean可以在客户端的一次请求期间共享和传递数据。 一次请求会经过多个处理环节的情况有: . 当使用 时,包含或者转向的页面与当前页面对应一 个request对象。 . 当使用RequestDispatcher分发请求时,请求会转发到其他页面。 . 当使用过滤器Filter时,请求对象request会被过滤器截获。 . 当使用请求相关的监听器时,请求对象request可在监听器内部获得。 3.session范围 . 指定为session范围的JavaBean在用户的一个会话期间有效。一个会话对应一 第5 章 JSP 中使用JavaBean 1 23 段时间内客户端的多次HTTP请求。 .session范围的JavaBean实际上是绑定在session对象上的一个属性,可以通过 session.getAttribute("beanName")来得到这个JavaBean对象。 .session范围的JavaBean可以在同一个客户端的多次请求期间共享和传递数据。 session范围的JavaBean一般用于: . 用户登录后,在session上绑定JavaBean来保存用户信息。 .session上绑定存储商品列表的JavaBean来实现购物车。 4.application范围 . 指定为application范围的JavaBean在Web应用停止之前一直有效。 .application范围的JavaBean实际上是绑定在application对象上的一个属性,程 序可以通过application.getAttribute("beanName")来得到这个JavaBean对象。 .application范围的JavaBean的作用域是所在的Web应用。通过application范围 的JavaBean,可以在不同客户端的不同次请求间共享和传递数据。 5.3 获取JavaBean的属性 在JSP页面中可以使用 动作元素获取并输出JavaBean的属性,也 允许使用表达式语言EL获取并输出JavaBean的属性。 5.3.1 动作元素用来访问JavaBean的属性。访问的属性值将被转化成 字符串,然后发送到输出流中。如果属性是一个对象,将调用该对象的toString()方法。 动作元素是通过调用JavaBean的Getter方法获取属性值的。 或者: 特别需要注意的是, 使用name属性给出JavaBean的名字,而 使用id属性给出JavaBean的名字,实际上它们是一致的,都是指绑定在 特定范围对象的一个属性(Attribute)名。 1.读取属性 getproperty.jsp 示例getproperty.jsp文件内容如下,运行结果如图5-5所示。 <%@ page contentType="text/html; charset=GB18030" %> 获取并输出属性的值 <% hello.setMessage("Hello JavaBean Again!"); %> 1 24 JSP 程序设计(第2 版)

图5-5 使用读取JavaBean的属性 2.深刻理解:getproperty2.jsp JSP中使用读取JavaBean的属性时,实际是通过调用Getter方 法完成的,而不管JavaBean中是否真的有与属性名对应的成员变量的定义。下面示例 中,JSP内读取message属性时,实际上是调用getMessage()方法,即使message属性不 存在,也能通过调用getMessage()方法得到一个字符串,运行结果如图5-6所示。 package cn.edu.uibe.model; public class MyBean { public String getMessage(){ return "重大利好!"; } } 图5-6 是调用getMessage()方法,而不是直接访问属性 getproperty2.jsp中实例化一个MyBean类的对象,并使用 读取属 性message。而在MyBean类的定义中,是没有名字为message的成员变量的,但仍然能 调用getMessage()方法返回结果。 <%@ page contentType="text/html; charset=GB18030" %> 深刻理解jsp:getProperty

5.3.2 使用EL 获取JavaBean 属性 在JSP2.0及之后的版本中,可以使用表达式语言EL获取并输出JavaBean的属性, 它与 动作元素的功能相同,但语法更加简洁。EL 实际上也是调用