Você está na página 1de 23

In this section, you will learn about:

* The JavaBeans architecture


* The Beans Event Model
* Introspection to query Beans about their contents
* Bean component creation
* Customization of Beans
* Persistence to store and retrieve Beans
* The development of applications comprised of Bean
components
* The creation of simple builder application
* The BDK Bean Box application
---------------------------------------------------

Introduction to JavaBeans

JavaBeans takes Java's "Write Once, Run Anywhere" capability and


extends it to include "reuse everywhere". "JavaBeans is a
portable, platform-independent component model, written in
Java." With it, you create small, reusable, software components.
A visual "builder" program combines components from disparate
sources to create applications quickly and easily.

What's a Bean?

A Bean is a JavaBeans component. Beans are independent, reusable


software modules. Beans may be visible objects, like AWT
components, or invisible objects, like queues and stacks. A
builder/integration tool manipulates Beans to create applets and
applications.
---------------------------------------------------

Beans Architecture

Beans consist of three things:

* Events
* Properties
* Methods

Also, since Beans rely on their state, they need to be able to


be persistent over time.
•Events

Events are for notifying others when something is happening. The


delegation-event model of AWT, introduced with Java 1.1,
demonstrates the Beans event model. It has three parts:

* EventObjects
AWTEvent in the AWT world.
* EventListeners
ActionListener, ItemListener, ...
* Event Sources (Beans)
The different AWT Components.

Anyone can register an EventListener with a Component, provided


the Component understands the event set. (For instance, you
cannot register an ActionListener with a TextArea, but you can
with a TextField). When something happens within the Component,
it notifies any listener(s) by sending each an EventObject,
through the appropriate method of the listener.

•Properties

Properties define the characteristics of the Bean. For instance,


when examining an AWT TextField for its properties, you will see
properties for the caret position, current text, and the echo
character, among others. In the simplest case, a method or
methods in the following design pattern defines a property:

public void setPropertyName(PropertyType value);


public PropertyType getPropertyName();

Where PropertyName is the name of the property, and PropertyType


is its datatype. If only one method is present, the property is
read-only (set missing) or write-only (get missing).

•Methods

Bean methods are available for anyone to call by just making


each public. However, you can restrict which methods are visible
to the Bean builder/integration tool by providing a
getMethodDescriptors method along with your Bean's BeanInfo.
Every Bean can provide a supporting BeanInfo class to customize
a Bean's appearance to an integration tool. More on BeanInfo
later.
----------------------------------------------------
Persistence

Persistence is the ability of an object to store its state, for


recreation later. Beans use Java's object serialization
capabilities for persistence. The ObjectInput and ObjectOutput
interfaces are the basis for serialization within Java. The
ObjectInputStream and ObjectOutputStream classes, respectively,
implement the interfaces. Serialization saves all non-static and
non-transient instance variables of an object. If one of those
is a reference to another object, that object is recursively
saved, too, until everything saved is primitive datatypes. If
one object is referred to by multiple references, Java
serializes it only once. This ensures the creation of an
identical object network when reading the primary object back.
To demonstrate, use a fictitious TreeNode class to create the
network:

TreeNode top = new TreeNode("top");


top.addChild(new TreeNode("left child"));
top.addChild(new TreeNode("right child"));

and then to save:

FileOutputStream fOut =
new FileOutputStream("test.out");
ObjectOutput out = new ObjectOutputStream(fOut);
out.writeObject(top);
out.flush();
out.close();

recrNow, your program can exit, knowing that when it restarts it


can eate the tree from the test.out file.

FileInputStream fIn = new FileInputStream("test.out");


ObjectInputStream in = new ObjectInputStream(fIn);
TreeNode n = (TreeNode)in.readObject();

•Technology Comparison to ActiveX/COM

* JavaBeans is a framework for building applications out of


Java components (Beans).
* ActiveX is a framework for building compound documents
with ActiveX controls.
* A Bean is very similar to an ActiveX control. However, a
Bean is written in Java, so has Java's security and cross
platform nature.
* ActiveX controls written in Java is possible, however they
require a port of Microsoft's Common Object Model (COM) to be
used outside of Microsoft Windows.
* More frequently, programmers create ActiveX controls in
Visual Basic or Visual C++ and compile them into native
libraries.

