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

Posted

技术标签:

【中文标题】Spring Boot 2.0.2.RELEASE 的模型验证异常【英文标题】:Model validation exception with Spring Boot 2.0.2.RELEASE 【发布时间】:2018-11-12 08:14:22 【问题描述】:

我在迁移到 Spring Boot 2.0.2.RELEASE 时遇到了一些问题。 似乎休眠验证器试图验证 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')


主类

@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")

测试类

@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$0(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$1.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$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.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 不会发生此异常。

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

【问题讨论】:

您是否知道仅使用 Java 类而不使用 Kotlin 来重现相同问题的方法? 嗯...它可以按预期与 Java 类一起使用。很可能是因为在 Java 中 Enum 类型的字段 nameordinal 具有私有范围,但在 Kotlin 中这些字段是公共的。 【参考方案1】:

所以问题如下:

反射 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 反射 API 返回的内容保持一致。否则它绝对不能用作 HV ParameterNameProvider 的一部分。

【讨论】:

所以,实际上问题仍然存在。如果 Spring 社区的人会看到此评论,请查看本期的讨论hibernate.atlassian.net/browse/HV-1619 @xpap 我编辑了我的答案以综合该问题。我真的不明白为什么他们会在没有进一步讨论的情况下关闭它。肯定有一些事情需要他们注意。

以上是关于Spring Boot 2.0.2.RELEASE 的模型验证异常的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot自动配置源码解析(基于Spring Boot 2.0.2.RELEASE)

升级到 Spring Boot 2.0.2 后 Spring Security .permitAll() 不再有效

eclipse快速创建一个Spring Boot应用

插件 [id: 'org.springframework.boot', version: '2.0.2.RELEASE'] 未找到

Spring Boot:CORS 问题

spring-boot-starter-parent的主要作用