はじめに
ReflectionパッケージのProxyを利用すると、指定したインターフェイスに対して、そのインターフェイスを実装したクラスとインスタンスを動的に作ることができます。
本稿では、Oracle JDBCドライバの制約をProxyを利用して回避する方法を示すことで、Proxyの具体的な利用方法を説明します。
Oracle JDBCドライバはJ2SEのAPI規定と異なり、GCによるConnectionやStatementの自動クローズを行いません。そのため、これらのJDBCオブジェクトを自動的にクローズする他のJDBC実装用のコードを流用するとリソースリークの原因となります。これを回避するには、アプリケーションがすべてのStatementなどのオブジェクトをクローズするか、またはミドルウェアなどでアプリケーションが作成したすべてのJDBCオブジェクトを保持しておき、なんらかのタイミングでクローズするように実装しなければなりません。
JDBCのConnectionやStatementはインターフェイスですので、Proxyの適用が可能です。このため、簡単なハンドラクラス(Proxyからの呼び出しを処理するクラス)を用意することで、後者の方法を実装できます。この場合、アプリケーションにとってはインターフェイスが透過なため呼び出し時の特別な考慮を必要としません。
本記事では、上記のシナリオに沿ってJDBCのインターフェイスに対するProxyを生成し、Connectionオブジェクトのクローズ時にそのConnectionオブジェクトが生成した未クローズのJDBCオブジェクトを自動的にクローズする、Oracle JDBCドライバの制約回避処理の実装方法を示します。
対象読者
本記事は、Javaプログラミングの初級者から中級者を対象に、java.lang.reflect.Proxyの利用方法、Proxyによって既存のクラスへ処理を追加する方法、およびそれによって得られる効果について説明します。
また、スタブを利用したユニットテストプログラミングについても解説します。
必要な環境
本記事のソースをビルド/実行するにはJ2SE 5.0を利用してください。また、antのビルドスクリプトを添付しているので、一括してビルドするにはant 1.6以上を用意すると良いでしょう。
参考までに筆者が利用した本記事のテスト環境は以下のものです。
| OS | OS X 10.4.2 |
| J2SE | 1.5.0_02 |
| Ant | 1.6.2 |
| JUnit | 3.8.1 |
| IDE | NetBeans4.1J |
なお、元々存在するインターフェイスの実装などを行っているため、ビルド時にdeprecatedメッセージなどが出力されますが、実行には問題ありません。
NetBeans 4.1からの利用
メニューから[ファイル]→[新規プロジェクト]→[プロジェクト―既存のAntスクリプトを使用するJavaプロジェクト]を順に選択し、[次へ]ボタンをクリックします。次に[場所]の[ブラウズ]ボタンをクリックし、ファイルオープンダイアログを表示します。アーカイブを展開したディレクトリの「proxy」ディレクトリを選択し、[開く]ボタン(OS Xの場合は[選択]ボタン)をクリックします。この操作で、[構築スクリプト][プロジェクトフォルダ]が自動的に設定されるため、残る[プロジェクト名]に適当な名前(「proxy」など)を入力し、[次へ]ボタンをクリックしてください。
一般的なメニュー項目のうち、[プロジェクトをテスト]に「junit」を設定します。[次へ]ボタンをクリックし、[ソースパッケージフォルダ]に「src」、[テストパッケージフォルダ]に「test」をそれぞれ追加し、[次へ]ボタンをクリックして完了します。この時、[ソースレベル]は[JDK 1.5]を選択します(デフォルト)。
以後、「build.xml」を右クリックすると表示されるコンテキストメニューで、[ターゲット実行]→[その他のターゲット]をクリックすると、「build.xml」に定義されている各種ターゲットをNetBeans上で実行できるようになります。
ファイル構成
ダウンロードしたファイルはzipで圧縮してあります。展開すると「proxy」というディレクトリを頂点としたディレクトリ階層ができます。すぐに実行できるようにコンパイル済みのクラスファイルも添付してあります。また、ソースファイルはすべてシフトJISでエンコードしています。
proxyディレクトリ
- 「build.xml」
antのビルドスクリプトです。
junit.homeプロパティの値は「junit.jar」が配置されたディレクトリ名に変更してください。以下のターゲットがあります。- デフォルト(build):クリーンアップ後に「src」ディレクトリのプログラムをビルド
- prepare:ビルドに必要なディレクトリを作成
- clean:ビルドしたクラスファイルを削除
- compile:srcディレクトリ下のソールファイルをコンパイル
- junit:
ProxyConnectionTestとStubGeneratorTestをビルドして実行
proxy\src\com\example\proxyディレクトリ
- 「ProxyConnection.java」
Proxyを利用してConnectionのclose呼び出し時に、Statement、ResultSetのclose漏れを検証するユーティリティ。もし未クローズならば代わりにcloseメソッドを呼び出す。
proxy\src\com\example\toolディレクトリ
- 「StubGenerator.java」
ユニットテスト用のスタブクラスを生成するユーティリティプログラム。
proxy\test\com\example\proxyディレクトリ
- 「ProxyConnectionTest.java」
Statement、ResultSetのcloseテストプログラム。
proxy\test\com\example\toolディレクトリ
- 「StubGeneratorTest.java」
多次元配列の型情報の生成テストプログラム。
解説
Proxy
proxy(プロクシ)とは、「代理人」や「代用品」、あるいは「……の代理」という意味の言葉です。コンピュータの世界だと、HTTPサーバーへのリクエスト/レスポンスの中継を行うプロクシサーバーが一番有名かも知れません。
java.lang.reflect.Proxyは名前の通り、指定したインターフェイスを持つオブジェクトを偽装することで、ユーザーが指定したハンドラへクライアントからの呼び出しを中継します。

