Spring Boot 2.0.2.RELEASE 的模型验证异常

Model validation exception with Spring Boot 2.0.2.RELEASE

我在迁移到 Spring Boot 2.0.2.RELEASE 时遇到一些问题。 似乎 hibernate 验证器试图验证 Enum Kotlin 类型的默认文件。

有一个简单的测试代码:

gradle.build

buildscript {
    ext {
        kotlinVersion = '1.2.41'
        springBootVersion = '2.0.2.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
        classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}")
        classpath("org.jetbrains.kotlin:kotlin-allopen:${kotlinVersion}")
    }
}

plugins {
    id "io.spring.dependency-management" version "1.0.5.RELEASE"
}

apply plugin: 'kotlin'
apply plugin: 'kotlin-spring'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

group = test
sourceCompatibility = 1.8
compileKotlin {
    kotlinOptions.jvmTarget = "1.8"
}

compileTestKotlin {
    kotlinOptions.jvmTarget = "1.8"
}

repositories {
    mavenCentral()
}

dependencies {

    compile("org.springframework.boot:spring-boot-starter-web")
    compile('org.springframework.boot:spring-boot-starter-data-jpa')

    compile 'com.h2database:h2'
    compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
    compile('org.jetbrains.kotlin:kotlin-reflect')

    testCompile('org.springframework.boot:spring-boot-starter-test')

}

主要class

@EnableJpaRepositories
@SpringBootApplication
class SimpleApplication

fun main(args: Array<String>) {
    run(SimpleApplication::class.java, *args)
}

枚举类型的模型

@Entity
data class SimpleModel(

    @Id
    @GeneratedValue
    var id: Long? = null,

    @get:NotEmpty
    @ElementCollection
    @JoinTable(name = "test_group", joinColumns = [JoinColumn(name = "id")])
    @Column(name = "group")
    @Enumerated(STRING)
        var groups: MutableSet<Group>? = null
)


enum class Group(val groupName: String, val groupDescription: String) {
    TEST1("TestGroup1", "Just for test"),
    TEST2("TestGroup2", "Just for test")
}

测试class

@RunWith(SpringRunner::class)
@TestConfiguration
@SpringBootTest(webEnvironment = RANDOM_PORT)
class SimpleServiceTest {

        @Autowired
        lateinit var applicationContext: ApplicationContext

        @Test
        fun testValidationError() {
            val model = SimpleModel().apply {
                groups = mutableSetOf(TEST1)
            }

            val resourceName = "${model.javaClass.simpleName.decapitalize()}.group[1]"

            val bindingResult = BeanPropertyBindingResult(TEST1, resourceName)

            val validator = applicationContext.getBean("mvcValidator", Validator::class.java)

            validator.validate(TEST1, bindingResult)
        } }

测试属性application.yml

spring:
  datasource:
    driver-class-name: org.h2.Driver
    jdbcUrl: jdbc:h2:mem:test
  jpa:
    properties:
      database: h2
      hibernate:
        hbm2ddl:
          ddl: true
          auto: create-drop
      javax:
        persistence:
          validation:
            mode: none

异常信息

java.lang.ArrayIndexOutOfBoundsException: 2

    at java.util.Arrays$ArrayList.get(Arrays.java:3841)
    at org.hibernate.validator.internal.metadata.aggregated.ParameterMetaData$Builder.build(ParameterMetaData.java:169)
    at org.hibernate.validator.internal.metadata.aggregated.ExecutableMetaData$Builder.findParameterMetaData(ExecutableMetaData.java:435)
    at org.hibernate.validator.internal.metadata.aggregated.ExecutableMetaData$Builder.build(ExecutableMetaData.java:388)
    at org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataImpl$BuilderDelegate.build(BeanMetaDataImpl.java:788)
    at org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataImpl$BeanMetaDataBuilder.build(BeanMetaDataImpl.java:648)
    at org.hibernate.validator.internal.metadata.BeanMetaDataManager.createBeanMetaData(BeanMetaDataManager.java:192)
    at org.hibernate.validator.internal.metadata.BeanMetaDataManager.lambda$getBeanMetaData[=17=](BeanMetaDataManager.java:160)
    at java.util.concurrent.ConcurrentMap.computeIfAbsent(ConcurrentMap.java:324)
    at org.hibernate.validator.internal.metadata.BeanMetaDataManager.getBeanMetaData(BeanMetaDataManager.java:159)
    at org.hibernate.validator.internal.engine.ValidationContext$ValidationContextBuilder.forValidate(ValidationContext.java:566)
    at org.hibernate.validator.internal.engine.ValidatorImpl.validate(ValidatorImpl.java:155)
    at org.springframework.validation.beanvalidation.SpringValidatorAdapter.validate(SpringValidatorAdapter.java:104)
    at org.springframework.boot.autoconfigure.validation.ValidatorAdapter.validate(ValidatorAdapter.java:64)
    at com.test.service.SimpleServiceTest.testValidationError(SimpleServiceTest.kt:36)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:73)
    at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:83)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access[=17=]0(ParentRunner.java:58)
    at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

Spring Boot 1.5.10 不会发生此异常。

有没有人遇到过同样的问题?我在互联网上和这里都没有找到任何答案。有人可以帮帮我吗?

所以问题如下:

  • 反射API提供的构造函数是:com.test.simple.Group(java.lang.String $enum$name, int $enum$ordinal, java.lang.String groupName, java.lang.String groupDescription)所以它有四个参数。我想名称和序号已传递给构造函数;
  • KotlinReflectionParameterNameDiscoverer提供的参数名称只有[groupName, groupDescription](所以不考虑附加参数)。

KotlinReflectionParameterNameDiscoverer应该尽量与Java反射APIreturns保持一致。否则它绝对不能用作 HV ParameterNameProvider.

的一部分