Recently, JavaWorld contained two articles comparing the


technologies. The first article is a strategic analysis of the
two. The second article is a head-to-head comparison.

JavaBeans Benefit Analysis


Write Once, Run Anywhere java.beans package part of Core API
Component Reusability Reuse Everywhere -- across
platforms/tools/solutions
Example: 3D Charting Bean - drop into any container, regardless
of platform tool
Interoperability Communicate with other component
architectures
Beans-ActiveX Bridge now beta, Beans-OpenDoc under development
----------------------------------------------------

Writing Bean Components

To create a Bean, you need to determine what it should do and


then define the events, properties, and methods to get it there.
Actually, most of the method definitions fall out of the
definition of events and properties for the Bean.
----------------------------------------------------

Events

An event allows your Beans to communicate when something


interesting happens. There are three parts to this communication:

* EventObject
* EventListener - (the sink)
* An Event Source (the Bean)

•EventObject

The java.util.EventObject class is the basis of all Beans events.

public class java.util.EventObject


extends Object implements java.io.Serializable {
public java.util.EventObject (Object source);
public Object getSource();
public String toString();
}

Although you can create EventObject instances directly for your


Bean events, design pattern guidelines require you to subclass
EventObject so you have a specific event type. For example, to
define an event for an employee's hire date, you could create a
HireEvent class.

public class HireEvent extends EventObject {


private long hireDate;
public HireEvent (Object source) {
super (source);
hireDate = System.currentTimeMillis();
}
public HireEvent (Object source, long hired) {
super (source);
hireDate = hired;
}
public long getHireDate () {
return hireDate;
}
}

•EventListener

The EventListener interface is empty, but serves as a tagging


interface that all event listeners must extend; that way, Beans
integration tools can work. A listener is something that desires
notification when an event happens. The listener receives the
specific EventObject subclass as a parameter. The name of the
new listener interface is EventTypeListener. For the new
HireEvent, the listener name would be HireListener. The actual
method names within the interface have no specific design
pattern to follow, but, should describe the event that is
happening.

public interface HireListener


extends java.util.EventListener {
public abstract void hired (HireEvent e);
}

•Event Source
Without an event source, the HireEvent and HireListener are
virtually useless. The event source defines when and where an
event will happen. Classes register themselves as interested in
the event, and they receive notification when the event happens.
A series of methods patterns represents the registration process:

public synchronized void addListenerType(ListenerType l);


public synchronized void removeListenerType(
ListenerType l);

The event source needs to maintain the list itself, so the


entire code to do this is:

private Vector hireListeners = new Vector();


public synchronized void addHireListener (
HireListener l) {
hireListeners.addElement (l);
}
public synchronized void removeHireListener (HireListener l) {
hireListeners.removeElement (l);
}

If you are using AWT components, AWT events already have this
behavior. Maintaining listeners is only necessary for new event
types, or adding listeners where they previously were not
(ActionEvent within a Canvas).

If you want to permit only one listener (unicast), you have the
addListenerType method throw the
java.util.TooManyListenersException exception when adding a
second listener (and you do not need a Vector). Also, remember
to synchronize the add/remove methods to avoid a race condition.

Once the event happens, it is necessary for the event source to


notify all the listeners. For the hiring example, this would
translate into a method like the following:

protected void notifyHired () {


Vector l;
// Create Event
HireEvent h = new HireEvent (this);
// Copy listener vector so it won't
// change while firing
synchronized (this) {
l = (Vector)hireListeners.clone();
}
for (int i=0;i<l.size();i++) {
HireListener hl = (HireListener)l.elementAt (i);
hl.hired(h);
}
}

You have to call the method directly when the triggering event
happens.
----------------------------------------------------------------
-------

Properties

A property is a public attribute of the Bean, usually


represented by a non-public instance variable. It can be read-
write, read-only, or write-only. There are four different types
of properties:

* Simple
* Indexed
* Bound
* Constrained

•Simple Properties

As the name implies, simple properties represent the simplest of


the four. To create a property, define a pair of set/get
routines. Whatever name used in the pair of routines, becomes
the property name (no matter what instance variable name you
use). Normally, the instance variable name matches the property
name. However, there is nothing that requires this. For
instance, to define a property salary for an Employee Bean:

