Skip to content

Commit

Permalink
Groovy-based bean definitions
Browse files Browse the repository at this point in the history
Formerly known as the Grails BeanBuilder, now in Spring proper. Based on spring-projects#355 but heavily refactored and restructured.

Issue: SPR-7123
  • Loading branch information
jhoeller committed Oct 17, 2013
1 parent bb8cf27 commit 8d6406b
Show file tree
Hide file tree
Showing 19 changed files with 2,689 additions and 39 deletions.
48 changes: 39 additions & 9 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ configure(allprojects) { project ->
version = qualifyVersionIfNecessary(version)

ext.aspectjVersion = "1.8.0.M1"
ext.groovyVersion = "1.8.9"
ext.hsqldbVersion = "1.8.0.10"
ext.junitVersion = "4.11"
ext.slf4jVersion = "1.6.1"
ext.gradleScriptDir = "${rootProject.projectDir}/gradle"

apply plugin: "propdeps"
apply plugin: "java"
apply plugin: 'groovy'
apply plugin: "test-source-set-dependencies"
apply from: "${gradleScriptDir}/ide.gradle"

Expand Down Expand Up @@ -50,9 +52,13 @@ configure(allprojects) { project ->
}

compileTestJava {
sourceCompatibility=1.8
targetCompatibility=1.8
compileTestJava.options.compilerArgs += "-parameters"
sourceCompatibility=1.7
targetCompatibility=1.7
}

compileGroovy {
sourceCompatibility=1.6
targetCompatibility=1.6
}

sourceSets.test.resources.srcDirs = ["src/test/resources", "src/test/java"]
Expand All @@ -68,9 +74,9 @@ configure(allprojects) { project ->
repositories {
maven { url "http://repo.spring.io/libs-release" }
maven { url "http://repo.spring.io/milestone" } // for AspectJ 1.8.0.M1
maven { url "https://repository.apache.org/content/repositories/releases" } // tomcat 8 RC3
maven { url "https://repository.apache.org/content/repositories/snapshots" } // tomcat-websocket-* snapshots
maven { url "https://maven.java.net/content/repositories/releases" } // javax.websocket, tyrus
maven { url "https://repository.apache.org/content/repositories/releases" } // tomcat 8 RC3
maven { url "https://repository.apache.org/content/repositories/snapshots" } // tomcat-websocket-* snapshots
maven { url "https://maven.java.net/content/repositories/releases" } // javax.websocket, tyrus
}

dependencies {
Expand Down Expand Up @@ -153,7 +159,6 @@ configure(subprojects - project(":spring-build-src")) { subproject ->

project("spring-build-src") {
description = "Exposes gradle buildSrc for IDE support"
apply plugin: "groovy"

dependencies {
compile gradleApi()
Expand Down Expand Up @@ -249,6 +254,12 @@ project("spring-core") {
include "org/springframework/objenesis/**"
}
}

compileTestJava {
sourceCompatibility=1.8
targetCompatibility=1.8
compileTestJava.options.compilerArgs += "-parameters"
}
}

project("spring-beans") {
Expand All @@ -263,6 +274,26 @@ project("spring-beans") {
}
}

project('spring-beans-groovy') {
description 'Groovy Bean Definitions'
merge.into = project(":spring-beans")

dependencies {
compile(project(":spring-core"))
compile "org.codehaus.groovy:groovy-all:${groovyVersion}"
}

// this module's Java and Groovy sources need to be compiled together
compileJava.enabled=false
sourceSets {
main {
groovy {
srcDir 'src/main/java'
}
}
}
}

project("spring-aop") {
description = "Spring AOP"

Expand Down Expand Up @@ -322,7 +353,7 @@ project("spring-context") {
optional("org.apache.geronimo.specs:geronimo-jms_1.1_spec:1.1")
optional("org.eclipse.persistence:javax.persistence:2.0.0")
optional("org.beanshell:bsh:2.0b4")
optional("org.codehaus.groovy:groovy-all:1.8.9")
optional("org.codehaus.groovy:groovy-all:${groovyVersion}")
optional("org.jruby:jruby:1.7.2")
optional("joda-time:joda-time:2.2")
optional("org.slf4j:slf4j-api:${slf4jVersion}")
Expand Down Expand Up @@ -827,7 +858,6 @@ configure(rootProject) {
description = "Spring Framework"

apply plugin: "docbook-reference"
apply plugin: "groovy"
// apply plugin: "detect-split-packages"
apply from: "${gradleScriptDir}/jdiff.gradle"

Expand Down
1 change: 1 addition & 0 deletions settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ rootProject.name = "spring"
include "spring-aop"
include "spring-aspects"
include "spring-beans"
include "spring-beans-groovy"
include "spring-context"
include "spring-context-support"
include "spring-core"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.beans.factory.groovy

import groovy.xml.StreamingMarkupBuilder
import org.springframework.beans.factory.config.BeanDefinitionHolder
import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate
import org.w3c.dom.Element

/**
* Used by GroovyBeanDefinitionReader to read a Spring XML namespace expression
* in the Groovy DSL.
*
* @author Jeff Brown
* @author Juergen Hoeller
* @since 4.0
*/
@groovy.transform.PackageScope
class GroovyDynamicElementReader extends GroovyObjectSupport {

private final String rootNamespace

private final Map<String, String> xmlNamespaces

private final BeanDefinitionParserDelegate delegate

private final GroovyBeanDefinitionWrapper beanDefinition

protected final boolean decorating;

private boolean callAfterInvocation = true


public GroovyDynamicElementReader(String namespace, Map<String, String> namespaceMap,
BeanDefinitionParserDelegate delegate, GroovyBeanDefinitionWrapper beanDefinition, boolean decorating) {
super();
this.rootNamespace = namespace
this.xmlNamespaces = namespaceMap
this.delegate = delegate
this.beanDefinition = beanDefinition;
this.decorating = decorating;
}


@Override
public Object invokeMethod(String name, Object args) {
if (name.equals("doCall")) {
def callable = args[0]
callable.resolveStrategy = Closure.DELEGATE_FIRST
callable.delegate = this
def result = callable.call()

if (this.callAfterInvocation) {
afterInvocation()
this.callAfterInvocation = false
}
return result
}

else {
StreamingMarkupBuilder builder = new StreamingMarkupBuilder();
def myNamespace = this.rootNamespace
def myNamespaces = this.xmlNamespaces

def callable = {
for (namespace in myNamespaces) {
mkp.declareNamespace([(namespace.key):namespace.value])
}
if (args && (args[-1] instanceof Closure)) {
args[-1].resolveStrategy = Closure.DELEGATE_FIRST
args[-1].delegate = builder
}
delegate."$myNamespace"."$name"(*args)
}

callable.resolveStrategy = Closure.DELEGATE_FIRST
callable.delegate = builder
def writable = builder.bind(callable)
def sw = new StringWriter()
writable.writeTo(sw)

Element element = this.delegate.readerContext.readDocumentFromString(sw.toString()).documentElement
this.delegate.initDefaults(element)
if (this.decorating) {
BeanDefinitionHolder holder = this.beanDefinition.beanDefinitionHolder;
holder = this.delegate.decorateIfRequired(element, holder, null)
this.beanDefinition.setBeanDefinitionHolder(holder)
}
else {
def beanDefinition = this.delegate.parseCustomElement(element)
if (beanDefinition) {
this.beanDefinition.setBeanDefinition(beanDefinition)
}
}
if (this.callAfterInvocation) {
afterInvocation()
this.callAfterInvocation = false
}
return element
}
}

/**
* Hook that subclass or anonymous classes can overwrite to implement custom behavior
* after invocation completes.
*/
protected void afterInvocation() {
// NOOP
}

}
Loading

0 comments on commit 8d6406b

Please sign in to comment.