JavaBeans的替代方案是什么?

11

我非常痛恨JavaBeans模式,就像一千个太阳般的激情在燃烧。为什么呢?

  • 过于冗长。现在是2009年了,我不应该再为属性写7行代码了。如果它们还有事件监听器,那就更加得小心谨慎。
  • 没有类型安全的引用。没有类型安全的方式来引用属性。Java的整个重点就是类型安全,而它最流行的模式却完全不具备类型安全性。

我希望的是类似这样的东西:

class Customer {
    public Property<String> name = new Property();
}

我主要是网页开发人员,因此需要JPA和Wicket支持。

帮我摆脱JavaBean的束缚!


“他们鼓励字符串引用”是什么意思? - cletus
我认为他的意思是很多Web框架和其他东西最终需要通过反射来引用它们。但我不太确定这与整个方法有什么关系,因为这似乎更像是框架的失败而不是其他什么。 - jsight
1
下了JavaBean的车?走这边,来到C#平台... - duffymo
既然你提到了Wicket,我想起了这个:http://wicketwebbeans.sourceforge.net/ 或许这能帮到你? - Esko
11个回答

10

我认为您的定义非常接近(如下所示)。但是,如果使用非JavaBeans方法,您可能会失去大多数工具提供的支持,这些工具假定JavaBeans协议正在生效。请注意,以下代码都是我临时想到的...

public class Property<T> {
    public final String name;
    T value;
    private final PropertyChangeSupport support;

    public static <T> Property<T> newInstance(String name, T value, 
                                              PropertyChangeSupport support) {
        return new Property<T>(name, value, support);
    }

    public static <T> Property<T> newInstance(String name, T value) {
        return newInstance(name, value, null);
    }

    public Property(String name, T value, PropertyChangeSupport support) {
        this.name = name;
        this.value = value;
        this.support = support;
    }

    public T getValue() { return value; }

    public void setValue(T value) {
        T old = this.value;
        this.value = value;
        if(support != null)
            support.firePropertyChange(name, old, this.value);
    }

    public String toString() { return value.toString(); }
}

然后继续使用它:

public class Customer {
    private final PropertyChangeSupport support = new PropertyChangeSupport();

    public final Property<String> name = Property.newInstance("name", "", support);
    public final Property<Integer> age = Property.newInstance("age", 0, support);

    ... declare add/remove listenener ...
}


Customer c = new Customer();
c.name.setValue("Hyrum");
c.age.setValue(49);
System.out.println("%s : %s", c.name, c.age);

因此,现在声明一个属性只需要一行代码,并且包括属性更改支持。我调用了setValue()和getValue()方法,这样它看起来仍然像是一个bean,对于类似Rhino的代码等,但为了简洁起见,您可以仅添加get()和set()。其余部分留给读者作为练习:
  • 正确处理序列化
  • 处理null值检查
  • 如果您关心自动装箱开销,则可以为原子类型添加专业知识。
  • ??我相信还有更多要注意的地方

还要注意,您可以子类化(通常作为匿名类),并覆盖setValue()以提供额外的参数检查。

我认为您真的无法摆脱"字符串引用",因为这基本上就是反射的全部内容。

不幸的是,在现今这个时代,这仍然有点像汇编语言编程...如果您有选择,Groovy、C#等可能仍然是更好的选择。


5

请查看我的Bean注释,网址为:

http://code.google.com/p/javadude/wiki/Annotations

基本上,您可以执行以下操作:

@Bean(
  properties={
    @Property(name="name"),
    @Property(name="phone", bound=true),
    @Property(name="friend", type=Person.class, kind=PropertyKind.LIST)
  }
)
public class Person extends PersonGen {}

相比于自己定义所有额外的get/set等方法,使用注解可以更加方便。

还有其他属性可以定义equals/hashCode、观察器、代理、混入等。

这是一组注解和注解处理器,在Eclipse或命令行构建(例如在ant中)中运行。处理器生成一个包含所有生成代码的超类(顺便说一下,注解处理器不能更改包含注解的类)。


3

您可能想了解一下Groovy - 这是一种动态类型的、基于JVM(完全兼容Java)的语言,具有“真正”的属性。


1

对于网络,我建议使用JSON(JavaScript对象表示法),

一种轻量级的数据交换格式

这里有一个关于JSON-Bean translator 的参考。


1
使用Spring框架。它的目的是通过抽象化许多你所抱怨的基础工作来简化Java开发。
你可以与Hibernate一起使用,这将为你简化与数据源的交互。
有用的网站:

www.springsource.org/download

www.hibernate.org/


1

当我第一次使用C#时,我喜欢属性,但是现在,在使用VS 2008一段时间后,我必须说我更喜欢set-/get-方法。

主要原因是我的个人工作方式。当我有一个新类并想知道我可以做什么时,我只需键入classname.set,Eclipse就会显示我可以更改的“属性”。同样适用于get。也许这只是VS的问题,但在那里,我必须滚动长列表的智能感知(其中所有内容都混合在一起,而不是首先显示属性),才能在编译后发现我想设置的属性是只读的...doh!

是的,在Java中,您需要很多行,但我只需编写我的属性并告诉IDE“请为我创建getter和setter”。但是这些方法往往会占用大量空间,这就是为什么我也希望在Java中有区域可以在IDE中折叠。


0

我也曾经寻找过同样的东西,但很惊讶地发现没有找到合适的库。因为我经常感到痛苦,所以两年前我开始了一个小项目来解决这些问题:Propertly。

它可以在以下网址找到: https://github.com/aditosoftware/propertly。 我们现在正在使用它来开发我们的产品。

它提供了泛型静态类型、不同级别的监听器、树形结构导航、动态模型等功能。其主要优点是模型的描述是静态完成的,因此信息始终可用,并且它是为可扩展性而设计的。因此,您可以使用自己的注释进行验证,或者定义数据的读取和存储位置。

使用示例:

一个简单的IPropertyPitProvider。属性包括名字、姓氏和年龄。

// Generics describe parent, self and children
public class StudentPropertyPitProvider 
    extends AbstractPPP<IPropertyPitProvider, StudentPropertyPitProvider, Object>
{
  // IPropertyDescription gives static access to an IProperty's meta data like name and type.
  public static final IPropertyDescription<StudentPropertyPitProvider, String> FIRST_NAME =
      PD.create(StudentPropertyPitProvider.class);

  public static final IPropertyDescription<StudentPropertyPitProvider, String> LAST_NAME =
      PD.create(StudentPropertyPitProvider.class);

  public static final IPropertyDescription<StudentPropertyPitProvider, Integer> AGE =
      PD.create(StudentPropertyPitProvider.class);


  // Getters and setters can of course still be used for easier access. 
  public String getFirstName()
  {
    // getValue and setValue is available at AbstractPPP. That class is used for easier access. 
    // Propertly can be used without inheriting from that class, too.
    return getValue(FIRST_NAME);
  }

  public void setFirstName(String pFirstName)
  {
    setValue(FIRST_NAME, pFirstName);
  }

  public String getLastName()
  {
    return getValue(LAST_NAME);
  }

  public void setLastName(String pLastName)
  {
    setValue(LAST_NAME, pLastName);
  }

  public Integer getAge()
  {
    return getValue(AGE);
  }

  public void setAge(Integer pAge)
  {
    setValue(AGE, pAge);
  }

}

使用定义的提供程序:

public class Sample
{

  public static void main(String[] args)
  {
    // Hierarchy is necessary to initialize the IPropertyPitProviders and for advanced features.
    Hierarchy<StudentPropertyPitProvider> hierarchy =
        new Hierarchy<>("student1", new StudentPropertyPitProvider());
    // The created student can be accessed from the hierarchy.
    StudentPropertyPitProvider student = hierarchy.getValue();
    // Listeners can be added.
    student.addPropertyEventListener(new PropertyPitEventAdapter()
    {
      @Override
      public void propertyChanged(IProperty pProperty, Object pOldValue, Object pNewValue)
      {
        System.out.println(pProperty.getName() + "=" + pNewValue);
      }
    });

    // The following calls will cause
    //  FIRST_NAME=Nils
    //  LAST_NAME=Holgersson
    //  AGE=32
    // to be printed on console through the listener.
    student.setFirstName("Nils");
    student.setLastName("Holgersson");
    student.setAge(32);
  }

}

0

你也可以构建一个代码生成器,从你编写的DSL中创建你的.java类。你可以有一些标记来描述你的类的名称、你想要的属性和它们的类型。然后,使用一个程序处理该文件,生成你的JavaBean。或者你可以使用注释,并使用类似ASM的东西对类文件进行后处理,注入访问器和修改器。我相信Spring也提供了一些这样的功能,但我没有使用过。


这绝对是可行的方法,但我认为它错过了帖子的重点:他认为JavaBeans太冗长和复杂。通过DSL或注释添加另一层并进行后处理难道不会使情况更糟吗? - Ian Varley
@ian: 使用DSL可能不是一个坏主意,因为使用DSL往往可以使事情简洁明了,这正是发帖人的目标。 - Chii
@ian:就短期而言,我同意这个观点,因为需要花费精力来创建后处理器。但是,一旦完成了这项工作,使用它的代码将按照所需的方式更简单。 - Arcane

0

尝试使用JBoss的SEAM框架,你应该会喜欢它。


0

我曾经尝试过这个:

interface IListenable {
    void addPropertyChangeListener( PropertyChangeListener listener );
    void removePropertyChangeListener( PropertyChangeListener listener );
}

abstract class MyBean extends IListenable {
    public abstract void setName(String name);
    public abstract String getName();

    // more things
}

public class JavaBeanFactory {

   public <T> Class<T> generate(Class<T> clazz) {
      // I used here CGLIB to generate dynamically a class that implements the methods:
      // getters
      // setters
      // addPropertyChangeListener
      // removePropertyChangeListener
   }
}

我用这个作为例子(仅供参考):

public class Foo {
    @Inject
    public Provider<MyBean> myBeanProvider;

    public MyBean createHook(MyBean a) {
        final MyBean b  = myBeanProvider.get();
        a.addPropertyChangeListener(new PropertyChangeListener() {
             public void propertyChange(PropertyChangeEvent evt) {
                 b.setName((String) evt.getNewValue());
             }
        });
        return b;
    }
}

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