【问题标题】:Override a Service in Grails using Spring Bean declaration使用 Spring Bean 声明覆盖 Grails 中的服务
【发布时间】:2016-03-30 23:23:07
【问题描述】:

我正在创建一个包含 CustomService 的新插件,该插件旨在从现有插件中替换现有服务。按照在自定义安全实现中找到并显示为here 的模式,我已将配置添加到 resources.groovy、oldService(path.to.new.CustomService)。我还尝试将所有注入的类添加到该服务的闭包中。 (实际的服务名称是代码块中的 RegistrationPersonRegistrationCompositeService 和 NewRegistrationPersonRegistrationCompositeService)

我不希望原始应用程序代码对新插件有任何引用。但是,应用程序级别的 BuildConfig 将需要 plugin.location 条目。我的 resource.groovy 模组在新插件中。我在这方面的努力没有成功。我是否修改了错误的 resources.groovy?如果需要在原始应用程序代码中进行此更改,我将无法保持原始代码不变。我没有扩展原始服务,也没有使用覆盖注释。我的意图是在启动时替换服务(Spring bean)。新插件依赖于旧插件,试图管理加载这些类的操作顺序。

旧服务之前注入控制器是否重要?这将需要我以相同的方式覆盖新插件中的控制器并为所需的行为注入正确的服务?

我发现文档显示在插件中,resources.groovy 将被忽略。此外,将 resources.groovy 构建成战争也是有问题的。我还没有找到解决方案。我没有得到任何可以分享的错误,只是缺少所需的行为;原始服务正在处理请求。

//was resource.groovy - now renamed to serviceOverRide.groovy - still located in \grails-app\conf\spring of plugin
//tried this with and without the BeanBuilder. Theory: I'm missing the autowire somehow
import org.springframework.context.ApplicationContext
import grails.spring.BeanBuilder

def bb = new BeanBuilder()

bb.beans {
    registrationPersonRegistrationCompositeService(path.to.services.registration.NewRegistrationPersonRegistrationCompositeService) { bean ->
        bean.autowire = true
        registrationRestrictionCompositeService = ref("registrationRestrictionCompositeService")
        registrationPersonTermVerificationService = ref("registrationPersonTermVerificationService")
    }
    classRegistrationController(path.to.services.registration.ClassRegistrationController) { bean ->
        bean.autowire = true
        selfServiceLookupService = ref("selfServiceLookupService")
        registrationPersonRegistrationCompositeService = ref("registrationPersonRegistrationCompositeService")
    }
}
ApplicationContext appContext = bb.createApplicationContext()

附加信息:在 PluginGrailsPlugin.groovy 中添加了以下行。原始服务仍在处理这些请求

def dependsOn = ['appPersonRegistration': '1.0.20  > *']
List loadAfter = ['appPersonRegistration']

def doWithSpring = {
    registrationPersonCourseRegistrationCompositeService(path.to.new.registration.TccRegistrationPersonCourseRegistrationCompositeService)
}

def doWithApplicationContext = { applicationContext ->
    SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL)
    DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext.getBeanFactory()
    beanFactory.registerBeanDefinition("registrationPersonCourseRegistrationCompositeService", BeanDefinitionBuilder.rootBeanDefinition(TccRegistrationPersonCourseRegistrationCompositeService.class.getName()).getBeanDefinition())
}

【问题讨论】:

    标签: spring grails dependency-injection


    【解决方案1】:

    我强烈建议您阅读Plugins 上的文档部分。我之所以推荐这个是因为插件:

    • 不要包含或使用resources.groovy
    • 通过doWithSpring提供一种实现spring应用的方法

    按照文档中的信息,在应用程序上下文中覆盖服务应该没有问题。

    您必须使用doWithSpring 实现对应用程序上下文的更改,这是解决问题的关键。

    【讨论】:

    • doWithAplicationContext 是该解决方案的一部分。它仍然无法加载 bean,但根本原因似乎是 bean 本身依赖于其他注册/注入的 bean。请参阅使用 resources.groovy / beans 声明以及将适用的 bean 链接到此声明的闭包的示例:@ 987654322@ 用于 SpringSecurity bean 及其注入的 bean。我的挑战是对 BeanFactory 和 doWithApplicationContext 做同样的事情。使用嵌套 bean 看到此问题或解决方案?
    【解决方案2】:

    在这个实现中,我在服务中有一个实用程序方法,我试图为其提供覆盖。问题是,Aspect 作为代理工作,并且必须覆盖直接从另一个类调用的方法。在我的 classRegistrationController 中,我调用了服务 processRegistration(),而后者又调用了 applyRules()。使用的仅示例方法名称。由于该服务正在调用它自己的实用程序,因此代理/包装器没有机会绕过对 applyRules() 的调用。一旦发现这一点,我就以这种方式重构了代码:控制器像往常一样调用 processRegistration。返回后,对服务进行另一个调用,processLocalRules()。新方法是一个空占位符,旨在被客户端的自定义逻辑覆盖。带有 Aspect 的插件现在可以使用 resources.groovy。我更喜欢 Joshua 解释的 doWithSpring ,原因是:我的意图是让插件在不修改原始 app-config 的情况下工作;否则 resource.groovy 是一种有效的方法。赞成 Joshua 的答案,因为它确实满足要求并且更清洁。谢谢!

    【讨论】:

      猜你喜欢
      • 2013-02-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-31
      • 2017-07-22
      • 1970-01-01
      相关资源
      最近更新 更多