Domaを使ったアプリのビルドにおいてMavenとaptの相性の悪さを解決する3つの方法

http://d.hatena.ne.jp/ktfs/20100331/1270061475
わかります。確かに「SUN、どうなってんの??!!Mavenどうなってんの??????!!!!!!」といいたくなりますね。
Mavenというかjavacには、ドキュメントに書いたようにaptに関する不具合があって工夫しないとビルドできません。
ドキュメントに書いたとおりに対応すれば、Mavenやjavacでビルドできるわけですが、そもそもjavacのバグを踏まない方法を説明します。方法は3つ紹介しますが、どれか1つの方法で解決できます。

方法1 DIコンテナを使う

このバグは、aptで生成されたクラス(たとえばDaoインタフェースの実装クラス)を他のパッケージから静的に参照していると発生します。つまり、静的に参照しなければ回避できるということです。

SpringやSeasarの場合、設定ファイルを使用した明示的な設定であれ自動登録であれ、Daoの実装クラスをコンポーネントとして登録し、Daoを呼び出す側はインタフェースにだけ依存するようにすれば問題が起きないということです。インタフェースに対してプログラミングするという一般的に優れたプログラミング方法を強制できてかえって便利かもしれません。

Guiceのようにプログラムで実装クラスを登録する場合は、ビルド時に静的に参照しないわけにはいけません。この場合は、他のパッケージからDaoインタフェースの実装クラスを参照しないようにします。つまり、ModuleをDaoの実装クラスと同じパッケージに作成しましょう。たとえば、hoge.foo.daoパッケージにDaoインタフェースを作成すると、デフォルトではこのパッケージに実装クラスが生成されます。Moduleもhoge.foo.daoパッケージに作成し、その中でDaoインタフェースと実装クラスを関連付ける処理を行うということです。もしくは@ImplementedByを使ってもいいかもしれません。とにかく、Daoの実装クラスは他パッケージから参照しないようにします。そして、クライアントはインタフェースを介してDaoの実装クラスを利用するようにします。

方法2 自前でFactoryを作成する

Daoインタフェースの実装クラスを静的に参照しなければいいので、リフレクションでインスタンス化するようにすれば解決します。
例外ハンドリングとか適当ですがたとえばこういったFactoryクラスを作ります。

public class DaoFactory {
    public static <T> T get(Class<T> daoInterface) {
        String implClassName = daoInterface.getName() + "Impl";
        try {
            Class<?> implClass = Class.forName(implClassName);
            return (T) implClass.newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

で、使うときは、どこのパッケージからでもこうできます。

EmployeeDao dao = DaoFactory.get(EmployeeDao.class);

Factoryを使わないときは下のようになりますが、そんなに手間は変わらないですね。

EmployeeDao dao = new EmployeeDaoImpl();

方法3 パッケージを分けない

Daoインタフェースの実装クラスを他のパッケージから参照しなければいいので、Daoインタフェースの実装クラスとそのクライアントのクラスを同じパッケージにしてしまいます。
ごくごく小規模なツールとかを作るだけならこの方法でもいいかもしれません。

まとめ

DIコンテナを使いたい人は方法1、DIコンテナをよく知らなかったり直感的にプログラムしたい人には方法2がお奨めです。
方法3はおまけ。方法3を使うよりも方法2がいいと思います。