JavaBean 究竟是什么?

2346

我认为“Bean”是一个具有属性和Getter/Setter的Java类。据我所知,它相当于C语言中的结构体。这是真的吗?

此外,JavaBean与普通类之间是否存在真正的语法区别?
是否有特殊的定义或接口?

基本上,为什么会有这个术语?

另外,“Serializable”接口是什么意思?


25
查看Java Beans的使用场景?,Java Beans是遵循特定约定的类。 - Matthew Flaschen
12
为了完整起见,这里提供了一个链接,指向JavaBeans规范。 - informatik01
7
请注意,如果你听到有人使用 POJO 这个术语,他们通常实际上是指 Bean。当你看到 POJO 时,它们几乎总是具有设置器和获取器、可序列化等特性... 实际上,POJO 不需要设置器和获取器、可序列化接口或任何其他东西--它只是一个没有特定要求的普通Java对象。 - Bill K
1
绝对不是一个结构体,但我很高兴你问了这个问题。 - Don Rolling
23个回答

2677
一个JavaBean只是一个标准。它是一个普通的Java class,但它遵循某些约定:
  1. 所有属性都是私有的(使用getter/setter
  2. 公共的无参构造函数
  3. 实现Serializable
这就是它。这只是一个约定。许多库都依赖于它。
关于Serializable,来自API文档

类的可序列化由类实现java.io.Serializable接口启用。没有实现此接口的类将不会序列化或反序列化其任何状态。可序列化类的所有子类型本身都是可序列化的。序列化接口没有方法或字段,仅用于标识可序列化的语义。

换句话说,可序列化对象可以写入流,因此可以写入文件、对象数据库等任何地方。
另外,JavaBean和其他类之间没有语法区别——如果一个类遵循标准,那么它就是JavaBean。

这个术语是有的,因为标准允许库以预定义的方式编程处理您定义的类实例。例如,如果一个库想要流式传输您传递给它的任何对象,它知道可以这样做,因为您的对象是可序列化的(假设该库要求您的对象是正确的JavaBeans)。


307
在我看来,关于豆类的几乎所有文件都不能像你这样简明地描述这个术语。赞一个。+1 - AndaP
18
一个bean的成员是否也必须是bean?这似乎是一个合理的要求。 - worldsayshi
28
不需要。例如,一个bean可以包含一个字符串;但是字符串不是一个bean。(字符串是不可变的,因此不能通过调用空构造函数和setter来创建它。)一个可序列化的对象应该有可序列化的成员,除非它在外部进行了序列化。因此,Java bean成员不需要具备任何Java bean方面的特征。虽然如果它们也是bean会更加简单。 - Viliam Búr
19
“所有属性都是私有的”是不正确的。属性是从getter和setter方法推断出来的(如果有一个名为getXxx()的方法,则该bean具有一个可读属性称为“xxx”;如果有一个名为setXxx(Xxx xxx)的方法,则该bean具有一个可写属性称为“xxx”)。属性可以由成员字段支持(但不一定要这样做),这些字段通常是私有的。 - Puce
3
为了成为一个 Java bean,一个类必须是公共的(public)。而且它是否需要实现 Serializable 接口呢? - Satyabrata sahoo
显示剩余5条评论

383

这个术语让它听起来很特别,但实际上并没有那么神秘。

基本上,一个“Bean”:

  • 是一个可序列化的对象(也就是说,它实现了java.io.Serializable接口,并且正确地执行了该操作),它具有
  • 一些“属性”,其getter和setter只是带有某些名称(例如,getFoo()是“Foo”属性的getter)的方法,以及
  • 具有公共零参数构造函数(因此可以随意创建并通过设置其属性进行配置)。

至于Serializable:那只是一个“标记接口”(一个不声明任何函数的接口),告诉Java实现类同意(并暗示其能力)进行“序列化”--将一个实例转换为字节流。 这些字节可以存储在文件中,通过网络连接发送等,并且具有足够的信息以允许JVM(至少是知道对象类型的JVM)稍后重构该对象--可能在应用程序的另一个实例甚至整个其他计算机上!

当然,为了做到这一点,该类必须遵守某些限制。 其中最重要的是,所有实例字段必须是原始类型(int,bool等),某些也可序列化的类的实例,或标记为transient以使Java不尝试包括它们。 (这当然意味着transient字段无法通过流进行旅行。具有transient字段的类应准备根据需要重新初始化它们。)

不能遵守这些限制的类不应实现Serializable(并且,如果我没记错的话,Java编译器甚至不会允许它这样做。)


1
这可能是一个愚蠢的问题,但除了原始类型或类的实例之外,实例字段还可以是什么? - kingfrito_5005
9
“@kingfrito_5005: 要么是这个,要么是那个。但如果它是一个类的实例,那么这个类是否可序列化就很重要。为了使一个类可序列化,它的非transient部分必须是可序列化类型。” - cHao
可能忘了提到构造函数不应该有参数。它有一个公共默认构造函数(因此可以随意创建并通过设置其属性进行配置)。 - Amos Kosgei
@AmosKosgei:没有忘记;只是多余了。按定义,一个默认构造函数可以不带参数调用。 - cHao
3
当我查看它时,似乎在Java中,“default constructor”与C ++中的含义略有不同。 : P 将“default”替换为“0-arg”。 - cHao
这个解释很好。我查了5个网站才明白'transient'是什么意思,而这个解释让我一下子就明白了。 - Nathan

118

JavaBeans是符合极其简单的编码规范的Java类。

  1. 实现 java.io.Serializable 接口 - 用于保存对象的状态
  2. 使用一个无参的公共构造函数 - 用于实例化对象
  3. 提供公共的getter/setter方法 - 用于获取和设置私有变量(属性)的值。

73

JavaBean的属性

JavaBean是一个满足特定编程约定的Java对象:

  1. JavaBean类必须实现SerializableExternalizable接口

  2. JavaBean类必须有一个无参构造函数

  3. 所有JavaBean属性必须具有公共的setter和getter方法

  4. 所有JavaBean实例变量应该是私有的

JavaBeans的示例

@Entity
public class Employee implements Serializable{

   @Id
   private int id;
   private String name;   
   private int salary;  

   public Employee() {}

   public Employee(String name, int salary) {
      this.name = name;
      this.salary = salary;
   }
   public int getId() {
      return id;
   }
   public void setId( int id ) {
      this.id = id;
   }
   public String getName() {
      return name;
   }
   public void setName( String name ) {
      this.name = name;
   }
   public int getSalary() {
      return salary;
   }
   public void setSalary( int salary ) {
      this.salary = salary;
   }
}

4
注释是否必要或者是 Java Bean 的一部分? - giannis christofakis
9
不需要注解。注解是Spring框架的一部分,该框架广泛使用Java Beans。 - Tianxiang Xiong
1
为什么需要一个无参构造函数? - Renato
8
@Renato 这很简单。想象一下必须自动实例化您的带参数构造函数的bean的Spring ... 它将传递什么参数作为参数?;) - Alex75

