Have you ever read the JavaBean (TM) specification? It really is from another era!
The JavaBean specification
The JavaBean specification, version 1.01, was the last specification that defined the approach to the common bean pattern used in many Java applications. Most developers have never read it, but if you want a history lesson, its a good read.
The spec dates from 1997, JDK 1.1 and Sun Microsystems, a scary 17 years ago. Serialization and Reflection are new technologies, and CORBA is a big deal.
The introduction starts with:
The goal of the JavaBeans APIs is to define a software component model for Java, so that third party ISVs can create and ship Java components that can be composed together into applications by end users.
It goes on to describe the key goal of being able to interact with the OpenDoc, COM/DCOM/ActiveX and LiveConnect object models, so JavaBeans can be embedded inside Word documents and Excel spreadsheets (via a "transparant bridge"). An early example is the desire to support "menubar merging", combining the menu from a JavaBean with that of the host application.
So, what is a JavaBean?:
A Java Bean is a reusable software component that can be manipulated visually in a builder tool.
The explanation goes on to consider builder tools as web page builders, visual application builders,
GUI layout builders, or document editors (like a Word document).
Beans may have a GUI, or be invisible, however GUI is clearly the main focus.
Note that GUI beans are required to subclass java.awt.Component
.
The unifying features of a bean are:
- Introspection - so tools can analyse a bean
- Customization - so tools can alter a bean
- Events - AWT style event handing
- Methods - the set of methods the bean declares as being callable
- Properties - the set of get/set properties
- Persistence - long term storage, primarily by serialization
Beans are expected to run in a container, and that container is expected to provide certain behaviour around persistence and gluing beans together. Notably, a connection from one bean to another can either be composition (where the bean is expected to serialize the composed bean) or a "pointer to an external bean", where it is expected that the container will recreate it.
The multi-threading section is very simplistic:
It is the responsibility of each java bean developer to make sure that their bean behaves properly under multi-threaded access. For simple beans this can generally be handled by simply making all the methods “synchronized”.
Another interesting section indicates a future plan to add a "view" layer, where multiple beans can be composed
together into a view. To enable this, two methods Beans.instantiate()
and
Beans.getInstanceOf()
are supposed to be used. Casts and instanceof
checks
are forbidden.
The most classic part of the spec is the scenarios section:
Buying the tools.
1.a) The user buys and installs the WombatBuilder Java application builder.
1.b) The user buys some JavaBeans components from Al’s Discount Component Store.
They get a win32 floppy containing the beans in a JAR file. This includes a
“Button” component and a “DatabaseViewer” component.
1.c) The user inserts the floppy into the machine and uses the WombatBuilder...
Floppy disks! Well this is 1997...
The section does highlight that bean customization is expected to make available a mechanism of serializing
beans as source code, via the PropertyEditor.getJavaInitializationString()
method.
Standard Java object serialization is a mandatory technique, while the source code route is optional.
Event handling is familiar enough, using java.util.EventListener
and
java.util.EventObject
(think mouse listeners for example).
Beans can define their own event hierarchies and publish the events that the bean defines.
The listeners are based on a naming convention:
The standard design pattern for EventListener registration is:
public void add<ListenerType>(<ListenerType> listener);
public void remove<ListenerType>(<ListenerType> listener);
And of course these methods are expected to access a Vector
to store the listeners.
A special case is that of limiting the number of listeners using java.util.TooManyListenersException
,
not something I've ever seen.
Oh, and you are supposed to declare and throw checked exceptions in listeners, not unchecked.
Section 7 covers properties, described as "discrete, named attributes of a JavaBean". They are defined by a naming convention we all know well:
void setFoo(PropertyType value); // simple setter
PropertyType getFoo(); // simple getter
There is also direct support for indexed properties, but only on arrays:
void setter(int index, PropertyType value); // indexed setter
PropertyType getter(int index); // indexed getter
void setter(PropertyType values[]); // array setter
PropertyType[] getter(); // array getter
Note that the spec is slightly unclear here, using "setter" and "getter" as a placeholder for a "setFoo" type name. All getters and setters are allowed to throw checked exceptions.
Bound and constrained properties get a mention, along with PropertyChangeListener
,
PropertyVetoException
and VetoableChangeListener
.
Vetoing gets complicated when there are multiple event listeners and some may have been notified
of a success before the veto occurs.
The spec has a suprising amount of detail on these two concepts, including a variety of ways of registering
listeners.
The introspection section finally talks in detail about using reflection ad design patterns to find
getters and setters, plus the ability to use a more specific BeanInfo
:
By default we will use a low level reflection mechanism to study the methods supported by a target bean and then apply simple design patterns to deduce from those methods what properties, events, and public methods are supported. However, if a bean implementor chooses to provide a BeanInfo class describing their bean then this BeanInfo class will be used to programmatically discover the beans behaviour.
...within Java Beans the use of method and type names that match design patterns is entirely optional. If a programmer is prepared to explicitly specify their properties, methods, and events using the BeanInfo interface then they can call their methods and types whatever they like. ... Although use of the standard naming patterns is optional, we strongly recommend their use as standard naming conventions are an extremely valuable documentation technique
Bean properties, getters and setters, are matched based on the "get" and "set" prefix, and they may be defined in a superclass. Read-only and write-only properties are permitted. Boolean properties may use the "is" prefix, which may be used instead of, or in addition to, the normal "get" prefixed method.
Bean events are similarly matched by "addFooListener" and "removeFooListener" pattern matching. Both must be present for an event to be recognised.
Bean methods are simply defined as all public methods of the class. The list of methods includes getters, setters and event methods.
The BeanInfo
class is used to selectively replace parts of the reflected information.
Thus, the properties could be defined, but the events obtained by reflection.
The java.beans.Introspector
class is used to obtain the combined view, including the
BeanInfo
and reflected data.
It turns out that there are specific rules around capitalization in property names:
Thus when we extract a property or event name from the middle of an existing Java name, we
normally convert the first character to lower case. However to support the occasional use of all
upper-case names, we check if the first two characters of the name are both upper case and if
so leave it alone. So for example,
“FooBah” becomes “fooBah”
“Z” becomes “z”
“URL” becomes “URL”
When customizing a bean, the default approach is to take the set of properties and expose an
editor. This would operate as a key-value table, but where each property can nominate a
java.beans.PropertyEditor
to provide a GUI for defining that property.
The classic example is a colour picker for a property of type Color
.
Again, any GUI is an AWT Component
.
And you can register an editor simply by naming it after the property being configured -
Foo
class is edited by FooEditor
in the same package or a set of search packages.
The official tutorial is also instructive.
The simple view is that a bean just needs to be serializable, have a no-args constructor and
have getters/setters matching a naming convention. While this is true, the actual spec
has a lot more in it, including many rules that most developers do not follow, such as the use
of Beans.instantiate()
and Beans.getInstanceOf()
.
Summary
This has been a whistlestop tour of the JavaBeans spec. What hits you between the eyes when you read it is a sense of how old it is. The world has very much moved on.
More significantly however, is that the "beans" that are so common in Java code, consisting of mutable classes with getters and setters are not what the authors of the spec had in mind. As such, we probably shouldn't really call them beans.
But perhaps it is time to write up a JavaBeans v2 spec to rethink the problem being solved in the absence of a properties language feature.
"Beans" as used nowadays are a horrible zombie pattern. People keep on wrapping fields in accessor methods because they think it provides a useful layer of abstraction and data security, but 99.9% of the time the methods are generated by an IDE and never changed after that.
ReplyDeleteIf value types are what you want, Scala's case classes are the way to go. Or, failing that, Octarine: http://github.com/poetix/octarine
Interesting writeup!
ReplyDeleteMy recollection is that at the time, a number of companies were pushing this idea of plugging components together - Microsoft being the big, dominant success with the VisualBasic component market, and proposing ActiveX as a standard way to distribute components, including in web pages. OpenDoc was a similar idea, from Apple. The original JavaBeans was in part a reaction to that whole movement - offering Java as a cross-platform pluggable component solution.
I think what then happened was that that model was repurposed in the applications space to have these "beans" represent reusable business logic - there were even companies, IIRC, who were offering pre-built EJB for accounting routines, for example. Unfortunately, this "ask, don't tell" approach ended up becoming the standard way to implement almost all domain objects in the years following.
Perhaps you’ll write up your thoughts on what a JB 2.0 might include?
Cheers
Patrick
Indeed, the whole JavaBeans reusable components premise has rather disappeared now. Stay tuned for more on JB 2.0!
DeleteA special thanks for this informative post. I definitely learned a few new things here.
ReplyDeleteTwo updates on this topic:
ReplyDelete• Java 9 provides for annotations to more conveniently define your JavaBean with BeanInfo details.
http://openjdk.java.net/jeps/256
• The Bean Validation standard is becoming a popular way to define business rules on an object’s values such as “Phone number is a required field and must not be null or empty string” or “Invoice total must be not be a negative number”.
http://beanvalidation.org