Groovy-based bean definitions
Formerly known as the Grails BeanBuilder, now in Spring proper. Based on https://github.com/spring-projects/spring-framework/pull/355 but heavily refactored and restructured. Issue: SPR-7123
This commit is contained in:
parent
bb8cf277fb
commit
8d6406bbae
48
build.gradle
48
build.gradle
|
|
@ -13,6 +13,7 @@ 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"
|
||||
|
|
@ -20,6 +21,7 @@ configure(allprojects) { project ->
|
|||
|
||||
apply plugin: "propdeps"
|
||||
apply plugin: "java"
|
||||
apply plugin: 'groovy'
|
||||
apply plugin: "test-source-set-dependencies"
|
||||
apply from: "${gradleScriptDir}/ide.gradle"
|
||||
|
||||
|
|
@ -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"]
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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()
|
||||
|
|
@ -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") {
|
||||
|
|
@ -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"
|
||||
|
||||
|
|
@ -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}")
|
||||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,811 @@
|
|||
/*
|
||||
* 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 java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import groovy.lang.Binding;
|
||||
import groovy.lang.Closure;
|
||||
import groovy.lang.GString;
|
||||
import groovy.lang.GroovyObject;
|
||||
import groovy.lang.GroovyObjectSupport;
|
||||
import groovy.lang.GroovyShell;
|
||||
import groovy.lang.GroovySystem;
|
||||
import groovy.lang.MetaClass;
|
||||
import org.codehaus.groovy.runtime.DefaultGroovyMethods;
|
||||
import org.codehaus.groovy.runtime.InvokerHelper;
|
||||
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
||||
import org.springframework.beans.factory.parsing.Location;
|
||||
import org.springframework.beans.factory.parsing.Problem;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinitionReader;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.GenericBeanDefinition;
|
||||
import org.springframework.beans.factory.support.ManagedList;
|
||||
import org.springframework.beans.factory.support.ManagedMap;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;
|
||||
import org.springframework.beans.factory.xml.NamespaceHandler;
|
||||
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
|
||||
import org.springframework.beans.factory.xml.XmlReaderContext;
|
||||
import org.springframework.core.io.DescriptiveResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.support.EncodedResource;
|
||||
import org.springframework.core.io.support.ResourcePatternUtils;
|
||||
|
||||
/**
|
||||
* A Groovy-based reader for Spring bean definitions: Like a Groovy builder,
|
||||
* but more of a DSL for Spring configuration. Allows syntax like:
|
||||
*
|
||||
* <pre>import org.hibernate.SessionFactory
|
||||
* import org.apache.commons.dbcp.BasicDataSource
|
||||
*
|
||||
* def reader = new GroovyBeanDefinitionReader(myApplicationContext)
|
||||
* reader.beans {
|
||||
* dataSource(BasicDataSource) { // <--- invokeMethod
|
||||
* driverClassName = "org.hsqldb.jdbcDriver"
|
||||
* url = "jdbc:hsqldb:mem:grailsDB"
|
||||
* username = "sa" // <-- setProperty
|
||||
* password = ""
|
||||
* settings = [mynew:"setting"]
|
||||
* }
|
||||
* sessionFactory(SessionFactory) {
|
||||
* dataSource = dataSource // <-- getProperty for retrieving references
|
||||
* }
|
||||
* myService(MyService) {
|
||||
* nestedBean = { AnotherBean bean -> // <-- setProperty with closure for nested bean
|
||||
* dataSource = dataSource
|
||||
* }
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* <p>You can also load resources containing beans defined as a Groovy script using
|
||||
* either the {@link #loadBeanDefinitions(org.springframework.core.io.Resource...)}
|
||||
* or {@link #loadBeanDefinitions(String...)} method, with a script looking as follows:
|
||||
*
|
||||
* <pre>import org.hibernate.SessionFactory
|
||||
* import org.apache.commons.dbcp.BasicDataSource
|
||||
*
|
||||
* beans {
|
||||
* dataSource(BasicDataSource) {
|
||||
* driverClassName = "org.hsqldb.jdbcDriver"
|
||||
* url = "jdbc:hsqldb:mem:grailsDB"
|
||||
* username = "sa"
|
||||
* password = ""
|
||||
* settings = [mynew:"setting"]
|
||||
* }
|
||||
* sessionFactory(SessionFactory) {
|
||||
* dataSource = dataSource
|
||||
* }
|
||||
* myService(MyService) {
|
||||
* nestedBean = { AnotherBean bean ->
|
||||
* dataSource = dataSource
|
||||
* }
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* <p>Typically applied to a
|
||||
* {@link org.springframework.beans.factory.support.DefaultListableBeanFactory}
|
||||
* or a {@link org.springframework.context.support.GenericApplicationContext},
|
||||
* but can be used against any {@link BeanDefinitionRegistry} implementation.
|
||||
*
|
||||
* @author Jeff Brown
|
||||
* @author Graeme Rocher
|
||||
* @author Juergen Hoeller
|
||||
* @since 4.0
|
||||
* @see BeanDefinitionRegistry
|
||||
* @see org.springframework.beans.factory.support.DefaultListableBeanFactory
|
||||
* @see org.springframework.context.support.GenericApplicationContext
|
||||
* @see org.springframework.context.support.GenericGroovyApplicationContext
|
||||
*/
|
||||
public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader implements GroovyObject {
|
||||
|
||||
private final XmlBeanDefinitionReader xmlBeanDefinitionReader;
|
||||
|
||||
private MetaClass metaClass = GroovySystem.getMetaClassRegistry().getMetaClass(getClass());
|
||||
|
||||
private Binding binding;
|
||||
|
||||
private GroovyBeanDefinitionWrapper currentBeanDefinition;
|
||||
|
||||
private final Map <String, String> namespaces = new HashMap<String, String>();
|
||||
|
||||
private final Map<String, DeferredProperty> deferredProperties = new HashMap<String, DeferredProperty>();
|
||||
|
||||
|
||||
/**
|
||||
* Create new GroovyBeanDefinitionReader for the given bean factory.
|
||||
* @param registry the BeanFactory to load bean definitions into,
|
||||
* in the form of a BeanDefinitionRegistry
|
||||
*/
|
||||
public GroovyBeanDefinitionReader(BeanDefinitionRegistry registry) {
|
||||
super(registry);
|
||||
this.xmlBeanDefinitionReader = new XmlBeanDefinitionReader(registry);
|
||||
this.xmlBeanDefinitionReader.setValidating(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new GroovyBeanDefinitionReader based on the given XmlBeanDefinitionReader,
|
||||
* using the same registry and delegating XML loading to it.
|
||||
* @param xmlBeanDefinitionReader the XmlBeanDefinitionReader to derive the registry
|
||||
* from and to delegate XML loading to
|
||||
*/
|
||||
public GroovyBeanDefinitionReader(XmlBeanDefinitionReader xmlBeanDefinitionReader) {
|
||||
super(xmlBeanDefinitionReader.getRegistry());
|
||||
this.xmlBeanDefinitionReader = xmlBeanDefinitionReader;
|
||||
}
|
||||
|
||||
|
||||
public void setMetaClass(MetaClass metaClass) {
|
||||
this.metaClass = metaClass;
|
||||
}
|
||||
|
||||
public MetaClass getMetaClass() {
|
||||
return this.metaClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the binding, i.e. the Groovy variables available in the scope
|
||||
* of a GroovyBeanDefinitionReader closure.
|
||||
*/
|
||||
public void setBinding(Binding binding) {
|
||||
this.binding = binding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a specified binding for Groovy variables, if any.
|
||||
*/
|
||||
public Binding getBinding() {
|
||||
return this.binding;
|
||||
}
|
||||
|
||||
|
||||
// TRADITIONAL BEAN DEFINITION READER METHODS
|
||||
|
||||
/**
|
||||
* Load bean definitions from the specified Groovy script.
|
||||
* @param resource the resource descriptor for the Groovy script
|
||||
* @return the number of bean definitions found
|
||||
* @throws BeanDefinitionStoreException in case of loading or parsing errors
|
||||
*/
|
||||
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
|
||||
return loadBeanDefinitions(new EncodedResource(resource));
|
||||
}
|
||||
|
||||
/**
|
||||
* Load bean definitions from the specified Groovy script.
|
||||
* @param encodedResource the resource descriptor for the Groovy script,
|
||||
* allowing to specify an encoding to use for parsing the file
|
||||
* @return the number of bean definitions found
|
||||
* @throws BeanDefinitionStoreException in case of loading or parsing errors
|
||||
*/
|
||||
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
|
||||
Closure beans = new Closure(this){
|
||||
public Object call(Object[] args) {
|
||||
invokeBeanDefiningClosure((Closure) args[0]);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
Binding binding = new Binding() {
|
||||
@Override
|
||||
public void setVariable(String name, Object value) {
|
||||
if (currentBeanDefinition !=null) {
|
||||
applyPropertyToBeanDefinition(name, value);
|
||||
}
|
||||
else {
|
||||
super.setVariable(name, value);
|
||||
}
|
||||
}
|
||||
};
|
||||
binding.setVariable("beans", beans);
|
||||
|
||||
|
||||
int countBefore = getRegistry().getBeanDefinitionCount();
|
||||
try {
|
||||
GroovyShell shell = new GroovyShell(getResourceLoader().getClassLoader(), binding);
|
||||
shell.evaluate(encodedResource.getReader(), encodedResource.getResource().getFilename());
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new BeanDefinitionParsingException(new Problem("Error evaluating Groovy script: " + ex.getMessage(),
|
||||
new Location(encodedResource.getResource()), null, ex));
|
||||
}
|
||||
return getRegistry().getBeanDefinitionCount() - countBefore;
|
||||
}
|
||||
|
||||
|
||||
// METHODS FOR CONSUMPTION IN A GROOVY CLOSURE
|
||||
|
||||
/**
|
||||
* Defines a set of beans for the given block or closure.
|
||||
* @param c the block or closure
|
||||
* @return this GroovyBeanDefinitionReader instance
|
||||
*/
|
||||
public GroovyBeanDefinitionReader beans(Closure c) {
|
||||
return invokeBeanDefiningClosure(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines an inner bean definition.
|
||||
* @param type the bean type
|
||||
* @return the bean definition
|
||||
*/
|
||||
public GenericBeanDefinition bean(Class<?> type) {
|
||||
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
|
||||
beanDefinition.setBeanClass(type);
|
||||
return beanDefinition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines an inner bean definition
|
||||
* @param type The bean type
|
||||
* @param args The constructors arguments and closure configurer
|
||||
* @return The bean definition
|
||||
*/
|
||||
public AbstractBeanDefinition bean(Class<?> type, Object...args) {
|
||||
GroovyBeanDefinitionWrapper current = this.currentBeanDefinition;
|
||||
try {
|
||||
Closure callable = null;
|
||||
Collection constructorArgs = null;
|
||||
if (args != null && args.length > 0) {
|
||||
int index = args.length;
|
||||
Object lastArg = args[index-1];
|
||||
|
||||
if (lastArg instanceof Closure) {
|
||||
callable = (Closure) lastArg;
|
||||
index--;
|
||||
}
|
||||
if (index > -1) {
|
||||
constructorArgs = resolveConstructorArguments(args, 0, index);
|
||||
}
|
||||
}
|
||||
this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(null, type, constructorArgs);
|
||||
if (callable != null) {
|
||||
callable.call(this.currentBeanDefinition);
|
||||
}
|
||||
return this.currentBeanDefinition.getBeanDefinition();
|
||||
|
||||
}
|
||||
finally {
|
||||
this.currentBeanDefinition = current;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines an Spring namespace definition to use.
|
||||
* @param definition the namespace definition
|
||||
*/
|
||||
public void xmlns(Map<String, String> definition) {
|
||||
if (!definition.isEmpty()) {
|
||||
for (Map.Entry<String,String> entry : definition.entrySet()) {
|
||||
String namespace = entry.getKey();
|
||||
String uri = entry.getValue();
|
||||
if (uri == null) {
|
||||
throw new IllegalArgumentException("Namespace definition must supply a non-null URI");
|
||||
}
|
||||
NamespaceHandler namespaceHandler = this.xmlBeanDefinitionReader.getNamespaceHandlerResolver().resolve(uri);
|
||||
if (namespaceHandler == null) {
|
||||
throw new BeanDefinitionParsingException(new Problem("No namespace handler found for URI: " + uri,
|
||||
new Location(new DescriptiveResource(("Groovy")))));
|
||||
}
|
||||
this.namespaces.put(namespace, uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports Spring bean definitions from either XML or Groovy sources into the current bean builder instance.
|
||||
* @param resourcePattern the resource pattern
|
||||
*/
|
||||
public void importBeans(String resourcePattern) throws IOException {
|
||||
Resource[] resources =
|
||||
ResourcePatternUtils.getResourcePatternResolver(getResourceLoader()).getResources(resourcePattern);
|
||||
for (Resource resource : resources) {
|
||||
String filename = resource.getFilename();
|
||||
if (filename.endsWith(".groovy")) {
|
||||
loadBeanDefinitions(resource);
|
||||
}
|
||||
else if (filename.endsWith(".xml")) {
|
||||
this.xmlBeanDefinitionReader.loadBeanDefinitions(resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// INTERNAL HANDLING OF GROOVY CLOSURES AND PROPERTIES
|
||||
|
||||
/**
|
||||
* This method overrides method invocation to create beans for each method name that
|
||||
* takes a class argument
|
||||
*/
|
||||
public Object invokeMethod(String name, Object arg) {
|
||||
Object[] args = (Object[])arg;
|
||||
if ("beans".equals(name) && args.length == 1 && args[0] instanceof Closure) {
|
||||
return beans((Closure) args[0]);
|
||||
}
|
||||
else if ("ref".equals(name)) {
|
||||
String refName;
|
||||
if (args[0] == null)
|
||||
throw new IllegalArgumentException("Argument to ref() is not a valid bean or was not found");
|
||||
|
||||
if (args[0] instanceof RuntimeBeanReference) {
|
||||
refName = ((RuntimeBeanReference)args[0]).getBeanName();
|
||||
}
|
||||
else {
|
||||
refName = args[0].toString();
|
||||
}
|
||||
boolean parentRef = false;
|
||||
if (args.length > 1) {
|
||||
if (args[1] instanceof Boolean) {
|
||||
parentRef = (Boolean) args[1];
|
||||
}
|
||||
}
|
||||
return new RuntimeBeanReference(refName, parentRef);
|
||||
}
|
||||
else if (this.namespaces.containsKey(name) && args.length > 0 && (args[0] instanceof Closure)) {
|
||||
GroovyDynamicElementReader reader = createDynamicElementReader(name);
|
||||
reader.invokeMethod("doCall", args);
|
||||
}
|
||||
else if (args.length > 0 && args[0] instanceof Closure) {
|
||||
// abstract bean definition
|
||||
return invokeBeanDefiningMethod(name, args);
|
||||
}
|
||||
else if (args.length > 0 && (args[0] instanceof Class || args[0] instanceof RuntimeBeanReference || args[0] instanceof Map)) {
|
||||
return invokeBeanDefiningMethod(name, args);
|
||||
}
|
||||
else if (args.length > 1 && args[args.length -1] instanceof Closure) {
|
||||
return invokeBeanDefiningMethod(name, args);
|
||||
}
|
||||
MetaClass mc = DefaultGroovyMethods.getMetaClass(getRegistry());
|
||||
if (!mc.respondsTo(getRegistry(), name, args).isEmpty()){
|
||||
return mc.invokeMethod(getRegistry(), name, args);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private boolean addDeferredProperty(String property, Object newValue) {
|
||||
if (newValue instanceof List) {
|
||||
this.deferredProperties.put(this.currentBeanDefinition.getBeanName() + '.' + property,
|
||||
new DeferredProperty(this.currentBeanDefinition, property, newValue));
|
||||
return true;
|
||||
}
|
||||
else if (newValue instanceof Map) {
|
||||
this.deferredProperties.put(this.currentBeanDefinition.getBeanName() + '.' + property,
|
||||
new DeferredProperty(this.currentBeanDefinition, property, newValue));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void finalizeDeferredProperties() {
|
||||
for (DeferredProperty dp : this.deferredProperties.values()) {
|
||||
if (dp.value instanceof List) {
|
||||
dp.value = manageListIfNecessary((List) dp.value);
|
||||
}
|
||||
else if (dp.value instanceof Map) {
|
||||
dp.value = manageMapIfNecessary((Map) dp.value);
|
||||
}
|
||||
dp.apply();
|
||||
}
|
||||
deferredProperties.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* When an methods argument is only a closure it is a set of bean definitions.
|
||||
* @param callable the closure argument
|
||||
* @return this GroovyBeanDefinitionReader instance
|
||||
*/
|
||||
protected GroovyBeanDefinitionReader invokeBeanDefiningClosure(Closure callable) {
|
||||
callable.setDelegate(this);
|
||||
callable.call();
|
||||
finalizeDeferredProperties();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called when a bean definition node is called.
|
||||
* @param beanName the name of the bean to define
|
||||
* @param args the arguments to the bean. The first argument is the class name, the last
|
||||
* argument is sometimes a closure. All the arguments in between are constructor arguments.
|
||||
* @return the bean definition wrapper
|
||||
*/
|
||||
private GroovyBeanDefinitionWrapper invokeBeanDefiningMethod(String beanName, Object[] args) {
|
||||
boolean hasClosureArgument = args[args.length - 1] instanceof Closure;
|
||||
if (args[0] instanceof Class) {
|
||||
Class beanClass = (args[0] instanceof Class ? (Class) args[0] : args[0].getClass());
|
||||
if (args.length >= 1) {
|
||||
if (hasClosureArgument) {
|
||||
if (args.length-1 != 1) {
|
||||
this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(
|
||||
beanName, beanClass, resolveConstructorArguments(args,1,args.length-1));
|
||||
}
|
||||
else {
|
||||
this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName, beanClass);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(
|
||||
beanName, beanClass, resolveConstructorArguments(args,1,args.length));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else if (args[0] instanceof RuntimeBeanReference) {
|
||||
this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName);
|
||||
this.currentBeanDefinition.getBeanDefinition().setFactoryBeanName(((RuntimeBeanReference) args[0]).getBeanName());
|
||||
}
|
||||
else if (args[0] instanceof Map) {
|
||||
// named constructor arguments
|
||||
if (args.length > 1 && args[1] instanceof Class) {
|
||||
List constructorArgs = resolveConstructorArguments(args, 2, hasClosureArgument ? args.length-1 : args.length);
|
||||
this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName, (Class)args[1], constructorArgs);
|
||||
Map namedArgs = (Map)args[0];
|
||||
for (Object o : namedArgs.keySet()) {
|
||||
String propName = (String) o;
|
||||
setProperty(propName, namedArgs.get(propName));
|
||||
}
|
||||
}
|
||||
// factory method syntax
|
||||
else {
|
||||
this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName);
|
||||
//First arg is the map containing factoryBean : factoryMethod
|
||||
Map.Entry factoryBeanEntry = (Map.Entry) ((Map) args[0]).entrySet().iterator().next();
|
||||
// If we have a closure body, that will be the last argument.
|
||||
// In between are the constructor args
|
||||
int constructorArgsTest = hasClosureArgument?2:1;
|
||||
// If we have more than this number of args, we have constructor args
|
||||
if (args.length > constructorArgsTest){
|
||||
// factory-method requires args
|
||||
int endOfConstructArgs = (hasClosureArgument? args.length - 1 : args.length);
|
||||
this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName, null,
|
||||
resolveConstructorArguments(args, 1, endOfConstructArgs));
|
||||
}
|
||||
else {
|
||||
this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName);
|
||||
}
|
||||
this.currentBeanDefinition.getBeanDefinition().setFactoryBeanName(factoryBeanEntry.getKey().toString());
|
||||
this.currentBeanDefinition.getBeanDefinition().setFactoryMethodName(factoryBeanEntry.getValue().toString());
|
||||
}
|
||||
|
||||
}
|
||||
else if (args[0] instanceof Closure) {
|
||||
this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName);
|
||||
this.currentBeanDefinition.getBeanDefinition().setAbstract(true);
|
||||
}
|
||||
else {
|
||||
List constructorArgs = resolveConstructorArguments(args, 0, hasClosureArgument ? args.length-1 : args.length);
|
||||
currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName, null, constructorArgs);
|
||||
}
|
||||
|
||||
if (hasClosureArgument) {
|
||||
Closure callable = (Closure)args[args.length-1];
|
||||
callable.setDelegate(this);
|
||||
callable.setResolveStrategy(Closure.DELEGATE_FIRST);
|
||||
callable.call(new Object[]{currentBeanDefinition});
|
||||
}
|
||||
|
||||
GroovyBeanDefinitionWrapper beanDefinition = currentBeanDefinition;
|
||||
this.currentBeanDefinition = null;
|
||||
beanDefinition.getBeanDefinition().setAttribute(GroovyBeanDefinitionWrapper.class.getName(), beanDefinition);
|
||||
getRegistry().registerBeanDefinition(beanName, beanDefinition.getBeanDefinition());
|
||||
return beanDefinition;
|
||||
}
|
||||
|
||||
protected List<Object> resolveConstructorArguments(Object[] args, int start, int end) {
|
||||
Object[] constructorArgs = Arrays.copyOfRange(args, start, end);
|
||||
for (int i = 0; i < constructorArgs.length; i++) {
|
||||
if (constructorArgs[i] instanceof GString) {
|
||||
constructorArgs[i] = constructorArgs[i].toString();
|
||||
}
|
||||
else if (constructorArgs[i] instanceof List) {
|
||||
constructorArgs[i] = manageListIfNecessary((List) constructorArgs[i]);
|
||||
}
|
||||
else if (constructorArgs[i] instanceof Map){
|
||||
constructorArgs[i] = manageMapIfNecessary((Map) constructorArgs[i]);
|
||||
}
|
||||
}
|
||||
return Arrays.asList(constructorArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether there are any {@link RuntimeBeanReference} inside the Map
|
||||
* and converts it to a ManagedMap if necessary.
|
||||
* @param map the original Map
|
||||
* @return either the original map or a managed copy of it
|
||||
*/
|
||||
private Object manageMapIfNecessary(Map<?, ?> map) {
|
||||
boolean containsRuntimeRefs = false;
|
||||
for (Object element : map.values()) {
|
||||
if (element instanceof RuntimeBeanReference) {
|
||||
containsRuntimeRefs = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (containsRuntimeRefs) {
|
||||
Map<Object, Object> managedMap = new ManagedMap<Object, Object>();
|
||||
managedMap.putAll(map);
|
||||
return managedMap;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether there are any {@link RuntimeBeanReference} inside the Lis
|
||||
* and converts it to a ManagedList if necessary.
|
||||
* @param list the original List
|
||||
* @return either the original list or a managed copy of it
|
||||
*/
|
||||
private Object manageListIfNecessary(List<?> list) {
|
||||
boolean containsRuntimeRefs = false;
|
||||
for (Object element : list) {
|
||||
if (element instanceof RuntimeBeanReference) {
|
||||
containsRuntimeRefs = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (containsRuntimeRefs) {
|
||||
List<Object> managedList = new ManagedList<Object>();
|
||||
managedList.addAll(list);
|
||||
return managedList;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method overrides property setting in the scope of the GroovyBeanDefinitionReader
|
||||
* to set properties on the current bean definition.
|
||||
*/
|
||||
public void setProperty(String name, Object value) {
|
||||
if (this.currentBeanDefinition != null) {
|
||||
applyPropertyToBeanDefinition(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
protected void applyPropertyToBeanDefinition(String name, Object value) {
|
||||
if (value instanceof GString) {
|
||||
value = value.toString();
|
||||
}
|
||||
if (addDeferredProperty(name, value)) {
|
||||
return;
|
||||
}
|
||||
else if (value instanceof Closure) {
|
||||
GroovyBeanDefinitionWrapper current = this.currentBeanDefinition;
|
||||
try {
|
||||
Closure callable = (Closure) value;
|
||||
Class parameterType = callable.getParameterTypes()[0];
|
||||
if (parameterType.equals(Object.class)) {
|
||||
this.currentBeanDefinition = new GroovyBeanDefinitionWrapper("");
|
||||
callable.call(this.currentBeanDefinition);
|
||||
}
|
||||
else {
|
||||
this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(null, parameterType);
|
||||
callable.call((Object) null);
|
||||
}
|
||||
|
||||
value = this.currentBeanDefinition.getBeanDefinition();
|
||||
}
|
||||
finally {
|
||||
this.currentBeanDefinition = current;
|
||||
}
|
||||
}
|
||||
this.currentBeanDefinition.addProperty(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method overrides property retrieval in the scope of the GroovyBeanDefinitionReader to either:
|
||||
* a) Retrieve a variable from the bean builder's binding if it exists
|
||||
* b) Retrieve a RuntimeBeanReference for a specific bean if it exists
|
||||
* c) Otherwise just delegate to MetaClass.getProperty which will resolve properties from the GroovyBeanDefinitionReader itself
|
||||
*/
|
||||
public Object getProperty(String name) {
|
||||
Binding binding = getBinding();
|
||||
if (binding != null && binding.hasVariable(name)) {
|
||||
return binding.getVariable(name);
|
||||
}
|
||||
else {
|
||||
if (this.namespaces.containsKey(name)) {
|
||||
return createDynamicElementReader(name);
|
||||
}
|
||||
if (getRegistry().containsBeanDefinition(name)) {
|
||||
GroovyBeanDefinitionWrapper beanDefinition = (GroovyBeanDefinitionWrapper)
|
||||
getRegistry().getBeanDefinition(name).getAttribute(GroovyBeanDefinitionWrapper.class.getName());
|
||||
if (beanDefinition != null) {
|
||||
return new GroovyRuntimeBeanReference(name, beanDefinition, false);
|
||||
}
|
||||
else {
|
||||
return new RuntimeBeanReference(name, false);
|
||||
}
|
||||
}
|
||||
// This is to deal with the case where the property setter is the last
|
||||
// statement in a closure (hence the return value)
|
||||
else if (this.currentBeanDefinition != null) {
|
||||
MutablePropertyValues pvs = this.currentBeanDefinition.getBeanDefinition().getPropertyValues();
|
||||
if (pvs.contains(name)) {
|
||||
return pvs.get(name);
|
||||
}
|
||||
else {
|
||||
DeferredProperty dp = this.deferredProperties.get(this.currentBeanDefinition.getBeanName() + name);
|
||||
if (dp != null) {
|
||||
return dp.value;
|
||||
}
|
||||
else {
|
||||
return getMetaClass().getProperty(this, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
return getMetaClass().getProperty(this, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private GroovyDynamicElementReader createDynamicElementReader(String namespace) {
|
||||
XmlReaderContext readerContext = this.xmlBeanDefinitionReader.createReaderContext(new DescriptiveResource("Groovy"));
|
||||
BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext, getEnvironment());
|
||||
boolean decorating = (this.currentBeanDefinition != null);
|
||||
if (!decorating) {
|
||||
this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(namespace);
|
||||
}
|
||||
return new GroovyDynamicElementReader(namespace, this.namespaces, delegate, this.currentBeanDefinition, decorating) {
|
||||
@Override
|
||||
protected void afterInvocation() {
|
||||
if (!this.decorating) {
|
||||
currentBeanDefinition = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This class is used to defer the adding of a property to a bean definition until later
|
||||
* This is for a case where you assign a property to a list that may not contain bean references at
|
||||
* that point of assignment, but may later hence it would need to be managed
|
||||
*/
|
||||
private static class DeferredProperty {
|
||||
|
||||
private final GroovyBeanDefinitionWrapper beanDefinition;
|
||||
|
||||
private final String name;
|
||||
|
||||
private Object value;
|
||||
|
||||
public DeferredProperty(GroovyBeanDefinitionWrapper beanDefinition, String name, Object value) {
|
||||
this.beanDefinition = beanDefinition;
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public void apply() {
|
||||
this.beanDefinition.addProperty(this.name, this.value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A RuntimeBeanReference that takes care of adding new properties to runtime references
|
||||
*/
|
||||
private class GroovyRuntimeBeanReference extends RuntimeBeanReference implements GroovyObject {
|
||||
|
||||
private final GroovyBeanDefinitionWrapper beanDefinition;
|
||||
|
||||
private MetaClass metaClass;
|
||||
|
||||
public GroovyRuntimeBeanReference(String beanName, GroovyBeanDefinitionWrapper beanDefinition, boolean toParent) {
|
||||
super(beanName, toParent);
|
||||
this.beanDefinition = beanDefinition;
|
||||
this.metaClass = InvokerHelper.getMetaClass(this);
|
||||
}
|
||||
|
||||
public MetaClass getMetaClass() {
|
||||
return this.metaClass;
|
||||
}
|
||||
|
||||
public Object getProperty(String property) {
|
||||
if (property.equals("beanName")) {
|
||||
return getBeanName();
|
||||
}
|
||||
else if (property.equals("source")) {
|
||||
return getSource();
|
||||
}
|
||||
else if (this.beanDefinition != null) {
|
||||
return new GroovyPropertyValue(
|
||||
property, this.beanDefinition.getBeanDefinition().getPropertyValues().get(property));
|
||||
}
|
||||
else {
|
||||
return this.metaClass.getProperty(this, property);
|
||||
}
|
||||
}
|
||||
|
||||
public Object invokeMethod(String name, Object args) {
|
||||
return this.metaClass.invokeMethod(this, name, args);
|
||||
}
|
||||
|
||||
public void setMetaClass(MetaClass metaClass) {
|
||||
this.metaClass = metaClass;
|
||||
}
|
||||
|
||||
public void setProperty(String property, Object newValue) {
|
||||
if (!addDeferredProperty(property, newValue)) {
|
||||
this.beanDefinition.getBeanDefinition().getPropertyValues().add(property, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Wraps a bean definition property an ensures that any RuntimeBeanReference
|
||||
* additions to it are deferred for resolution later.
|
||||
*/
|
||||
private class GroovyPropertyValue extends GroovyObjectSupport {
|
||||
|
||||
private final String propertyName;
|
||||
|
||||
private final Object propertyValue;
|
||||
|
||||
public GroovyPropertyValue(String propertyName, Object propertyValue) {
|
||||
this.propertyName = propertyName;
|
||||
this.propertyValue = propertyValue;
|
||||
}
|
||||
|
||||
public void leftShift(Object value) {
|
||||
InvokerHelper.invokeMethod(this.propertyValue, "leftShift", value);
|
||||
updateDeferredProperties(value);
|
||||
}
|
||||
|
||||
public boolean add(Object value) {
|
||||
boolean retVal = (Boolean) InvokerHelper.invokeMethod(this.propertyValue, "add", value);
|
||||
updateDeferredProperties(value);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public boolean addAll(Collection values) {
|
||||
boolean retVal = (Boolean) InvokerHelper.invokeMethod(this.propertyValue, "addAll", values);
|
||||
for (Object value : values) {
|
||||
updateDeferredProperties(value);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public Object invokeMethod(String name, Object args) {
|
||||
return InvokerHelper.invokeMethod(this.propertyValue, name, args);
|
||||
}
|
||||
|
||||
public Object getProperty(String name) {
|
||||
return InvokerHelper.getProperty(this.propertyValue, name);
|
||||
}
|
||||
|
||||
public void setProperty(String name, Object value) {
|
||||
InvokerHelper.setProperty(this.propertyValue, name, value);
|
||||
}
|
||||
|
||||
private void updateDeferredProperties(Object value) {
|
||||
if (value instanceof RuntimeBeanReference) {
|
||||
deferredProperties.put(beanDefinition.getBeanName(),
|
||||
new DeferredProperty(beanDefinition, this.propertyName, this.propertyValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,241 @@
|
|||
/*
|
||||
* 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 java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import groovy.lang.GroovyObjectSupport;
|
||||
|
||||
import org.springframework.beans.BeanWrapper;
|
||||
import org.springframework.beans.BeanWrapperImpl;
|
||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.config.ConstructorArgumentValues;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.GenericBeanDefinition;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Internal wrapper for a Spring BeanDefinition, allowing for Groovy-style
|
||||
* property access within a {@link GroovyBeanDefinitionReader} closure.
|
||||
*
|
||||
* @author Jeff Brown
|
||||
* @author Juergen Hoeller
|
||||
* @since 4.0
|
||||
*/
|
||||
class GroovyBeanDefinitionWrapper extends GroovyObjectSupport {
|
||||
|
||||
private static final String PARENT = "parent";
|
||||
private static final String AUTOWIRE = "autowire";
|
||||
private static final String CONSTRUCTOR_ARGS = "constructorArgs";
|
||||
private static final String FACTORY_BEAN = "factoryBean";
|
||||
private static final String FACTORY_METHOD = "factoryMethod";
|
||||
private static final String INIT_METHOD = "initMethod";
|
||||
private static final String DESTROY_METHOD = "destroyMethod";
|
||||
private static final String SINGLETON = "singleton";
|
||||
|
||||
private static final List<String> dynamicProperties = new ArrayList<String>(8);
|
||||
|
||||
static {
|
||||
dynamicProperties.add(PARENT);
|
||||
dynamicProperties.add(AUTOWIRE);
|
||||
dynamicProperties.add(CONSTRUCTOR_ARGS);
|
||||
dynamicProperties.add(FACTORY_BEAN);
|
||||
dynamicProperties.add(FACTORY_METHOD);
|
||||
dynamicProperties.add(INIT_METHOD);
|
||||
dynamicProperties.add(DESTROY_METHOD);
|
||||
dynamicProperties.add(SINGLETON);
|
||||
}
|
||||
|
||||
|
||||
private String beanName;
|
||||
|
||||
private Class<?> clazz;
|
||||
|
||||
private Collection<?> constructorArgs;
|
||||
|
||||
private AbstractBeanDefinition definition;
|
||||
|
||||
private BeanWrapper definitionWrapper;
|
||||
|
||||
private String parentName;
|
||||
|
||||
|
||||
public GroovyBeanDefinitionWrapper(String beanName) {
|
||||
this.beanName = beanName;
|
||||
}
|
||||
|
||||
public GroovyBeanDefinitionWrapper(String beanName, Class<?> clazz) {
|
||||
this.beanName = beanName;
|
||||
this.clazz = clazz;
|
||||
}
|
||||
|
||||
public GroovyBeanDefinitionWrapper(String beanName, Class<?> clazz, Collection<?> constructorArgs) {
|
||||
this.beanName = beanName;
|
||||
this.clazz = clazz;
|
||||
this.constructorArgs = constructorArgs;
|
||||
}
|
||||
|
||||
|
||||
public String getBeanName() {
|
||||
return this.beanName;
|
||||
}
|
||||
|
||||
public void setBeanDefinition(AbstractBeanDefinition definition) {
|
||||
this.definition = definition;
|
||||
}
|
||||
|
||||
public AbstractBeanDefinition getBeanDefinition() {
|
||||
if (this.definition == null) {
|
||||
this.definition = createBeanDefinition();
|
||||
}
|
||||
return this.definition;
|
||||
}
|
||||
|
||||
protected AbstractBeanDefinition createBeanDefinition() {
|
||||
AbstractBeanDefinition bd = new GenericBeanDefinition();
|
||||
bd.setBeanClass(this.clazz);
|
||||
if (!CollectionUtils.isEmpty(this.constructorArgs)) {
|
||||
ConstructorArgumentValues cav = new ConstructorArgumentValues();
|
||||
for (Object constructorArg : this.constructorArgs) {
|
||||
cav.addGenericArgumentValue(constructorArg);
|
||||
}
|
||||
bd.setConstructorArgumentValues(cav);
|
||||
}
|
||||
if (this.parentName != null) {
|
||||
bd.setParentName(this.parentName);
|
||||
}
|
||||
this.definitionWrapper = new BeanWrapperImpl(bd);
|
||||
return bd;
|
||||
}
|
||||
|
||||
public void setBeanDefinitionHolder(BeanDefinitionHolder holder) {
|
||||
this.definition = (AbstractBeanDefinition) holder.getBeanDefinition();
|
||||
this.beanName = holder.getBeanName();
|
||||
}
|
||||
|
||||
public BeanDefinitionHolder getBeanDefinitionHolder() {
|
||||
return new BeanDefinitionHolder(getBeanDefinition(), getBeanName());
|
||||
}
|
||||
|
||||
public void setParent(Object obj) {
|
||||
if (obj == null) {
|
||||
throw new IllegalArgumentException("Parent bean cannot be set to a null runtime bean reference!");
|
||||
}
|
||||
if (obj instanceof String) {
|
||||
this.parentName = (String) obj;
|
||||
}
|
||||
else if (obj instanceof RuntimeBeanReference) {
|
||||
this.parentName = ((RuntimeBeanReference) obj).getBeanName();
|
||||
}
|
||||
else if (obj instanceof GroovyBeanDefinitionWrapper) {
|
||||
this.parentName = ((GroovyBeanDefinitionWrapper) obj).getBeanName();
|
||||
}
|
||||
getBeanDefinition().setParentName(this.parentName);
|
||||
getBeanDefinition().setAbstract(false);
|
||||
}
|
||||
|
||||
public GroovyBeanDefinitionWrapper addProperty(String propertyName, Object propertyValue) {
|
||||
if (propertyValue instanceof GroovyBeanDefinitionWrapper) {
|
||||
propertyValue = ((GroovyBeanDefinitionWrapper) propertyValue).getBeanDefinition();
|
||||
}
|
||||
getBeanDefinition().getPropertyValues().add(propertyName, propertyValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public Object getProperty(String property) {
|
||||
if (this.definitionWrapper.isReadableProperty(property)) {
|
||||
return this.definitionWrapper.getPropertyValue(property);
|
||||
}
|
||||
else if (dynamicProperties.contains(property)) {
|
||||
return null;
|
||||
}
|
||||
return super.getProperty(property);
|
||||
}
|
||||
|
||||
public void setProperty(String property, Object newValue) {
|
||||
if (PARENT.equals(property)) {
|
||||
setParent(newValue);
|
||||
}
|
||||
else {
|
||||
AbstractBeanDefinition bd = getBeanDefinition();
|
||||
if (AUTOWIRE.equals(property)) {
|
||||
if ("byName".equals(newValue)) {
|
||||
bd.setAutowireMode(AutowireCapableBeanFactory.AUTOWIRE_BY_NAME);
|
||||
}
|
||||
else if ("byType".equals(newValue)) {
|
||||
bd.setAutowireMode(AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE);
|
||||
}
|
||||
else if ("constructor".equals(newValue)) {
|
||||
bd.setAutowireMode(AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
|
||||
}
|
||||
else if (Boolean.TRUE.equals(newValue)) {
|
||||
bd.setAutowireMode(AutowireCapableBeanFactory.AUTOWIRE_BY_NAME);
|
||||
}
|
||||
}
|
||||
// constructorArgs
|
||||
else if (CONSTRUCTOR_ARGS.equals(property) && newValue instanceof List) {
|
||||
ConstructorArgumentValues cav = new ConstructorArgumentValues();
|
||||
List args = (List) newValue;
|
||||
for (Object arg : args) {
|
||||
cav.addGenericArgumentValue(arg);
|
||||
}
|
||||
bd.setConstructorArgumentValues(cav);
|
||||
}
|
||||
// factoryBean
|
||||
else if (FACTORY_BEAN.equals(property)) {
|
||||
if (newValue != null) {
|
||||
bd.setFactoryBeanName(newValue.toString());
|
||||
}
|
||||
}
|
||||
// factoryMethod
|
||||
else if (FACTORY_METHOD.equals(property)) {
|
||||
if (newValue != null)
|
||||
bd.setFactoryMethodName(newValue.toString());
|
||||
}
|
||||
// initMethod
|
||||
else if (INIT_METHOD.equals(property)) {
|
||||
if (newValue != null) {
|
||||
bd.setInitMethodName(newValue.toString());
|
||||
}
|
||||
}
|
||||
// destroyMethod
|
||||
else if (DESTROY_METHOD.equals(property)) {
|
||||
if (newValue != null) {
|
||||
bd.setDestroyMethodName(newValue.toString());
|
||||
}
|
||||
}
|
||||
// singleton property
|
||||
else if (SINGLETON.equals(property)) {
|
||||
bd.setScope(Boolean.TRUE.equals(newValue) ?
|
||||
BeanDefinition.SCOPE_SINGLETON : BeanDefinition.SCOPE_PROTOTYPE);
|
||||
}
|
||||
else if (this.definitionWrapper.isWritableProperty(property)) {
|
||||
this.definitionWrapper.setPropertyValue(property, newValue);
|
||||
}
|
||||
else {
|
||||
super.setProperty(property, newValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
/**
|
||||
*
|
||||
* Support package for Groovy-based bean definitions.
|
||||
*
|
||||
*/
|
||||
package org.springframework.beans.factory.groovy;
|
||||
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -259,6 +259,18 @@ public class MutablePropertyValues implements PropertyValues, Serializable {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the raw property value, if any.
|
||||
* @param propertyName the name to search for
|
||||
* @return the raw property value, or {@code null}
|
||||
* @see #getPropertyValue(String)
|
||||
* @see PropertyValue#getValue()
|
||||
*/
|
||||
public Object get(String propertyName) {
|
||||
PropertyValue pv = getPropertyValue(propertyName);
|
||||
return (pv != null ? pv.getValue() : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PropertyValues changesSince(PropertyValues old) {
|
||||
MutablePropertyValues changes = new MutablePropertyValues();
|
||||
|
|
|
|||
|
|
@ -324,6 +324,13 @@ public class BeanDefinitionParserDelegate {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialize the default settings assuming a {@code null} parent delegate.
|
||||
*/
|
||||
public void initDefaults(Element root) {
|
||||
initDefaults(root, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the default lazy-init, autowire, dependency check settings,
|
||||
* init-method, destroy-method and merge settings. Support nested 'beans'
|
||||
|
|
@ -337,16 +344,6 @@ public class BeanDefinitionParserDelegate {
|
|||
this.readerContext.fireDefaultsRegistered(this.defaults);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the default settings assuming a {@code null} parent delegate.
|
||||
* @deprecated in Spring 3.1 in favor of
|
||||
* {@link #initDefaults(Element, BeanDefinitionParserDelegate)}
|
||||
*/
|
||||
@Deprecated
|
||||
public void initDefaults(Element root) {
|
||||
initDefaults(root, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the given DocumentDefaultsDefinition instance with the default lazy-init,
|
||||
* autowire, dependency check settings, init-method, destroy-method and merge settings.
|
||||
|
|
@ -1454,7 +1451,7 @@ public class BeanDefinitionParserDelegate {
|
|||
return finalDefinition;
|
||||
}
|
||||
|
||||
private BeanDefinitionHolder decorateIfRequired(
|
||||
public BeanDefinitionHolder decorateIfRequired(
|
||||
Node node, BeanDefinitionHolder originalDef, BeanDefinition containingBd) {
|
||||
|
||||
String namespaceUri = getNamespaceURI(node);
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocume
|
|||
// then ultimately reset this.delegate back to its original (parent) reference.
|
||||
// this behavior emulates a stack of delegates without actually necessitating one.
|
||||
BeanDefinitionParserDelegate parent = this.delegate;
|
||||
this.delegate = createHelper(this.readerContext, root, parent);
|
||||
this.delegate = createDelegate(this.readerContext, root, parent);
|
||||
|
||||
preProcessXml(root);
|
||||
parseBeanDefinitions(root, this.delegate);
|
||||
|
|
@ -144,10 +144,10 @@ public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocume
|
|||
this.delegate = parent;
|
||||
}
|
||||
|
||||
protected BeanDefinitionParserDelegate createHelper(
|
||||
protected BeanDefinitionParserDelegate createDelegate(
|
||||
XmlReaderContext readerContext, Element root, BeanDefinitionParserDelegate parentDelegate) {
|
||||
|
||||
BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext, environment);
|
||||
BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext, this.environment);
|
||||
delegate.initDefaults(root, parentDelegate);
|
||||
return delegate;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -381,13 +381,13 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
|
|||
* @param resource the resource descriptor for the XML file
|
||||
* @return the number of bean definitions found
|
||||
* @throws BeanDefinitionStoreException in case of loading or parsing errors
|
||||
* @see #doLoadDocument
|
||||
* @see #registerBeanDefinitions
|
||||
*/
|
||||
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
|
||||
throws BeanDefinitionStoreException {
|
||||
try {
|
||||
int validationMode = getValidationModeForResource(resource);
|
||||
Document doc = this.documentLoader.loadDocument(
|
||||
inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
|
||||
Document doc = doLoadDocument(inputSource, resource);
|
||||
return registerBeanDefinitions(doc, resource);
|
||||
}
|
||||
catch (BeanDefinitionStoreException ex) {
|
||||
|
|
@ -415,6 +415,20 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually load the specified document using the configured DocumentLoader.
|
||||
* @param inputSource the SAX InputSource to read from
|
||||
* @param resource the resource descriptor for the XML file
|
||||
* @return the DOM Document
|
||||
* @throws Exception when thrown from the DocumentLoader
|
||||
* @see #setDocumentLoader
|
||||
* @see DocumentLoader#loadDocument
|
||||
*/
|
||||
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
|
||||
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
|
||||
getValidationModeForResource(resource), isNamespaceAware());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the validation mode for the specified {@link Resource}. If no explicit
|
||||
|
|
@ -508,12 +522,20 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
|
|||
/**
|
||||
* Create the {@link XmlReaderContext} to pass over to the document reader.
|
||||
*/
|
||||
protected XmlReaderContext createReaderContext(Resource resource) {
|
||||
public XmlReaderContext createReaderContext(Resource resource) {
|
||||
return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
|
||||
this.sourceExtractor, this, getNamespaceHandlerResolver());
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazily create a default NamespaceHandlerResolver, if not set before.
|
||||
* @see #createDefaultNamespaceHandlerResolver()
|
||||
*/
|
||||
public NamespaceHandlerResolver getNamespaceHandlerResolver() {
|
||||
if (this.namespaceHandlerResolver == null) {
|
||||
this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
|
||||
}
|
||||
return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
|
||||
this.sourceExtractor, this, this.namespaceHandlerResolver);
|
||||
return this.namespaceHandlerResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -16,6 +16,12 @@
|
|||
|
||||
package org.springframework.beans.factory.xml;
|
||||
|
||||
import java.io.StringReader;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.parsing.ProblemReporter;
|
||||
import org.springframework.beans.factory.parsing.ReaderContext;
|
||||
|
|
@ -83,4 +89,14 @@ public class XmlReaderContext extends ReaderContext {
|
|||
return generatedName;
|
||||
}
|
||||
|
||||
public Document readDocumentFromString(String documentContent) {
|
||||
InputSource is = new InputSource(new StringReader(documentContent));
|
||||
try {
|
||||
return this.reader.doLoadDocument(is, getResource());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new BeanDefinitionStoreException("Failed to read XML document", ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,239 @@
|
|||
/*
|
||||
* 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.context.support;
|
||||
|
||||
import groovy.lang.GroovyObject;
|
||||
import groovy.lang.GroovySystem;
|
||||
import groovy.lang.MetaClass;
|
||||
|
||||
import org.springframework.beans.BeanWrapper;
|
||||
import org.springframework.beans.BeanWrapperImpl;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
/**
|
||||
* An {@link org.springframework.context.ApplicationContext} implementation that extends
|
||||
* {@link GenericApplicationContext} and implements {@link GroovyObject} such that beans
|
||||
* can be retrieved with the dot de-reference syntax instead of using {@link #getBean}.
|
||||
*
|
||||
* <p>Consider this as the equivalent of {@link GenericXmlApplicationContext} for
|
||||
* Groovy bean definitions. The main difference is that, within a Groovy script,
|
||||
* the context can be used with an inline bean definition closure like as follows:
|
||||
*
|
||||
* <pre>import org.hibernate.SessionFactory
|
||||
* import org.apache.commons.dbcp.BasicDataSource
|
||||
*
|
||||
* def context = new GenericGroovyApplicationContext()
|
||||
* context.reader.beans {
|
||||
* dataSource(BasicDataSource) { // <--- invokeMethod
|
||||
* driverClassName = "org.hsqldb.jdbcDriver"
|
||||
* url = "jdbc:hsqldb:mem:grailsDB"
|
||||
* username = "sa" // <-- setProperty
|
||||
* password = ""
|
||||
* settings = [mynew:"setting"]
|
||||
* }
|
||||
* sessionFactory(SessionFactory) {
|
||||
* dataSource = dataSource // <-- getProperty for retrieving references
|
||||
* }
|
||||
* myService(MyService) {
|
||||
* nestedBean = { AnotherBean bean -> // <-- setProperty with closure for nested bean
|
||||
* dataSource = dataSource
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* context.refresh()</pre>
|
||||
*
|
||||
* <p>Alternatively, load a Groovy bean definition script like the following
|
||||
* from an external resource (e.g. an "applicationContext.groovy" file):
|
||||
*
|
||||
* <pre>import org.hibernate.SessionFactory
|
||||
* import org.apache.commons.dbcp.BasicDataSource
|
||||
*
|
||||
* beans {
|
||||
* dataSource(BasicDataSource) {
|
||||
* driverClassName = "org.hsqldb.jdbcDriver"
|
||||
* url = "jdbc:hsqldb:mem:grailsDB"
|
||||
* username = "sa"
|
||||
* password = ""
|
||||
* settings = [mynew:"setting"]
|
||||
* }
|
||||
* sessionFactory(SessionFactory) {
|
||||
* dataSource = dataSource
|
||||
* }
|
||||
* myService(MyService) {
|
||||
* nestedBean = { AnotherBean bean ->
|
||||
* dataSource = dataSource
|
||||
* }
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* <p>With the following Java code creating the {@code GenericGroovyApplicationContext}
|
||||
* (potentially using Ant-style '*'/'**' location patterns):
|
||||
*
|
||||
* <pre>GenericGroovyApplicationContext context = new GenericGroovyApplicationContext();
|
||||
* context.load("org/myapp/applicationContext.groovy");
|
||||
* context.refresh();</pre>
|
||||
*
|
||||
* <p>Or even more concise, provided that no extra configuration is needed:
|
||||
*
|
||||
* <pre>ApplicationContext context = new GenericGroovyApplicationContext("org/myapp/applicationContext.groovy");</pre>
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Jeff Brown
|
||||
* @since 4.0
|
||||
* @see org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader
|
||||
*/
|
||||
public class GenericGroovyApplicationContext extends GenericApplicationContext implements GroovyObject {
|
||||
|
||||
private final GroovyBeanDefinitionReader reader = new GroovyBeanDefinitionReader(this);
|
||||
|
||||
private final BeanWrapper contextWrapper = new BeanWrapperImpl(this);
|
||||
|
||||
private MetaClass metaClass = GroovySystem.getMetaClassRegistry().getMetaClass(getClass());
|
||||
|
||||
|
||||
/**
|
||||
* Create a new GenericGroovyApplicationContext that needs to be
|
||||
* {@link #load loaded} and then manually {@link #refresh refreshed}.
|
||||
*/
|
||||
public GenericGroovyApplicationContext() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new GenericGroovyApplicationContext, loading bean definitions
|
||||
* from the given resources and automatically refreshing the context.
|
||||
* @param resources the resources to load from
|
||||
*/
|
||||
public GenericGroovyApplicationContext(Resource... resources) {
|
||||
load(resources);
|
||||
refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new GenericGroovyApplicationContext, loading bean definitions
|
||||
* from the given resource locations and automatically refreshing the context.
|
||||
* @param resourceLocations the resources to load from
|
||||
*/
|
||||
public GenericGroovyApplicationContext(String... resourceLocations) {
|
||||
load(resourceLocations);
|
||||
refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new GenericGroovyApplicationContext, loading bean definitions
|
||||
* from the given resource locations and automatically refreshing the context.
|
||||
* @param relativeClass class whose package will be used as a prefix when
|
||||
* loading each specified resource name
|
||||
* @param resourceNames relatively-qualified names of resources to load
|
||||
*/
|
||||
public GenericGroovyApplicationContext(Class<?> relativeClass, String... resourceNames) {
|
||||
load(relativeClass, resourceNames);
|
||||
refresh();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Exposes the underlying {@link GroovyBeanDefinitionReader} for convenient access
|
||||
* to the {@code loadBeanDefinition} methods on it as well as the ability
|
||||
* to specify an inline Groovy bean definition closure.
|
||||
* @see GroovyBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.Resource...)
|
||||
* @see GroovyBeanDefinitionReader#loadBeanDefinitions(String...)
|
||||
*/
|
||||
public final GroovyBeanDefinitionReader getReader() {
|
||||
return this.reader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates the given environment to underlying {@link GroovyBeanDefinitionReader}.
|
||||
* Should be called before any call to {@code #load}.
|
||||
*/
|
||||
@Override
|
||||
public void setEnvironment(ConfigurableEnvironment environment) {
|
||||
super.setEnvironment(environment);
|
||||
this.reader.setEnvironment(getEnvironment());
|
||||
}
|
||||
|
||||
/**
|
||||
* Load bean definitions from the given Groovy scripts.
|
||||
* @param resources one or more resources to load from
|
||||
*/
|
||||
public void load(Resource... resources) {
|
||||
this.reader.loadBeanDefinitions(resources);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load bean definitions from the given Groovy scripts.
|
||||
* @param resourceLocations one or more resource locations to load from
|
||||
*/
|
||||
public void load(String... resourceLocations) {
|
||||
this.reader.loadBeanDefinitions(resourceLocations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load bean definitions from the given Groovy scripts.
|
||||
* @param relativeClass class whose package will be used as a prefix when
|
||||
* loading each specified resource name
|
||||
* @param resourceNames relatively-qualified names of resources to load
|
||||
*/
|
||||
public void load(Class<?> relativeClass, String... resourceNames) {
|
||||
Resource[] resources = new Resource[resourceNames.length];
|
||||
for (int i = 0; i < resourceNames.length; i++) {
|
||||
resources[i] = new ClassPathResource(resourceNames[i], relativeClass);
|
||||
}
|
||||
this.load(resources);
|
||||
}
|
||||
|
||||
|
||||
// IMPLEMENTATION OF THE GROOVYOBJECT INTERFACE
|
||||
|
||||
public void setMetaClass(MetaClass metaClass) {
|
||||
this.metaClass = metaClass;
|
||||
}
|
||||
|
||||
public MetaClass getMetaClass() {
|
||||
return this.metaClass;
|
||||
}
|
||||
|
||||
public Object invokeMethod(String name, Object args) {
|
||||
return this.metaClass.invokeMethod(this, name, args);
|
||||
}
|
||||
|
||||
public void setProperty(String property, Object newValue) {
|
||||
if (newValue instanceof BeanDefinition) {
|
||||
registerBeanDefinition(property, (BeanDefinition) newValue);
|
||||
}
|
||||
else {
|
||||
this.metaClass.setProperty(this, property, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
public Object getProperty(String property) {
|
||||
if (containsBean(property)) {
|
||||
return getBean(property);
|
||||
}
|
||||
else if (this.contextWrapper.isReadableProperty(property)) {
|
||||
return this.contextWrapper.getPropertyValue(property);
|
||||
}
|
||||
throw new NoSuchBeanDefinitionException(property);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -45,10 +45,9 @@ public class GenericXmlApplicationContext extends GenericApplicationContext {
|
|||
|
||||
/**
|
||||
* Create a new GenericXmlApplicationContext that needs to be
|
||||
* {@linkplain #load loaded} and then manually {@link #refresh refreshed}.
|
||||
* {@link #load loaded} and then manually {@link #refresh refreshed}.
|
||||
*/
|
||||
public GenericXmlApplicationContext() {
|
||||
reader.setEnvironment(this.getEnvironment());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -83,6 +82,15 @@ public class GenericXmlApplicationContext extends GenericApplicationContext {
|
|||
refresh();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Exposes the underlying {@link XmlBeanDefinitionReader} for additional
|
||||
* configuration facilities and {@code loadBeanDefinition} variations.
|
||||
*/
|
||||
public final XmlBeanDefinitionReader getReader() {
|
||||
return this.reader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to use XML validation. Default is {@code true}.
|
||||
*/
|
||||
|
|
@ -91,14 +99,13 @@ public class GenericXmlApplicationContext extends GenericApplicationContext {
|
|||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>Delegates the given environment to underlying {@link XmlBeanDefinitionReader}.
|
||||
* Should be called before any call to {@link #load}.
|
||||
* Delegates the given environment to underlying {@link XmlBeanDefinitionReader}.
|
||||
* Should be called before any call to {@code #load}.
|
||||
*/
|
||||
@Override
|
||||
public void setEnvironment(ConfigurableEnvironment environment) {
|
||||
super.setEnvironment(environment);
|
||||
this.reader.setEnvironment(this.getEnvironment());
|
||||
this.reader.setEnvironment(getEnvironment());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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.context.groovy
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException
|
||||
import org.springframework.context.support.GenericGroovyApplicationContext
|
||||
|
||||
/**
|
||||
* @author Jeff Brown
|
||||
*/
|
||||
class GroovyApplicationContextDynamicBeanPropertyTests extends GroovyTestCase {
|
||||
|
||||
void testAccessDynamicBeanProperties() {
|
||||
def ctx = new GenericGroovyApplicationContext();
|
||||
ctx.reader.loadBeanDefinitions("org/springframework/context/groovy/applicationContext.groovy");
|
||||
ctx.refresh()
|
||||
|
||||
def framework = ctx.framework
|
||||
assertNotNull 'could not find framework bean', framework
|
||||
assertEquals 'Grails', framework
|
||||
}
|
||||
|
||||
void testAccessingNonExistentBeanViaDynamicProperty() {
|
||||
def ctx = new GenericGroovyApplicationContext();
|
||||
ctx.reader.loadBeanDefinitions("org/springframework/context/groovy/applicationContext.groovy");
|
||||
ctx.refresh()
|
||||
|
||||
def err = shouldFail(NoSuchBeanDefinitionException) {
|
||||
ctx.someNonExistentBean
|
||||
}
|
||||
|
||||
assertEquals "No bean named 'someNonExistentBean' is defined", err
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* 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.context.groovy;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.springframework.context.support.GenericGroovyApplicationContext;
|
||||
|
||||
/**
|
||||
* @author Jeff Brown
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public class GroovyApplicationContextTests extends TestCase {
|
||||
|
||||
public void testLoadingConfigFile() {
|
||||
GenericGroovyApplicationContext ctx = new GenericGroovyApplicationContext(
|
||||
"org/springframework/context/groovy/applicationContext.groovy");
|
||||
|
||||
Object framework = ctx.getBean("framework");
|
||||
assertNotNull("could not find framework bean", framework);
|
||||
assertEquals("Grails", framework);
|
||||
}
|
||||
|
||||
public void testLoadingMultipleConfigFiles() {
|
||||
GenericGroovyApplicationContext ctx = new GenericGroovyApplicationContext(
|
||||
"org/springframework/context/groovy/applicationContext2.groovy",
|
||||
"org/springframework/context/groovy/applicationContext.groovy");
|
||||
|
||||
Object framework = ctx.getBean("framework");
|
||||
assertNotNull("could not find framework bean", framework);
|
||||
assertEquals("Grails", framework);
|
||||
|
||||
Object company = ctx.getBean("company");
|
||||
assertNotNull("could not find company bean", company);
|
||||
assertEquals("SpringSource", company);
|
||||
}
|
||||
|
||||
public void testLoadingMultipleConfigFilesWithRelativeClass() {
|
||||
GenericGroovyApplicationContext ctx = new GenericGroovyApplicationContext();
|
||||
ctx.load(GroovyApplicationContextTests.class, "applicationContext2.groovy", "applicationContext.groovy");
|
||||
ctx.refresh();
|
||||
|
||||
Object framework = ctx.getBean("framework");
|
||||
assertNotNull("could not find framework bean", framework);
|
||||
assertEquals("Grails", framework);
|
||||
|
||||
Object company = ctx.getBean("company");
|
||||
assertNotNull("could not find company bean", company);
|
||||
assertEquals("SpringSource", company);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package org.springframework.context.groovy
|
||||
|
||||
beans {
|
||||
framework String, 'Grails'
|
||||
foo String, 'hello'
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package org.springframework.context.groovy
|
||||
|
||||
beans {
|
||||
company String, 'SpringSource'
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
|
||||
|
||||
<bean id="foo" class="java.lang.String">
|
||||
<constructor-arg value="hello"/>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
Loading…
Reference in New Issue