29

举个例子来解释。

1. 导入java.io.Serializable

关于序列化,可以查看文档

2. 私有字段

字段应该是私有的,以防止外部类轻易地修改这些字段。通常使用getter/setter方法来访问这些字段。

3. 构造函数

一个没有任何参数的公共构造函数。

4. getter/setter

用于访问和修改私有字段的getter和setter方法。

/** 1. import java.io.Serializable */
public class User implements java.io.Serializable {
    /** 2. private fields */
    private int id;
    private String name;

    /** 3. Constructor */
    public User() {
    }
    public User(int id, String name) {
        this.id = id;
        this.name = name;
    }

    /** 4. getter/setter */
    // getter
    public int getId() {
        return id;
    }
    public String getName() {
        return name;
    }
    // setter
    public void setId(int id) {
        this.id = id;
    }
    public void setName(String name) {
        this.name = name;
    }
}

3
针对 setId(int id) 方法的方法体,我猜你想说的是 this.id = id; 而不是 this.id = is; - steven7mwesigwa

23

Java Beans 被用来实现少代码多工作的方法...

Java Beans 在 Java EE 中被广泛地用作运行时发现和访问的通用契约。例如,JavaServer Pages (JSP) 使用 Java Beans 作为页面或 servlet 与 JSP 之间数据传输对象的一种方式。Java EE 的 JavaBeans Activation Framework 利用 Java Beans 将对 MIME 数据类型的支持集成到 Java EE 中。Java EE 管理 API 则将 JavaBeans 作为在 Java EE 环境中管理资源仪表化的基础。

