【发布时间】:2016-06-23 21:46:11
【问题描述】:
我正在开发一个拆分的 Grails 项目,请参见此处:grails3 using a plugin for domain classes, specifically spring security User/Role
基本上,我正在尝试将我的域类放入可用于多个其他项目的插件中。
我在尝试启动服务器时收到此异常:
ERROR org.springframework.boot.SpringApplication - Application startup failed
java.lang.ArrayIndexOutOfBoundsException: 0
at wcommon.WebsiteRole.<init>(WebsiteRole.groovy:15)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
at org.springsource.loaded.ri.ReflectiveInterceptor.jlrConstructorNewInstance(ReflectiveInterceptor.java:1075)
at org.codehaus.groovy.reflection.CachedConstructor.invoke(CachedConstructor.java:83)
at org.codehaus.groovy.runtime.callsite.ConstructorSite$ConstructorSiteNoUnwrapNoCoerce.callConstructor(ConstructorSite.java:105)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:60)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
at BootStrap$_closure1.doCall(BootStrap.groovy:9)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springsource.loaded.ri.ReflectiveInterceptor.jlrMethodInvoke(ReflectiveInterceptor.java:1426)
at org.codehaus.groovy.ron.CachedMethod.invoke(CachedMethod.java:93)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:294)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1021)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1086)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1021)
at groovy.lang.Closure.call(Closure.java:426)
at groovy.lang.Closure.call(Closure.java:420)
at grails.util.Environment.evaluateEnvironmentSpecificBlock(Environment.java:437)
at grails.util.Environment.executeForEnvironment(Environment.java:430)
at grails.util.Environment.executeForCurrentEnvironment(Environment.java:406)
at org.grails.web.servlet.boostrap.DefaultGrailsBootstrapClass.callInit(DefaultGrailsBootstrapClass.java:62)
at org.grails.web.servlet.context.GrailsConfigUtils.executeGrailsBootstraps(GrailsConfigUtils.java:65)
at org.grails.plugins.web.servlet.context.BootStrapClassRunner.onStartup(BootStrapClassRunner.groovy:53)
at grails.boot.config.GrailsApplicationPostProcessor.onApplicationEvent(GrailsApplicationPostProcessor.groovy:240)
at grails.boot.config.GrailsApplicationPostProcessor.onApplicationEvent(GrailsApplicationPostProcessor.groovy)
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:163)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:136)
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:381)
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:335)
at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:855)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.finishRefresh(EmbeddedWebApplicationContext.java:140)
at org.spmework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:541)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:118)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:766)
at org.springframework.boot.SpringApplication.createAndRefreshContext(SpringApplication.java:361)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:307)
at grails.boot.GrailsApp.run(GrailsApp.groovy:55)
at grails.boot.GrailsApp.run(GrailsApp.groovy:365)
at grails.boot.GrailsApp.run(GrailsApp.groovy:354)
at grails.boot.GrailsApp$run.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:133)
at webp.Application.main(Application.groovy:8)
:WebP:bootRun FAILED
我完全不知道为什么会这样。 有问题的行是对 this() 的调用 为什么会抛出这样的异常? 我最初的反应是删除 this() 并向类添加一个空的默认构造函数,但这显然破坏了依赖注入,这导致了我在上面喜欢的问题中描述的问题。
我这边涉及的代码是:
package wcommon
import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString
@EqualsAndHashCode(includes='authority')
@ToString(includes='authority', includeNames=true, includePackage=false)
class WebsiteRole implements Serializable {
private static final long serialVersionUID = 1
String authority
WebsiteRole(String authority) {
**this();** // line 15
this.authority = authority
}
static constraints = {
authority blank: false, unique: true
}
static mapping = {
cache true
}
}
import grails.core.GrailsApplication
import wcommon.WebsiteUserWebsiteRole;
class BootStrap {
GrailsApplication grailsApplication
def init = { servletContext ->
**WebsiteUserWebsiteRole.initDefaults(grailsApplication)** //line 9
}
def destroy = {
}
}
initDefaults 函数如下所示:
**static def initDefaults(GrailsApplication grailsApplication) {
def patientRole = new WebsiteRole("ROLE_PATIENT").save();
def adminRole = new WebsiteRole("ROLE_ADMIN").save()
def doctorRole = new WebsiteRole("ROLE_DOCTOR").save()
def nurseRole = new WebsiteRole("ROLE_NURSE").save()
def serverRole = new WebsiteRole("ROLE_SERVER").save()
def user1 = new WebsiteUser("admin", "password").save()
WebsiteUserWebsiteRole.create user1, adminRole, true
def user2 = new WebsiteUser("doctor", "password").save()
WebsiteUserWebsiteRole.create user2, doctorRole, true
def user3 = new WebsiteUser("nurse", "password").save()
WebsiteUserWebsiteRole.create user3, nurseRole, true
}**
编辑: 使用 Grails 3.1.1 版
java -version 是 1.7.0_79
spring 安全插件是 'org.grails.plugins:spring-security-core:3.0.3'
我不太确定我使用的是哪个数据库版本,我只是使用默认包含的 h2,依赖项只是说: 运行时“com.h2database:h2”
项目设置通常是我的域类在一个项目中,这是一个插件项目。 有问题的项目包括域类通过 编译项目(':WebCommon') 在依赖项中。编译工作正常。
编辑 2: 因此,如果我删除 this() 并添加一个默认构造函数,它不会抛出异常,但是此类中 spring 安全服务的依赖注入不起作用:
package wcommon
import grails.plugin.springsecurity.SpringSecurityService;
import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString
@EqualsAndHashCode(includes='username')
@ToString(includes='username', includeNames=true, includePackage=false)
class WebsiteUser implements Serializable {
private static final long serialVersionUID = 1
public SpringSecurityService springSecurityService
String username
String password
boolean enabled = true
boolean accountExpired
boolean accountLocked
boolean passwordExpired
WebsiteUser() {
}
WebsiteUser(String username, String password) {
this.username = username
this.password = password
}
Set<WebsiteRole> getAuthorities() {
WebsiteUserWebsiteRole.findAllByWebsiteUser(this)*.websiteRole
}
def beforeInsert() {
encodePassword()
}
def beforeUpdate() {
if (isDirty('password')) {
encodePassword()
}
}
protected void encodePassword() {
println springSecurityService // prints null
password = springSecurityService?.passwordEncoder ? springSecurityService.encodePassword(password) : password
}
static transients = ['springSecurityService']
static constraints = {
username blank: false, unique: true
password blank: false
}
static mapping = {
password column: '`password`'
}
}
在我将属性从 def springSecurityService 更改为 public SpringSecurityService 后,它告诉我间接依赖于 HttpServletRequest。将这些依赖项添加到插件项目可以解决这个问题,但这并不会改变注入不起作用的任何内容:
compile "org.springframework.boot:spring-boot-starter-tomcat"
compile "org.grails:grails-web-boot"
我认为我对 User 和 Role 类的更改以修复 this() 问题导致了依赖注入问题。但也许我错了,这两个问题都是由其他原因引起的,可能与我的项目设置有关。 我不知道,这一切真的很混乱,从昨天开始就一直在和 grails 战斗。
编辑: 为了让它工作,我现在首先通过删除 this() 并添加默认构造函数来修复异常,然后我将 springSecurityService 注入到我的主项目的 Bootstrap 中,用于在插件项目中的类上设置静态公共字段这需要springSecurityService。 丑陋,但它比我迄今为止尝试过的任何东西都好。 ... 好吧其实现在这个简单的控制器测试:
import grails.converters.JSON
import grails.plugin.springsecurity.annotation.Secured
@Secured(['ROLE_ADMIN', 'ROLE_SERVER'])
class ApiController {
def index() {
render getAuthenticatedUser() as JSON
}
}
抛出关于 getAuthenticatedUser() 的 MissingMethodException
那个控制器是通过 create-controller 添加的 我猜这整个“将项目分成多个部分”根本不起作用,并且破坏了一些非常非常深入的东西,导致所有这些问题都出现了。我认为与其尝试拆分我的项目,不如创建一个配置了一个标志的 Web 应用程序项目,该标志告诉它激活和停用哪些功能....
如果有人可以告诉我如何使用公共库中的公共域对象正确拆分 grails 项目,请查看此问题并在此处发布: grails3 using a plugin for domain classes, specifically spring security User/Role
【问题讨论】:
-
请显示
wcommon.WebsiteRole的代码 - 粗体15行和BootStrap.groovy的代码 - 粗体9行 -
为什么要在没有无参数构造函数的类中调用
this()? -
该代码不是我写的,它是由 grails spring security 插件通过命令 s2-quickstart 生成的。添加 this() 解决了这个问题,但它不应该存在,从我在别处读到的内容来看,应该以某种方式生成无参数构造函数,并且对于依赖注入很重要。添加无参数构造函数会导致我的用户类中的依赖注入失败。在另一个项目中,生成的 Role 和 User 类在未实现时也会调用 this() 并且它在那里工作。
-
此外,您使用的是哪个版本的 Grails、spring-security、db 和 jdk?
-
“我没有编写该代码,它是由 grails spring 安全插件通过命令 s2-quickstart 生成的。” - 这让我感到惊讶。这是插件中的一个错误。