これでようやくhookupメソッドの動的な働きを説明する準備が出来ました。次のソース を見てください。generateHookupメソッドは、二つのBeans間のイベント関係を仲立ち するAdapterクラスのソースを生成し、それをファイルに吐き出してコンパイルして、 クラス・ファイルを生成します。このメソッドが返すのは、生成したクラス・ファイル の名前です。その次のリストは、実際にBeanBoxが吐き出したソースです。
クラスの名前は、"___Hookup_140d8abc8c"と奇妙なものになってはいますが、これが、
先に見たAdapterクラスと、本質的には同じであることを確認して下さい。なお、この
ファイルは、パッケージの名前を見れば想像できると思いますが、
${BDK_HOME}/beanbox/tmp/sun/beanbox/ というディレクトリに作られます。BeanBoxで
イベントのwiringをしたことのある人は、一度是非、このディレクトリを見て、
作られたJavaファイルの働きを考えて見てください。
-------------------------------------------------------------------------------
static void hookup(EventSetDescriptor esd,
Method listenerMethod,
Wrapper sourceWrapper,
Wrapper targetWrapper,
Method targetMethod) {
try {
// If we were smart, we would cache and reuse hookups. We aren't.
Object source = sourceWrapper.getBean();
Object target = targetWrapper.getBean();
String hookupName = generateHookup(esd, listenerMethod, source, target, targetMethod);
SimpleClassLoader loader = SimpleClassLoader.ourLoader;
javaFiles.addElement(hookupName);
Class hookupClass = loader.loadFromFile(hookupName);
Object hookup = hookupClass.newInstance();
Method setTargetMethod = findMethod(hookupClass, "setTarget");
Object args[] = new Object[1];
args[0] = target;
setTargetMethod.invoke(hookup, args);
sourceWrapper.addEventTarget(esd.getName(), targetWrapper, hookup);
} catch (Exception ex) {
System.err.println("Hookup caught " + ex);
ex.printStackTrace();
}
}
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
// Automatically generated event hookup file.
package tmp.sun.beanbox;
public class ___Hookup_140d8abc8c implements java.awt.event.ActionListener, java.io.Serializable {
public void setTarget(sunw.demo.juggler.Juggler t) {
target = t;
}
public void actionPerformed(java.awt.event.ActionEvent arg0) {
target.stop();
}
private sunw.demo.juggler.Juggler target;
}
-------------------------------------------------------------------------------
次に、このメソッドで重要なのは、次のシーケンスです。
Class hookupClass = loader.loadFromFile(hookupName);
Object hookup = hookupClass.newInstance();
loaderの働きなど細かなことは省略しますが、これで、プログラムの実行中に作られた
新しいAdapterクラス hookupClass クラスとそのインスタンス hookupが生成され、
これらのオブジェクトをプログラムの中で利用できるようになります。
メソッドfindMethodは、内部で、JDK1.1で拡張されたreflectionの機能を用いて、次の ようにして、hookupClassクラスに含まれるメソッドの配列を獲得します。
Method methods[] = hookupClass.getMethods();hookupClassには、setTargetとactionPerformedという二つのメソッドが含まれている はずです。 ここでは、setTargetメソッドをsetTargetMethodという変数に入れています。
reflectionの機能を使った次のシーケンスは、とても重要です。
setTargetMethod.invoke(hookup, args);メソッドinvokeは、パッケージjava.lang.reflectのMethodクラスで定義されており、 メソッド.invoke(オブジェクト、引数)というシンタックスで、オブジェクト上で メソッドを与えられた引数で呼び出します。ですから、先のinvokeをつかった シーケンスは、次のシーケンスとまったく同じ働きをします。
hookup.setTarget(args);こうして、先にみた、adapter.setTarget(juggler) を実行することが、 「ダイナミックに」可能となったわけです。後は button.addActionListener(adapter) を動的に実行することが残されています。
hookupで残された、次の
sourceWrapper.addEventTarget(esd.getName(), targetWrapper, hookup);で、この処理がやられています。メソッドaddEventTargetのソースを次に掲げます。 自分で、そのメカニズムを解明して見てください。
-------------------------------------------------------------------------------
synchronized void addEventTarget(String eventSetName,
Wrapper targetWrapper, Object listener) {
WrapperEventTarget et = new WrapperEventTarget();
et.eventSetName = eventSetName;
if (targetWrapper != null) {
et.targetBean = targetWrapper.getBean();
}
et.targetListener = listener;
eventTargets.addElement(et);
EventSetDescriptor esd = (EventSetDescriptor) esdMap.get(eventSetName);
if (esd == null) {
System.err.println("Internal error: Wrapper.addEventTarget missing event set");
System.err.println(" eventSetName = " + eventSetName);
System.err.println(" bean = " + bean);
return;
}
Method adder = esd.getAddListenerMethod();
Method remover = esd.getRemoveListenerMethod();
if (adder == null || remover == null) {
System.err.println("Internal error: Wrapper.addEventTarget missing add/remote listener");
System.err.println(" eventSetName = " + eventSetName);
System.err.println(" bean = " + bean);
return;
}
try {
Object args[] = { listener };
adder.invoke(bean, args);
} catch (Exception ex) {
System.err.println("Wrapper: adding event listener for " + eventSetName + " failed:");
System.err.println(" " + ex);
}
}
-------------------------------------------------------------------------------