简体   繁体   中英

Load spring beans from custom groovy files in grails app

Trying to load spring beans from custom groovy file in Grails 2.3.7. I know this question has been asked before, but after hours of searching, I'm unable to find a consistent approach that loads from the classpath.

The Goal

  • Modularize resources.groovy into multiple custom resource files
  • Put custom resource files in standard location: grails-app/conf/spring
  • Use plugin to do the magic; minimize overhead

Tried...

//## grails-app/conf/spring/MyBeansConfig.groovy 
beans {
   testsvc(TestService){
      msg = 'hello'
   }
}

Note above, I'm using beans {} , not beans = {} , apparently it makes a difference:

resources.groovy

This works...

beans = {
  loadBeans 'file:C:\\Proj\Test1\grails-app\\conf\\spring\\MyBeansConfig.groovy'
}

...and according to docs , this should too, but doesn't:

importBeans("classpath:*MyBeansConfig.groovy")

UPDATE - WORKING OPTIONS

(working for Grails 2.3.7)

Option 1: (src/java)

Following lukelazarovic advice, this approach works since Grails automatically copies (not compiles) groovy files in src/java to the classpath; works fine in eclipse and with war deployment:

//bean config files here
src/java/MyBeansConfig.groovy
src/java/FooBeansConfig.groovy

//resources.groovy
loadBeans('classpath*:*BeansConfig.groovy')

Options 2: (grails-app/conf/spring)

This approach allows for custom bean config files in grails-app/conf/spring (credits to ideascultor and mark.esher )

   //bean config files here
   grails-app/conf/spring/MyBeansConfig.groovy

   //## resources.groovy
   //for eclipse/local testing
   loadBeans('file:./grails-app/conf/spring/*BeansConfig.groovy')
   //for WAR/classpath 
   loadBeans('classpath*:*BeansConfig.groovy')


   //## BuildConfig.groovy
   grails.war.resources = { stagingDir, args ->
      copy(todir: "${stagingDir}/WEB-INF/classes/spring") {
         fileset(dir:"grails-app/conf/spring") {
            include(name: "*BeansConfig.groovy")
            exclude(name: "resources.groovy")        
            exclude(name: "resources.xml")
         }
      }
   }

Options 3: (Custom Plugin)

If you're using custom plugins, this approach is ideal; boiler plate config gets refactored into the plugin:

Plugin Config

   //## scripts/_Events.groovy
   eventCreateWarStart = { warName, stagingDir ->
      def libDir = new File("${stagingDir}/WEB-INF/classes/spring")

      ant.copy(todir: libDir) {
         fileset(dir:"grails-app/conf/spring") {
            include(name: "*BeansConfig.groovy")
            exclude(name: "resources.groovy")
            exclude(name: "resources.xml")
         }
      }
   }

   //## MyGrailsPlugin.groovy
   def doWithSpring = {       
      loadBeans('file:./grails-app/conf/spring/*BeansConfig.groovy')
      loadBeans('classpath*:*BeansConfig.groovy') 
   }

Grails App

No config!...just create your bean config files using the *BeansConfig.groovy convention, good to go!

//bean config files here
grails-app/conf/spring/MyBeansConfig.groovy

Update 9/24/2015

Found some issues with option 3:

  • loading of duplicate resource files
  • spring resources not configured correctly for test-app

We managed to fix the above issue such that any resource files in grails-app/conf/spring work the same when executing Grails in eclipse, WAR, test-app, etc.

First step: we created a class to have more control over locating and loading resource files; this was necessary as some files were being loaded more than once due to cross-referenced beans. We're using a plugin to encapsulate this functionality, so this class lives in that plugin:

class CustomResourceLoader {
   static loadSpringBeans(BeanBuilder bb){         
         def files = ['*'] //load all resource files 
         //match resources using multiple methods
         def matchedResourceList = []
         files.each {
            matchedResourceList +=
               getPatternResolvedResources("file:./grails-app/conf/spring/" + it + ".groovy").toList()
            matchedResourceList +=
               getPathMatchedResources("classpath*:spring/" + it + ".groovy").toList()
         }

         //eliminate duplicates resource loaded from recursive reference
         def resourceMap = [:]       
         matchedResourceList.each{
            if(it) resourceMap.put(it.getFilename(), it) 
         }           
         //make resources.groovy first in list
         def resourcesFile = resourceMap.remove("resources.groovy")
         if(!resourcesFile)
            throw new RuntimeException("resources.groovy was not found where expected!")

         def resourcesToLoad = [resourcesFile]
         resourceMap.each{k,v -> resourcesToLoad << v }         
         log.debug("Spring resource files about to load: ")        
         resourcesToLoad.each{ bb.loadBeans(it) }
   }

   static def getPatternResolvedResources(path){
      ResourcePatternResolver resourcePatternResolver =
         new PathMatchingResourcePatternResolver();
      return resourcePatternResolver.getResources(path);
   }

   static def getPathMatchedResources(path){
      return new PathMatchingResourcePatternResolver().getResources(path)
   }
}

Removed BeansConfig.groovy suffix; WAR generation now picks up any resources in grails-app/conf/spring

plugin, _Events.groovy
eventCreateWarStart = { warName, stagingDir ->
      def libDir = new File("${stagingDir}/WEB-INF/classes/spring")
      ant.copy(todir: libDir) {
         fileset(dir:"grails-app/conf/spring") {
            include(name: "*.groovy")          
            exclude(name: "resources.xml")
         }
      }
   }
}

In the plugin definition file, call the loader from doWithSpring() , passing BeanBuilder (the delegate) as the argument (very important):

def doWithSpring = {       
   CustomResourceLoader.loadSpringBeans(delegate)
}

That's it, there is no requirement on users of the plugin aside from creating custom resource files in grails-app/conf/spring

I had a similar problem just a few days ago, with a groovy configuration file that I added into grails-app/conf . While this works with other resources (they are copied and available on the classpath), the problem with the groovy file was simply that it was compiled and the class file was included, ie not the groovy file itself.

I didn't find any good documentation on how this should be done and finally just added it to web-app/WEB-INF/classes . Groovy files placed here will be copied (not compiled) and available on the classpath.

I had the same problem with custom XML files in Grails 2.1.2.

Having XML resources in grails-app/conf/spring didn't work in production environment AFAIR.

To make it working both in development and production environments I finally put the resources into src/java . I think you can achieve the same result by putting your groovy files into src/groovy .

We can import beans from different groovy/xml file in the following way too : - use the following in resources.groovy -

importBeans 'file:camel-beans.groovy' OR importBeans('classpath:/camel-config.xml')

Place camel-beans.groovy along with resources.groovy and provide package as "package spring" for first case, otherwise put it in web app classpath and use the second way to do it.

If your resources.groovy is at following path

grails-app/conf/spring/resources.groovy

and your camel-beans.groovy is at following path

grails-app/conf/spring/camel-beans.groovy

then you can reference camel-beans.groovy in resources.groovy file by adding following line in resources.groovy

importBeans('file:**/camel-beans.groovy')

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM