From 55b169d06ccff257f1394681658a671ab0083432 Mon Sep 17 00:00:00 2001 From: sbohlen Date: Mon, 15 Nov 2010 20:35:54 +0000 Subject: [PATCH] container refresh modifications to interact with ConfigurationClassPostProcessor as required --- .../Advice/SpringObjectMethodInterceptor.cs | 92 ++++++++++++ .../Attributes/ConfigurationClassMethod.cs | 5 +- ...onfigurationClassObjectDefinitionReader.cs | 7 + .../ConfigurationClassPostProcessor.cs | 138 ++++++++++++------ .../Spring.Core.Configuration.2010.csproj | 1 + .../Support/AbstractApplicationContext.cs | 102 ++++++++++--- .../Objects/Factory/Parsing/Location.cs | 3 +- .../Objects/Factory/Parsing/Problem.cs | 2 +- .../ConfigurationClassPostProcessorTests.cs | 65 ++++++--- 9 files changed, 320 insertions(+), 95 deletions(-) create mode 100644 src/Spring/Spring.Core.Configuration/Context/Advice/SpringObjectMethodInterceptor.cs diff --git a/src/Spring/Spring.Core.Configuration/Context/Advice/SpringObjectMethodInterceptor.cs b/src/Spring/Spring.Core.Configuration/Context/Advice/SpringObjectMethodInterceptor.cs new file mode 100644 index 00000000..d3f3a5f0 --- /dev/null +++ b/src/Spring/Spring.Core.Configuration/Context/Advice/SpringObjectMethodInterceptor.cs @@ -0,0 +1,92 @@ +#region License + +/* + * Copyright © 2002-2008 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. + */ + +#endregion + +using System; +using System.Reflection; +using AopAlliance.Intercept; +using Common.Logging; +using Spring.Objects.Factory.Config; +using Spring.Context.Attributes; + +namespace Spring.Context.Advice +{ + /// + /// Intercepts calls to methods within the configuration object + /// + /// Mark Pollack + /// Erich Eichinger + public class SpringObjectMethodInterceptor : IMethodInterceptor + { + #region Logging Definition + + private static readonly ILog LOG = LogManager.GetLogger(typeof(SpringObjectMethodInterceptor)); + + #endregion + + private readonly IConfigurableListableObjectFactory _configurableListableObjectFactory; + + + public SpringObjectMethodInterceptor(IConfigurableListableObjectFactory configurableListableObjectFactory) + { + _configurableListableObjectFactory = configurableListableObjectFactory; + + } + + #region IMethodInterceptor Members + + public object Invoke(IMethodInvocation invocation) + { + MethodInfo m = invocation.Method; + if (m.Name.StartsWith("set_") || m.Name.StartsWith("get_")) + return invocation.Proceed(); + + string name = m.Name; + + object[] attribs = m.GetCustomAttributes(typeof(DefinitionAttribute), true); + if (attribs.Length > 0) + { + + } + + + if (IsCurrentlyInCreation(name)) + { + if (LOG.IsDebugEnabled) + { + LOG.Debug(name + " currently in creation, created one."); + } + return invocation.Proceed(); + } + if (LOG.IsDebugEnabled) + { + LOG.Debug(name + " not in creation, asked the application context for one"); + } + + return _configurableListableObjectFactory.GetObject(name); + } + + private bool IsCurrentlyInCreation(string name) + { + return _configurableListableObjectFactory.IsCurrentlyInCreation(name); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Core.Configuration/Context/Attributes/ConfigurationClassMethod.cs b/src/Spring/Spring.Core.Configuration/Context/Attributes/ConfigurationClassMethod.cs index 14a7aa78..36d629e0 100644 --- a/src/Spring/Spring.Core.Configuration/Context/Attributes/ConfigurationClassMethod.cs +++ b/src/Spring/Spring.Core.Configuration/Context/Attributes/ConfigurationClassMethod.cs @@ -85,7 +85,8 @@ namespace Spring.Context.Attributes public void Validate(IProblemReporter problemReporter) { - if (Attribute.GetCustomAttribute(ConfigurationClass.GetType(), typeof(ConfigurationAttribute)) != null) + //TODO: shouldn't this be "if method has Definition attribute" instead of "if class has Configuration attribute" --???? + if (Attribute.GetCustomAttribute(ConfigurationClass.ConfigurationClassType, typeof(ConfigurationAttribute)) != null) { if (!MethodMetadata.IsVirtual) { @@ -113,7 +114,7 @@ namespace Spring.Context.Attributes private class NonVirtualMethodError : Problem { public NonVirtualMethodError(string methodName, Location location) - : base(String.Format("Method '{0}' must not be private, final or static; change the method's modifiers to continue", + : base(String.Format("Method '{0}' must be public virtual; change the method's modifiers to continue", methodName), location) { } } diff --git a/src/Spring/Spring.Core.Configuration/Context/Attributes/ConfigurationClassObjectDefinitionReader.cs b/src/Spring/Spring.Core.Configuration/Context/Attributes/ConfigurationClassObjectDefinitionReader.cs index e50b5a3e..21fd0423 100644 --- a/src/Spring/Spring.Core.Configuration/Context/Attributes/ConfigurationClassObjectDefinitionReader.cs +++ b/src/Spring/Spring.Core.Configuration/Context/Attributes/ConfigurationClassObjectDefinitionReader.cs @@ -7,6 +7,7 @@ using Spring.Collections.Generic; using System.Reflection; using Spring.Objects.Factory.Config; using Common.Logging; +using Spring.Objects; namespace Spring.Context.Attributes { @@ -90,9 +91,14 @@ namespace Spring.Context.Attributes RootObjectDefinition objDef = new ConfigurationClassObjectDefinition(); //beanDef.Resource = configClass.Resource; //beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource())); + + // ???? if we don't do this here, how is the type supposed to be set? + //objDef.ObjectType = metadata.ReturnType; + objDef.FactoryObjectName = configClass.ObjectName; objDef.FactoryMethodName = metadata.Name; objDef.AutowireMode = Objects.Factory.Config.AutoWiringMode.Constructor; + //beanDef.setAttribute(RequiredAnnotationBeanPostProcessor.SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE); // consider name and any aliases @@ -214,6 +220,7 @@ namespace Spring.Context.Attributes private class ConfigurationClassObjectDefinition : RootObjectDefinition { + } } diff --git a/src/Spring/Spring.Core.Configuration/Context/Attributes/ConfigurationClassPostProcessor.cs b/src/Spring/Spring.Core.Configuration/Context/Attributes/ConfigurationClassPostProcessor.cs index f21fb73d..a1e425cc 100644 --- a/src/Spring/Spring.Core.Configuration/Context/Attributes/ConfigurationClassPostProcessor.cs +++ b/src/Spring/Spring.Core.Configuration/Context/Attributes/ConfigurationClassPostProcessor.cs @@ -8,6 +8,10 @@ using Spring.Objects.Factory.Config; using Spring.Collections.Generic; using Spring.Objects.Factory; using Spring.Core; +using Spring.Aop.Framework; +using Spring.Context.Advice; +using Spring.Aop.Framework.DynamicProxy; +using Spring.Aop; namespace Spring.Context.Attributes { @@ -21,13 +25,16 @@ namespace Spring.Context.Attributes private IProblemReporter _problemReporter = new FailFastProblemReporter(); + public int Order + { + get { return int.MinValue; } + } + public IProblemReporter ProblemReporter { set { _problemReporter = (value ?? new FailFastProblemReporter()); } } - - public void PostProcessObjectDefinitionRegistry(IObjectDefinitionRegistry registry) { if (_postProcessObjectDefinitionRegistryCalled) @@ -42,46 +49,6 @@ namespace Spring.Context.Attributes ProcessConfigObjectDefinitions(registry); } - - private Type GenerateProxyType(Type objectType) - { - /* - ProxyFactory proxyFactory = new ProxyFactory(); - proxyFactory.ProxyTargetAttributes = true; - proxyFactory.Interfaces = Type.EmptyTypes; - proxyFactory.TargetSource = new ObjectFactoryTargetSource(originalBeanName, owningObjectFactory); - SpringObjectMethodInterceptor methodInterceptor = new SpringObjectMethodInterceptor(owningObjectFactory, objectNamingStrategy); - proxyFactory.AddAdvice(methodInterceptor); - - //TODO check type of object isn't infrastructure type. - - InheritanceAopProxyTypeBuilder iaptb = new InheritanceAopProxyTypeBuilder(proxyFactory); - //iaptb.ProxyDeclaredMembersOnly = true; // make configurable. - Type type = iaptb.BuildProxyType(); - */ - - return null; - } - private void EnhanceConfigurationClasses(IConfigurableListableObjectFactory objectFactory) - { - string[] objectNames = objectFactory.GetObjectDefinitionNames(); - - for (int i = 0; i < objectNames.Length; i++) - { - IObjectDefinition objDef = objectFactory.GetObjectDefinition(objectNames[i]); - if (Attribute.GetCustomAttribute(objDef.ObjectType, typeof(ConfigurationAttribute)) != null) - { - //configNames.Add(objectNames[i]); - - //ObjectType is READONLY :( - //objDef.ObjectType = GenerateProxyType(objDef.ObjectType); - } - } - - - - - } public void PostProcessObjectFactory(IConfigurableListableObjectFactory objectFactory) { if (_postProcessObjectFactoryCalled) @@ -96,10 +63,60 @@ namespace Spring.Context.Attributes // Simply call processConfigBeanDefinitions lazily at this point then. ProcessConfigObjectDefinitions((IObjectDefinitionRegistry)objectFactory); } - + EnhanceConfigurationClasses(objectFactory); } + private void EnhanceConfigurationClasses(IConfigurableListableObjectFactory objectFactory) + { + string[] objectNames = objectFactory.GetObjectDefinitionNames(); + + for (int i = 0; i < objectNames.Length; i++) + { + IObjectDefinition objDef = objectFactory.GetObjectDefinition(objectNames[i]); + + if (((AbstractObjectDefinition)objDef).HasObjectType) + { + if (Attribute.GetCustomAttribute(objDef.ObjectType, typeof(ConfigurationAttribute)) != null) + { + + ProxyFactory proxyFactory = new ProxyFactory(); + proxyFactory.ProxyTargetAttributes = true; + proxyFactory.Interfaces = Type.EmptyTypes; + proxyFactory.TargetSource = new ObjectFactoryTargetSource(objectNames[i], objectFactory); + SpringObjectMethodInterceptor methodInterceptor = new SpringObjectMethodInterceptor(objectFactory); + proxyFactory.AddAdvice(methodInterceptor); + + //TODO check type of object isn't infrastructure type. + + InheritanceAopProxyTypeBuilder iaptb = new InheritanceAopProxyTypeBuilder(proxyFactory); + //iaptb.ProxyDeclaredMembersOnly = true; // make configurable. + ((IConfigurableObjectDefinition)objDef).ObjectType = iaptb.BuildProxyType(); + + objDef.ConstructorArgumentValues.AddIndexedArgumentValue(objDef.ConstructorArgumentValues.ArgumentCount, proxyFactory); + + } + } + + } + + } + + private Type GenerateProxyType(string objectName, IConfigurableListableObjectFactory objectFactory) + { + ProxyFactory proxyFactory = new ProxyFactory(); + proxyFactory.ProxyTargetAttributes = true; + proxyFactory.Interfaces = Type.EmptyTypes; + proxyFactory.TargetSource = new ObjectFactoryTargetSource(objectName, objectFactory); + SpringObjectMethodInterceptor methodInterceptor = new SpringObjectMethodInterceptor(objectFactory); + proxyFactory.AddAdvice(methodInterceptor); + + //TODO check type of object isn't infrastructure type. + + InheritanceAopProxyTypeBuilder iaptb = new InheritanceAopProxyTypeBuilder(proxyFactory); + //iaptb.ProxyDeclaredMembersOnly = true; // make configurable. + return iaptb.BuildProxyType(); + } private void ProcessConfigObjectDefinitions(IObjectDefinitionRegistry registry) { @@ -143,10 +160,39 @@ namespace Spring.Context.Attributes reader.LoadObjectDefinitions(parser.ConfigurationClasses); } - - public int Order + public class ObjectFactoryTargetSource : ITargetSource { - get { return int.MinValue; } + private readonly string _objectName; + private readonly IConfigurableListableObjectFactory _objectFactory; + + public ObjectFactoryTargetSource(string objectName, IConfigurableListableObjectFactory objectFactory) + { + _objectName = objectName; + _objectFactory = objectFactory; + } + + #region ITargetSource Members + + public object GetTarget() + { + return _objectFactory.GetObject(_objectName); + } + + public bool IsStatic + { + get { return _objectFactory.IsSingleton(_objectName); } + } + + public void ReleaseTarget(object target) + { + } + + public Type TargetType + { + get { return _objectFactory.GetType(_objectName); } + } + + #endregion } } } diff --git a/src/Spring/Spring.Core.Configuration/Spring.Core.Configuration.2010.csproj b/src/Spring/Spring.Core.Configuration/Spring.Core.Configuration.2010.csproj index 62e04478..b5a72565 100644 --- a/src/Spring/Spring.Core.Configuration/Spring.Core.Configuration.2010.csproj +++ b/src/Spring/Spring.Core.Configuration/Spring.Core.Configuration.2010.csproj @@ -39,6 +39,7 @@ + diff --git a/src/Spring/Spring.Core/Context/Support/AbstractApplicationContext.cs b/src/Spring/Spring.Core/Context/Support/AbstractApplicationContext.cs index ce8e612b..1f8dbc4d 100644 --- a/src/Spring/Spring.Core/Context/Support/AbstractApplicationContext.cs +++ b/src/Spring/Spring.Core/Context/Support/AbstractApplicationContext.cs @@ -406,7 +406,7 @@ namespace Spring.Context.Support if (exceptions.HasExceptions) { Delegate target = ContextEvent.GetInvocationList()[0]; - Exception exception = (Exception) exceptions[target]; + Exception exception = (Exception)exceptions[target]; throw new ApplicationContextException(string.Format("An unhandled exception occured during processing application event {0} in handler {1}", e.GetType(), target.Method), exception); } } @@ -487,33 +487,92 @@ namespace Spring.Context.Support /// /// /// In the case of errors. - private void InvokeObjectFactoryPostProcessors() + private void InvokeObjectFactoryPostProcessors(IConfigurableListableObjectFactory objectFactory) { - // Do NOT include IFactoryObjects; they (typically) need to be instantiated - // to determine the Type of object that they create, and if they are instantiated - // then we won't be able to do any factory post processin' on 'em... - ArrayList factoryProcessorNames = new ArrayList(); - string[] names = GetObjectNamesForType(typeof (IObjectFactoryPostProcessor), true, false); - foreach (string s in names) + // Invoke BeanDefinitionRegistryPostProcessors first, if any. + ArrayList processedObjects = new ArrayList(); + + if (objectFactory is IObjectDefinitionRegistry) { - factoryProcessorNames.Add(s); + IObjectDefinitionRegistry registry = (IObjectDefinitionRegistry)objectFactory; + ArrayList regularPostProcessors = new ArrayList(); + ArrayList registryPostProcessors = new ArrayList(); + + foreach (IObjectFactoryPostProcessor factoryProcessor in ObjectFactoryPostProcessors) + { + if (factoryProcessor is IObjectDefinitionRegistryPostProcessor) + { + ((IObjectDefinitionRegistryPostProcessor)factoryProcessor).PostProcessObjectDefinitionRegistry(registry); + registryPostProcessors.Add(factoryProcessor); + } + else + { + regularPostProcessors.Add(factoryProcessor); + } + } + + IDictionary objectMap = objectFactory.GetObjectsOfType(typeof(IObjectDefinitionRegistryPostProcessor), true, false); + + ArrayList registryPostProcessorObjects = new ArrayList(objectMap.Values); + registryPostProcessorObjects.Sort(new OrderComparator()); + + foreach (System.Object processor in registryPostProcessorObjects) + { + ((IObjectDefinitionRegistryPostProcessor)processor).PostProcessObjectDefinitionRegistry(registry); + } + + InvokeObjectFactoryPostProcessors(registryPostProcessors, objectFactory); + InvokeObjectFactoryPostProcessors(registryPostProcessorObjects, objectFactory); + InvokeObjectFactoryPostProcessors(regularPostProcessors, objectFactory); + + // processedObjects.Add(objectMap.Keys); + + foreach (DictionaryEntry entry in objectMap) + { + processedObjects.Add(entry.Key); + } + } - + else + { + foreach (IObjectFactoryPostProcessor factoryProcessor in ObjectFactoryPostProcessors) + { + // Invoke factory processors registered with the context instance. + factoryProcessor.PostProcessObjectFactory(objectFactory); + } + } + + // Do not initialize FactoryBeans here: We need to leave all regular beans + // uninitialized to let the bean factory post-processors apply to them! + ArrayList factoryProcessorNames = new ArrayList(); + string[] names = GetObjectNamesForType(typeof(IObjectFactoryPostProcessor), true, false); + foreach (string name in names) + { + factoryProcessorNames.Add(name); + } + + // Separate between ObjectFactoryPostProcessors that implement PriorityOrdered, + // Ordered, and the rest. ArrayList priorityOrderedFactoryProcessors = new ArrayList(); ArrayList orderedFactoryProcessorsNames = new ArrayList(); ArrayList nonOrderedFactoryProcessorNames = new ArrayList(); - + for (int i = 0; i < factoryProcessorNames.Count; ++i) { - string processorName = (string) factoryProcessorNames[i]; - if (IsTypeMatch(processorName, typeof(IPriorityOrdered))) + string processorName = (string)factoryProcessorNames[i]; + if (processedObjects.Contains(processorName)) + { + //skip -- already processed in first phase above + Debug.WriteLine(""); + } + else if (IsTypeMatch(processorName, typeof(IPriorityOrdered))) { priorityOrderedFactoryProcessors.Add(ObjectFactory.GetObject(processorName, typeof(IObjectFactoryPostProcessor))); } else if (IsTypeMatch(processorName, typeof(IOrdered))) { orderedFactoryProcessorsNames.Add(processorName); - } + } else { nonOrderedFactoryProcessorNames.Add(processorName); @@ -527,17 +586,17 @@ namespace Spring.Context.Support foreach (string orderedFactoryProcessorsName in orderedFactoryProcessorsNames) { orderedFactoryProcessors.Add(ObjectFactory.GetObject(orderedFactoryProcessorsName, - typeof (IObjectFactoryPostProcessor))); + typeof(IObjectFactoryPostProcessor))); } orderedFactoryProcessors.Sort(new OrderComparator()); InvokeObjectFactoryPostProcessors(orderedFactoryProcessors, ObjectFactory); - + // and then the unordered ones... ArrayList nonOrderedPostProcessors = new ArrayList(); foreach (string nonOrderedFactoryProcessorName in nonOrderedFactoryProcessorNames) { nonOrderedPostProcessors.Add(ObjectFactory.GetObject(nonOrderedFactoryProcessorName, - typeof (IObjectFactoryPostProcessor))); + typeof(IObjectFactoryPostProcessor))); } InvokeObjectFactoryPostProcessors(nonOrderedPostProcessors, ObjectFactory); @@ -563,7 +622,7 @@ namespace Spring.Context.Support // Now will find any additional IObjectFactoryPostProcessors that implement IPriorityOrdered that may have been // resolved due to using TypeAlias string[] factoryProcessorNamesAfterTypeAlias = GetObjectNamesForType(typeof(IObjectFactoryPostProcessor), true, false); - priorityOrderedFactoryProcessors.Clear(); + priorityOrderedFactoryProcessors.Clear(); foreach (string factoryProcessorName in factoryProcessorNamesAfterTypeAlias) { if (!factoryProcessorNames.Contains(factoryProcessorName)) @@ -912,10 +971,7 @@ namespace Spring.Context.Support #endregion - foreach (IObjectFactoryPostProcessor factoryProcessor in ObjectFactoryPostProcessors) - { - factoryProcessor.PostProcessObjectFactory(objectFactory); - } + #region Instrumentation @@ -939,7 +995,7 @@ namespace Spring.Context.Support #endregion - InvokeObjectFactoryPostProcessors(); + InvokeObjectFactoryPostProcessors(objectFactory); RegisterObjectPostProcessors(objectFactory); InitEventRegistry(); diff --git a/src/Spring/Spring.Core/Objects/Factory/Parsing/Location.cs b/src/Spring/Spring.Core/Objects/Factory/Parsing/Location.cs index 48832944..6ca9a321 100644 --- a/src/Spring/Spring.Core/Objects/Factory/Parsing/Location.cs +++ b/src/Spring/Spring.Core/Objects/Factory/Parsing/Location.cs @@ -18,7 +18,8 @@ namespace Spring.Objects.Factory.Parsing /// public Location(IResource resource, object source) { - AssertUtils.ArgumentNotNull(resource, "resource"); + //TODO: look into re-enabling this since resource *is* NULL when parsing config classes vs. acquiring IResources + //AssertUtils.ArgumentNotNull(resource, "resource"); this.resource = resource; this.source = source; } diff --git a/src/Spring/Spring.Core/Objects/Factory/Parsing/Problem.cs b/src/Spring/Spring.Core/Objects/Factory/Parsing/Problem.cs index 0848261a..7fa96f46 100644 --- a/src/Spring/Spring.Core/Objects/Factory/Parsing/Problem.cs +++ b/src/Spring/Spring.Core/Objects/Factory/Parsing/Problem.cs @@ -59,7 +59,7 @@ namespace Spring.Objects.Factory.Parsing public string ResourceDescription { - get { return _location.Resource.Description; } + get { return _location.Resource!=null ? _location.Resource.Description : string.Empty; } } public override string ToString() diff --git a/test/Spring/Spring.Core.Configuration.Tests/Context/Attributes/ConfigurationClassPostProcessorTests.cs b/test/Spring/Spring.Core.Configuration.Tests/Context/Attributes/ConfigurationClassPostProcessorTests.cs index 50351b9b..b01740ff 100644 --- a/test/Spring/Spring.Core.Configuration.Tests/Context/Attributes/ConfigurationClassPostProcessorTests.cs +++ b/test/Spring/Spring.Core.Configuration.Tests/Context/Attributes/ConfigurationClassPostProcessorTests.cs @@ -11,67 +11,87 @@ namespace Spring.Context.Annotation { private GenericApplicationContext _ctx; - private ConfigurationClassPostProcessor _postProcessor; - [SetUp] public void _SetUp() { _ctx = new GenericApplicationContext(); - var builder = ObjectDefinitionBuilder.GenericObjectDefinition(typeof(ObjectDefinitions)); + var builder = ObjectDefinitionBuilder.GenericObjectDefinition(typeof(TheConfigurationClass)); _ctx.RegisterObjectDefinition("whoCares", builder.ObjectDefinition); - _postProcessor = new ConfigurationClassPostProcessor(); - _postProcessor.PostProcessObjectDefinitionRegistry(_ctx); + var b2 = ObjectDefinitionBuilder.GenericObjectDefinition(typeof(ConfigurationClassPostProcessor)); + _ctx.RegisterObjectDefinition("ccpp", b2.ObjectDefinition); + + _ctx.Refresh(); } [Test] - public void CanRegisterDefintions() + public void Can_Parse_and_Register_Defintions() { - Assert.That(_ctx.ObjectDefinitionCount, Is.EqualTo(3)); + Assert.That(_ctx.ObjectDefinitionCount, Is.EqualTo(4)); } [Test] - public void CanRetreiveActualObjectsFromContext() + public void Can_Retreive_Actual_Objects_From_Context() { - Assert.That(_ctx[typeof(Parent).Name], Is.TypeOf()); - Assert.That(_ctx[typeof(Child).Name], Is.TypeOf()); + Assert.That(_ctx[typeof(SingletonParent).Name], Is.TypeOf()); + Assert.That(_ctx[typeof(PrototypeChild).Name], Is.TypeOf()); } [Test] - public void CanSatisfyDependenciesOfObjects() + public void Can_Satisfy_Dependencies_Of_Objects() { - Assert.That(((Parent)_ctx[typeof(Parent).Name]).Child, Is.Not.Null); + Assert.That(((SingletonParent)_ctx[typeof(SingletonParent).Name]).Child, Is.Not.Null); + } + + [Test] + public void Can_Respect_Default_Singleton_Scope() + { + var firstObject = (SingletonParent)_ctx[typeof(SingletonParent).Name]; + var secondObject = (SingletonParent)_ctx[typeof(SingletonParent).Name]; + + Assert.That(firstObject, Is.SameAs(secondObject)); + } + + [Test] + public void Can_Respect_Explicit_PrototypeScope() + { + var firstObject = (PrototypeChild)_ctx[typeof(PrototypeChild).Name]; + var secondObject = (PrototypeChild)_ctx[typeof(PrototypeChild).Name]; + + Assert.That(firstObject, Is.Not.SameAs(secondObject)); } } [Configuration] - public class ObjectDefinitions + public class TheConfigurationClass { [Definition] - public Parent Parent() + [Scope(ObjectScope.Prototype)] + public virtual PrototypeChild PrototypeChild() { - return new Parent(Child()); + return new PrototypeChild(); } [Definition] - public Child Child() + public virtual SingletonParent SingletonParent() { - return new Child(); + return new SingletonParent(PrototypeChild()); } + } - public class Parent + public class SingletonParent { - private Child _child; + private PrototypeChild _child; - public Parent(Child child) + public SingletonParent(PrototypeChild child) { _child = child; } - public Child Child + public PrototypeChild Child { get { @@ -80,5 +100,6 @@ namespace Spring.Context.Annotation } } - public class Child { } + + public class PrototypeChild { } }