float salary;
public void setSalary (float newSalary) {
salary = newSalary;
}
public float getSalary () {
return salary;
}

If you need a read-only property, only define a getPropertyName


routine. If you want a write-only property, only define a
setPropertyName routine. Boolean properties can change their get
routine to an isPropertyName routine:

boolean trained;
public void setTrained (boolean trained) {
this.trained = trained;
}
public boolean isTrained () {
return trained;
}

•Indexed Properties

An indexed property is for when a single property can hold an


array of values. The design pattern for these properties is:

public void setPropertyName (PropertyType[] list)


public void setPropertyName (
PropertyType element, int position)
public PropertyType[] getPropertyName ()
public PropertyType getPropertyName (int position)

For instance, if you were to complete the item indexed property


for the AWT List component, it might look something like this:

public class ListBean extends List {


public String[] getItem () {
return getItems ();
}
public synchronized void setItem (String item[]) {
removeAll();
for (int i=0;i<item.length;i++)
addItem (item[i]);
}
public void setItem (String item, int position) {
replaceItem (item, position)
}
}

The String getItem (int position) routine already exists for


List.

The AWT List class is still a Bean without these methods.


However, item is not fully defined to be an indexed property.

•Bound Properties

Revisit the Employee Bean, and make salary a bound property.


That way, if two people were using an employee Bean (say a
manager and a spouse), and one (the manager) changes an
employee's salary, the other (the spouse) would like to know
about said change. In order for the notification to happen, you
need to maintain a watch list for PropertyChangeEvents via the
PropertyChangeSupport class. First, you have to create a list of
listeners to maintain:

private PropertyChangeSupport changes =


new PropertyChangeSupport (this);

And then, you have to maintain the list:

public void addPropertyChangeListener (


PropertyChangeListener p) {
changes.addPropertyChangeListener (p);
}
public void removePropertyChangeListener (
PropertyChangeListener p) {
changes.removePropertyChangeListener (p);
}

Finally, when the change happens (setSalary in this case), you


check to see if the property value changed, and if so, notify
all the listeners.

public void setSalary (float salary) {


Float oldSalary = new Float (this.salary);
this.salary = salary;
changes.firePropertyChange (
"salary", oldSalary, new Float (this.salary));
}

On the receiving end, you then need a propertyChange method.

public void propertyChange(PropertyChangeEvent e);

Since Java reports PropertyChangeEvents at the class (Bean)


level (versus the property level), you need to check the
property name via getPropertyName to see if you received a
PropertyChangeEvent you were expecting, or something else. Also,
if the property datatype is primitive, you need to wrap it into
the appropriate object when firing.

•Constrained Properties
Constrained properties are similar to bound properties. In
addition to maintaining a list of PropertyChangeListeners, the
Bean maintains a list of VetoableChangeListeners. Then, prior to
the Bean changing a property value, it asks the
VetoableChangeListeners if its okay. If it isn't, the listener
throws a PropertyVetoException, which you declare the set
routine to throw.

Changing salary to be a constrained property (so a spouse can


veto salary decreases), results in the following additions:

private VetoableChangeSupport vetoes =


new VetoableChangeSupport (this);
public void addVetoableChangeListener (
VetoableChangeListener v) {
vetoes.addVetoableChangeListener (v);
}
public void removeVetoableChangeListener (
VetoableChangeListener v) {
vetoes.removeVetoableChangeListener (v);
}

And changes:

public void setSalary (float salary)


throws PropertyVetoException {
Float oldSalary = new Float (this.salary);
vetoes.fireVetoableChange (
"salary", oldSalary, new Float (salary));
this.salary = salary;
changes.firePropertyChange (
"salary", oldSalary, new Float (this.salary));
}

On the receiving end, your VetoableChangeListener needs a


vetoableChange method.

public void vetoableChange(PropertyChangeEvent e)


throws PropertyVetoException;

Instead of providing one listener for all property change


events, and another for all vetoable change events, you can
elect to maintain separate lists of listeners for each property.
The design pattern for this is:
public void addPropertyNameListener (
PropertyChangeListener p);
public void removePropertyNameListener (
PropertyChangeListener p);

and

public void addPropertyNameListener (


VetoableChangeListener v);
public void removePropertyNameListener (
VetoableChangeListener v);
----------------------------------------------------