図は、インターフェイスを介したクライアントとオブジェクトの関連を示したものです。コードで示すと、たとえば次のようになります。
public void foo(Map map) { map.put("a", "b"); }
上のリストで、クライアント(fooメソッド)は、Mapインターフェイスを実装したオブジェクトを直接利用しているわけではなく、あくまでもMapインターフェイスを介して利用しています。ただし、インターフェイスはオブジェクトではなく、あくまでもメソッド定義の集合なので実体があるわけではありません。したがって、クライアントのメソッド呼び出しは、実行時には直接オブジェクトに対して行われます。その意味では、インターフェイスは設計時の文字通りインターフェイスの定義と、コンパイル時のスタブの役割しかないと言っても良いでしょう。
Proxyはインターフェイスに対して実体を提供します。

図のようにProxyクラスは、特定のインターフェイスを偽装して(あるいは実行時に動的に実装して)クライアントからの呼び出しをユーザー定義のハンドラオブジェクトへデリゲートします。
Proxyクラスに特定のインターフェイスを実装したオブジェクトを生成させるには、次のリストのようにProxy.newProxyInstance staticメソッドを利用します。
foo((Map)Proxy.newProxyInstance(Map.class.getClassLoader(),
new Class[] { Map.class },
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) {
if (method.getName().equals("put")) {
System.out.println("key=" + args[0] + ", value=" + args[1]);
return null;
}
throw new UnsupportedOperationException("only put can handle");
}
});
リストはMapインターフェイスを実装したProxyオブジェクトの生成方法を示したものです。
Proxy.newProxyInstanceメソッドの引数は
- Proxyオブジェクトを定義するのに利用するクラスローダ
通常はこの例のように偽装するインターフェイスのクラスローダを指定すれば良いでしょう。
- 偽装するインターフェイスの配列
Proxyは同時に複数のインターフェイスを実装することができます。
- InvocationHandlerのインスタンス
クライアントからの呼び出しを受け取るユーザー供与のオブジェクトです。
の3つです。
このうち3番目の引数として与えるInvocationHandlerインターフェイス(invocationはメソッドの呼び出しの意味)は、3引数のinvokeメソッドを定義したイベント受信用インターフェイスです。Proxyオブジェクトはクライアントからの呼び出しを受けるとInvocationHandler#invokeメソッドを呼び出します(図)。

InvocationHandler#invokeメソッドの引数は
- Proxyオブジェクト
同一ハンドラで複数の
Proxyオブジェクトの通知を受ける時の識別に利用できます - Methodオブジェクト
クライアントが呼び出したメソッド
- Object配列
クライアントがメソッドに与えた引数の配列
の3つです。また戻り値は、クライアントが呼び出したメソッドの戻り値と、型を一致させる必要があります。もしvoid型メソッドの場合にはnullを返します。この時、戻り値の型が一致しないとClassCastExceptionがスローされます。また、invokeメソッドからスローした例外が呼び出されたメソッドで宣言していないチェック例外の場合にはUndeclaredThrowableExceptionがスローされます。
Proxyの利用用途
Proxyを利用すると、比較的少ない手数で以下のデザインパターンのインスタンスを生成することができます。
- decorator パターンの実装
Proxyオブジェクトからのメソッド呼び出し通知を内包するオブジェクトへデリゲートする前後で、新たな機能を追加します(図を参照)。 - adapter パターンの実装
クライアントが利用したいインターフェイスの
Proxyオブジェクトを生成し、InvocationHandler内でクライアントからの呼び出しを実際のオブジェクトの呼び出しへ変換します。 - proxy パターンの実装
Proxyオブジェクトからのメソッド呼び出し通知を内包するオブジェクトへデリゲートする前後で、必要なアクセス制御を行います。

