Grails3集成Spring Session JDBC时无法获取SessionRepository
背景
尝试在Grails 3.3.15应用中通过Spring Session JDBC将HttpSession持久化到PostgreSQL,参考官方Redis实现指南(无JDBC相关指南)后做了以下配置:
已完成的配置
- build.gradle添加依赖:
compile "org.springframework.session:spring-session-jdbc" compile 'org.springframework.boot:spring-boot-starter-data-jpa'
- application.groovy配置数据源:
spring.datasource.url=System.getenv("DB_URL") spring.datasource.username=System.getenv("DB_USERNAME") spring.datasource.password=System.getenv("DB_PASSWORD")
- application.yml配置Spring Session:
spring: session: store-type: "jdbc" table-name: "SPRING_SESSION"
启动错误
启动应用时触发BeanCreationException,异常栈如下:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.boot.autoconfigure.session.SessionAutoConfiguration$SessionRepositoryValidator': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: No session repository could be auto-configured, check your configuration (session store type is 'jdbc') at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:137) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:407) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1622) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:481) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:312) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:308) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:756) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:542) at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:123) at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:666) at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:353) at org.springframework.boot.SpringApplication.run(SpringApplication.java:300) at grails.boot.GrailsApp.run(GrailsApp.groovy:84) at grails.boot.GrailsApp.run(GrailsApp.groovy:393) at grails.boot.GrailsApp.run(GrailsApp.groovy:380) at grails.boot.GrailsApp$run.call(Unknown Source) at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:136) at com.foo.bar.Application.main(Application.groovy:8)
排查结果
调试发现org.springframework.boot.autoconfigure.session.SessionRepositoryValidator的checkSessionRepository()方法中,sessionRepositoryProvider.getIfAvailable()返回null,导致抛出IllegalArgumentException,说明SessionRepository未被正确配置。
解决方案
1. 修正依赖版本与完整性
Grails 3.3.15对应的Spring Boot版本是1.5.x,需确保Spring Session JDBC版本与Spring Boot兼容(1.5.x对应Spring Session JDBC 1.3.x版本),同时添加Spring JDBC依赖(Spring Session JDBC依赖Spring JDBC而非仅JPA):
修改build.gradle:
compile "org.springframework.session:spring-session-jdbc:1.3.5.RELEASE" compile 'org.springframework.boot:spring-boot-starter-jdbc' compile 'org.springframework.boot:spring-boot-starter-data-jpa' // 保留JPA依赖(如果业务需要)
2. 手动配置SessionRepository Bean
由于Grails自动配置未触发Spring Session JDBC的自动配置,需手动创建JdbcOperationsSessionRepository Bean:
在grails-app/conf/spring/resources.groovy中添加:
import org.springframework.session.jdbc.JdbcOperationsSessionRepository import org.springframework.session.jdbc.config.annotation.web.http.EnableJdbcHttpSession beans = { jdbcOperationsSessionRepository(JdbcOperationsSessionRepository, ref('dataSource'), ref('transactionManager')) { tableName = 'SPRING_SESSION' } } // 启用Spring Session JDBC @EnableJdbcHttpSession(tableName = "SPRING_SESSION") class SpringSessionConfig { }
3. 创建Spring Session所需数据库表
Spring Session JDBC需要特定表结构,执行以下PostgreSQL脚本创建表:
CREATE TABLE SPRING_SESSION ( PRIMARY_ID CHAR(36) NOT NULL, SESSION_ID CHAR(36) NOT NULL, CREATION_TIME BIGINT NOT NULL, LAST_ACCESS_TIME BIGINT NOT NULL, MAX_INACTIVE_INTERVAL INT NOT NULL, EXPIRY_TIME BIGINT NOT NULL, PRINCIPAL_NAME VARCHAR(100), CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID) ); CREATE UNIQUE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID); CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME); CREATE INDEX SPRING_SESSION_IX3 ON SPRING_SESSION (PRINCIPAL_NAME); CREATE TABLE SPRING_SESSION_ATTRIBUTES ( SESSION_PRIMARY_ID CHAR(36) NOT NULL, ATTRIBUTE_NAME VARCHAR(200) NOT NULL, ATTRIBUTE_BYTES BYTEA NOT NULL, CONSTRAINT SPRING_SESSION_ATTRIBUTES_PK PRIMARY KEY (SESSION_PRIMARY_ID, ATTRIBUTE_NAME), CONSTRAINT SPRING_SESSION_ATTRIBUTES_FK FOREIGN KEY (SESSION_PRIMARY_ID) REFERENCES SPRING_SESSION(PRIMARY_ID) ON DELETE CASCADE ); CREATE INDEX SPRING_SESSION_ATTRIBUTES_IX1 ON SPRING_SESSION_ATTRIBUTES (SESSION_PRIMARY_ID);
4. 补全数据源驱动配置
在application.groovy中添加PostgreSQL驱动类配置:
spring.datasource.driverClassName = 'org.postgresql.Driver'
内容的提问来源于stack exchange,提问作者Manish Kapoor