Methods

Methods are operations for others to interact with a Bean.


Basically, anything can call any public method of a Bean. Beans
receive notification of events by having the appropriate method
called on them by the event source.

Besides all the methods required for each property and event of
a Bean, you usually create support methods which accept no
arguments or an argument of an event you listen for. That way, a
builder application can inspect a class quickly for a list of
the appropriate methods it can connect to. Non-public support
methods may also be available. However, public methods with
other parameter patterns are usually the result of a bad design
pattern, and not easily connectable from a builder application.
For instance, if you always pass along a time-stamp with an
event, you should probably subclass the event, add a time-stamp
attribute, and pass the subclass around. This results in a
simplified design and easier maintainability.

•Intro to Customization
Customization of Beans allows you as the Beans developer to
control what a Bean-integrator will see when they use a builder
tool. By default, a builder tool uses reflection to determine
what to display when designing programs with Beans. In most
cases, this is sufficient. However, there are times when you
want to provide different functionality. For instance, if you
want to provide your own customization interface, instead of
using the default property sheet, you can implement the
Customizer interface and provide a custom Panel. The OurButton
Bean in the Beans Development Kit (BDK) provides a Customizer.

On the other hand, if you want to provide an alternative input


mechanism for one property (like an enumerated list of valid
choices), you can extend the PropertyEditorSupport class. The
Molecule Bean in the BDK uses a property editor to provide a
list of molecules it can display.

Using either of these mechanisms requires your Bean to provide a


supporting class, along with the Bean itself. This second class
implements the BeanInfo interface.

•BeanInfo

The BeanInfo interface allows you to describe your Bean in


greater detail then reflection alone can do. You usually want to
do this to provide your Bean integrator with less options, or
more restrictive options, so the Bean is easier to work with. In
order for the system to locate the BeanInfo for a Bean, you must
name it after the Bean, with BeanInfo at the end of the Bean's
classname. For instance, if your Bean was called Foo, the
BeanInfo for the Foo Bean would be called FooBeanInfo. It isn't
necessary to implement the entire BeanInfo yourself. The
SimpleBeanInfo class provides a basis, then you just selectively
override methods.

Design-time vs. Run-time Beans "mode"

Beans must be able to operate in a running application as well


as inside a builder. At design-time, Beans must provide the
design information necessary to edit properties and customize
behavior. Also it has to expose methods and events so a builder
tool can create code that interacts with the Bean at run-time.
The Beans.isDesignTime method allows you to check for the
current mode.

•Introspection

Introspection is the process of determining the supported


properties, methods, and events of a Bean. It can be done with
the help of the Introspector class, or directly through the use
of the Reflection API. The Introspector provides access to the
BeanInfo for the Bean component via its getBeanInfo method,
which requires an instance of Class as its parameter:

TextField tf = new TextField ();


BeanInfo bi =
Introspector.getBeanInfo (tf.getClass());
If you don't provide BeanInfo for a Bean, the Reflection API is
used to determine the different pieces for you.

•Events

The getEventSetDescriptors method reports all the events that


this Bean fires. For every pair of
add/removeListenerTypeListener methods (that return void), an
event set is defined for the Bean.

EventSetDescriptor[] esd =
bi.getEventSetDescriptors();
for (int i=0;i<esd.length;i++)
System.out.print (esd[i].getName() + " ");
System.out.println ();

For a TextField, this would print:

text mouse key component action focus mouseMotion


----------------------------------------------------

Properties

The getPropertyDescriptors method reports all the properties of


a Bean. A property is defined by one or more routines with the
following pattern:

public void setPropertyName(PropertyType value);


public PropertyType getPropertyName();
public boolean isPropertyName();

PropertyDescriptor pd[] =
bi.getPropertyDescriptors();
for (int i=0;i<pd.length;i++)
System.out.print (pd[i].getName() + " ");
System.out.println ();

For a TextField, this would print:

selectionStart enabled text preferredSize


foreground visible background selectedText
echoCharacter font columns echoChar name
caretPosition selectionEnd minimumSize editable
----------------------------------------------------
Methods

The getMethodDescriptors method reports all the methods of a


Bean. This is an all-inclusive list of public methods, which
enable you to call any one of them without knowing their name
before hand. For each method descriptor, you can discover the
parameter types through the getParameterDescriptors method.