关于序列化:

在对象序列化中,一个对象可以被表示为一个字节序列,包括了对象的数据以及关于对象类型和存储在对象中的数据类型的信息。

当一个序列化对象已经被写入文件后,它可以从文件中读取并进行反序列化,即使用类型信息和代表对象及其数据的字节来重新创建对象。


21

对于Bean概念的一些背景和更新说明。其他答案通常涵盖了Bean的"是什么",但缺乏足够的"为什么"。

早期Java中的GUI构建过程中,Bean被发明出来。它们遵循易于工具拆分的模式,可以创建属性面板,以便您可以编辑Bean的属性。总的来说,Bean属性代表屏幕上的控件(例如x、y、width、height、text等)。

您还可以将其视为强类型数据结构。

随着时间的推移,这些Bean变得对许多使用相同类型访问的工具非常有用(例如,Hibernate将数据结构持久化到数据库中)。

随着工具的演变,它们更趋向于注释,而不是分离setter/getter名称。现在大多数系统不需要Beans,它们可以使用带有注释属性的任何普通的Java对象告诉它们如何操作。

现在我将Bean视为带注释的属性球——它们实际上只有携带注释时才有用。

本身,Java Beans 并不是一种健康的模式,因为它们的本质破坏了封装性,由于将所有属性暴露给外部操作,使用时往往会倾向于创建用于外部操纵 bean 的代码而非在 bean 内部创建代码(违反了“不要问一个对象的值,而是要求对象为你做某事”的原则)。使用带注释的 POJOs(简单 Java 对象)并最小化 getter 函数和不使用 setter 函数更符合面向对象的设计思想,恢复了封装性并提供了不可变性。

顺便说一下,在所有这些事情发生时,有人将这个概念扩展到被称为企业级 JavaBeans 的东西。这些是……不同的。它们足够复杂,以至于许多人觉得他们不理解整个 Bean 概念,因此停止使用该术语。我认为这就是为什么通常听到 beans 被称为 POJOs(由于每个 Java 对象都是 POJO,因此从技术上讲这是可以接受的,但当你听到有人说 POJO 时,他们大多数时候在思考遵循 bean 模式的某些东西)。


没错 - 违反了“不要询问一个对象的值,而是要请求对象为你做某事”的原则。 - ARK

20

在将您的项目部署到多个服务器时,您会发现序列化非常有用,因为bean将被持久化并在它们之间传输。


3
请提供有关在多个服务器上部署项目的更多信息。谢谢。 - Hanfeng
4
如果你有几台服务器组成一个集群,在Websphere中部署EAR文件,可以参考这个链接:http://stackoverflow.com/questions/3193345/how-to-deploye-ear-file-in-websphre-clustered-environment - Truong Ha

15

