Попытка загрузить bean-компоненты Spring из пользовательского файла groovy в Grails 2.3.7.
Я знаю, что этот вопрос уже задавали раньше, но после нескольких часов поиска я не смог найти последовательный подход, который загружается из пути к классам.
Цель
- Модульность
resources.groovy
в несколько пользовательских файлов ресурсов - Поместите пользовательские файлы ресурсов в стандартное расположение:
grails-app/conf/spring
- Используйте плагин, чтобы творить чудеса; минимизировать накладные расходы
Пытался...
//## grails-app/conf/spring/MyBeansConfig.groovy
beans {
testsvc(TestService){
msg = 'hello'
}
}
Обратите внимание, что я использую beans {}
, а не beans = {}
, очевидно это имеет значение:
resources.groovy
Это работает...
beans = {
loadBeans 'file:C:\\Proj\Test1\grails-app\\conf\\spring\\MyBeansConfig.groovy'
}
...и, согласно документам, это тоже должно, но не:
importBeans("classpath:*MyBeansConfig.groovy")
ОБНОВЛЕНИЕ - РАБОЧИЕ ВАРИАНТЫ
(работает с Grails 2.3.7)
Вариант 1: (источник/java)
Следуя совету lukelazarovic, этот подход работает, поскольку Grails автоматически копирует (а не компилирует) файлы groovy в src/java
в classpath; отлично работает в eclipse и с военным развертыванием:
//bean config files here
src/java/MyBeansConfig.groovy
src/java/FooBeansConfig.groovy
//resources.groovy
loadBeans('classpath*:*BeansConfig.groovy')
Вариант 2: (grails-app/conf/spring)
Этот подход позволяет создавать пользовательские файлы конфигурации bean-компонентов в grails-app/conf/spring
(спасибо автор идей и 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")
}
}
}
Варианты 3: (Пользовательский плагин)
Если вы используете пользовательские плагины, этот подход идеален; Конфигурация котла переработана в плагин:
Конфигурация плагина
//## 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
Никакой конфигурации!... просто создайте файлы конфигурации bean-компонента, используя соглашение *BeansConfig.groovy
, готово!
//bean config files here
grails-app/conf/spring/MyBeansConfig.groovy
Обновление от 24.09.2015
Обнаружены некоторые проблемы с вариантом 3:
- загрузка дубликатов файлов ресурсов
- ресурсы Spring неправильно настроены для
test-app
Нам удалось исправить указанную выше проблему, чтобы все файлы ресурсов в grails-app/conf/spring
работали одинаково при выполнении Grails в eclipse, WAR, test-app и т. д.
Первый шаг: мы создали класс, чтобы лучше контролировать поиск и загрузку файлов ресурсов; это было необходимо, так как некоторые файлы загружались более одного раза из-за перекрестных ссылок bean-компонентов. Мы используем плагин для инкапсуляции этой функциональности, поэтому этот класс живет в этом плагине:
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)
}
}
Убран суффикс BeansConfig.groovy
; Генерация WAR теперь берет любые ресурсы в 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")
}
}
}
}
В файле определения плагина вызовите загрузчик из doWithSpring()
, передав BeanBuilder
(делегат) в качестве аргумента (очень важно):
def doWithSpring = {
CustomResourceLoader.loadSpringBeans(delegate)
}
Вот и все, от пользователей плагина не требуется ничего, кроме создания пользовательских файлов ресурсов в grails-app/conf/spring
.