MethodDescriptor md[] = bi.getMethodDescriptors();


for (int i=0;i<md.length;i++)
System.out.print (md[i].getName() + " ");
System.out.println ();

For a TextField, this would print the names of all 155 methods
available, most of which are inherited from Component. This
includes the add/remove event listener methods, as they are
methods like the others.

•BeanInfo

Although the examples have been using the Introspector so far,


when a custom BeanInfo isn't provided for a Bean, the
Introspector uses the Reflection API to determine the events,
properties, and methods of the Bean. If you want to restrict a
builder tool to display less choices, you can override the
default behavior and implement your own BeanInfo. The BeanInfo
interface is not implemented by the Bean itself, but by a
secondary class. If we've created a SizedTextField Bean, the
SizedTextFieldBeanInfo class would provide the Bean's BeanInfo.
If you only wanted to display one property, length, you could
define that class as such:

import java.beans.*;
public class SizedTextFieldBeanInfo
extends SimpleBeanInfo {
private final static Class beanClass =
SizedTextField.class;
public PropertyDescriptor[]
getPropertyDescriptors() {
try {
PropertyDescriptor length =
new PropertyDescriptor("length", beanClass);
PropertyDescriptor rv[] = {length};
return rv;
} catch (IntrospectionException e) {
throw new Error(e.toString());
}
}
}

Then, instead of the 17 properties of TextField, the only


property displayed by the tool, would be the new length
property. The SizedTextField class still needs to implement the
set/getLength methods. Also, creating a custom BeanInfo does not
prevent someone from calling the methods of the now hidden
properties. The property accessor methods are still public. So,
by using the Reflection API (or just knowing the method names),
you can still invoke all the public methods.

Also, if you wanted to have the Bean have a space delimited


name, instead of being all crunched together, you could add the
following to the SizedTextFieldBeanInfo definition:

public BeanDescriptor getBeanDescriptor() {


BeanDescriptor bd = new BeanDescriptor(beanClass);
bd.setDisplayName("Sized Text Field");
return bd;
}

And, to wrap things up, you could even supply an icon for your
Bean with the help of an image file and the following added to
your BeanInfo definition:

public Image getIcon (int iconKind) {


if (iconKind == BeanInfo.ICON_COLOR_16x16) {
Image img = loadImage("sized.gif");
return img;
}
return null;
}
----------------------------------------------------

Customizers

Using Customizers allow YOU, the Bean builder, to control how


your Bean user is going to configure visually your Bean at
design time. Instead of relying on the builder tool's property
sheet to setup the properties of a Bean, you can provide either
a full-screen customization option or an alternative to the
datatype's default property sheet option.
•Writing Customizers

If the Employee Bean only had a salary property, you could


create a customizer like the following. It would only permit
numeric characters in the input field. Customizers are much more
powerful than this implies. However, they result in rather
lengthy examples and this should provide a sufficient framework
to get you started.

package employee;

import java.awt.*;
import java.awt.event.*;
import java.beans.*;

public class EmployeeCustomizer extends Panel


implements Customizer, KeyListener {
private Employee target;
private TextField salaryField;
private PropertyChangeSupport support =
new PropertyChangeSupport(this);
public void setObject(Object obj) {
target = (Employee) obj;
Label t1 = new Label("Salary :");
add(t1);
salaryField = new TextField(
String.valueOf(target.getSalary()), 20);
add(salaryField);
salaryField.addKeyListener(this);
}
public Dimension getPreferredSize() {
return new Dimension(225,50);
}
public void keyPressed(KeyEvent e) {}
public void keyTyped(KeyEvent e) {}
public void keyReleased(KeyEvent e) {
Object source = e.getSource();
if (source==salaryField) {
String txt = salaryField.getText();
try {
target.setSalary(
(new Float(txt)).floatValue());
} catch (NumberFormatException ex) {
salaryField.setText(
String.valueOf(target.getSalary()));
}
support.firePropertyChange("", null, null);
}
}
public void addPropertyChangeListener(
PropertyChangeListener l) {
support.addPropertyChangeListener(l);
}
public void removePropertyChangeListener(
PropertyChangeListener l) {
support.removePropertyChangeListener(l);
}
}