为了将Java类用作Java bean,其方法名称需要符合JavaBeans指南(也称为设计模式),包括属性方法事件。该类需要是公共类,以便任何beanbox工具或容器都可以访问。容器必须能够实例化它;由于类是公共的,即使没有提供显式的、公共的零参数构造函数,容器也应该能够这样做。(具有默认公共零参数构造函数的Java公共类没有显式构造函数。)因此,最少需要一个Java公共类,即使只有一个属性作为唯一成员(当然,伴随着必要的公共getter和setter)或者一个公共方法作为唯一成员,也是一个Java bean。该属性可以是只读属性(它具有getter方法但不具有setter)或只写属性(仅具有setter方法)。一个具有公共事件监听器注册方法作为唯一成员的Java公共类也是一个Java bean。JavaBeans规范并不要求如果这样的Java类有一个显式的公共构造函数,它应该是一个零参数的构造函数。如果可以提供一个包含序列化实例的文件(例如扩展名为.ser的文件),则beanbox工具可能可以使用该文件来实例化原型bean。否则,该类将需要一个公共且零参数的构造函数,可以是显式的或默认的构造函数。
一旦Bean被实例化,JavaBeans API(java.beans.*)可以内省它并调用其方法。如果没有实现BeanInfo接口或扩展BeanInfo实现的类可用,例如SimpleBeanInfo类,则内省将使用反射(隐式内省)来研究目标bean支持的方法,然后应用简单的设计模式(指南)来推断出这些方法支持哪些属性、事件和公共方法。如果有实现BeanInfo接口的类(对于名为Foo的bean,它必须命名为FooBeanInfo)可用,则API将绕过隐式内省并使用此类的公共方法(getPropertyDescriptor()、getMethodDescriptors()、getEventSetDescriptors())来获取信息。如果有扩展SimpleBeanInfo的类可用,根据覆盖了哪些SimpleBeanInfo公共方法(getPropertyDescriptor()、getMethodDescriptors()、getEventSetDescriptors()),它将使用这些覆盖的方法来获取信息;对于未覆盖的方法,它将默认使用相应的隐式内省。无论如何,都需要实例化Bean,即使没有对其进行隐式内省。因此,需要公共的零参数构造函数。但是,当然,不需要实现Serializable或Externalizable接口来被识别。但是,JavaBeans规范说:“我们还希望对于一个只想保存其内部状态而不想考虑它的微小Bean的常见情况,它也应该是‘微不足道’的。”因此,所有Bean都必须实现Serializable或Externalizable接口。
总体而言,JavaBeans规范并不严格要求何为bean。"编写JavaBeans组件非常容易。你不需要特殊的工具,也无需实现任何接口。编写bean只是遵循某些编码约定的问题。你所要做的就是让你的类像一个bean一样 - 使用bean的工具将能够识别和使用你的bean。" 简单地说,即使以下类也是一个Java bean, public class Trivial implements java.io.Serializable {} 到目前为止的描述是Java SE版本(JavaBeans)。下面所述的beans是Java EE版本。这些版本基于上面解释的基本思想构建而成。特别地,它们考虑的一个主要思想是,如果一个bean构造函数有一些参数怎么办。这些参数可以是简单类型、类/接口类型或两者都有。应该有一种方式让容器知道可以替代参数的值,当实例化bean时。程序员可以通过注解、XML配置文件或两者混合来进行配置(指定值)。
Spring Beans Spring beans运行在Spring IoC容器中。程序员可以通过XML配置文件、注解或两者混合来进行配置。
在Spring中,如果一个bean的构造函数有简单类型或类/接口类型参数,则可以以类型安全的方式将值分配为字符串(在前一种情况下作为构造函数参数元素的属性,在后一种情况下作为构造函数参数的元素)。通过构造函数参数元素中的元素引用其他Spring bean(称为协作者)基本上是依赖注入,也是类型安全的。显然,依赖项(协作者bean)可能具有具有注入参数的构造函数;这些注入的依赖项可能具有带参数的构造函数,依此类推。这种情况最终应该在容器可以通过构造函数实例化的原型bean的注入依赖项处终止。
JSF托管bean

JSF管理的bean在Web容器中运行。它们可以通过@ManagedBean注释或应用程序配置资源文件managed-bean.xml进行配置。JSF规范仅支持通过资源注入(不是类型安全的)进行注入。此注入不适合构造函数注入。无论如何,规范要求JSF管理的bean必须具有公共的零参数构造函数。此外,规范指出:“从本规范的2.3版开始,强烈不建议使用本节中指定的托管bean工具。解决同样问题的更好、更具凝聚力的集成方案是使用上下文和依赖注入(CDI),如JSR-365中所指定。”换句话说,应该使用CDI托管的bean,其提供类似于Spring bean的构造函数类型安全依赖注入。CDI规范采用了托管bean规范,该规范适用于JEE平台的所有容器,而不仅仅是Web层。因此,Web容器需要实现CDI规范。

托管Bean

这是托管Bean规范的一部分摘录:“托管Bean是最小要求的容器管理对象,也被称为“POJOs”(普通旧Java对象)……它们可以被看作是在Java SE平台上找到的JavaBeans组件模型的Java EE平台增强版本……读者不会错过托管Bean在JavaServer Faces(JSF)技术中发现的同名设施的前身……在本规范中定义的托管Bean代表了JSF中发现的托管Bean的泛化;特别是,托管Bean可以在Java EE应用程序的任何地方使用,而不仅仅是在Web模块中。例如,在基本组件模型中,托管Bean必须提供一个无参数构造函数,但是建立在托管Bean之上的规范(如CDI(JSR-299))可以放松该要求,并允许托管Bean提供具有更复杂签名的构造函数,只要它们遵循一些明确定义的规则……托管Bean不能是:final类、抽象类或非静态内部类。与常规JavaBean组件不同,托管Bean可能不可序列化。”因此,托管Bean的规范,也被称为POJOs或POJO Bean,允许像CDI这样的扩展。 CDI规范重新定义托管Bean为:在Java EE中运行时,顶级Java类满足以下要求,则为托管Bean:

