環境
-
SpringBoot
: 2.1.9 -
Kotlin
: 1.3.50 -
kotlin-maven-allopen
導入済み
問題
以下のようなアノテーションを自作し、Autowire
でSpringBoot
のValidator
を持ってきてバリデーションしようとしました。
自作アノテーション
@Target(AnnotationTarget.CLASS, AnnotationTarget.FIELD)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
@ReportAsSingleViolation
@Constraint(validatedBy = [MyClassValidator::class, MyFieldValidator::class])
annotation class MyAnnotation(
val message: String = "",
val groups: Array<KClass<out Any>> = [],
val payload: Array<KClass<out Payload>> = []
)
// クラスに対するバリデーション想定
class MyClassValidator: ConstraintValidator<MyAnnotation, Any> {
override fun isValid(value: Any?, context: ConstraintValidatorContext?): Boolean {
return false
}
}
// フィールドに対するバリデーション想定
class MyFieldValidator: ConstraintValidator<MyAnnotation, Boolean> {
override fun isValid(value: Boolean?, context: ConstraintValidatorContext?): Boolean {
return false
}
}
すると、以下のようなエラーになりました(最下層のCause以外省略)。
例ではクラスとフィールドの2つのConstraintValidator
を実装していますが、両方とも同じようなエラーとなりました。
スタックトレース
Caused by: java.lang.IllegalArgumentException: Cannot subclass final class ${パッケージ名.Validatorのクラス名}
at org.springframework.cglib.proxy.Enhancer.generateClass(Enhancer.java:657) ~[spring-core-5.1.10.RELEASE.jar:5.1.10.RELEASE]
at org.springframework.cglib.transform.TransformingClassGenerator.generateClass(TransformingClassGenerator.java:33) ~[spring-core-5.1.10.RELEASE.jar:5.1.10.RELEASE]
at org.springframework.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25) ~[spring-core-5.1.10.RELEASE.jar:5.1.10.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$ClassLoaderAwareUndeclaredThrowableStrategy.generate(CglibAopProxy.java:1008) ~[spring-aop-5.1.10.RELEASE.jar:5.1.10.RELEASE]
at org.springframework.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:358) ~[spring-core-5.1.10.RELEASE.jar:5.1.10.RELEASE]
at org.springframework.cglib.proxy.Enhancer.generate(Enhancer.java:582) ~[spring-core-5.1.10.RELEASE.jar:5.1.10.RELEASE]
at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:110) ~[spring-core-5.1.10.RELEASE.jar:5.1.10.RELEASE]
at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:108) ~[spring-core-5.1.10.RELEASE.jar:5.1.10.RELEASE]
at org.springframework.cglib.core.internal.LoadingCache$2.call(LoadingCache.java:54) ~[spring-core-5.1.10.RELEASE.jar:5.1.10.RELEASE]
at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266) ~[na:1.8.0_181]
at java.util.concurrent.FutureTask.run(FutureTask.java) ~[na:1.8.0_181]
at org.springframework.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:61) ~[spring-core-5.1.10.RELEASE.jar:5.1.10.RELEASE]
at org.springframework.cglib.core.internal.LoadingCache.get(LoadingCache.java:34) ~[spring-core-5.1.10.RELEASE.jar:5.1.10.RELEASE]
at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:134) ~[spring-core-5.1.10.RELEASE.jar:5.1.10.RELEASE]
at org.springframework.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:319) ~[spring-core-5.1.10.RELEASE.jar:5.1.10.RELEASE]
at org.springframework.cglib.proxy.Enhancer.createHelper(Enhancer.java:569) ~[spring-core-5.1.10.RELEASE.jar:5.1.10.RELEASE]
at org.springframework.cglib.proxy.Enhancer.createClass(Enhancer.java:416) ~[spring-core-5.1.10.RELEASE.jar:5.1.10.RELEASE]
at org.springframework.aop.framework.ObjenesisCglibAopProxy.createProxyClassAndInstance(ObjenesisCglibAopProxy.java:57) ~[spring-aop-5.1.10.RELEASE.jar:5.1.10.RELEASE]
at org.springframework.aop.framework.CglibAopProxy.getProxy(CglibAopProxy.java:205) ~[spring-aop-5.1.10.RELEASE.jar:5.1.10.RELEASE]
buildDefaultValidatorFactoryを使う場合
Validation.buildDefaultValidatorFactory().validator
で定義されたValidator
を用いる場合はエラーになりませんでした。
対処法
ConstraintValidator
の実装をopen
するとエラーが出なくなりました。
@Target(AnnotationTarget.CLASS, AnnotationTarget.FIELD)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
@ReportAsSingleViolation
@Constraint(validatedBy = [MyClassValidator::class, MyFieldValidator::class])
annotation class MyAnnotation(
val message: String = "",
val groups: Array<KClass<out Any>> = [],
val payload: Array<KClass<out Payload>> = []
)
// クラスに対するバリデーション想定
open class MyClassValidator: ConstraintValidator<MyAnnotation, Any> {
override fun isValid(value: Any?, context: ConstraintValidatorContext?): Boolean {
return false
}
}
// フィールドに対するバリデーション想定
open class MyFieldValidator: ConstraintValidator<MyAnnotation, Boolean> {
override fun isValid(value: Boolean?, context: ConstraintValidatorContext?): Boolean {
return false
}
}
雑感
去年アノテーション自作記事を書いた時はこんなことなかったはずなので、これが起きるのは環境由来のなんかだと思います。
最小環境を作って再現することはできていないので、暇があればやってみようと思います。