LoginSignup
11
13

More than 5 years have passed since last update.

Springのアノテーションを合成してプロジェクト固有のステレオタイプを作る

Posted at

通常、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;
}
11
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
13