From f7c3706361d7663f261731418a5106113d39c8a1 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Tue, 18 Jan 2022 11:18:15 +0000 Subject: [PATCH] Port GroovyDynamicElementReader to Java Prior to this commit, GroovyDynamicElementReader was implemented in Groovy, which required that developers have Groovy language support installed and configured in their IDEs. This commit ports GroovyDynamicElementReader from Groovy to Java, making the compilation much simpler and allowing developers to open the project in Eclipse or VSCode (or other IDEs without Groovy support) without compiler errors. This commit also converts related tests from Groovy to Java. Closes gh-27945 --- spring-beans/spring-beans.gradle | 24 +- .../groovy/GroovyDynamicElementReader.groovy | 125 -- .../AutowiredAnnotationBeanPostProcessor.java | 3 +- .../groovy/GroovyDynamicElementReader.java | 144 ++ .../factory/support/AbstractBeanFactory.java | 1 + spring-context/spring-context.gradle | 1 - .../GroovyBeanDefinitionReaderTests.groovy | 1063 -------------- ...ationContextDynamicBeanPropertyTests.java} | 36 +- .../GroovyBeanDefinitionReaderTests.java | 1298 +++++++++++++++++ 9 files changed, 1465 insertions(+), 1230 deletions(-) delete mode 100644 spring-beans/src/main/groovy/org/springframework/beans/factory/groovy/GroovyDynamicElementReader.groovy create mode 100644 spring-beans/src/main/java/org/springframework/beans/factory/groovy/GroovyDynamicElementReader.java delete mode 100644 spring-context/src/test/groovy/org/springframework/context/groovy/GroovyBeanDefinitionReaderTests.groovy rename spring-context/src/test/{groovy/org/springframework/context/groovy/GroovyApplicationContextDynamicBeanPropertyTests.groovy => java/org/springframework/context/groovy/GroovyApplicationContextDynamicBeanPropertyTests.java} (50%) create mode 100644 spring-context/src/test/java/org/springframework/context/groovy/GroovyBeanDefinitionReaderTests.java diff --git a/spring-beans/spring-beans.gradle b/spring-beans/spring-beans.gradle index 3c5d34179e7..ddc008baf97 100644 --- a/spring-beans/spring-beans.gradle +++ b/spring-beans/spring-beans.gradle @@ -1,6 +1,5 @@ description = "Spring Beans" -apply plugin: "groovy" apply plugin: "kotlin" dependencies { @@ -14,25 +13,4 @@ dependencies { testImplementation("jakarta.annotation:jakarta.annotation-api") testFixturesApi("org.junit.jupiter:junit-jupiter-api") testFixturesImplementation("org.assertj:assertj-core") -} - -// This module does joint compilation for Java and Groovy code with the compileGroovy task. -sourceSets { - main.groovy.srcDirs += "src/main/java" - main.java.srcDirs = [] -} - -compileGroovy { - options.compilerArgs += "-Werror" -} - -// This module also builds Kotlin code and the compileKotlin task naturally depends on -// compileJava. We need to redefine dependencies to break task cycles. -tasks.named('compileGroovy') { - // Groovy only needs the declared dependencies (and not the result of Java compilation) - classpath = sourceSets.main.compileClasspath -} -tasks.named('compileKotlin') { - // Kotlin also depends on the result of Groovy compilation - classpath += files(sourceSets.main.groovy.classesDirectory) -} +} \ No newline at end of file diff --git a/spring-beans/src/main/groovy/org/springframework/beans/factory/groovy/GroovyDynamicElementReader.groovy b/spring-beans/src/main/groovy/org/springframework/beans/factory/groovy/GroovyDynamicElementReader.groovy deleted file mode 100644 index 8157450773b..00000000000 --- a/spring-beans/src/main/groovy/org/springframework/beans/factory/groovy/GroovyDynamicElementReader.groovy +++ /dev/null @@ -1,125 +0,0 @@ -/* - * 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 - * - * https://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 xmlNamespaces - - private final BeanDefinitionParserDelegate delegate - - private final GroovyBeanDefinitionWrapper beanDefinition - - protected final Boolean decorating; - - private boolean callAfterInvocation = true - - - public GroovyDynamicElementReader(String namespace, Map 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 - } - -} diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java index 2ded080cd5d..00e1b73f342 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java @@ -541,9 +541,8 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA * @param ann the Autowired annotation * @return whether the annotation indicates that a dependency is required */ + @SuppressWarnings("cast") protected boolean determineRequiredStatus(MergedAnnotation ann) { - // Cast to (AnnotationAttributes) is required. Otherwise, the :spring-beans:compileGroovy - // task fails in the Gradle build. return determineRequiredStatus((AnnotationAttributes) ann.asMap(mergedAnnotation -> new AnnotationAttributes(mergedAnnotation.getType()))); } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/groovy/GroovyDynamicElementReader.java b/spring-beans/src/main/java/org/springframework/beans/factory/groovy/GroovyDynamicElementReader.java new file mode 100644 index 00000000000..7cd9e852b6e --- /dev/null +++ b/spring-beans/src/main/java/org/springframework/beans/factory/groovy/GroovyDynamicElementReader.java @@ -0,0 +1,144 @@ +/* + * Copyright 2002-2022 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 + * + * https://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.io.StringWriter; +import java.util.Map; + +import groovy.lang.Closure; +import groovy.lang.GroovyObject; +import groovy.lang.GroovyObjectSupport; +import groovy.lang.Writable; +import groovy.xml.StreamingMarkupBuilder; +import org.w3c.dom.Element; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.BeanDefinitionHolder; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate; + +/** + * Used by GroovyBeanDefinitionReader to read a Spring XML namespace expression + * in the Groovy DSL. + * + * @author Jeff Brown + * @author Juergen Hoeller + * @author Dave Syer + * @since 4.0 + */ +class GroovyDynamicElementReader extends GroovyObjectSupport { + + private final String rootNamespace; + + private final Map xmlNamespaces; + + private final BeanDefinitionParserDelegate delegate; + + private final GroovyBeanDefinitionWrapper beanDefinition; + + protected final boolean decorating; + + private boolean callAfterInvocation = true; + + + public GroovyDynamicElementReader(String namespace, Map 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 obj) { + Object[] args = (Object[]) obj; + if (name.equals("doCall")) { + @SuppressWarnings("unchecked") + Closure callable = (Closure) args[0]; + callable.setResolveStrategy(Closure.DELEGATE_FIRST); + callable.setDelegate(this); + Object result = callable.call(); + + if (this.callAfterInvocation) { + afterInvocation(); + this.callAfterInvocation = false; + } + return result; + } + else { + StreamingMarkupBuilder builder = new StreamingMarkupBuilder(); + String myNamespace = this.rootNamespace; + Map myNamespaces = this.xmlNamespaces; + + Closure callable = new Closure<>(this) { + @Override + public Object call(Object... arguments) { + ((GroovyObject) getProperty("mkp")).invokeMethod("declareNamespace", new Object[] {myNamespaces}); + int len = args.length; + if (len > 0 && args[len-1] instanceof Closure callable) { + callable.setResolveStrategy(Closure.DELEGATE_FIRST); + callable.setDelegate(builder); + } + return ((GroovyObject) ((GroovyObject) getDelegate()).getProperty(myNamespace)).invokeMethod(name, args); + } + }; + + callable.setResolveStrategy(Closure.DELEGATE_FIRST); + callable.setDelegate(builder); + Writable writable = (Writable) builder.bind(callable); + StringWriter sw = new StringWriter(); + try { + writable.writeTo(sw); + } + catch (IOException ex) { + throw new IllegalStateException(ex); + } + + Element element = this.delegate.getReaderContext().readDocumentFromString(sw.toString()).getDocumentElement(); + this.delegate.initDefaults(element); + if (this.decorating) { + BeanDefinitionHolder holder = this.beanDefinition.getBeanDefinitionHolder(); + holder = this.delegate.decorateIfRequired(element, holder, null); + this.beanDefinition.setBeanDefinitionHolder(holder); + } + else { + BeanDefinition beanDefinition = this.delegate.parseCustomElement(element); + if (beanDefinition != null) { + this.beanDefinition.setBeanDefinition((AbstractBeanDefinition) beanDefinition); + } + } + if (this.callAfterInvocation) { + afterInvocation(); + this.callAfterInvocation = false; + } + return element; + } + } + + /** + * Hook that subclasses or anonymous classes can override to implement custom behavior + * after invocation completes. + */ + protected void afterInvocation() { + // NOOP + } + +} diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java index 6c2e05068c2..9c00e0fcf38 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java @@ -1935,6 +1935,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp * * @since 5.3 */ + @SuppressWarnings("serial") private class BeanPostProcessorCacheAwareList extends CopyOnWriteArrayList { @Override diff --git a/spring-context/spring-context.gradle b/spring-context/spring-context.gradle index f7bb843fcc1..60130032046 100644 --- a/spring-context/spring-context.gradle +++ b/spring-context/spring-context.gradle @@ -1,6 +1,5 @@ description = "Spring Context" -apply plugin: "groovy" apply plugin: "kotlin" dependencies { diff --git a/spring-context/src/test/groovy/org/springframework/context/groovy/GroovyBeanDefinitionReaderTests.groovy b/spring-context/src/test/groovy/org/springframework/context/groovy/GroovyBeanDefinitionReaderTests.groovy deleted file mode 100644 index eac5493d907..00000000000 --- a/spring-context/src/test/groovy/org/springframework/context/groovy/GroovyBeanDefinitionReaderTests.groovy +++ /dev/null @@ -1,1063 +0,0 @@ -/* - * Copyright 2002-2019 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 - * - * https://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.junit.jupiter.api.Test - -import org.springframework.aop.SpringProxy -import org.springframework.beans.factory.ObjectFactory -import org.springframework.beans.factory.config.Scope -import org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader -import org.springframework.context.support.GenericApplicationContext -import org.springframework.context.support.GenericGroovyApplicationContext -import org.springframework.stereotype.Component - -import static groovy.test.GroovyAssert.* - -/** - * @author Jeff Brown - * @author Sam Brannen - */ -class GroovyBeanDefinitionReaderTests { - - @Test - void importSpringXml() { - def appCtx = new GenericApplicationContext() - def reader = new GroovyBeanDefinitionReader(appCtx) - - reader.beans { - importBeans "classpath:org/springframework/context/groovy/test.xml" - } - - appCtx.refresh() - - def foo = appCtx.getBean("foo") - assertEquals "hello", foo - } - - @Test - void importBeansFromGroovy() { - def appCtx = new GenericApplicationContext() - def reader = new GroovyBeanDefinitionReader(appCtx) - - reader.beans { - importBeans "classpath:org/springframework/context/groovy/applicationContext.groovy" - } - - appCtx.refresh() - - def foo = appCtx.getBean("foo") - assertEquals "hello", foo - } - - @Test - void singletonPropertyOnBeanDefinition() { - def appCtx = new GenericApplicationContext() - def reader = new GroovyBeanDefinitionReader(appCtx) - reader.beans { - singletonBean(Bean1) { bean -> - bean.singleton = true - } - nonSingletonBean(Bean1) { bean -> - bean.singleton = false - } - unSpecifiedScopeBean(Bean1) - } - appCtx.refresh() - - assertTrue 'singletonBean should have been a singleton', appCtx.isSingleton('singletonBean') - assertFalse 'nonSingletonBean should not have been a singleton', appCtx.isSingleton('nonSingletonBean') - assertTrue 'unSpecifiedScopeBean should not have been a singleton', appCtx.isSingleton('unSpecifiedScopeBean') - } - - @Test - void inheritPropertiesFromAbstractBean() { - def appCtx = new GenericApplicationContext() - def reader = new GroovyBeanDefinitionReader(appCtx) - - reader.beans { - myB(Bean1){ - person = "wombat" - } - - myAbstractA(Bean2){ bean -> - bean.'abstract' = true - age = 10 - bean1 = myB - } - myConcreteB { - it.parent = myAbstractA - } - } - - appCtx.refresh() - - def bean = appCtx.getBean("myConcreteB") - assertEquals 10, bean.age - assertNotNull bean.bean1 - } - - @Test - void contextComponentScanSpringTag() { - def appCtx = new GenericApplicationContext() - def reader = new GroovyBeanDefinitionReader(appCtx) - - reader.beans { - xmlns context:"http://www.springframework.org/schema/context" - - context.'component-scan'( 'base-package' :" org.springframework.context.groovy" ) - } - - appCtx.refresh() - - def p = appCtx.getBean("person") - assertTrue(p instanceof AdvisedPerson) - } - - @Test - void useSpringNamespaceAsMethod() { - def appCtx = new GenericApplicationContext() - def reader = new GroovyBeanDefinitionReader(appCtx) - - reader.beans { - xmlns aop:"http://www.springframework.org/schema/aop" - - fred(AdvisedPerson) { - name = "Fred" - age = 45 - } - birthdayCardSenderAspect(BirthdayCardSender) - - aop { - config("proxy-target-class":true) { - aspect( id:"sendBirthdayCard",ref:"birthdayCardSenderAspect" ) { - after method:"onBirthday", pointcut: "execution(void org.springframework.context.groovy.AdvisedPerson.birthday()) and this(person)" - } - } - } - } - - appCtx.refresh() - - def fred = appCtx.getBean("fred") - assertTrue (fred instanceof SpringProxy) - fred.birthday() - - BirthdayCardSender birthDaySender = appCtx.getBean("birthdayCardSenderAspect") - - assertEquals 1, birthDaySender.peopleSentCards.size() - assertEquals "Fred", birthDaySender.peopleSentCards[0].name - } - - @Test - void useTwoSpringNamespaces() { - def appCtx = new GenericApplicationContext() - def reader = new GroovyBeanDefinitionReader(appCtx) - TestScope scope = new TestScope() - appCtx.getBeanFactory().registerScope("test", scope) - - reader.beans { - xmlns aop:"http://www.springframework.org/schema/aop" - xmlns util:"http://www.springframework.org/schema/util" - scopedList(ArrayList) { bean -> - bean.scope = "test" - aop.'scoped-proxy'() - } - util.list(id: 'foo') { - value 'one' - value 'two' - } - } - appCtx.refresh() - - assert ['one', 'two'] == appCtx.getBean("foo") - - assertNotNull appCtx.getBean("scopedList") - assertNotNull appCtx.getBean("scopedList").size() - assertNotNull appCtx.getBean("scopedList").size() - - // should only be true because bean not initialized until proxy called - assertEquals 2, scope.instanceCount - - appCtx = new GenericApplicationContext() - reader = new GroovyBeanDefinitionReader(appCtx) - appCtx.getBeanFactory().registerScope("test", scope) - - reader.beans { - xmlns aop:"http://www.springframework.org/schema/aop", - util:"http://www.springframework.org/schema/util" - scopedList(ArrayList) { bean -> - bean.scope = "test" - aop.'scoped-proxy'() - } - util.list(id: 'foo') { - value 'one' - value 'two' - } - } - appCtx.refresh() - - assert ['one', 'two'] == appCtx.getBean("foo") - - assertNotNull appCtx.getBean("scopedList") - assertNotNull appCtx.getBean("scopedList").size() - assertNotNull appCtx.getBean("scopedList").size() - - // should only be true because bean not initialized until proxy called - assertEquals 4, scope.instanceCount - } - - @Test - void springAopSupport() { - def appCtx = new GenericApplicationContext() - def reader = new GroovyBeanDefinitionReader(appCtx) - - reader.beans { - xmlns aop:"http://www.springframework.org/schema/aop" - - fred(AdvisedPerson) { - name = "Fred" - age = 45 - } - birthdayCardSenderAspect(BirthdayCardSender) - - aop.config("proxy-target-class":true) { - aspect( id:"sendBirthdayCard",ref:"birthdayCardSenderAspect" ) { - after method:"onBirthday", pointcut: "execution(void org.springframework.context.groovy.AdvisedPerson.birthday()) and this(person)" - } - } - } - - appCtx.refresh() - - def fred = appCtx.getBean("fred") - assertTrue (fred instanceof SpringProxy) - fred.birthday() - - BirthdayCardSender birthDaySender = appCtx.getBean("birthdayCardSenderAspect") - - assertEquals 1, birthDaySender.peopleSentCards.size() - assertEquals "Fred", birthDaySender.peopleSentCards[0].name - } - - @Test - void springScopedProxyBean() { - def appCtx = new GenericApplicationContext() - def reader = new GroovyBeanDefinitionReader(appCtx) - - TestScope scope = new TestScope() - appCtx.getBeanFactory().registerScope("test", scope) - reader.beans { - xmlns aop:"http://www.springframework.org/schema/aop" - scopedList(ArrayList) { bean -> - bean.scope = "test" - aop.'scoped-proxy'() - } - } - appCtx.refresh() - - assertNotNull appCtx.getBean("scopedList") - assertNotNull appCtx.getBean("scopedList").size() - assertNotNull appCtx.getBean("scopedList").size() - - // should only be true because bean not initialized until proxy called - assertEquals 2, scope.instanceCount - } - - @Test - void springNamespaceBean() { - def appCtx = new GenericApplicationContext() - def reader = new GroovyBeanDefinitionReader(appCtx) - reader.beans { - xmlns util: 'http://www.springframework.org/schema/util' - util.list(id: 'foo') { - value 'one' - value 'two' - } - } - appCtx.refresh() - - assert ['one', 'two'] == appCtx.getBean('foo') - } - - @Test - void namedArgumentConstructor() { - def appCtx = new GenericApplicationContext() - def reader = new GroovyBeanDefinitionReader(appCtx) - reader.beans { - holyGrail(HolyGrailQuest) - knights(KnightOfTheRoundTable, "Camelot", leader:"lancelot", quest: holyGrail) - } - appCtx.refresh() - - KnightOfTheRoundTable knights = appCtx.getBean("knights") - HolyGrailQuest quest = appCtx.getBean("holyGrail") - - assertEquals "Camelot", knights.name - assertEquals "lancelot", knights.leader - assertEquals quest, knights.quest - } - - @Test - void abstractBeanDefinition() { - def appCtx = new GenericGroovyApplicationContext() - appCtx.reader.beans { - abstractBean { - leader = "Lancelot" - } - quest(HolyGrailQuest) - knights(KnightOfTheRoundTable, "Camelot") { bean -> - bean.parent = abstractBean - quest = quest - } - } - appCtx.refresh() - - def knights = appCtx.knights - assert knights - shouldFail(org.springframework.beans.factory.BeanIsAbstractException) { - appCtx.abstractBean - } - assertEquals "Lancelot", knights.leader - } - - @Test - void abstractBeanDefinitionWithClass() { - def appCtx = new GenericGroovyApplicationContext() - appCtx.reader.beans { - abstractBean(KnightOfTheRoundTable) { bean -> - bean.'abstract' = true - leader = "Lancelot" - } - quest(HolyGrailQuest) - knights("Camelot") { bean -> - bean.parent = abstractBean - quest = quest - } - } - appCtx.refresh() - - shouldFail(org.springframework.beans.factory.BeanIsAbstractException) { - appCtx.abstractBean - } - def knights = appCtx.knights - assert knights - assertEquals "Lancelot", knights.leader - } - - @Test - void scopes() { - def appCtx = new GenericGroovyApplicationContext() - appCtx.reader.beans { - myBean(ScopeTestBean) { bean -> - bean.scope = "prototype" - } - myBean2(ScopeTestBean) - } - appCtx.refresh() - - def b1 = appCtx.myBean - def b2 = appCtx.myBean - - assert b1 != b2 - - b1 = appCtx.myBean2 - b2 = appCtx.myBean2 - - assertEquals b1, b2 - } - - @Test - void simpleBean() { - def appCtx = new GenericApplicationContext() - def reader = new GroovyBeanDefinitionReader(appCtx) - reader.beans { - bean1(Bean1) { - person = "homer" - age = 45 - props = [overweight:true, height:"1.8m"] - children = ["bart", "lisa"] - } - } - appCtx.refresh() - - assert appCtx.containsBean("bean1") - def bean1 = appCtx.getBean("bean1") - - assertEquals "homer", bean1.person - assertEquals 45, bean1.age - assertEquals true, bean1.props?.overweight - assertEquals "1.8m", bean1.props?.height - assertEquals(["bart", "lisa"], bean1.children) - - } - - @Test - void beanWithParentRef() { - def parentAppCtx = new GenericApplicationContext() - def parentBeanReader = new GroovyBeanDefinitionReader(parentAppCtx) - parentBeanReader.beans { - homer(Bean1) { - person = "homer" - age = 45 - props = [overweight:true, height:"1.8m"] - children = ["bart", "lisa"] - } - } - parentAppCtx.refresh() - - def appCtx = new GenericApplicationContext(parentAppCtx) - def reader = new GroovyBeanDefinitionReader(appCtx) - reader.beans { - bart(Bean2) { - person = "bart" - parent = ref("homer", true) - } - } - appCtx.refresh() - - assert appCtx.containsBean("bart") - def bart = appCtx.getBean("bart") - assertEquals "homer",bart.parent?.person - } - - @Test - void withAnonymousInnerBean() { - def appCtx = new GenericApplicationContext() - def reader = new GroovyBeanDefinitionReader(appCtx) - reader.beans { - bart(Bean1) { - person = "bart" - age = 11 - } - lisa(Bean1) { - person = "lisa" - age = 9 - } - marge(Bean2) { - person = "marge" - bean1 = { Bean1 b -> - person = "homer" - age = 45 - props = [overweight:true, height:"1.8m"] - children = ["bart", "lisa"] } - children = [bart, lisa] - } - } - appCtx.refresh() - - def marge = appCtx.getBean("marge") - assertEquals "homer", marge.bean1.person - } - - @Test - void withUntypedAnonymousInnerBean() { - def appCtx = new GenericApplicationContext() - def reader = new GroovyBeanDefinitionReader(appCtx) - reader.beans { - homer(Bean1Factory) - bart(Bean1) { - person = "bart" - age = 11 - } - lisa(Bean1) { - person = "lisa" - age = 9 - } - marge(Bean2) { - person = "marge" - bean1 = { bean -> - bean.factoryBean = "homer" - bean.factoryMethod = "newInstance" - person = "homer" } - children = [bart, lisa] - } - } - appCtx.refresh() - - def marge = appCtx.getBean("marge") - assertEquals "homer", marge.bean1.person - } - - @Test - void beanReferences() { - def appCtx = new GenericApplicationContext() - def reader = new GroovyBeanDefinitionReader(appCtx) - reader.beans { - homer(Bean1) { - person = "homer" - age = 45 - props = [overweight:true, height:"1.8m"] - children = ["bart", "lisa"] - } - bart(Bean1) { - person = "bart" - age = 11 - } - lisa(Bean1) { - person = "lisa" - age = 9 - } - marge(Bean2) { - person = "marge" - bean1 = homer - children = [bart, lisa] - } - } - appCtx.refresh() - - def homer = appCtx.getBean("homer") - def marge = appCtx.getBean("marge") - def bart = appCtx.getBean("bart") - def lisa = appCtx.getBean("lisa") - - assertEquals homer, marge.bean1 - assertEquals 2, marge.children.size() - - assertTrue marge.children.contains(bart) - assertTrue marge.children.contains(lisa) - } - - @Test - void beanWithConstructor() { - def appCtx = new GenericApplicationContext() - def reader = new GroovyBeanDefinitionReader(appCtx) - reader.beans { - homer(Bean1) { - person = "homer" - age = 45 - } - marge(Bean3, "marge", homer) { - age = 40 - } - } - appCtx.refresh() - - def marge = appCtx.getBean("marge") - assertEquals "marge", marge.person - assertEquals "homer", marge.bean1.person - assertEquals 40, marge.age - } - - @Test - void beanWithFactoryMethod() { - def appCtx = new GenericApplicationContext() - def reader = new GroovyBeanDefinitionReader(appCtx) - reader.beans { - homer(Bean1) { - person = "homer" - age = 45 - } - def marge = marge(Bean4) { - person = "marge" - } - marge.factoryMethod = "getInstance" - } - appCtx.refresh() - - def marge = appCtx.getBean("marge") - - assert "marge", marge.person - } - - @Test - void beanWithFactoryMethodUsingClosureArgs() { - def appCtx = new GenericApplicationContext() - def reader = new GroovyBeanDefinitionReader(appCtx) - reader.beans { - homer(Bean1) { - person = "homer" - age = 45 - } - marge(Bean4) { bean -> - bean.factoryMethod = "getInstance" - person = "marge" - } - } - appCtx.refresh() - - def marge = appCtx.getBean("marge") - assert "marge", marge.person - } - - @Test - void beanWithFactoryMethodWithConstructorArgs() { - def appCtx = new GenericApplicationContext() - def reader = new GroovyBeanDefinitionReader(appCtx) - reader.beans { - beanFactory(Bean1FactoryWithArgs){} - - homer(beanFactory:"newInstance", "homer") { - age = 45 - } - //Test with no closure body - marge(beanFactory:"newInstance", "marge") - - //Test more verbose method - mcBain("mcBain"){ - bean -> - bean.factoryBean="beanFactory" - bean.factoryMethod="newInstance" - - } - } - appCtx.refresh() - - def homer = appCtx.getBean("homer") - - assert "homer", homer.person - assert 45, homer.age - - assert "marge", appCtx.getBean("marge").person - - assert "mcBain", appCtx.getBean("mcBain").person - } - - @Test - void getBeanDefinitions() { - def appCtx = new GenericApplicationContext() - def reader = new GroovyBeanDefinitionReader(appCtx) - reader.beans { - jeff(Bean1) { - person = 'jeff' - } - graeme(Bean1) { - person = 'graeme' - } - guillaume(Bean1) { - person = 'guillaume' - } - } - - assertEquals 'beanDefinitions was the wrong size', 3, reader.registry.beanDefinitionCount - assertNotNull 'beanDefinitions did not contain jeff', reader.registry.getBeanDefinition('jeff') - assertNotNull 'beanDefinitions did not contain guillaume', reader.registry.getBeanDefinition('guillaume') - assertNotNull 'beanDefinitions did not contain graeme', reader.registry.getBeanDefinition('graeme') - } - - @Test - void beanWithFactoryBean() { - def appCtx = new GenericApplicationContext() - def reader = new GroovyBeanDefinitionReader(appCtx) - reader.beans { - myFactory(Bean1Factory) - - homer(myFactory) { bean -> - bean.factoryMethod = "newInstance" - person = "homer" - age = 45 - } - } - appCtx.refresh() - - def homer = appCtx.getBean("homer") - - assertEquals "homer", homer.person - } - - @Test - void beanWithFactoryBeanAndMethod() { - def appCtx = new GenericApplicationContext() - def reader = new GroovyBeanDefinitionReader(appCtx) - reader.beans { - myFactory(Bean1Factory) - - homer(myFactory:"newInstance") { bean -> - person = "homer" - age = 45 - } - } - - appCtx.refresh() - - def homer = appCtx.getBean("homer") - assertEquals "homer", homer.person - } - - @Test - void loadExternalBeans() { - def appCtx = new GenericGroovyApplicationContext("org/springframework/context/groovy/applicationContext.groovy") - - assert appCtx.containsBean("foo") - def foo = appCtx.getBean("foo") - } - - @Test - void loadExternalBeansWithExplicitRefresh() { - def appCtx = new GenericGroovyApplicationContext() - appCtx.load("org/springframework/context/groovy/applicationContext.groovy") - appCtx.refresh() - - assert appCtx.containsBean("foo") - def foo = appCtx.getBean("foo") - } - - @Test - void holyGrailWiring() { - def appCtx = new GenericApplicationContext() - def reader = new GroovyBeanDefinitionReader(appCtx) - - reader.beans { - quest(HolyGrailQuest) - - knight(KnightOfTheRoundTable, "Bedivere") { - quest = ref("quest") - } - } - - appCtx.refresh() - - def knight = appCtx.getBean("knight") - knight.embarkOnQuest() - } - - @Test - void abstractBeanSpecifyingClass() { - def appCtx = new GenericApplicationContext() - def reader = new GroovyBeanDefinitionReader(appCtx) - - reader.beans { - abstractKnight(KnightOfTheRoundTable) { bean -> - bean.'abstract' = true - leader = "King Arthur" - } - - lancelot("lancelot") { bean -> - bean.parent = ref("abstractKnight") - } - - abstractPerson(Bean1) { bean -> - bean.'abstract'=true - age = 45 - } - homerBean { bean -> - bean.parent = ref("abstractPerson") - person = "homer" - } - } - appCtx.refresh() - - def lancelot = appCtx.getBean("lancelot") - assertEquals "King Arthur", lancelot.leader - assertEquals "lancelot", lancelot.name - - def homerBean = appCtx.getBean("homerBean") - - assertEquals 45, homerBean.age - assertEquals "homer", homerBean.person - } - - @Test - void groovyBeanDefinitionReaderWithScript() { - def script = ''' -def appCtx = new org.springframework.context.support.GenericGroovyApplicationContext() -appCtx.reader.beans { -quest(org.springframework.context.groovy.HolyGrailQuest) {} - -knight(org.springframework.context.groovy.KnightOfTheRoundTable, "Bedivere") { quest = quest } -} -appCtx.refresh() -return appCtx -''' - def appCtx = new GroovyShell().evaluate(script) - - def knight = appCtx.getBean('knight') - knight.embarkOnQuest() - } - - // test for GRAILS-5057 - @Test - void registerBeans() { - def appCtx = new GenericApplicationContext() - def reader = new GroovyBeanDefinitionReader(appCtx) - - reader.beans { - personA(AdvisedPerson) { - name = "Bob" - } - } - - appCtx.refresh() - assertEquals "Bob", appCtx.getBean("personA").name - - appCtx = new GenericApplicationContext() - reader = new GroovyBeanDefinitionReader(appCtx) - reader.beans { - personA(AdvisedPerson) { - name = "Fred" - } - } - - appCtx.refresh() - assertEquals "Fred", appCtx.getBean("personA").name - } - - @Test - void listOfBeansAsConstructorArg() { - def appCtx = new GenericApplicationContext() - def reader = new GroovyBeanDefinitionReader(appCtx) - - reader.beans { - someotherbean(SomeOtherClass, new File('somefile.txt')) - someotherbean2(SomeOtherClass, new File('somefile.txt')) - - somebean(SomeClass, [someotherbean, someotherbean2]) - } - - assert appCtx.containsBean('someotherbean') - assert appCtx.containsBean('someotherbean2') - assert appCtx.containsBean('somebean') - } - - @Test - void beanWithListAndMapConstructor() { - def appCtx = new GenericApplicationContext() - def reader = new GroovyBeanDefinitionReader(appCtx) - reader.beans { - bart(Bean1) { - person = "bart" - age = 11 - } - lisa(Bean1) { - person = "lisa" - age = 9 - } - - beanWithList(Bean5, [bart, lisa]) - - // test runtime references both as ref() and as plain name - beanWithMap(Bean6, [bart:bart, lisa:ref('lisa')]) - } - appCtx.refresh() - - def beanWithList = appCtx.getBean("beanWithList") - assertEquals 2, beanWithList.people.size() - assertEquals "bart", beanWithList.people[0].person - - def beanWithMap = appCtx.getBean("beanWithMap") - assertEquals 9, beanWithMap.peopleByName.lisa.age - assertEquals "bart", beanWithMap.peopleByName.bart.person - } - - @Test - void anonymousInnerBeanViaBeanMethod() { - def appCtx = new GenericApplicationContext() - def reader = new GroovyBeanDefinitionReader(appCtx) - reader.beans { - bart(Bean1) { - person = "bart" - age = 11 - } - lisa(Bean1) { - person = "lisa" - age = 9 - } - marge(Bean2) { - person = "marge" - bean1 = bean(Bean1) { - person = "homer" - age = 45 - props = [overweight:true, height:"1.8m"] - children = ["bart", "lisa"] - } - children = [bart, lisa] - } - } - appCtx.refresh() - - def marge = appCtx.getBean("marge") - assertEquals "homer", marge.bean1.person - } - - @Test - void anonymousInnerBeanViaBeanMethodWithConstructorArgs() { - def appCtx = new GenericApplicationContext() - def reader = new GroovyBeanDefinitionReader(appCtx) - reader.beans { - bart(Bean1) { - person = "bart" - age = 11 - } - lisa(Bean1) { - person = "lisa" - age = 9 - } - marge(Bean2) { - person = "marge" - bean3 = bean(Bean3, "homer", lisa) { - person = "homer" - age = 45 - } - children = [bart, lisa] - } - } - appCtx.refresh() - - def marge = appCtx.getBean("marge") - - assertEquals "homer", marge.bean3.person - assertEquals "lisa", marge.bean3.bean1.person - } -} - - -class HolyGrailQuest { - void start() { println "lets begin" } -} - -class KnightOfTheRoundTable { - String name - String leader - - KnightOfTheRoundTable(String n) { - this.name = n - } - - HolyGrailQuest quest - - void embarkOnQuest() { - quest.start() - } -} - -// simple bean -class Bean1 { - String person - int age - Properties props - List children -} - -// bean referencing other bean -class Bean2 { - int age - String person - Bean1 bean1 - Bean3 bean3 - Properties props - List children - Bean1 parent -} - -// bean with constructor args -class Bean3 { - Bean3(String person, Bean1 bean1) { - this.person = person - this.bean1 = bean1 - } - String person - Bean1 bean1 - int age -} - -// bean with factory method -class Bean4 { - private Bean4() {} - static Bean4 getInstance() { - return new Bean4() - } - String person -} - -// bean with List-valued constructor arg -class Bean5 { - Bean5(List people) { - this.people = people - } - List people -} - -// bean with Map-valued constructor arg -class Bean6 { - Bean6(Map peopleByName) { - this.peopleByName = peopleByName - } - Map peopleByName -} - -// a factory bean -class Bean1Factory { - Bean1 newInstance() { - return new Bean1() - } -} - -class ScopeTestBean { -} - -class TestScope implements Scope { - - int instanceCount - - @Override - public Object remove(String name) { - // do nothing - } - - @Override - public void registerDestructionCallback(String name, Runnable callback) { - } - - @Override - public String getConversationId() { - return "mock" - } - - @Override - public Object get(String name, ObjectFactory objectFactory) { - instanceCount++ - objectFactory.getObject() - } - - @Override - public Object resolveContextualObject(String s) { - return null; // noop - } -} - -class BirthdayCardSender { - List peopleSentCards = [] - - public void onBirthday(AdvisedPerson person) { - peopleSentCards << person - } -} - -@Component(value = "person") -public class AdvisedPerson { - int age; - String name; - - public void birthday() { - ++age; - } -} - -class SomeClass { - public SomeClass(List soc) {} -} - -class SomeOtherClass { - public SomeOtherClass(File f) {} -} - -// a factory bean that takes arguments -class Bean1FactoryWithArgs { - Bean1 newInstance(String name) { - new Bean1(person:name) - } -} diff --git a/spring-context/src/test/groovy/org/springframework/context/groovy/GroovyApplicationContextDynamicBeanPropertyTests.groovy b/spring-context/src/test/java/org/springframework/context/groovy/GroovyApplicationContextDynamicBeanPropertyTests.java similarity index 50% rename from spring-context/src/test/groovy/org/springframework/context/groovy/GroovyApplicationContextDynamicBeanPropertyTests.groovy rename to spring-context/src/test/java/org/springframework/context/groovy/GroovyApplicationContextDynamicBeanPropertyTests.java index 948fef89ea2..51ba8d349fc 100644 --- a/spring-context/src/test/groovy/org/springframework/context/groovy/GroovyApplicationContextDynamicBeanPropertyTests.groovy +++ b/spring-context/src/test/java/org/springframework/context/groovy/GroovyApplicationContextDynamicBeanPropertyTests.java @@ -14,14 +14,16 @@ * limitations under the License. */ -package org.springframework.context.groovy +package org.springframework.context.groovy; -import org.junit.jupiter.api.Test +import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.NoSuchBeanDefinitionException -import org.springframework.context.support.GenericGroovyApplicationContext +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.context.support.GenericGroovyApplicationContext; -import static groovy.test.GroovyAssert.* +import static groovy.test.GroovyAssert.assertEquals; +import static groovy.test.GroovyAssert.assertNotNull; +import static groovy.test.GroovyAssert.assertThrows; /** * @author Jeff Brown @@ -31,24 +33,26 @@ class GroovyApplicationContextDynamicBeanPropertyTests { @Test void testAccessDynamicBeanProperties() { - def ctx = new GenericGroovyApplicationContext(); - ctx.reader.loadBeanDefinitions("org/springframework/context/groovy/applicationContext.groovy"); - ctx.refresh() + var ctx = new GenericGroovyApplicationContext(); + ctx.getReader().loadBeanDefinitions("org/springframework/context/groovy/applicationContext.groovy"); + ctx.refresh(); - def framework = ctx.framework - assertNotNull 'could not find framework bean', framework - assertEquals 'Grails', framework + var framework = ctx.getProperty("framework"); + assertNotNull("could not find framework bean", framework); + assertEquals("Grails", framework); + ctx.close(); } @Test void testAccessingNonExistentBeanViaDynamicProperty() { - def ctx = new GenericGroovyApplicationContext(); - ctx.reader.loadBeanDefinitions("org/springframework/context/groovy/applicationContext.groovy"); - ctx.refresh() + var ctx = new GenericGroovyApplicationContext(); + ctx.getReader().loadBeanDefinitions("org/springframework/context/groovy/applicationContext.groovy"); + ctx.refresh(); - def err = shouldFail NoSuchBeanDefinitionException, { ctx.someNonExistentBean } + var err = assertThrows(NoSuchBeanDefinitionException.class, () -> ctx.getProperty("someNonExistentBean")); - assertEquals "No bean named 'someNonExistentBean' available", err.message + assertEquals("No bean named 'someNonExistentBean' available", err.getMessage()); + ctx.close(); } } diff --git a/spring-context/src/test/java/org/springframework/context/groovy/GroovyBeanDefinitionReaderTests.java b/spring-context/src/test/java/org/springframework/context/groovy/GroovyBeanDefinitionReaderTests.java new file mode 100644 index 00000000000..fa3fe2ea5c9 --- /dev/null +++ b/spring-context/src/test/java/org/springframework/context/groovy/GroovyBeanDefinitionReaderTests.java @@ -0,0 +1,1298 @@ +/* + * Copyright 2002-2022 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 + * + * https://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 java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import groovy.lang.GroovyShell; +import org.junit.jupiter.api.Test; + +import org.springframework.aop.SpringProxy; +import org.springframework.beans.factory.ObjectFactory; +import org.springframework.beans.factory.config.Scope; +import org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader; +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.context.support.GenericGroovyApplicationContext; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.stereotype.Component; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +public class GroovyBeanDefinitionReaderTests { + + @Test + void importSpringXml() { + var appCtx = new GenericApplicationContext(); + var reader = new GroovyBeanDefinitionReader(appCtx); + + reader.loadBeanDefinitions(new ByteArrayResource((""" + beans { + importBeans "classpath:org/springframework/context/groovy/test.xml" + } + """).getBytes())); + + appCtx.refresh(); + + var foo = appCtx.getBean("foo"); + assertThat(foo).isEqualTo("hello"); + } + + @Test + void importBeansFromGroovy() { + var appCtx = new GenericApplicationContext(); + var reader = new GroovyBeanDefinitionReader(appCtx); + + reader.loadBeanDefinitions(new ByteArrayResource((""" + beans { + importBeans "classpath:org/springframework/context/groovy/applicationContext.groovy" + } + """).getBytes())); + + appCtx.refresh(); + + var foo = appCtx.getBean("foo"); + assertThat(foo).isEqualTo("hello"); + } + + @Test + void singletonPropertyOnBeanDefinition() { + var appCtx = new GenericApplicationContext(); + var reader = new GroovyBeanDefinitionReader(appCtx); + reader.loadBeanDefinitions(new ByteArrayResource((""" + package org.springframework.context.groovy; + beans { + singletonBean(Bean1) { bean -> + bean.singleton = true + } + nonSingletonBean(Bean1) { bean -> + bean.singleton = false + } + unSpecifiedScopeBean(Bean1) + } + """).getBytes())); + appCtx.refresh(); + + assertThat(appCtx.isSingleton("singletonBean")).as("singletonBean should have been a singleton").isTrue(); + assertThat(appCtx.isSingleton("nonSingletonBean")).as("nonSingletonBean should not have been a singleton").isFalse(); + assertThat(appCtx.isSingleton("unSpecifiedScopeBean")).as("unSpecifiedScopeBean should not have been a singleton").isTrue(); + } + + @Test + void inheritPropertiesFromAbstractBean() { + var appCtx = new GenericApplicationContext(); + var reader = new GroovyBeanDefinitionReader(appCtx); + + reader.loadBeanDefinitions(new ByteArrayResource((""" + package org.springframework.context.groovy; + beans { + myB(Bean1){ + person = "wombat" + } + + myAbstractA(Bean2){ bean -> + bean."abstract" = true + age = 10 + bean1 = myB + } + myConcreteB { + it.parent = myAbstractA + } + } + """).getBytes())); + appCtx.refresh(); + + Bean2 bean = (Bean2) appCtx.getBean("myConcreteB"); + assertThat(bean.age).isEqualTo(10); + assertThat(bean.bean1).isNotNull(); + } + + @Test + void contextComponentScanSpringTag() { + var appCtx = new GenericApplicationContext(); + var reader = new GroovyBeanDefinitionReader(appCtx); + + reader.loadBeanDefinitions(new ByteArrayResource((""" + beans { + xmlns context:"http://www.springframework.org/schema/context" + + context.'component-scan'( 'base-package':"org.springframework.context.groovy" ) + } + """).getBytes())); + appCtx.refresh(); + + var p = appCtx.getBean("person"); + assertThat(p).isInstanceOf(AdvisedPerson.class); + } + + @Test + void useSpringNamespaceAsMethod() { + var appCtx = new GenericApplicationContext(); + var reader = new GroovyBeanDefinitionReader(appCtx); + + reader.loadBeanDefinitions(new ByteArrayResource((""" + package org.springframework.context.groovy; + beans { + xmlns aop:"http://www.springframework.org/schema/aop" + + fred(AdvisedPerson) { + name = "Fred" + age = 45 + } + birthdayCardSenderAspect(BirthdayCardSender) + + aop { + config("proxy-target-class":true) { + aspect( id:"sendBirthdayCard",ref:"birthdayCardSenderAspect" ) { + after method:"onBirthday", pointcut: "execution(void org.springframework.context.groovy.AdvisedPerson.birthday()) and this(person)" + } + } + } + } + """).getBytes())); + + appCtx.refresh(); + + AdvisedPerson fred = (AdvisedPerson) appCtx.getBean("fred"); + assertThat(fred).isInstanceOf(SpringProxy.class); + fred.birthday(); + + BirthdayCardSender birthDaySender = (BirthdayCardSender) appCtx.getBean("birthdayCardSenderAspect"); + + assertThat(birthDaySender.peopleSentCards).hasSize(1); + assertThat(birthDaySender.peopleSentCards.get(0).getName()).isEqualTo("Fred"); + } + + @SuppressWarnings("unchecked") + @Test + void useTwoSpringNamespaces() { + var appCtx = new GenericApplicationContext(); + var reader = new GroovyBeanDefinitionReader(appCtx); + TestScope scope = new TestScope(); + appCtx.getBeanFactory().registerScope("test", scope); + + reader.loadBeanDefinitions(new ByteArrayResource((""" + package org.springframework.context.groovy + beans { + xmlns aop:"http://www.springframework.org/schema/aop" + xmlns util:"http://www.springframework.org/schema/util" + bean1(Bean1) { bean -> + bean.scope = "test" + aop.'scoped-proxy'() + } + util.list(id: 'foo') { + value 'one' + value 'two' + } + } + """).getBytes())); + appCtx.refresh(); + + assertThat((List)appCtx.getBean("foo")).containsExactly("one", "two"); + + assertThat(appCtx.getBean("bean1")).isNotNull(); + assertThat(((Bean1)appCtx.getBean("bean1")).getPerson()).isNull(); + assertThat(((Bean1)appCtx.getBean("bean1")).getPerson()).isNull(); + + // should only be true because bean not initialized until proxy called + assertThat(scope.instanceCount).isEqualTo(2); + + appCtx = new GenericApplicationContext(); + reader = new GroovyBeanDefinitionReader(appCtx); + appCtx.getBeanFactory().registerScope("test", scope); + + reader.loadBeanDefinitions(new ByteArrayResource((""" + package org.springframework.context.groovy + import java.util.ArrayList + beans { + xmlns aop:"http://www.springframework.org/schema/aop", + util:"http://www.springframework.org/schema/util" + bean1(Bean1) { bean -> + bean.scope = "test" + aop.'scoped-proxy'() + } + util.list(id: 'foo') { + value 'one' + value 'two' + } + } + """).getBytes())); + appCtx.refresh(); + + assertThat((List)appCtx.getBean("foo")).containsExactly("one", "two"); + + assertThat(appCtx.getBean("bean1")).isNotNull(); + assertThat(((Bean1)appCtx.getBean("bean1")).getPerson()).isNull(); + assertThat(((Bean1)appCtx.getBean("bean1")).getPerson()).isNull(); + + // should only be true because bean not initialized until proxy called + assertThat(scope.instanceCount).isEqualTo(4); + } + + @Test + void springAopSupport() { + var appCtx = new GenericApplicationContext(); + var reader = new GroovyBeanDefinitionReader(appCtx); + + reader.loadBeanDefinitions(new ByteArrayResource((""" + package org.springframework.context.groovy + beans { + xmlns aop:"http://www.springframework.org/schema/aop" + + fred(AdvisedPerson) { + name = "Fred" + age = 45 + } + birthdayCardSenderAspect(BirthdayCardSender) + + aop.config("proxy-target-class":true) { + aspect( id:"sendBirthdayCard",ref:"birthdayCardSenderAspect" ) { + after method:"onBirthday", pointcut: "execution(void org.springframework.context.groovy.AdvisedPerson.birthday()) and this(person)" + } + } + } + """).getBytes())); + + appCtx.refresh(); + + AdvisedPerson fred = (AdvisedPerson) appCtx.getBean("fred"); + assertThat(fred).isInstanceOf(SpringProxy.class); + fred.birthday(); + + BirthdayCardSender birthDaySender = (BirthdayCardSender) appCtx.getBean("birthdayCardSenderAspect"); + + assertThat(birthDaySender.peopleSentCards).hasSize(1); + assertThat(birthDaySender.peopleSentCards.get(0).getName()).isEqualTo("Fred"); + } + + @Test + void springScopedProxyBean() { + var appCtx = new GenericApplicationContext(); + var reader = new GroovyBeanDefinitionReader(appCtx); + + TestScope scope = new TestScope(); + appCtx.getBeanFactory().registerScope("test", scope); + reader.loadBeanDefinitions(new ByteArrayResource((""" + package org.springframework.context.groovy + beans { + xmlns aop:"http://www.springframework.org/schema/aop" + scopedList(Bean1) { bean -> + bean.scope = "test" + aop.'scoped-proxy'() + } + } + """).getBytes())); + appCtx.refresh(); + + assertThat(appCtx.getBean("scopedList")).isNotNull(); + assertThat(((Bean1)appCtx.getBean("scopedList")).getPerson()).isNull(); + assertThat(((Bean1)appCtx.getBean("scopedList")).getPerson()).isNull(); + + // should only be true because bean not initialized until proxy called + assertThat(scope.instanceCount).isEqualTo(2); + } + + @Test + void springNamespaceBean() { + var appCtx = new GenericApplicationContext(); + var reader = new GroovyBeanDefinitionReader(appCtx); + reader.loadBeanDefinitions(new ByteArrayResource((""" + package org.springframework.context.groovy + beans { + xmlns util: 'http://www.springframework.org/schema/util' + util.list(id: 'foo') { + value 'one' + value 'two' + } + } + """).getBytes())); + appCtx.refresh(); + + assertThat((List)appCtx.getBean("foo")).contains("one", "two"); + } + + @Test + void namedArgumentConstructor() { + var appCtx = new GenericApplicationContext(); + var reader = new GroovyBeanDefinitionReader(appCtx); + reader.loadBeanDefinitions(new ByteArrayResource((""" + package org.springframework.context.groovy + beans { + holyGrail(HolyGrailQuest) + knights(KnightOfTheRoundTable, "Camelot", leader:"lancelot", quest: holyGrail) + } + """).getBytes())); + appCtx.refresh(); + + KnightOfTheRoundTable knights = (KnightOfTheRoundTable) appCtx.getBean("knights"); + HolyGrailQuest quest = (HolyGrailQuest) appCtx.getBean("holyGrail"); + + assertThat(knights.getName()).isEqualTo("Camelot"); + assertThat(knights.leader).isEqualTo("lancelot"); + assertThat(knights.quest).isEqualTo(quest); + } + + @Test + void abstractBeanDefinition() { + var appCtx = new GenericGroovyApplicationContext(); + appCtx.getReader().loadBeanDefinitions(new ByteArrayResource((""" + package org.springframework.context.groovy + beans { + abstractBean { + leader = "Lancelot" + } + quest(HolyGrailQuest) + knights(KnightOfTheRoundTable, "Camelot") { bean -> + bean.parent = abstractBean + quest = quest + } + } + """).getBytes())); + appCtx.refresh(); + + KnightOfTheRoundTable knights = (KnightOfTheRoundTable) appCtx.getProperty("knights"); + assertThat(knights).isNotNull(); + assertThatExceptionOfType(org.springframework.beans.factory.BeanIsAbstractException.class).isThrownBy(() -> + appCtx.getProperty("abstractBean")); + assertThat(knights.leader).isEqualTo("Lancelot"); + } + + @Test + void abstractBeanDefinitionWithClass() { + var appCtx = new GenericGroovyApplicationContext(); + appCtx.getReader().loadBeanDefinitions(new ByteArrayResource((""" + package org.springframework.context.groovy + beans { + abstractBean(KnightOfTheRoundTable) { bean -> + bean.'abstract' = true + leader = "Lancelot" + } + quest(HolyGrailQuest) + knights("Camelot") { bean -> + bean.parent = abstractBean + quest = quest + } + } + """).getBytes())); + appCtx.refresh(); + + assertThatExceptionOfType(org.springframework.beans.factory.BeanIsAbstractException.class).isThrownBy(() -> + appCtx.getProperty("abstractBean")); + + KnightOfTheRoundTable knights = (KnightOfTheRoundTable) appCtx.getProperty("knights"); + assertThat(knights).isNotNull(); + assertThat(knights.leader).isEqualTo("Lancelot"); + } + + @Test + void scopes() { + var appCtx = new GenericGroovyApplicationContext(); + appCtx.getReader().loadBeanDefinitions(new ByteArrayResource((""" + package org.springframework.context.groovy + beans { + myBean(ScopeTestBean) { bean -> + bean.scope = "prototype" + } + myBean2(ScopeTestBean) + } + """).getBytes())); + appCtx.refresh(); + + var b1 = appCtx.getProperty("myBean"); + var b2 = appCtx.getProperty("myBean"); + + assertThat(b1).isNotSameAs(b2); + + b1 = appCtx.getProperty("myBean2"); + b2 = appCtx.getProperty("myBean2"); + + assertThat(b1).isEqualTo(b2); + } + + @Test + void simpleBean() { + var appCtx = new GenericApplicationContext(); + var reader = new GroovyBeanDefinitionReader(appCtx); + reader.loadBeanDefinitions(new ByteArrayResource((""" + package org.springframework.context.groovy + beans { + bean1(Bean1) { + person = "homer" + age = 45 + props = [overweight:"true", height:"1.8m"] + children = ["bart", "lisa"] + } + } + """).getBytes())); + appCtx.refresh(); + + assertThat(appCtx.containsBean("bean1")).isTrue(); + Bean1 bean1 = (Bean1) appCtx.getBean("bean1"); + + assertThat(bean1.person).isEqualTo("homer"); + assertThat(bean1.age).isEqualTo(45); + assertThat(bean1.props.getProperty("overweight")).isEqualTo("true"); + assertThat(bean1.props.getProperty("height")).isEqualTo("1.8m"); + assertThat(bean1.children).containsExactly("bart", "lisa"); + + } + + @Test + void beanWithParentRef() { + var parentAppCtx = new GenericApplicationContext(); + var parentBeanReader = new GroovyBeanDefinitionReader(parentAppCtx); + parentBeanReader.loadBeanDefinitions(new ByteArrayResource((""" + package org.springframework.context.groovy + beans { + homer(Bean1) { + person = "homer" + age = 45 + props = [overweight:true, height:"1.8m"] + children = ["bart", "lisa"] + } + } + """).getBytes())); + parentAppCtx.refresh(); + + var appCtx = new GenericApplicationContext(parentAppCtx); + var reader = new GroovyBeanDefinitionReader(appCtx); + reader.loadBeanDefinitions(new ByteArrayResource((""" + package org.springframework.context.groovy + beans { + bart(Bean2) { + person = "bart" + parent = ref("homer", true) + } + } + """).getBytes())); + appCtx.refresh(); + + assertThat(appCtx.containsBean("bart")).isTrue(); + Bean2 bart = (Bean2) appCtx.getBean("bart"); + assertThat(bart.parent.person).isEqualTo("homer"); + } + + @Test + void withAnonymousInnerBean() { + var appCtx = new GenericApplicationContext(); + var reader = new GroovyBeanDefinitionReader(appCtx); + reader.loadBeanDefinitions(new ByteArrayResource((""" + package org.springframework.context.groovy + beans { + bart(Bean1) { + person = "bart" + age = 11 + } + lisa(Bean1) { + person = "lisa" + age = 9 + } + marge(Bean2) { + person = "marge" + bean1 = { Bean1 b -> + person = "homer" + age = 45 + props = [overweight:true, height:"1.8m"] + children = ["bart", "lisa"] } + children = [bart, lisa] + } + } + """).getBytes())); + appCtx.refresh(); + + Bean2 marge = (Bean2) appCtx.getBean("marge"); + assertThat(marge.bean1.person).isEqualTo("homer"); + } + + @Test + void withUntypedAnonymousInnerBean() { + var appCtx = new GenericApplicationContext(); + var reader = new GroovyBeanDefinitionReader(appCtx); + reader.loadBeanDefinitions(new ByteArrayResource((""" + package org.springframework.context.groovy + beans { + homer(Bean1Factory) + bart(Bean1) { + person = "bart" + age = 11 + } + lisa(Bean1) { + person = "lisa" + age = 9 + } + marge(Bean2) { + person = "marge" + bean1 = { bean -> + bean.factoryBean = "homer" + bean.factoryMethod = "newInstance" + person = "homer" } + children = [bart, lisa] + } + } + """).getBytes())); + appCtx.refresh(); + + Bean2 marge = (Bean2) appCtx.getBean("marge"); + assertThat(marge.bean1.person).isEqualTo("homer"); + } + + @Test + void beanReferences() { + var appCtx = new GenericApplicationContext(); + var reader = new GroovyBeanDefinitionReader(appCtx); + reader.loadBeanDefinitions(new ByteArrayResource((""" + package org.springframework.context.groovy + beans { + homer(Bean1) { + person = "homer" + age = 45 + props = [overweight:true, height:"1.8m"] + children = ["bart", "lisa"] + } + bart(Bean1) { + person = "bart" + age = 11 + } + lisa(Bean1) { + person = "lisa" + age = 9 + } + marge(Bean2) { + person = "marge" + bean1 = homer + children = [bart, lisa] + } + } + """).getBytes())); + appCtx.refresh(); + + var homer = appCtx.getBean("homer"); + Bean2 marge = (Bean2) appCtx.getBean("marge"); + Bean1 bart = (Bean1) appCtx.getBean("bart"); + Bean1 lisa = (Bean1) appCtx.getBean("lisa"); + + assertThat(marge.bean1).isEqualTo(homer); + assertThat(marge.children).hasSize(2); + + assertThat(marge.children).containsExactlyInAnyOrder(bart, lisa); + } + + @Test + void beanWithConstructor() { + var appCtx = new GenericApplicationContext(); + var reader = new GroovyBeanDefinitionReader(appCtx); + reader.loadBeanDefinitions(new ByteArrayResource((""" + package org.springframework.context.groovy + beans { + homer(Bean1) { + person = "homer" + age = 45 + } + marge(Bean3, "marge", homer) { + age = 40 + } + } + """).getBytes())); + appCtx.refresh(); + + Bean3 marge = (Bean3) appCtx.getBean("marge"); + assertThat(marge.person).isEqualTo("marge"); + assertThat(marge.bean1.person).isEqualTo("homer"); + assertThat(marge.age).isEqualTo(40); + } + + @Test + void beanWithFactoryMethod() { + var appCtx = new GenericApplicationContext(); + var reader = new GroovyBeanDefinitionReader(appCtx); + reader.loadBeanDefinitions(new ByteArrayResource((""" + package org.springframework.context.groovy + beans { + homer(Bean1) { + person = "homer" + age = 45 + } + def marge = marge(Bean4) { + person = "marge" + } + marge.factoryMethod = "getInstance" + } + """).getBytes())); + appCtx.refresh(); + + Bean4 marge = (Bean4) appCtx.getBean("marge"); + assertThat(marge.person).isEqualTo("marge"); + } + + @Test + void beanWithFactoryMethodUsingClosureArgs() { + var appCtx = new GenericApplicationContext(); + var reader = new GroovyBeanDefinitionReader(appCtx); + reader.loadBeanDefinitions(new ByteArrayResource((""" + package org.springframework.context.groovy + beans { + homer(Bean1) { + person = "homer" + age = 45 + } + marge(Bean4) { bean -> + bean.factoryMethod = "getInstance" + person = "marge" + } + } + """).getBytes())); + appCtx.refresh(); + + Bean4 marge = (Bean4) appCtx.getBean("marge"); + assertThat(marge.person).isEqualTo("marge"); + } + + @Test + void beanWithFactoryMethodWithConstructorArgs() { + var appCtx = new GenericApplicationContext(); + var reader = new GroovyBeanDefinitionReader(appCtx); + reader.loadBeanDefinitions(new ByteArrayResource((""" + package org.springframework.context.groovy + beans { + beanFactory(Bean1FactoryWithArgs){} + + homer(beanFactory:"newInstance", "homer") { + age = 45 + } + //Test with no closure body + marge(beanFactory:"newInstance", "marge") + + //Test more verbose method + mcBain("mcBain"){ + bean -> + bean.factoryBean="beanFactory" + bean.factoryMethod="newInstance" + + } + } + """).getBytes())); + appCtx.refresh(); + + Bean1 homer = (Bean1) appCtx.getBean("homer"); + + assertThat(homer.person).isEqualTo("homer"); + assertThat(homer.age).isEqualTo(45); + + assertThat(((Bean1)appCtx.getBean("marge")).person).isEqualTo("marge"); + + assertThat(((Bean1)appCtx.getBean("mcBain")).person).isEqualTo("mcBain"); + } + + @Test + void getBeanDefinitions() { + var appCtx = new GenericApplicationContext(); + var reader = new GroovyBeanDefinitionReader(appCtx); + reader.loadBeanDefinitions(new ByteArrayResource((""" + package org.springframework.context.groovy + beans { + jeff(Bean1) { + person = 'jeff' + } + graeme(Bean1) { + person = 'graeme' + } + guillaume(Bean1) { + person = 'guillaume' + } + } + """).getBytes())); + + assertThat(reader.getRegistry().getBeanDefinitionCount()).as("beanDefinitions was the wrong size").isEqualTo(3); + assertThat(reader.getRegistry().getBeanDefinition("jeff")).as("beanDefinitions did not contain jeff").isNotNull(); + assertThat(reader.getRegistry().getBeanDefinition("guillaume")).as("beanDefinitions did not contain guillaume").isNotNull(); + assertThat(reader.getRegistry().getBeanDefinition("graeme")).as("beanDefinitions did not contain graeme").isNotNull(); + } + + @Test + void beanWithFactoryBean() { + var appCtx = new GenericApplicationContext(); + var reader = new GroovyBeanDefinitionReader(appCtx); + reader.loadBeanDefinitions(new ByteArrayResource((""" + package org.springframework.context.groovy + beans { + myFactory(Bean1Factory) + + homer(myFactory) { bean -> + bean.factoryMethod = "newInstance" + person = "homer" + age = 45 + } + } + """).getBytes())); + appCtx.refresh(); + + Bean1 homer = (Bean1) appCtx.getBean("homer"); + + assertThat(homer.person).isEqualTo("homer"); + } + + @Test + void beanWithFactoryBeanAndMethod() { + var appCtx = new GenericApplicationContext(); + var reader = new GroovyBeanDefinitionReader(appCtx); + reader.loadBeanDefinitions(new ByteArrayResource((""" + package org.springframework.context.groovy + beans { + myFactory(Bean1Factory) + + homer(myFactory:"newInstance") { bean -> + person = "homer" + age = 45 + } + } + """).getBytes())); + + appCtx.refresh(); + + Bean1 homer = (Bean1) appCtx.getBean("homer"); + assertThat(homer.person).isEqualTo("homer"); + } + + @Test + void loadExternalBeans() { + var appCtx = new GenericGroovyApplicationContext("org/springframework/context/groovy/applicationContext.groovy"); + + assertThat(appCtx.containsBean("foo")).isTrue(); + var foo = appCtx.getBean("foo"); + assertThat(foo).isEqualTo("hello"); + } + + @Test + void loadExternalBeansWithExplicitRefresh() { + var appCtx = new GenericGroovyApplicationContext(); + appCtx.load("org/springframework/context/groovy/applicationContext.groovy"); + appCtx.refresh(); + + assertThat(appCtx.containsBean("foo")).isTrue(); + var foo = appCtx.getBean("foo"); + assertThat(foo).isEqualTo("hello"); + } + + @Test + void holyGrailWiring() { + var appCtx = new GenericApplicationContext(); + var reader = new GroovyBeanDefinitionReader(appCtx); + + reader.loadBeanDefinitions(new ByteArrayResource((""" + package org.springframework.context.groovy + beans { + quest(HolyGrailQuest) + + knight(KnightOfTheRoundTable, "Bedivere") { + quest = ref("quest") + } + } + """).getBytes())); + + appCtx.refresh(); + + KnightOfTheRoundTable knight = (KnightOfTheRoundTable) appCtx.getBean("knight"); + knight.embarkOnQuest(); + } + + @Test + void abstractBeanSpecifyingClass() { + var appCtx = new GenericApplicationContext(); + var reader = new GroovyBeanDefinitionReader(appCtx); + + reader.loadBeanDefinitions(new ByteArrayResource((""" + package org.springframework.context.groovy + beans { + abstractKnight(KnightOfTheRoundTable) { bean -> + bean.'abstract' = true + leader = "King Arthur" + } + + lancelot("lancelot") { bean -> + bean.parent = ref("abstractKnight") + } + + abstractPerson(Bean1) { bean -> + bean.'abstract'=true + age = 45 + } + homerBean { bean -> + bean.parent = ref("abstractPerson") + person = "homer" + } + } + """).getBytes())); + appCtx.refresh(); + + KnightOfTheRoundTable lancelot = (KnightOfTheRoundTable) appCtx.getBean("lancelot"); + assertThat(lancelot.leader).isEqualTo("King Arthur"); + assertThat(lancelot.name).isEqualTo("lancelot"); + + Bean1 homerBean = (Bean1) appCtx.getBean("homerBean"); + + assertThat(homerBean.age).isEqualTo(45); + assertThat(homerBean.person).isEqualTo("homer"); + } + + @Test + void groovyBeanDefinitionReaderWithScript() throws Exception { + var script = """ +def appCtx = new org.springframework.context.support.GenericGroovyApplicationContext() +appCtx.reader.beans { +quest(org.springframework.context.groovy.HolyGrailQuest) {} + +knight(org.springframework.context.groovy.KnightOfTheRoundTable, "Bedivere") { quest = quest } +} +appCtx.refresh() +return appCtx +"""; + GenericGroovyApplicationContext appCtx = (GenericGroovyApplicationContext) new GroovyShell().evaluate(script); + + KnightOfTheRoundTable knight = (KnightOfTheRoundTable) appCtx.getBean("knight"); + knight.embarkOnQuest(); + } + + // test for GRAILS-5057 + @Test + void registerBeans() { + var appCtx = new GenericApplicationContext(); + var reader = new GroovyBeanDefinitionReader(appCtx); + + reader.loadBeanDefinitions(new ByteArrayResource((""" + package org.springframework.context.groovy + beans { + personA(AdvisedPerson) { + name = "Bob" + } + } + """).getBytes())); + + appCtx.refresh(); + assertThat(((AdvisedPerson)appCtx.getBean("personA")).name).isEqualTo("Bob"); + + appCtx = new GenericApplicationContext(); + reader = new GroovyBeanDefinitionReader(appCtx); + reader.loadBeanDefinitions(new ByteArrayResource((""" + package org.springframework.context.groovy + beans { + personA(AdvisedPerson) { + name = "Fred" + } + } + """).getBytes())); + + appCtx.refresh(); + assertThat(((AdvisedPerson)appCtx.getBean("personA")).name).isEqualTo("Fred"); + } + + @Test + void listOfBeansAsConstructorArg() { + var appCtx = new GenericApplicationContext(); + var reader = new GroovyBeanDefinitionReader(appCtx); + + reader.loadBeanDefinitions(new ByteArrayResource((""" + package org.springframework.context.groovy + beans { + someotherbean(SomeOtherClass, new File('somefile.txt')) + someotherbean2(SomeOtherClass, new File('somefile.txt')) + + somebean(SomeClass, [someotherbean, someotherbean2]) + } + """).getBytes())); + + assertThat(appCtx.containsBean("someotherbean")).isTrue(); + assertThat(appCtx.containsBean("someotherbean2")).isTrue(); + assertThat(appCtx.containsBean("somebean")).isTrue(); + } + + @Test + void beanWithListAndMapConstructor() { + var appCtx = new GenericApplicationContext(); + var reader = new GroovyBeanDefinitionReader(appCtx); + reader.loadBeanDefinitions(new ByteArrayResource((""" + package org.springframework.context.groovy + beans { + bart(Bean1) { + person = "bart" + age = 11 + } + lisa(Bean1) { + person = "lisa" + age = 9 + } + + beanWithList(Bean5, [bart, lisa]) + + // test runtime references both as ref() and as plain name + beanWithMap(Bean6, [bart:bart, lisa:ref('lisa')]) + } + """).getBytes())); + appCtx.refresh(); + + Bean5 beanWithList = (Bean5) appCtx.getBean("beanWithList"); + assertThat(beanWithList.people).hasSize(2); + assertThat(beanWithList.people.get(0).person).isEqualTo("bart"); + + Bean6 beanWithMap = (Bean6) appCtx.getBean("beanWithMap"); + assertThat(beanWithMap.peopleByName.get("lisa").age).isEqualTo(9); + assertThat(beanWithMap.peopleByName.get("bart").person).isEqualTo("bart"); + } + + @Test + void anonymousInnerBeanViaBeanMethod() { + var appCtx = new GenericApplicationContext(); + var reader = new GroovyBeanDefinitionReader(appCtx); + reader.loadBeanDefinitions(new ByteArrayResource((""" + package org.springframework.context.groovy + beans { + bart(Bean1) { + person = "bart" + age = 11 + } + lisa(Bean1) { + person = "lisa" + age = 9 + } + marge(Bean2) { + person = "marge" + bean1 = bean(Bean1) { + person = "homer" + age = 45 + props = [overweight:true, height:"1.8m"] + children = ["bart", "lisa"] + } + children = [bart, lisa] + } + } + """).getBytes())); + appCtx.refresh(); + + Bean2 marge = (Bean2) appCtx.getBean("marge"); + assertThat(marge.bean1.person).isEqualTo("homer"); + } + + @Test + void anonymousInnerBeanViaBeanMethodWithConstructorArgs() { + var appCtx = new GenericApplicationContext(); + var reader = new GroovyBeanDefinitionReader(appCtx); + reader.loadBeanDefinitions(new ByteArrayResource((""" + package org.springframework.context.groovy + beans { + bart(Bean1) { + person = "bart" + age = 11 + } + lisa(Bean1) { + person = "lisa" + age = 9 + } + marge(Bean2) { + person = "marge" + bean3 = bean(Bean3, "homer", lisa) { + person = "homer" + age = 45 + } + children = [bart, lisa] + } + } + """).getBytes())); + appCtx.refresh(); + + Bean2 marge = (Bean2) appCtx.getBean("marge"); + + assertThat(marge.bean3.person).isEqualTo("homer"); + assertThat(marge.bean3.bean1.person).isEqualTo("lisa"); + } + +} + +class HolyGrailQuest { + void start() { System.out.println("lets begin"); } +} + +class KnightOfTheRoundTable { + String name; + String leader; + + KnightOfTheRoundTable(String n) { + this.name = n; + } + + HolyGrailQuest quest; + + void embarkOnQuest() { + quest.start(); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getLeader() { + return leader; + } + + public void setLeader(String leader) { + this.leader = leader; + } + + public HolyGrailQuest getQuest() { + return quest; + } + + public void setQuest(HolyGrailQuest quest) { + this.quest = quest; + } +} + +// simple bean +class Bean1 { + String person; + int age; + Properties props = new Properties(); + List children = new ArrayList<>(); + public String getPerson() { + return person; + } + public void setPerson(String person) { + this.person = person; + } + public int getAge() { + return age; + } + public void setAge(int age) { + this.age = age; + } + public Properties getProps() { + return props; + } + public void setProps(Properties props) { + this.props.putAll(props); + } + public List getChildren() { + return children; + } + public void setChildren(List children) { + this.children = children; + } +} + +// bean referencing other bean +class Bean2 { + int age; + Bean1 bean1; + Bean3 bean3; + String person; + Bean1 parent; + List children = new ArrayList<>(); + public int getAge() { + return age; + } + public void setAge(int age) { + this.age = age; + } + public String getPerson() { + return person; + } + public void setPerson(String person) { + this.person = person; + } + public Bean1 getParent() { + return parent; + } + public void setParent(Bean1 parent) { + this.parent = parent; + } + public Bean1 getBean1() { + return bean1; + } + public void setBean1(Bean1 bean1) { + this.bean1 = bean1; + } + public Bean3 getBean3() { + return bean3; + } + public void setBean3(Bean3 bean3) { + this.bean3 = bean3; + } + public List getChildren() { + return children; + } + public void setChildren(List children) { + this.children = children; + } +} + +// bean with constructor args +class Bean3 { + Bean3(String person, Bean1 bean1) { + this.person = person; + this.bean1 = bean1; + } + String person; + Bean1 bean1; + int age; + public int getAge() { + return age; + } + public void setAge(int age) { + this.age = age; + } + public String getPerson() { + return person; + } + public void setPerson(String person) { + this.person = person; + } + public Bean1 getBean1() { + return bean1; + } + public void setBean1(Bean1 bean1) { + this.bean1 = bean1; + } +} + +// bean with factory method +class Bean4 { + private Bean4() {} + static Bean4 getInstance() { + return new Bean4(); + } + String person; + public String getPerson() { + return person; + } + public void setPerson(String person) { + this.person = person; + } +} + +// bean with List-valued constructor arg +class Bean5 { + Bean5(List people) { + this.people = people; + } + List people; +} + +// bean with Map-valued constructor arg +class Bean6 { + Bean6(Map peopleByName) { + this.peopleByName = peopleByName; + } + Map peopleByName; +} + +// a factory bean +class Bean1Factory { + Bean1 newInstance() { + return new Bean1(); + } +} + +class ScopeTestBean { +} + +class TestScope implements Scope { + + int instanceCount; + + @Override + public Object remove(String name) { + // do nothing + return null; + } + + @Override + public void registerDestructionCallback(String name, Runnable callback) { + } + + @Override + public String getConversationId() { + return "mock"; + } + + @Override + public Object get(String name, ObjectFactory objectFactory) { + instanceCount++; + return objectFactory.getObject(); + } + + @Override + public Object resolveContextualObject(String s) { + return null; // noop + } +} + +class BirthdayCardSender { + List peopleSentCards = new ArrayList<>(); + + public void onBirthday(AdvisedPerson person) { + peopleSentCards.add(person); + } +} + +@Component(value = "person") +class AdvisedPerson { + int age; + String name; + + public void birthday() { + ++age; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} + +class SomeClass { + public SomeClass(List soc) {} +} + +class SomeOtherClass { + public SomeOtherClass(File f) {} +} + +// a factory bean that takes arguments +class Bean1FactoryWithArgs { + Bean1 newInstance(String name) { + Bean1 bean = new Bean1(); + bean.person = name; + return bean; + } +}