将带@PostConstruct的Service注入Taglib时启动异常的解决办法
这个问题我之前在Grails项目里也踩过坑,核心原因是Taglib的初始化时机比GORM框架的初始化要早:当你的Service被Taglib注入时,Spring会提前触发Service的初始化逻辑,导致@PostConstruct里的MyDomain.list()在GORM还没完全就绪的阶段就执行了,这时候域类还没被Grails框架托管,自然就抛出了那个IllegalStateException。
下面给你几个可靠的解决思路,按推荐程度排序:
方法一:改用Spring上下文刷新事件触发初始化
把@PostConstruct替换成监听Spring的ContextRefreshedEvent,这个事件会在整个Spring应用上下文完全初始化(包括GORM所有配置、域类映射都就绪)后才触发,从根源上避免时机问题。
修改你的Service代码:
import org.springframework.context.ApplicationListener import org.springframework.context.event.ContextRefreshedEvent class YourService implements ApplicationListener<ContextRefreshedEvent> { @Override void onApplicationEvent(ContextRefreshedEvent event) { // 加个判断避免重复执行(部分场景下这个事件会触发多次) if (!alreadyInitialized) { MyDomain.list().each { createDataSource(it) } alreadyInitialized = true } } private boolean alreadyInitialized = false // 你的其他Service业务方法... }
方法二:在Taglib中懒加载Service
如果你的Taglib不是启动时就必须用到这个Service,可以改成懒加载模式,不在Taglib的字段上直接注入,而是在第一次使用时通过ApplicationContext获取:
import org.springframework.context.ApplicationContext import org.codehaus.groovy.grails.web.taglib.GrailsTagLib class YourTagLib extends GrailsTagLib { ApplicationContext applicationContext private YourService yourService def customTag = { attrs -> // 第一次调用标签时才初始化Service if (!yourService) { yourService = applicationContext.getBean(YourService) } // 后续使用yourService的业务逻辑... } }
这种方式下,Taglib启动时不会触发Service的初始化,直到第一次调用标签时才会去获取Service,这时候GORM已经完全就绪了。
方法三:手动指定Bean初始化顺序(适合Grails 2.x旧版本)
如果是Grails 2.x这类旧版本,你可以在grails-app/conf/spring/resources.groovy里手动指定Service的依赖,确保它在GORM核心组件之后初始化:
beans = { yourService(YourService) { // 依赖sessionFactory和grailsApplication,确保GORM先就绪 dependsOn = ['sessionFactory', 'grailsApplication'] } }
这样Spring会先初始化sessionFactory和grailsApplication,再创建你的Service,@PostConstruct里的域类调用就能正常执行了。
内容的提问来源于stack exchange,提问作者dre

