Java:パッケージに実行時取得可能情報を付加する

実行時にパッケージの情報を得たいと思う。そのパッケージが格納しているクラスが一口に言って、どのような内容であるかを実行時に得たい。

これを得ることによって、巨大なプログラムのデバッグが楽になる。ロギングを行っている場合に、注目すべきパッケージについてだけトレースをONにすることで、そのトレース情報を出力させるのだが、この場合に各パッケージが何をするものかを「実行時にその場で」把握できれば楽だろう。

※もちろん、javadocを作成し、それを見ながらということも可能は可能だが、パッケージの数が多い巨大なプロジェクトの場合にはやっていられない。

これを簡単に行う方法があったのである。これまで知らなかったのだが、パッケージにはアノテーションをつけることができ、そのアノテーションを実行時に取得することができるのだ。これは以下のように行う。

パッケージにのみつけられるアノテーションの定義

package packageTest;
import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PACKAGE)
public @interface PackageDesc {
  public String summary();
  public String desc() default "";  
}

上記アノテーションをつけたpackage-info.java

/**
 * これはテスト用パッケージ
 */
@PackageDesc(summary="test", desc="パッケージテスト")
package packageTest;

パッケージのアノテーションを読み出すプログラム

package packageTest;

public class Main {

  void get(String packagename) {
    // 指定されたパッケージの"package-info"につけられたPackageDescアノテーションがある場合に
    // その内容を取得する
    getPackageInfoClass(packageName)
      .flatMap(this::getPackageDesc)
      .ifPresent(pd-> {
        // PackageDescを得られたので、その内容を処理する
      });
  }

  /**
   * "package-info"クラスの{@link PackageDesc}アノテーションを取得する
   * @param packageInfoClass "package-info"クラス
   * @return {@link PackageDesc}アノテーション、あるいは空
   */
  Optional<PackageDesc>getPackageDesc(Class<?>packageInfoClass) {
    Package pkg = packageInfoClass.getClassLoader()
      .getDefinedPackage(packageInfoClass.getPackageName());
    return Optional.ofNullable((PackageDesc)pkg.getAnnotation(PackageDesc.class));
  }

  /**
   * 指定されたパッケージ名称のpackage-infoクラスを取得する。
   * <p>
   * 例えば"foo.bar"パッケージが指定されたら、"foo.bar.package-info"クラスを取得する
   * </p>
   * @param packageName パッケージ名称
   * @return "package-info"クラス、あるいは空
   */
  Optional<Class<?>>getPackageInfoClass(String packageName) {
    try {
      return Optional.of( Class.forName(packageName+".package-info"));
    } catch (ClassNotFoundException ex) {
      return Optional.empty();
    }
  }  
}