All of this work results in the following screen being displayed


when the developer chooses to customize your Bean.

This also assumes that the following EmployeeBeanInfo class is


defined:

package employee;
import java.beans.*;
public class EmployeeBeanInfo extends SimpleBeanInfo {
public BeanDescriptor getBeanDescriptor() {
return new BeanDescriptor(
beanClass, customizerClass);
}
private final static Class beanClass =
Employee.class;
private final static Class customizerClass =
EmployeeCustomizer.class;
}

•Writing Property Customizers

If you think providing a custom screen for all the properties is


not necessary, but want to have some control over what is
displayed in the property sheet for a specific property,
implementing PropertyEditor may be in store for you. You can
implement the class yourself or use the framework in the
PropertyEditorSupport class as your basis.

If you add a position property to the Employee, you can provide


a list of positions by creating a EmployeePositionEditor class
(that subclasses PropertyEditorSupport).
import java.beans.*;
public class EmployeePositionEditor
extends PropertyEditorSupport {
public String[] getTags() {
String values[] = {
"President",
"Technical Support",
"Window Washer",
"Janitor"};
return values;
}
}

When using a tag list with a property, the property must be


initialized to one of the tags. If you forget, when a Bean user
goes to use the property editor, a mysterious
IllegalArgumentException will be thrown.

Then you add a few things to the EmployeeBeanInfo:

public PropertyDescriptor[] getPropertyDescriptors() {


try {
PropertyDescriptor pd =
new PropertyDescriptor("position", beanClass);
pd.setPropertyEditorClass(positionEditorClass);
PropertyDescriptor result[] = { pd };
return result;
} catch (Exception e) {
System.err.println("Unexpected exception: " + e);
return null;
}
}

And, the property sheet for the Employee Bean might look like
this:

If there are multiple PropertyDescriptors, the order in the


array determines the display order. If you do not call
setPropertyEditorClass for a particular PropertyDescriptor, the
PropertyEditorManager locates a suitable/default PropertyEditor.

•Using System Property Editors

The JDK comes with a handful of suitable property editors


available for your use. You should expect similar editors
available with other tools:

Datatype Editor
Boolean BoolEditor
Byte ByteEditor
Color ColorEditor
Double DoubleEditor
Float FloatEditor
Font FontEditor
Int IntEditor
Long LongEditor
Number NumberEditor
Short ShortEditor
String StringEditor

All of these are part of the sun.beans.editors package. You do


not have to do anything special to use them. The BDK's BeanBox
tool is smart enough to know that if you have a float or Float
property, to use the FloatEditor, similarly for the other
datatypes. If you are not using the BeanBox, you can try to use
them in your own environment.
----------------------------------------------------

Persistence

Persistence is the ability of an object to store its state.


Beans use Java's object Serialization API to provide a great
medium-weight solution for persistence. In the simplest case,
the way to enable the serialization of any object is to
implement the Serializable interface. The Serializable interface
has no methods that must be implemented; however, any data
fields of a class that implements this interface must also be
serializable.

•Bean Serialization

A Serializable object is serialized by calling the


ObjectOutput.writeObject method, with the object as its
parameter. To deserialize, the ObjectInput.readObject method is
called. You, as a Bean creator, do NOT call these methods. What
you do need to do is ensure your Bean is serializable.

1. Is your class Serializable?


2. Are all instance variables Serializable?
3. Do you want everything that isn't transient or static to
be saved?
4. Should you serialize the object in its current structure?
5. How do I initialize transient and static variables upon
deserialization?
6. Do you want to add a validator to the serialization
process?

If you're Serializable and want the default behavior for


everything else, you're set and don't have to do anything extra.
However, if not, you have something to do.

The first is easy, does your class implement the Serializable


interface (directly or indirectly)? If it doesn't, make it.
(Note, there is also an Externalizable interface for when you
need to control all aspect of Serialization, usually for
security purposes.)

For the second, check to see if all each instance variable is


Serializable. Primitive datatypes are, by default. The rest you
have to check manually, until you learn which aren't. (Image
isn't). If one isn't, you need to figure out what to do about
it. (For instance, image files can be kept separate and re-read
when a Bean is deserialized.) Do you initialize it to a default
value, make it transient, or provide some other means to
initialize?

