通常、Java ではアノテーションを継承したり合成したりできませんが、Spring のアノテーションはできます。ややこしい準備も必要なく、アノテーションを書くだけで実現できます。
基本
アノテーションを定義し、その型に合成したいアノテーションを付します。元のアノテーションのパラメータについては、定数であれば通常通りそのパラメータに指定すれば良いですし、また @AliasFor
を使って新しいアノテーションのパラメータを合成したアノテーションに引き渡すこともできます。
@Retention(RetentionPolicy.RUNTIME) // 実行時にアノテーションを参照できるようにする
@Validated
@ModelAttribute(binding = true)
public @interface ValidModel {
@AliasFor(annotation = ModelAttribute.class, attribute = "name")
String value() default "";
}
@SpringBootApplication
や @GetMapping
等そこらかしこで合成アノテーションが使われているので、そのソースを追えば使い方はつかめるかと思います。
チェック例外発生時にトランザクションをロールバックさせる
@Transactional
はデフォルトだとチェック例外が送出されてもコミットされるという恐ろしい動作になっていますが、独自アノテーションを定義することで毎回 rollbackFor
を付けなくて済むようになります。
@Retention(RetentionPolicy.RUNTIME)
@Transactional(rollbackFor = Exception.class)
public @interface OurTransactional {
@AliasFor(annotation = Transactional.class, attribute = "readOnly")
boolean readOnly() default false;
}
DAO や Repository がトランザクションの外で呼ばれたら例外を投げる
DAO をトランザクション境界にすることはないと思いますが、トランザクションの内側かどうかのチェックも下記のようにアノテーションでできます。
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Component
@Transactional(propagation = Propagation.MANDATORY)
public @interface OurDao {
@AliasFor(annotation = Transactional.class, attribute = "readOnly")
boolean readOnly() default false;
}
Controller をトランザクション境界にする
Play 1 系のように Controller でトランザクションを切るようにするアノテーションはこんな感じ。
@Retention(RetentionPolicy.RUNTIME)
@Controller
@RequestMapping
@Transactional
public @interface OurController {
@AliasFor(annotation = RequestMapping.class, attribute = "path")
String value() default "";
@AliasFor(annotation = Transactional.class, attribute = "readOnly")
boolean readOnly() default false;
}