 |
The Unknown JavaBean
Posted by rbair on May 31, 2006 at 06:19 PM | Comments (20)
What is a JavaBean? What is the JavaBean design pattern? If I asked that question in a room of a hundred people, I suspect fewer than 10 would really get it right.
For the majority of Java developers, the JavaBean pattern is simply a naming convention for accessors/mutators (getters/setters) for a property. For example:
public class Supplier {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
This example highlights the predominate usage of a "JavaBean". There is a property called "name". By convention, the accessor method is "get[Name of Property]" and the mutator method is "set[Name of Property]". If the property happens to be a boolean, the accessor could also be named "is[Name of Property]". For example, "isEnabled()". The other thing that most people get right is that a JavaBean has a default constructor. Why JFreeChart's ChartPanel doesn't have a default constructor is beyond me, and actually the irritation that caused this blog. I just wanna put a chart into my Matisse form. Is that too much to ask?
In any case, accessors and mutators are all well and good, but this doesn't make our class a JavaBean. Well, not a real JavaBean. JavaBeans are intended to be the component model for Java. You know, the equivilent of all those fancy components you can use in the .NET world. The idea being that there would be some standard component model, and then a component market would sprout up so that developers just go into the marketplace, pay a few hundred dollars for the charting component library they need, and wire it all together in the IDE. This approach has worked for other development environments (Visual Basic being the predominant example, Delphi being another), but has never been quite the same for the Java market.
I'm sure there are reasons for that, and I could write a very long blog on the subject and stir, I'm sure, some controversy in the process, but that isn't the point here. First, let's all get on the same page as to what a JavaBean really is.
A JavaBean does much more than provide accessor/mutator methods for internal state that follows some naming convention. To be properly understood, you have to think of things in terms of components. Internal state is internal state and should never be exposed in the API. What is exposed is that state that can be fiddled with. You know, background color, name, address, preference factory... that sort of stuff. Think of the bean as a black box, reveal those switches and properties to the outside world that they should be aware of. Whatever happens inside the box, stays inside the box.
JavaBeans are also observable. When one of the public properties of the bean changes, notification has to be sent so that everybody knows that the bean has had a property change. In the JavaBeans world, these are PropertyChange events. Every real JavaBean allows you to register PropertyChangeListeners with the bean, and observe PropertyChangeEvents as they occur. This is a critical component to using a real component model -- and very handy for utility applications as well (such as databinding systems).
The standard way to support these property change events is to use PropertyChangeSupport, which is a class in the java.beans package. This is normally the kind of code you'd write to do this:
public class MyBean {
/**
* Helper class that manages all the property change notification machinery.
* PropertyChangeSupport can not be extended directly because it requires
* a bean in the constructor, and the "this" argument is not valid until
* after super construction. Hence, delegation instead of extension
*/
private PropertyChangeSupport pcs;
/** Creates a new instance of MyBean */
public MyBean() {
pcs = new PropertyChangeSupport(this);
}
/**
* This method shows what all the methods really do -- delegate to
* the PropertyChangeSupport. The other methods have been trimmed for
* space efficiency. This is just a blog, after all
*/
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcs.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {...}
public PropertyChangeListener[] getPropertyChangeListeners() {...}
public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {...}
public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {...}
public PropertyChangeListener[] getPropertyChangeListeners(String propertyName) {...}
public void firePropertyChange(String propertyName, Object oldValue, Object newValue) {...}
public void firePropertyChange(String propertyName, int oldValue, int newValue) {...}
public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) {...}
public void firePropertyChange(PropertyChangeEvent evt) {...}
public void fireIndexedPropertyChange(String propertyName, int index,
Object oldValue, Object newValue) {...}
public void fireIndexedPropertyChange(String propertyName, int index,
int oldValue, int newValue) {...}
public void fireIndexedPropertyChange(String propertyName, int index,
boolean oldValue, boolean newValue) {...}
public boolean hasListeners(String propertyName) {...}
}
Kind of a pain, huh? Fortunately, if you are writing a visual component, all this nonsense is handled for you in java.awt.Component. All you have to do is remember to fire the property change notification in your setter method. Like so:
public void setFoo(String newFoo) {
String old = getFoo();
this.foo = newFoo;
firePropertyChange("foo", old, getFoo());
}
Ok, that's a lot better. But JavaBeans and PropertyChange notification is very important for the nonvisual component as well as the visual component. Why do you want nonvisual components? .NET has a useful one: Timer. Its a simple component with a few properties like "delay", and a single event that is fired when the timer has expired. This means that in your GUI builder of choice, you simply pick the "Timer" component from the palette, add it to the form, change the properties in the property sheet, fill out an event handler (all through the GUI builder, mind you) and you're done. All you hand-gui-code-gurus out there are probably laughing right now, but I'm not talking to you. I'll try to convince you of the error of your hand-gui-coding ways some other time.
To ease development of non visual JavaBean components, there is a base class in the SwingX project that simplifies this problem called JavaBean. It makes it as trivial to fire event notification and write a true JavaBean as it is for a subclass of Component. The only hitch is, you have to extend JavaBean. That shouldn't be that big a deal. Take your most root objects (those that extend Object) and instead extend JavaBean. Done.
So what does this look like? Well, taking our original example:
public class Supplier extends JavaBean {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
String old = getName();
this.name = name;
firePropertyChange("name", old, getName());
}
}
Bam. Two lines of new code, and bingo. I've got a real JavaBean. And this class is really valuable for a lot more than just GUI builders. There are several really interesting frameworks that could use this class and others like it, such as databinding. I'll leave other framework examples as an exercise to the reader. Hint: check the slides for the Data Access Strategies talk from this past JavaOne.
There is a lot more that can go into writing a full fledged JavaBean, such as BeanInfo's, EventSetDescriptors, and the like. But this is enough for one blog. Now, back to trying to make ChartPanel a friggin' JavaBean...
Bookmark blog post: del.icio.us Digg DZone Furl Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment
-
Thanks Richard, great summary :)
I confess i'm guilty (mis)labelling everything a bean. Eg. in "Bean Curd 1" (with "Bean Curd 2: Native Query Beans" coming up next week) , I introduced a "model bean" which i used to call a "model object"
(in a 40 page newsletter article for former client) and one day i woke up and renamed everything a bean (in my aptframework project). Because it sounds cooler. A marketing gimmick. I justified it in my own mind because i use java.beans BeanInfo and PropertyDescriptor to get the accessor/mutator i need to invoke.
But I don't use PropertyChange so it's not really a bean in practice. Or in spirit, because as you say, the spirit is a "component programming model" for Java.
So i use the term "bean" interchangeably with "object." Which is wrong. "Objection! To what? Frankly to the tone of your beans, your honour!" ;)
Posted by: evanx on June 01, 2006 at 01:01 AM
-
Hi Richard,
Might be worth distinguishing the requirements for "property change events" and "all other events". In the JavaBeans spec (p. 29 and the bits just before and after), there is also a naming convention specified for "all other events" enabling tools to wire up listeners of the correct type with the bean:
The standard design pattern for EventListener registration is:
public void add( listener);
public void remove( listener);
The above straightforward naming convention (generally neglected by developers in my experience) is also worth noting while we're on the subject of tool-friendly bean conventions.
- Chris
Posted by: chris_e_brown on June 01, 2006 at 01:15 AM
-
I guess I need to amend my earlier posting, which the java.net blogging software has mangled...
The standard design pattern for EventListener registration is:
public void add<ListenerType<(<ListenerType> listener);
public void remove<ListenerType>(<ListenerType> listener);
Posted by: chris_e_brown on June 01, 2006 at 01:18 AM
-
Any way to do this with an annotation? I'd love to be able to slap @JavaBean on a class and be done with the boiler plate code.
Posted by: dwalend on June 01, 2006 at 05:13 AM
-
I believe the JavaBean spec also says they need to be Serializable. Your example isn't.
http://java.sun.com/j2se/1.4.2/docs/guide/beans/spec/beancontext.html#wp916944
Posted by: wcrosman on June 01, 2006 at 06:16 AM
-
For the annotation remark: check out the javabean aspect at
handyaspects.dev.java.net.
Never tried it, but it definitely looks interesting.
Posted by: fstaes on June 01, 2006 at 07:01 AM
-
wcrosman, chris_e_brown: Ya, I was thinking of doing a followup blog with the events, serialization (including the new XML serialization), BeanInfos and such. Thanks for pointing those things out!
Posted by: rbair on June 01, 2006 at 07:24 AM
-
May be wikipedia need update? http://en.wikipedia.org/wiki/Java_Beans
Posted by: carfield on June 01, 2006 at 07:51 AM
-
Any chance of getting that JavaBean class into java.beans? I was surprised that there wasn't such a class there and have created my own superclass too. Actually JavaBean should be an interface with a DefaultJavaBean implementation.
Slightly off-topic, but I've also been surprised about how many developers overlook the PropertyEditor stuff in java.beans and write their own. I think some people think JavaBeans are only for use in GUI builders.
Posted by: neilweber on June 01, 2006 at 08:53 AM
-
Seriously, the @JavaBean annotation comment is really a good one!
You'd simply mark a class as a @JavaBean with the properties marked as @JavaBeanProperty or something. Then, apt could generate the getters and setters, including the code for the firing of property change events, etc.
The advantage would be it would then make it available for classes which already extend another base class, avoiding the single inheritance limitation. Seems like a really good fit for an annotation.
Posted by: bobsledbob on June 01, 2006 at 03:36 PM
-
It seems like a language feature exposing properties properly would be better than annotations (annotations are a good second choice, I suppose, if you aren't using Aspects). It is a sticky issue once you get into the details -- maybe worth another blog.
Posted by: rbair on June 01, 2006 at 03:53 PM
-
The idea behind "getters" and "setters" (property accessors and mutators) is that in your "getXXX" or "setXXX" methods, you can perform checks (such as ensuring validity constraints aren't violated), and you can work with virtual properties (you might have private variables called width and height with respective getWidth() and getHeight() methods plus a read-only getArea() property returning a calculated value: width * height). If all you're doing is:
// for brevity, I've skipped event-related stuff...
private int foo;
public int getFoo() {
return this.foo;
}
public void setFoo(int foo) {
this.foo = foo;
}
...and all you want is this:
@JavaBeanProperty
private int foo;
// annotation will allow generation of getters/setters
...then that would be fair enough, but apart from trivial data objects, you often need to do a lot more in the getter/setter than simply change the value of a private variable. I suppose you could argue that the annotation could be made more powerful by allowing the expression of constraints (say a numeric range or regular expression) but that could quickly get messy with complex types, and in anycase, the superior solution is just to code it in Java in the getter/setter method body... Furthermore, this sort of stuff would also need to consider how to approch ACID/transactional properties, vetoed changes, and so on.
Richard, I imagine the language feature you've got in mind is along the lines of C#-style properties, such as:
private int toto;
public property toto
{
get()
{
return this.toto;
}
set(int toto)
{
// validation here for example
this.toto = toto;
}
}
That, for me, is a far superior solution (getters/setters of course would still be recognised as equivalent), especially as get/set/is can lead to contrived and clumsy method names, such isAreAllNodesReady()... and it gets really ugly when this English naming convention gets mixed with other languages.
- Chris
Posted by: chris_e_brown on June 02, 2006 at 01:07 AM
-
Maybe JavaBeans weren't a good idea in the sense that almost no one seems to care about them. As in, by evidence from the lack of JavaBeans and the several years since the creation of the spec.
Getters and setters are popular. And POJOs are popular. But JavaBeans maybe are just too complicated for any value that might come from them.
Posted by: tjpalmer on June 02, 2006 at 08:13 AM
-
I'm interested in hearing from anyone who might be using JavaBean-style events/listeners in web applications. Is it even possible to use events/listeners in servlets/jsps/Actions/etc.? If it is, is it thread-safe? If it is, are events/listeners useful to webapps, or only worth using in desktop apps? I never hear about them in webapps, but maybe component-based frameworks like JSF or Wicket or Tapestry are using events/listeners? Or do they use a different mechanism?
My guess is that if events/listeners have no real value in webapps then that may be the primary reason they've fallen out of use since the early days of Java when desktop apps were all supposed to be drag-and-drop wiring of JavaBeans but due to well-documented issues most developers moved to J2EE and webapp programming. The least common denominator rules, and the conventions were probably the only part of value to webapp frameworks. But if events/listeners have real value in webapps, then somebody needs to get out there and promote that (article, framework using them, etc.).
Could the JFreeChart guys be designing primariy for constructor-based dependency injection? This would account for lack of default constructor. It's hard to design for what you don't know about - I come across lots of "components" that could be more generally useful if some extra work was done to the API but are usually tied to a particular usage/interest of the author(s).
Posted by: gerryg on June 02, 2006 at 08:58 AM
-
Nice article
Just one note: The "JavaBean" link is broken - only refers to "http:/"
Posted by: ricon on June 02, 2006 at 11:29 AM
-
"It seems like a language feature exposing properties properly would be better than annotations"
Posted by: coxcu on June 02, 2006 at 11:54 AM
-
amen to c# properties. they've been added added to the ecmascript specification as well i think. implicit public mutator/accesor invocation removes so much crap from code, i bet you code bases would divide LOC count by half overnight.
Posted by: ilazarte on June 02, 2006 at 01:04 PM
-
Okay so all real beans should support:
* public void addPropertyChangeListener(PropertyChangeListener x);
* public void removePropertyChangeListener(PropertyChangeListener x);
Why isn't there an interface for these methods, called say PropertyChangeAnnouncer or ObservableBean? I'm not being entirely rhetorical.
I guess its way too late now but I feel like the JavaBeans spec should have required any observable bean to *explicitly* implement some interface. Then when I was writing something that consumed any observable bean (ala the old beanbox) I could take in a PropertyChangeAnnouncer argument and just call addPropertyChangeListener. Instead I have to take in an object and use introspection (Method.invoke) to check for and call addPropertyChangeListener. Yuck.
(On a related note, its disappointing that while C# has properties built into the language, properties are not automatically observable/bounded. :-/ )
Posted by: phowar on June 04, 2006 at 12:00 AM
-
I never liked the standard JavaBeans/AWT/Swing convention for names for methods related to event listener registration.
Instead of:
public void add[ListenerType]([ListenerType] listener);
Why not:
public void addListener([ListenerType] listener);
After all, parameter types are part of the method signature. Is someone able to explain me the reason for the ugly standard convention?
Posted by: flozano on July 04, 2006 at 12:18 PM
-
A PropertyChangeListener works perfect for JavaBeans in combination with Beans Binding (JSR-295). This gives an example: Bind a JTextField to a name property of a Person:
package com.tutego.binding;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
public class Person
{
private PropertyChangeSupport pcs = new PropertyChangeSupport( this );
private String name = "";
public void addPropertyChangeListener( PropertyChangeListener x )
{
pcs.addPropertyChangeListener( x );
}
public void removePropertyChangeListener( PropertyChangeListener x )
{
pcs.removePropertyChangeListener( x );
}
public String getName()
{
return name;
}
public void setName( String name )
{
String old = getName();
this.name = name;
pcs.firePropertyChange( "name", old, getName() );
System.out.println( "Changed name!" );
}
}
The code for the JavaBean Person is a litte bit cumbersome because of the PropertyChangeListener who will notify the Binding Framework if the model change.
package com.tutego.binding;
import javax.beans.binding.BindingContext;
import javax.swing.JFrame;
import javax.swing.JTextField;
public class BindingDemo
{
public static void main( String[] args )
{
Person p = new Person();
JTextField tf = new JTextField();
BindingContext bindingContext = new BindingContext();
bindingContext.addBinding( p, "${name}", tf, "text" );
bindingContext.bind();
JFrame f = new JFrame();
f.add( tf );
f.pack();
f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
f.setVisible( true );
p.setName( "Christian Ullenboom" );
}
}
Swing on the other side will although notify the model if the user type text in the text field and press Return. This will change the model.
Christian
Posted by: ullenboom on May 09, 2007 at 12:32 PM
|