epubjs
Version:
Render ePub documents in the browser, across many devices
211 lines (207 loc) • 27.7 kB
HTML
<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Handcoding with Beans</title><link rel="stylesheet" href="core.css" type="text/css"/><meta name="generator" content="DocBook XSL Stylesheets V1.74.0"/></head><body><div class="sect1" title="Handcoding with Beans"><div class="titlepage"><div><div><h1 class="title"><a id="learnjava3-CHP-22-SECT-10"/>Handcoding with Beans</h1></div></div></div><p>So far, we’ve seen how to create and use beans within a bean
application builder environment. That is the primary motivation for
JavaBeans, at least in GUI development. But beans are not limited to being
used by automated tools. There’s no reason we can’t use beans in
handwritten code. You could use a builder to assemble beans for the user
interface of your application and then load that serialized bean or a
collection of beans in your own code, just as NetBeans does when told to
use object serialization. We’ll give an example of that in a
moment.</p><div class="sect2" title="Bean Instantiation and Type Management"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-22-SECT-10.1"/>Bean Instantiation and Type Management</h2></div></div></div><p><a id="I_indexterm22_id823548" class="indexterm"/>Beans are an abstraction over simple Java classes. They
add, by convention, features that are not part of the Java language. To
enable certain additional capabilities of JavaBeans, we use special
tools that take the place of basic language operations. Specifically,
when working with beans, we are provided with replacements for three
basic Java operations: creating an object with <a id="I_indexterm22_id823563" class="indexterm"/><code class="literal">new</code>, checking the type
of an object with the <a id="I_indexterm22_id823576" class="indexterm"/><code class="literal">instanceof</code> operator,
and casting a type with a <code class="literal">cast</code>
expression. In place of these, use the corresponding static methods of
the <code class="literal">java.beans.Beans</code> class, shown in
<a class="xref" href="ch22s10.html#learnjava3-CHP-22-TABLE-1" title="Table 22-1. Methods of the java.beans.Beans class">Table 22-1</a>.</p><div class="table"><a id="learnjava3-CHP-22-TABLE-1"/><p class="title">Table 22-1. Methods of the java.beans.Beans class</p><div class="table-contents"><table summary="Methods of the java.beans.Beans class" style="border-collapse: collapse;border-top: 0.5pt solid ; border-bottom: 0.5pt solid ; "><colgroup><col/><col/></colgroup><thead><tr><th style="text-align: left"><p>Operator</p></th><th style="text-align: left"><p>Equivalent</p></th></tr></thead><tbody><tr><td style="text-align: left"><p> <code class="literal">New</code> </p></td><td style="text-align: left"><p> <code class="literal">Beans.instantiate(classloader, name)</code>
</p></td></tr><tr><td style="text-align: left"><p> <code class="literal">Instanceof</code> </p></td><td style="text-align: left"><p> <code class="literal">Beans.isInstanceOf(object, class)</code>
</p></td></tr></tbody></table></div></div><p><code class="literal">Beans.instantiate()</code> is the
<code class="literal">new</code> operation for beans. It takes a
class loader and the name of a bean class or serialized bean as
arguments. Its advantage over the plain <code class="literal">new</code> operator is that it can also load beans
from a serialized form. If you use <a id="I_indexterm22_id823708" class="indexterm"/><code class="literal">instantiate()</code>, you
don’t have to specify in advance whether you will provide the bean as a
class or as a serialized object. The <code class="literal">instantiate()</code> method first tries to load a
resource file based on the name bean, by turning package-style names
(with dots) into a path-style name with slashes and then appending the
suffix <span class="emphasis"><em>.ser</em></span>. For example, <code class="literal">magicbeans.NumericField</code> becomes
<span class="emphasis"><em>magicbeans/NumericField.ser</em></span>. If the serialized form
of the bean is not found, the
<code class="literal">instantiate()</code> method attempts to
create an instance of the class by name.<sup>[<a id="learnjava3-CHP-22-FN-3" href="#ftn.learnjava3-CHP-22-FN-3" class="footnote">47</a>]</sup></p><p><a id="I_indexterm22_id823770" class="indexterm"/> <code class="literal">Beans.isInstanceOf()</code>
and <a id="I_indexterm22_id823782" class="indexterm"/><code class="literal">Beans.getInstanceOf()</code>
do the jobs of checking a bean’s type and casting it to a new type.
These methods were intended to allow one or more beans to work together
to implement “virtual” or dynamic types. They are supposed to allow
beans to take control of this behavior, providing different “views” of
themselves. However, they currently don’t add any functionality and
aren’t widely used.</p></div><div class="sect2" title="Working with Serialized Beans"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-22-SECT-10.2"/>Working with Serialized Beans</h2></div></div></div><p><a id="idx11158" class="indexterm"/>Remember the <code class="literal">Juggler</code> we
serialized a while back? Well, it’s time to revive him, just like Han
Solo from his “Carbonite” tomb in <span class="emphasis"><em>Star Wars</em></span>. We’ll
assume that you saved the <code class="literal">Juggler</code> by
flipping on the Serialization property while working with the <code class="literal">LearnJava1</code> class and that NetBeans, therefore,
saved him in the file <span class="emphasis"><em>LearnJava1_juggler1.ser</em></span>. If
you didn’t do this, you can use the following snippet of code to
serialize the bean to a file of your choice:</p><a id="I_22_tt1237"/><pre class="programlisting"> <code class="c1">// Serialize a Juggler instance to a file...</code>
<code class="kn">import</code> <code class="nn">magicbeans.sunw.demo.juggler.Juggler</code><code class="o">;</code>
<code class="kn">import</code> <code class="nn">java.io.*</code><code class="o">;</code>
<code class="err"> </code>
<code class="kd">public</code> <code class="kd">class</code> <code class="nc">SerializeJuggler</code> <code class="o">{</code>
<code class="kd">public</code> <code class="kd">static</code> <code class="kt">void</code> <code class="nf">main</code><code class="o">(</code> <code class="n">String</code> <code class="o">[]</code> <code class="n">args</code> <code class="o">)</code> <code class="kd">throws</code> <code class="n">Exception</code>
<code class="o">{</code>
<code class="n">Juggler</code> <code class="n">duke</code> <code class="o">=</code> <code class="k">new</code> <code class="n">Juggler</code><code class="o">();</code>
<code class="n">ObjectOutputStream</code> <code class="n">oout</code> <code class="o">=</code> <code class="k">new</code> <code class="n">ObjectOutputStream</code><code class="o">(</code>
<code class="k">new</code> <code class="nf">FileOutputStream</code><code class="o">(</code><code class="s">"juggler.ser"</code><code class="o">)</code> <code class="o">);</code>
<code class="n">oout</code><code class="o">.</code><code class="na">writeObject</code><code class="o">(</code> <code class="n">duke</code> <code class="o">);</code>
<code class="n">oout</code><code class="o">.</code><code class="na">close</code><code class="o">();</code>
<code class="o">}</code>
<code class="o">}</code></pre><p>Once you have the frozen <span class="emphasis"><em>Juggler</em></span>, compile the
following small application:</p><a id="I_22_tt1238"/><pre class="programlisting"> <code class="c1">//file: BackFromTheDead.java</code>
<code class="kn">import</code> <code class="nn">java.awt.Component</code><code class="o">;</code>
<code class="kn">import</code> <code class="nn">javax.swing.*</code><code class="o">;</code>
<code class="kn">import</code> <code class="nn">java.beans.*</code><code class="o">;</code>
<code class="err"> </code>
<code class="kd">public</code> <code class="kd">class</code> <code class="nc">BackFromTheDead</code> <code class="kd">extends</code> <code class="n">JFrame</code> <code class="o">{</code>
<code class="err"> </code>
<code class="kd">public</code> <code class="nf">BackFromTheDead</code><code class="o">(</code> <code class="n">String</code> <code class="n">name</code> <code class="o">)</code> <code class="o">{</code>
<code class="kd">super</code><code class="o">(</code><code class="s">"Revived Beans!"</code><code class="o">);</code>
<code class="k">try</code> <code class="o">{</code>
<code class="n">Object</code> <code class="n">bean</code> <code class="o">=</code> <code class="n">Beans</code><code class="o">.</code><code class="na">instantiate</code><code class="o">(</code>
<code class="n">getClass</code><code class="o">().</code><code class="na">getClassLoader</code><code class="o">(),</code> <code class="n">name</code> <code class="o">);</code>
<code class="err"> </code>
<code class="k">if</code> <code class="o">(</code> <code class="n">Beans</code><code class="o">.</code><code class="na">isInstanceOf</code><code class="o">(</code> <code class="n">bean</code><code class="o">,</code> <code class="n">JComponent</code><code class="o">.</code><code class="na">class</code><code class="o">)</code> <code class="o">)</code> <code class="o">{</code>
<code class="n">JComponent</code> <code class="n">comp</code> <code class="o">=</code> <code class="o">(</code><code class="n">JComponent</code><code class="o">)</code>
<code class="n">Beans</code><code class="o">.</code><code class="na">getInstanceOf</code><code class="o">(</code><code class="n">bean</code><code class="o">,</code> <code class="n">JComponent</code><code class="o">.</code><code class="na">class</code><code class="o">);</code>
<code class="n">getContentPane</code><code class="o">().</code><code class="na">add</code><code class="o">(</code><code class="s">"Center"</code><code class="o">,</code> <code class="n">comp</code><code class="o">);</code>
<code class="o">}</code> <code class="k">else</code> <code class="o">{</code>
<code class="n">System</code><code class="o">.</code><code class="na">out</code><code class="o">.</code><code class="na">println</code><code class="o">(</code><code class="s">"Bean is not a JComponent..."</code><code class="o">);</code>
<code class="o">}</code>
<code class="o">}</code> <code class="k">catch</code> <code class="o">(</code> <code class="n">java</code><code class="o">.</code><code class="na">io</code><code class="o">.</code><code class="na">IOException</code> <code class="n">e1</code> <code class="o">)</code> <code class="o">{</code>
<code class="n">System</code><code class="o">.</code><code class="na">out</code><code class="o">.</code><code class="na">println</code><code class="o">(</code><code class="s">"Error loading the serialized object"</code><code class="o">);</code>
<code class="o">}</code> <code class="k">catch</code> <code class="o">(</code> <code class="n">ClassNotFoundException</code> <code class="n">e2</code> <code class="o">)</code> <code class="o">{</code>
<code class="n">System</code><code class="o">.</code><code class="na">out</code><code class="o">.</code><code class="na">println</code><code class="o">(</code>
<code class="s">"Can't find the class that goes with the object"</code><code class="o">);</code>
<code class="o">}</code>
<code class="o">}</code>
<code class="err"> </code>
<code class="kd">public</code> <code class="kd">static</code> <code class="kt">void</code> <code class="nf">main</code><code class="o">(</code><code class="n">String</code> <code class="o">[]</code> <code class="n">args</code><code class="o">)</code> <code class="o">{</code>
<code class="n">JFrame</code> <code class="n">frame</code> <code class="o">=</code> <code class="k">new</code> <code class="n">BackFromTheDead</code><code class="o">(</code> <code class="n">args</code><code class="o">[</code><code class="mi">0</code><code class="o">]</code> <code class="o">);</code>
<code class="n">frame</code><code class="o">.</code><code class="na">pack</code><code class="o">();</code>
<code class="n">frame</code><code class="o">.</code><code class="na">setVisible</code><code class="o">(</code><code class="kc">true</code><code class="o">);</code>
<code class="o">}</code>
<code class="o">}</code></pre><p>Run this program, passing the name of your serialized object as an
argument and making sure that our <span class="emphasis"><em>magicbeans.jar</em></span>
file is in your classpath. The name should <span class="emphasis"><em>not</em></span>
include the <span class="emphasis"><em>.ser</em></span> extension in the name; the
<code class="literal">Beans.instantiate()</code> method adds this
automatically in its search for the serialized or class version. The
juggler should spring back to life, juggling once again as shown in
<a class="xref" href="ch22s10.html#learnjava3-CHP-22-FIG-11" title="Figure 22-9. The restored Juggler">Figure 22-9</a>.</p><div class="figure"><a id="learnjava3-CHP-22-FIG-11"/><div class="figure-contents"><div class="mediaobject"><a id="I_22_tt1239"/><img src="httpatomoreillycomsourceoreillyimages1707713.png" alt="The restored Juggler"/></div></div><p class="title">Figure 22-9. The restored Juggler</p></div><p>In <code class="literal">BackFromTheDead</code>, we use
<code class="literal">Beans.instantiate()</code> to load our
serialized bean by name. We then check to see whether it is a GUI
component using <code class="literal">Beans.isInstanceOf()</code>.
(It is, because the <code class="literal">Juggler</code> is a
subclass of <code class="literal">java.awt.Component</code>.)
Finally, we cast the instantiated object to a <code class="literal">Component</code> with <code class="literal">Beans.getInstanceOf()</code> and add it to our
application’s <code class="literal">JFrame</code>. Notice that we
still need a static Java cast to turn the <code class="literal">Object</code> returned by <code class="literal">getInstanceOf()</code> into a <code class="literal">JComponent</code>. This cast may seem gratuitous, but
it is the bridge between the dynamic beans lookup of the type and the
static, compile-time view of the type.</p><p>Everything we’ve done here could be done using the plain <code class="literal">java.io.ObjectInputStream</code> discussed in <a class="xref" href="ch12.html" title="Chapter 12. Input/Output Facilities">Chapter 12</a>. But these bean management methods are
intended to shield the user from details of how the beans are
implemented and stored.</p><p>One more thing before we move on. We blithely noted that when the
<code class="literal">Juggler</code> was restored, the bean began
juggling again. This implies that threads were started when the bean was
deserialized. Serialization doesn’t automatically manage transient
resources such as threads or even loaded images, but it’s easy to take
control of the process to finish reconstructing the bean’s state when it
is deserialized. Have a look at the <code class="literal">Juggler</code> source code (provided with the
examples) and refer to <a class="xref" href="ch12.html" title="Chapter 12. Input/Output Facilities">Chapter 12</a> for a
discussion of object deserialization using the <code class="literal">readObject()</code> method.<a id="I_indexterm22_id824055" class="indexterm"/></p></div><div class="sect2" title="Runtime Event Hookups with Reflection"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-22-SECT-10.3"/>Runtime Event Hookups with Reflection</h2></div></div></div><p><a id="idx11157" class="indexterm"/>We’ve discussed reflection largely in terms of how design
tools use it to analyze classes. Today, reflection is frequently finding
its way into applications to perform dynamic activities that wouldn’t be
possible otherwise. In this section, we’ll look at a dynamic event
adapter that can be configured at runtime.</p><p>In <a class="xref" href="ch16.html" title="Chapter 16. Swing">Chapter 16</a>, we saw how adapter
classes could be built to connect event firings to arbitrary methods in
our code, allowing us to cleanly separate GUI and logic in our
applications. In this chapter, we have seen how NetBeans interposes this
adapter code between beans to do this for us.</p><p>The AWT/Swing event model reduces the need to subclass components
to perform simple hookups. If we start relying heavily on special
adapter classes, we can quickly end up with as many adapters as
components. Anonymous inner classes let us hide these classes, but
they’re still there. A potential solution for large or specialized
applications is to create <span class="emphasis"><em>generic</em></span> event adapters
that serve a number of event sources and targets simultaneously.</p><p>The <a id="I_indexterm22_id824113" class="indexterm"/><code class="literal">java.beans.EventHandler</code>
is a dynamic event dispatcher that simply calls methods in response to
events. What makes the <code class="literal">EventHandler</code>
unusual in Java is that it is the first standard utility to use
reflection to allow us to specify the method by
<span class="emphasis"><em>name</em></span>. In other words, you ask the <code class="literal">EventHandler</code> to direct events to a handler by
specifying the handler object and the string name of the method to
invoke on that object.</p><p>We can use the <code class="literal">create()</code> method
of <code class="literal">EventHandler</code> to get an adapter for
a specified type of event listener, specifying a target object and
method name to call when that event occurs. The target object doesn’t
have to be a listener for the particular event type or any other
particular kind of object. The following application, <code class="literal">DynamicHookup</code>, uses the <code class="literal">EventHandler</code> to connect a button to a <code class="literal">launchTheMissiles()</code> method in our
class:</p><a id="I_22_tt1240"/><pre class="programlisting"> <code class="c1">//file: DynamicHookup.java</code>
<code class="kn">import</code> <code class="nn">javax.swing.*</code><code class="o">;</code>
<code class="kn">import</code> <code class="nn">java.awt.event.*</code><code class="o">;</code>
<code class="kn">import</code> <code class="nn">java.beans.EventHandler</code><code class="o">;</code>
<code class="kd">public</code> <code class="kd">class</code> <code class="nc">DynamicHookup</code> <code class="kd">extends</code> <code class="n">JFrame</code> <code class="o">{</code>
<code class="n">JLabel</code> <code class="n">label</code> <code class="o">=</code> <code class="k">new</code> <code class="n">JLabel</code><code class="o">(</code> <code class="s">"Ready..."</code><code class="o">,</code> <code class="n">JLabel</code><code class="o">.</code><code class="na">CENTER</code> <code class="o">);</code>
<code class="kt">int</code> <code class="n">count</code><code class="o">;</code>
<code class="kd">public</code> <code class="nf">DynamicHookup</code><code class="o">()</code> <code class="o">{</code>
<code class="n">JButton</code> <code class="n">launchButton</code> <code class="o">=</code> <code class="k">new</code> <code class="n">JButton</code><code class="o">(</code><code class="s">"Launch!"</code><code class="o">);</code>
<code class="n">getContentPane</code><code class="o">().</code><code class="na">add</code><code class="o">(</code> <code class="n">launchButton</code><code class="o">,</code> <code class="s">"South"</code> <code class="o">);</code>
<code class="n">getContentPane</code><code class="o">().</code><code class="na">add</code><code class="o">(</code> <code class="n">label</code><code class="o">,</code> <code class="s">"Center"</code> <code class="o">);</code>
<code class="n">launchButton</code><code class="o">.</code><code class="na">addActionListener</code><code class="o">(</code>
<code class="o">(</code><code class="n">ActionListener</code><code class="o">)</code><code class="n">EventHandler</code><code class="o">.</code><code class="na">create</code><code class="o">(</code>
<code class="n">ActionListener</code><code class="o">.</code><code class="na">class</code><code class="o">,</code> <code class="k">this</code><code class="o">,</code> <code class="s">"launchTheMissiles"</code><code class="o">));</code>
<code class="o">}</code>
<code class="kd">public</code> <code class="kt">void</code> <code class="nf">launchTheMissiles</code><code class="o">()</code> <code class="o">{</code>
<code class="n">label</code><code class="o">.</code><code class="na">setText</code><code class="o">(</code><code class="s">"Launched: "</code><code class="o">+</code> <code class="n">count</code><code class="o">++</code> <code class="o">);</code>
<code class="o">}</code>
<code class="kd">public</code> <code class="kd">static</code> <code class="kt">void</code> <code class="nf">main</code><code class="o">(</code><code class="n">String</code><code class="o">[]</code> <code class="n">args</code><code class="o">)</code> <code class="o">{</code>
<code class="n">JFrame</code> <code class="n">frame</code> <code class="o">=</code> <code class="k">new</code> <code class="n">DynamicHookup</code><code class="o">();</code>
<code class="n">frame</code><code class="o">.</code><code class="na">setDefaultCloseOperation</code><code class="o">(</code> <code class="n">JFrame</code><code class="o">.</code><code class="na">EXIT_ON_CLOSE</code> <code class="o">);</code>
<code class="n">frame</code><code class="o">.</code><code class="na">setSize</code><code class="o">(</code><code class="mi">150</code><code class="o">,</code> <code class="mi">150</code><code class="o">);</code>
<code class="n">frame</code><code class="o">.</code><code class="na">setVisible</code><code class="o">(</code> <code class="kc">true</code> <code class="o">);</code>
<code class="o">}</code>
<code class="o">}</code></pre><p>Here, we call the <code class="literal">EventHandler</code>’s <code class="literal">create()</code> method, passing it the <code class="literal">ActionListener</code> class, the target object
(<code class="literal">this</code>), and a string with the name of
the method to invoke on the target when the event arrives. <code class="literal">EventHandler</code> internally creates a listener of
the appropriate type and registers our target information. Not only do
we eliminate an inner class, but the implementation of <code class="literal">EventHandler</code> may allow it to share adapters
internally, producing very few objects.</p><p>This example shows how we would call a method that takes no
arguments—but the <code class="literal">EventHandler</code> can
actually do more, setting JavaBeans properties in response to events.
The following form of <code class="literal">create()</code> tells
<code class="literal">EventHandler</code> to call the <code class="literal">launchTheMissiles()</code> method, passing the
<code class="literal">source</code> property of the <code class="literal">ActionEvent</code> as an argument:</p><a id="I_22_tt1241"/><pre class="programlisting"> <code class="n">EventHandler</code><code class="o">.</code><code class="na">create</code><code class="o">(</code>
<code class="n">ActionListener</code><code class="o">.</code><code class="na">class</code><code class="o">,</code> <code class="n">target</code><code class="o">,</code> <code class="s">"launchTheMissiles"</code><code class="o">,</code> <code class="s">"source"</code><code class="o">)</code></pre><p>All events have a <code class="literal">source</code>
property (via the <code class="literal">getSource()</code>
method), but we can go further, specifying a chain of property “gets”
separated by dots, which are applied before the value is passed to the
method. For example:</p><a id="I_22_tt1242"/><pre class="programlisting"> <code class="n">EventHandler</code><code class="o">.</code><code class="na">create</code><code class="o">(</code>
<code class="n">ActionListener</code><code class="o">.</code><code class="na">class</code><code class="o">,</code> <code class="n">target</code><code class="o">,</code> <code class="s">"launchTheMissiles"</code><code class="o">,</code> <code class="s">"source.text"</code><code class="o">)</code></pre><p>The <code class="literal">source.text</code> parameter
causes the value <code class="literal">getSource().getText()</code> to be passed as an
argument to <code class="literal">launchTheMissiles()</code>. In
our case, that would be the label of our button. Other forms of <code class="literal">create()</code> allow more flexibility in selecting
which methods of a multimethod listener interface are used as well as
other options. We won’t cover every detail of the tool here.</p><div class="sect3" title="How it works"><div class="titlepage"><div><div><h3 class="title"><a id="learnjava3-CHP-22-SECT-10.3.2"/>How it works</h3></div></div></div><p>The <code class="literal">EventHandler</code> uses the
<a id="I_indexterm22_id824344" class="indexterm"/><code class="literal">java.lang.reflect.Proxy</code>, which is a factory
that can generate adapters implementing any type of interface at
runtime. By specifying one or more event listener interfaces (e.g.,
<code class="literal">ActionListener</code>), we get an adapter
that implements those listener interfaces generated for us on the fly.
The adapter is a specially created class that delegates all the method
calls on its interfaces to a designated <code class="literal">InvocationHandler</code> object. See <a class="xref" href="ch01.html" title="Chapter 1. A Modern Language">Chapter 1</a> for more information about proxy
classes.<a id="I_indexterm22_id824374" class="indexterm"/></p></div></div><div class="footnotes"><br/><hr/><div class="footnote"><p><sup>[<a id="ftn.learnjava3-CHP-22-FN-3" href="#learnjava3-CHP-22-FN-3" class="para">47</a>] </sup>This feature would seemingly be applicable to XML-serialized
Java beans using the <code class="literal">XMLOutputStream</code> as well, but it is not
currently implemented for them. This is another sign that the
JavaBeans APIs have stagnated.</p></div></div></div></body></html>