なお、筆者には正直なところ本記事の目的の実装がdecorator パターンなのかproxy パターンなのか、はっきりわかりません。新たな機能を追加しているわけではないのでproxy パターンのインスタンスだと思いますが、プロクシと言うとリモートオブジェクトの転送の受け口のイメージが強いため、decorator パターンのインスタンスのようにも感じるからです。もし、おわかりであればフォーラムの本記事のスレッドにご投稿いただけたらと思います。
なお、Proxyはデザインパターンの実装以外にも、Mapに対するProxyオブジェクトの例で示したputされた内容を標準出力へ出力するダミーのMapのように、スタブの作成にも利用できるでしょう。
Proxyを利用したOracle JDBC Connectionの作成
はじめに書いたように、OracleのJDBC実装には、生成したJDBCオブジェクトを明示的にクローズする必要があるという制限があります。そのため、きちんとcloseメソッドを呼ばないとWebアプリケーションのように長期間に渡って実行するアプリケーションではResultSetなどが解放されないまま蓄積されていき、最終的にJVMのヒープが食いつぶされてしまうという問題があります。
「ProxyConnection.java」は、この制限に対応できるように、Connection#closeで、そのConnectionから返されたStatement、およびそのStatementから返されたResultSetで未クローズのオブジェクトのcloseメソッドを呼び出すクラスです。このクラスはProxyを利用することでクライアントに対してはConnectionとして振る舞います。
package com.example.proxy; import java.lang.reflect.*; import java.sql.*; import java.util.*; public class ProxyConnection { Connection connection; Set<Statement> openedStatements; Set<ResultSet> openedResultSets; Object proxy; ProxyConnection(Connection org) { connection = org; openedStatements = new HashSet<Statement>(); openedResultSets = new HashSet<ResultSet>(); proxy = Proxy.newProxyInstance (Connection.class.getClassLoader(), new Class[] { Connection.class }, new ConnectionHandler()); } /** * 指定されたコネクションのプロクシを生成する。 * @param c 生成対象のコネクション * @return PreparedStatementとResultSetのclose検証処理を含んだコネクション */ public static Connection createProxy(Connection c) { ProxyConnection pc = new ProxyConnection(c); return (Connection)pc.proxy } Object send(Object target, Method m, Object[] args) throws Throwable { try { return m.invoke(target, args); } catch (InvocationTargetException e) { if (e.getCause() != null) { throw e.getCause(); } throw e; } } void remove(Object o) { if (o instanceof ResultSet) { openedResultSets.remove(o); } else if (o instanceof Statement) { openedStatements.remove(o); } else { throw new IllegalArgumentException("bad class:" + o); } } void closeAll() { for (ResultSet rs : openedResultSets) { try { rs.close(); } catch (SQLException e) { } } for (Statement ps : openedStatements) { try { ps.close(); } catch (SQLException e) { } } } class ConnectionHandler implements InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.getName().equals("close")) { closeAll(); } Object o = send(connection, method, args); if (o instanceof Statement) { openedStatements.add((Statement)o); o = new Delegate(o, PreparedStatement.class).proxy; } return o; } } class Delegate implements InvocationHandler { Object proxy; Object original; Delegate(Object o, Class c) { original = o; proxy = Proxy.newProxyInstance(c.getClassLoader(), new Class[] { c }, this); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.getName().equals("close")) { remove(original); } Object o = send(original, method, args); if (o instanceof ResultSet) { openedResultSets.add((ResultSet)o); o = new Delegate(o, ResultSet.class).proxy; } return o; } } }
importなどの冗長な記述は簡素化してあります。フィールド
ProxyConnectionは以下のフィールドを持ちます。
- Connection
connection;元のConnectionのインスタンスを保持します。 - Set<Statement>
openedStatements;Connectionから返されたStatementのうち、未クローズのオブジェクトを保持します。 - Set<ResultSet>
openedResultSets;Statementから返されたResultSetのうち、未クローズのオブジェクトを保持します。 - Object
proxy;作成したProxyのインスタンスを保持します。
コンストラクタ
ProxyConnectionは、デコレーションの対象となるConnectionのインスタンスを受け取り、Connectionインターフェイスに対するProxyを生成します。
createProxy
外部へ公開するstaticメソッドです。ProxyConnectionを利用するプログラムは、たとえば以下のように当メソッドを呼び出すことを想定します。
Connection connection; ... try { connection = ProxyConnection.createProxy(dataSource.getConnection()); } catch (SQLException e) { log("connectionの取得に失敗", e); }
これはProxyConnectionクラスそのものを直接クライアントに見せる必要が無いからです。なお、一見するとせっかく生成したProxyConnectionのインスタンスが誰からも参照されないため(クライアントへ返しているのはProxyのインスタンスです)すぐにGCされてしまいそうですが、実際にはインナークラスのConnectionHandlerのインスタンスがthisを参照しているためConnectionHandlerが有効な間はGCされることはありません。また、ConnectionHandlerの寿命はProxyと一致するため、結局、クライアントがConnectionオブジェクト(のプロクシ)の参照を保持している限り有効となります。
なお、本記事でJDBCオブジェクトのクローズのトリガーをConnection#closeにしている理由は、ここで意図的にProxyConnectionの呼び出しが必要な以上、Connectionを特別扱いするプログラムの存在を前提できるからです。たとえば、Webアプリケーションであれば、DataSourceのProxyを作り、getConnectionの呼び出し時にこのメソッドを実行し、リクエストの終了時に作成したConnectionのcloseを呼ばせるように実装することが可能です。
send
内部処理用のメソッドです。Proxyから与えられたMethodオブジェクトと引数を利用して、内包するConnectionやStatementなどのオブジェクトへ処理をデリゲートします。
ここでキャッチしているInvocationTargetExceptionは、Method#invokeの呼び出し先でスローされた例外を保持する例外です。したがって、Connectionオブジェクトなどがスローした例外はMethod#invoke内でInvocationTargetExceptionにラップされることになります。クライアントにProxyを意識させないために、ここでInvocationTargetException#getCauseにより元の例外を取得し再スローしています。リスト上はgetCauseの内容がnullかチェックしていますが、本来nullになることはありません。
なお、InvocationTargetException以外の例外は、Method#invokeの処理そのものがスローしたものとなります。このような例外としては、たとえばargsが呼び出し対象のメソッドとマッチしないなどが考えられます。しかし、クライアントはConnectionインターフェイスのメソッドを呼び出すソースをコンパイルしたと想定できるため、このような状況はバグ以外にはあり得ません。したがって、ここでは特にキャッチせずにそのままクライアントへスローされるようにしています。
remove
登録したResultSetやStatementオブジェクトのclose呼び出し時に利用します。closeが呼ばれた場合、該当オブジェクトをそれ以上保持する必要はないため、当メソッドで登録解除します。
closeAll
Connection#closeの呼び出しを受けて、すべての未クローズのStatementおよびResultSetオブジェクトをクローズします。なお、この時点でSQLExceptionをキャッチしても処理できたないため単純に無視しています。意味的にはファイナライザでのクローズと同様の扱いだからです。
ConnectionHandler(InvocationHandler実装)
Connectionオブジェクトのデコレータとして以下の処理を実行します。
close呼び出し時にcloseAllを呼び出して未クローズのリソースを解放する。- メソッド呼び出しを元の
Connectionオブジェクトへデリゲートする。 - メソッドの結果が
Statementのインスタンス(PreparedStatementなどを含みます)であれば、未クローズオブジェクトとして登録します。また、新たなProxyを生成します。
なお、StatementかPreoparedStatement、CallableStatementの区別なくStatementインターフェイスおよびその継承インターフェイスをPreparedStatementインターフェイスとして、Proxyオブジェクトを生成しています。ここは必要に応じて細分化を行うなどしても良いでしょう。たとえば、クライアントが何らかの処理で、PreparedStatementインターフェイスかStatementインターフェイスかによって処理を分岐するような場合には、この実装では正しく処理できないことに注意してください。
Delegate(InvocationHandler実装)
Connectionオブジェクト専用のConnectionHandlerと異なり、Delegateクラスは、StatementとResultSetの両方を扱うハンドラです。なお、ここでは個々のリソース単位に新たなDelegateのインスタンスを割り当てていますが(ConnectionHandler#invoke内)、InvocationHandler#invokeメソッドの第1引数で説明したように、実際には単一のインスタンスで複数のProxyオブジェクトを扱うことなども可能です。しかし、ここでは処理の単純さを重視してオブジェクトごとに作成しています。
コンストラクタでは、デリゲート先のオブジェクト(元のオブジェクト)をフィールドへ保持し、Proxyオブジェクトを生成しています。ここで生成したProxyオブジェクトは、Delegateのインスタンスの生成と同時にメソッドの戻り値としてそのままクライアントへ返します(各InvocationHandlerがDelegateオブジェクトを生成している個所を参照)。
invokeメソッドでは、ResultSet、Statement(および派生したPreparedStatement)に対するデリゲートを実行します。ただし、closeメソッドが呼び出された場合はremoveソッドを呼び出して該当するオブジェクトの登録を解除します。また戻り値の型がResultSetの場合には新規に対応するDelegateオブジェクトを生成しクライアントへはその結果を返します。
別の選択肢――Stubの生成
スタブを利用したユニットテスト
次のリストは、ProxyConnectionのテストプログラムです。
package com.example.proxy; import com.example.tool.*; import java.lang.reflect.*; import java.sql.*; import junit.framework.*; public class ProxyConnectionTest extends TestCase { int close; protected void setUp() throws Exception { close = 0; } // PreparedStatement, ResultSetがクローズされるか検証 public void testClose() throws Exception { Connection c = new StubConnection() { public void close() { // 最初のclose呼び出しか確認 assertEquals(2, close); close++; } public PreparedStatement prepareStatement(String s) { return new TestStubPreparedStatement(); } }; c = ProxyConnection.createProxy(c); PreparedStatement ps = c.prepareStatement("select"); ResultSet rs = ps.executeQuery(); c.close(); assertEquals(3, close); } // PreparedStatement, ResultSetがクローズされている場合に // 何も行わないことを検証 public void testClosed() throws Exception { Connection c = new StubConnection() { public void close() { // 既にclose呼び出し済みか確認 assertEquals(2, close); close++; } public PreparedStatement prepareStatement(String s) { return new TestStubPreparedStatement(); } }; c = ProxyConnection.createProxy(c); PreparedStatement ps = c.prepareStatement("select"); ResultSet rs = ps.executeQuery(); rs.close(); ps.close(); c.close(); assertEquals(3, close); } // 例外が透過に受け取れるか検証 public void testTransparent() throws Exception { Connection c = new StubConnection() { public PreparedStatement prepareStatement(String s) throws SQLException { throw new SQLException("test exception"); } }; c = ProxyConnection.createProxy(c); try { c.prepareStatement("select"); fail("no exception"); } catch (SQLException e) { assertEquals("test exception", e.getMessage()); } } class TestStubPreparedStatement extends StubPreparedStatement { public void close() { close++; } public ResultSet executeQuery() { /* * スタブを利用した実装 return new TestStubResultSet(); */ /* * Proxyを利用した実装 */ return (ResultSet)Proxy.newProxyInstance( ResultSet.class.getClassLoader(), new Class[] { ResultSet.class }, new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.getName().equals("close")) { close++; return null; } else if (method.getName().equals("next")) { return Boolean.FALSE; } else if (method.getName().equals("equals") || method.getName().equals("hashCode")) { return method.invoke(this, args); } throw new UnsupportedOperationException ("only close can handle"); } }); } } class TestStubResultSet extends StubResultSet { public void close() { close++; } } public static void main(String[] args) { junit.textui.TestRunner.run(ProxyConnectionTest.class); } }
JDBCドライバを直接利用してテストをすることは環境の用意の問題があることや、単純に実装するとテストケースごとにコネクションが必要となりテスト実行に時間がかかることから、ロジックのテストについてはスタブやモックを利用したユニットテストプログラムを記述します。ProxyConnectionTestもそのようなスタブを利用したプログラムの例です。
このプログラムでは、StubConnectionおよびStubPreparedStatementという2つのスタブクラス(いずれもソースは、アーカイブの「proxy\test\com\example\tool」ディレクトリに格納してあります)および、ResultSetについてはProxyオブジェクトを使ったスタブを利用しています(スタブクラスを利用するためのコードも含んでいます)。
スタブクラスはすべてのメソッドのデフォルト処理(大抵はnullや0を返します)を実装しただけのクラスで、Proxyと異なり、継承可能であればインターフェイスだけではなくクラスについても作成可能だという長所があります。スタブクラスを利用したテストの場合、テストの都合に合わせて呼び出しの対象となるメソッドをオーバーライドした無名クラスを、その都度生成することも(StubConnectionの利用方法)、オーバーライドしたメソッドがテスト全体で共用されるのであれば、テストプログラム内でスタブクラスを継承したクラスを定義することも(StubPreparedStatementの利用方法)可能です。
一方、Proxyオブジェクトを利用する場合(TestStubPreparedStatement#executeQueryメソッドでResultSetへ適用しています)は、InvocationHandler#invokeでテストに必要なメソッドについて処理を実装します。なお、ProxyConnectionTestの例では、HashSetに格納されるため、hashCodeメソッドおよびequalsメソッドについても処理していますが、スタブクラスの場合はスタブクラス自体が実装を持つためそのまま利用可能です。そのため同じくHashSetに格納されるTestStubPreparedStatementではequalsメソッドやhashCodeメソッドを特にオーバーライドせずに利用しています。
このような点からテスト用途にはProxyクラスの利用は手軽ですが、再利用性を考えるとスタブクラスを用意したほうがより望ましいと言えます。
また、Proxyよりも直接内包するオブジェクトへデリゲートするフィルタのほうが、処理効率は(呼び出し頻度にも依存しますが)一般的に良好です。このようなフィルタの例として、J2SEにはjava.io.FilterReader/FilterWriterなどが存在します。これらのクラスはコンストラクタで指定したオブジェクトを内包しすべてのメソッドをデリゲートします。プロクシパターンやデコレータパターンをインスタンス化したい場合には、必要なメソッドをオーバーライドして利用することになります。
スタブジェネレータ
アーカイブに添付したJDBCのスタブクラスは以下のような機械的なプログラムです。
package com.example.tool; /** * java.sql.Connectionのスタブクラス。 * @author StubGenerator by arton. */ public class StubConnection implements java.sql.Connection { public StubConnection() { } public java.sql.Statement createStatement (int a0, int a1, int a2) throws java.sql.SQLException { return null; } public java.sql.Statement createStatement(int a0, int a1) throws java.sql.SQLException { return null; } public java.sql.Statement createStatement() throws java.sql.SQLException { return null; }
このようなプログラムは一度作れば何度でも利用できるとは言え、手で打ち込むのはあまり賢い方法ではありません。
本記事のおまけとして、「proxy\src\com\example\tool」ディレクトリにreflectionを利用して指定されたインターフェイスや、クラスのスタブを生成するユーティリティを添付してありますので、興味があったら利用してください。アーカイブの「proxy\test\com\example\tool」ディレクトリ下には、当クラスの一部機能のテストプログラムであるStubGeneratorTestを除いて、StubGeneratorによって生成したクラスを格納してあります。
なお、StubGeneratorについては、アーカイブ内の他のソースと異なり、フェアライセンス(日本語訳)を適用します。ただし、生成したソースにまでは効力は及ばないものとします。
まとめ
Proxyクラスを利用して、decoratorパターンやproxyパターンを比較的簡単に実装することができます。また、テスト用のスタブが必要な機能が少なければProxyクラスを利用してスタブを作ることも可能です。特にメソッドが多数含まれているインターフェイスのごく1部のメソッドのみテストで利用したい場合にはProxyクラスの利用は効果的です。
本記事のサンプルソースは自由に使ってかまいません(ただし、StubGeneratorについてはフェアライセンスを適用します)。いろいろ試してJavaの学習などに利用してみてください。
なお、ProxyConnectionおよびその派生物を実際の運用に利用する場合については、無保証であること、筆者および翔泳社は、利用したことによって生じたいかなる問題についても一切の責を追わないことを認めることを利用許諾の条件とします。利用される場合にはProxyConnectionのソースコードについては特にライセンスを定めません(元の著作権者の表示を不要とするライセンスが見つからなかったのが理由です)ので、むしろ改変して利用するようにしてください。

含んだコネクション
*/