The third is easy. Is it okay to save everything?

Fourth, is it more efficient to save the data in a different


format? For instance, it is more efficient to serialize a
Hashtable's pieces, and recreate the structure, then to save the
mostly empty hashtable. Also, should you encrypt the information?

Fifth, is it okay to use default values for non-persistent


variables? If not, you have to add code to handle this.

Finally, do you want to validate the deserialized object


network? Like, is your graph consistent? If yes, you need to
register a validation method.

For instance, if you wanted to have a Date instance variable of


a class, but wanted it to be the 'last-created' date vs. a
'first-created' date, you could have a transient variable that
is reset when deserialized:

public class TreeNode implements Serializable {


Vector children;
TreeNode parent;
String name;
transient Date date;
public TreeNode(String s) {
children = new Vector(5);
name = s;
initClass();
}
private void initClass () {
date = new Date();
}
...
private void writeObject(ObjectOutputStream s)
throws IOException {
s.defaultWriteObject();
}
private void readObject(ObjectInputStream s)
throws ClassNotFoundException, IOException {
s.defaultReadObject();
initClass();
}
}

Although the writeObject does the default functionality,


whenever a readObject method is provided, a writeObject method
is necessary. [complete source]

•Bean Reconstitution

Normally, you call new when you want to create an instance of a


class. With Beans, you can still do that, however, there is
another way. The Beans.instantiate method is normally used by
builder tools to recreate a Bean from a serialized Bean source.
The reason being, tool vendors do not want to restrict what
Beans you could use to only those it knew about when the tool
was created. For instance, to create a TextField, without new,
just call:

Component c =
(Component)Beans.instantiate(
null, "java.awt.TextField");

By itself, this is identical to Component c = new TextField();


However, before you get too excited, build on this a bit. First,
create your own TextField class, by subclassing TextField.

public class MyTextField extends java.awt.TextField {


}

Nothing fancy yet. But, create a little program to save a


serialized MyTextField, with a couple of properties set:

import java.awt.*;
import java.io.*;

public class TfSaver {


public static void main(String args[]) {
MyTextField mtf = new MyTextField();

// set the properties


Font ft = new Font("Serif", Font.ITALIC, 36);
mtf.setFont(ft);
mtf.setText("Hello World");

// serialize
try {
FileOutputStream f = new FileOutputStream(
"MyTextField.ser");
ObjectOutputStream s =
new ObjectOutputStream(f);
s.writeObject(mtf);
s.flush();
} catch (Exception e) {
System.out.println(e);
}
System.exit(0);
}
}

Now, if you create an application that uses a MyTextField


instance, it can start with the font and text properties already
set (to 36-point italic Serif font and "Hello World" text).

Component c =
(Component)Beans.instantiate(null, "MyTextField");

If a filename of the form Classname.ser exists (MyTextField.ser


in this case), then the class is deserialized. Otherwise, the
Bean will be created by using the default no-arg constructor of
the class.

With MyTextField.ser
Without MyTextField.ser

•Bean Versioning

Changes made to a Bean that change the persistence structure


flag the new version as incompatible with the old, and prevents
reconstitution. While this sounds good in theory, in practice it
has its shortcomings. Adding any new method to a Bean makes the
new class definition incompatible (for deserialization),
resulting in a java.io.InvalidClassException being thrown.
However, since a method does not add (or remove) any state
information (behavior is not serialized), the two classes should
be compatible. In order to ensure the new version can be
reconstituted from the original, you need to add a Stream Unique
Identifier (SUID) variable to your class definition.

private static final long serialVersionUID =


-2966288784432217853L;

Then, when the versions are truly incompatible, you change the
SUID variable. When this does happen, you should keep track of
all the old values so you can properly handle the differences
(e.g., default setting for new instance variable). If you forget
to save the old setting, it is necessary to revert back to an
old version, regenerating the value, and moving forward again.

The values are generated from a hash algorithm, not randomly


picked out of the air nor even a one-up numbering scheme. Since
static variables are not serialized, the value is regenerated
during deserialization and compared against the current class
definition. The program to generate the values is serialver and
has one argument of the classname. If you run the program with
the -show argument instead, serialver displays a graphical
interface, which allows you to cut and paste the results into
your editor.

Você também pode gostar