• 它不是内部类。 • 它是一个非抽象类,或者被标注为@Decorator。 • 它没有实现javax.enterprise.inject.spi.Extension。 • 它没有被标注为@Vetoed,或者在被标注为@Vetoed的包中。 • 它有一个合适的构造函数,要么:这个类有一个没有参数的构造函数,要么这个类声明了一个被标注为@Inject的构造函数。

所有符合这些条件的Java类都是托管Bean,因此不需要特殊声明来定义托管Bean。或者

如果它被任何其他Java EE规范定义为托管Bean,并且如果

• 它没有被EJB组件定义注释标注或在ejb-jar.xml中声明为EJB bean类。

Bean构造函数可以具有简单类型参数,因为简单类型可以使用@Inject注释进行注入。

EJBs

EJB运行在EJB容器中。EJB规范指出:“会话Bean组件是托管Bean。”“对于会话Bean和消息驱动Bean,它说:‘该类必须有一个不带参数的公共构造函数。’ 此外,它说:“会话Bean类不需要实现SessionBean接口或Serializable接口。”由于JSF Bean相同的原因,即EJB3依赖注入基本上是资源注入,JSF Bean不支持带有参数的构造函数,即通过依赖注入。然而,如果EJB容器实现了CDI,“可选地:该类可以具有使用@Inject注释的附加构造函数,”它对于会话Bean和消息驱动Bean都有所说明,因为“打包到CDI bean存档中且未注释javax.enterprise.inject.Vetoed注释的EJB被视为启用CDI的bean。”

1
长篇的文字堆砌在一起,是否可以将它们分成小节?或许也可以添加小标题。 - Peter Mortensen
这是什么意思? - Farid
嗨@Farid,正如Peter Mortensen在评论中指出的那样,缺乏子分区可能会影响一些可读性/理解能力。否则,在发布我的答案之前,我已经阅读了这里的大多数答案并尝试写出更好的答案。您现在可以阅读它,并很高兴听到您的具体意见。 - rps
3
这个回答值得更多点赞。它不仅澄清了一些关于JavaBeans的误解(例如,公共零参数构造函数只是惯例而非规范允许的唯一选项),还将JavaBeans与许多其他常见的bean进行比较,以提供更广泛的背景。总结得很好。 - wlnirvana

15

JavaBeans是一种标准,其基本语法要求已经被其他答案清晰地解释过了。

然而,在我看来,它不仅仅是一个简单的语法标准。JavaBeans的真正含义或旨在使用,与该标准周围的各种工具支持一起,是为了促进代码重用和组件化软件工程,即使开发人员能够通过组装现有组件(类)来构建应用程序,而无需编写任何代码(或只需要编写少量粘合代码)。不幸的是,这项技术在工业界被严重低估和低效利用,这可以从本文中的回答中得知。

如果您阅读Oracle的JavaBeans教程,您就可以更好地了解它。


有用的帖子和链接。当我想到“beans”时,确实会想到“Visual Builder”类型的东西,就像Oracle文章中所示。我想知道是否还有许多其他框架在很大程度上使用它们... - mike rodent
你可以举一个例子来说明“促进代码重用”的含义,而不是使用像“促进代码重用和基于组件的软件工程,即使开发人员能够通过组装现有组件(类)构建应用程序而无需编写任何代码(或只需编写少量粘合代码)”这样的术语。 - Farid
虽然这是一个正确的历史注释,但它对于如何在实践中使用Java Beans几乎没有影响。这不是因为"技术被低估了",而是因为它曾经尝试过并失败了。它只是旧的"无编码编程"梦想的另一种伪装,每隔几年就会以不同的名称重新包装。我相信当时它被称为"RAD",现在被称为"低代码",在此期间它被称为"MDA"。它从来没有奏效。 - Michael Borgwardt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接