From f02b3244ecf6f8383092fc34a6e1e920a7b25e73 Mon Sep 17 00:00:00 2001 From: Thomas Trageser Date: Thu, 27 Sep 2012 16:24:42 +0100 Subject: [PATCH] SPRNET-881 Add XML configuration option for default-autowire-candidates/autowire-candidate and modify IObjectFactory.GetObject() to return requested object with the use of autowire-candidate filter --- .../Objects/Factory/IObjectFactory.cs | 31 ++++++ .../Support/AbstractObjectDefinition.cs | 4 +- .../Factory/Support/AbstractObjectFactory.cs | 32 ++++++ .../Support/DefaultListableObjectFactory.cs | 31 +++++- .../Support/ObjectDefinitionBuilder.cs | 22 ++++ .../Factory/Xml/DocumentDefaultsDefinition.cs | 11 ++ .../Factory/Xml/ObjectDefinitionConstants.cs | 16 +++ .../Xml/ObjectDefinitionParserHelper.cs | 14 +++ .../Factory/Xml/ObjectsNamespaceParser.cs | 20 +++- .../Factory/Xml/spring-objects-2.0.xsd | 100 +++++++++++------- .../DefaultListableObjectFactoryTests.cs | 30 ++++++ .../Xml/XmlObjectDefinitionReaderTests.cs | 32 ++++++ 12 files changed, 295 insertions(+), 48 deletions(-) diff --git a/src/Spring/Spring.Core/Objects/Factory/IObjectFactory.cs b/src/Spring/Spring.Core/Objects/Factory/IObjectFactory.cs index aff582d0..7ffba218 100644 --- a/src/Spring/Spring.Core/Objects/Factory/IObjectFactory.cs +++ b/src/Spring/Spring.Core/Objects/Factory/IObjectFactory.cs @@ -260,6 +260,37 @@ namespace Spring.Objects.Factory #endif object this[string name] { get; } + /// + /// Return an instance (possibly shared or independent) of the given object name. + /// + /// + /// + /// This method allows an object factory to be used as a replacement for the + /// Singleton or Prototype design pattern. + /// + /// + /// Note that callers should retain references to returned objects. There is no + /// guarantee that this method will be implemented to be efficient. For example, + /// it may be synchronized, or may need to run an RDBMS query. + /// + /// + /// Will ask the parent factory if the object cannot be found in this factory + /// instance. + /// + /// + /// The type of the object to return. + /// The instance of the object. + /// + /// If there's no such object definition. + /// + /// + /// If there is more than a single object of the requested type defined in the factory. + /// + /// + /// If the object could not be created. + /// + T GetObject(); + /// /// Return an instance (possibly shared or independent) of the given object name. /// diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/AbstractObjectDefinition.cs b/src/Spring/Spring.Core/Objects/Factory/Support/AbstractObjectDefinition.cs index d0a0cd02..2ce78874 100644 --- a/src/Spring/Spring.Core/Objects/Factory/Support/AbstractObjectDefinition.cs +++ b/src/Spring/Spring.Core/Objects/Factory/Support/AbstractObjectDefinition.cs @@ -814,7 +814,8 @@ namespace Spring.Objects.Factory.Support AutowireMode = other.AutowireMode; ResourceDescription = other.ResourceDescription; IsPrimary = other.IsPrimary; - + IsAutowireCandidate = other.IsAutowireCandidate; + AbstractObjectDefinition aod = other as AbstractObjectDefinition; if (aod != null) { @@ -846,6 +847,7 @@ namespace Spring.Objects.Factory.Support buffer.Append("; Singleton = ").Append(IsSingleton); buffer.Append("; LazyInit = ").Append(IsLazyInit); buffer.Append("; Autowire = ").Append(AutowireMode); + buffer.Append("; Autowire-Candidate = ").Append(IsAutowireCandidate); buffer.Append("; Primary = ").Append(IsPrimary); buffer.Append("; DependencyCheck = ").Append(DependencyCheck); buffer.Append("; InitMethodName = ").Append(InitMethodName); diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/AbstractObjectFactory.cs b/src/Spring/Spring.Core/Objects/Factory/Support/AbstractObjectFactory.cs index ac65c8a0..2958e596 100644 --- a/src/Spring/Spring.Core/Objects/Factory/Support/AbstractObjectFactory.cs +++ b/src/Spring/Spring.Core/Objects/Factory/Support/AbstractObjectFactory.cs @@ -1917,6 +1917,38 @@ namespace Spring.Objects.Factory.Support return GetObjectInternal(name, null, null, false); } + + /// + /// Return an instance (possibly shared or independent) of the given object name. + /// + /// + /// + /// This method allows an object factory to be used as a replacement for the + /// Singleton or Prototype design pattern. + /// + /// + /// Note that callers should retain references to returned objects. There is no + /// guarantee that this method will be implemented to be efficient. For example, + /// it may be synchronized, or may need to run an RDBMS query. + /// + /// + /// Will ask the parent factory if the object cannot be found in this factory + /// instance. + /// + /// + /// The type of the object to return. + /// The instance of the object. + /// + /// If there's no such object definition. + /// + /// + /// If there is more than a single object of the requested type defined in the factory. + /// + /// + /// If the object could not be created. + /// + public abstract T GetObject(); + /// /// Return an instance (possibly shared or independent) of the given object name. /// diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/DefaultListableObjectFactory.cs b/src/Spring/Spring.Core/Objects/Factory/Support/DefaultListableObjectFactory.cs index bad5a33d..81419945 100644 --- a/src/Spring/Spring.Core/Objects/Factory/Support/DefaultListableObjectFactory.cs +++ b/src/Spring/Spring.Core/Objects/Factory/Support/DefaultListableObjectFactory.cs @@ -949,20 +949,41 @@ namespace Spring.Objects.Factory.Support /// /// If the object could not be created. /// - public T GetObject() + public override T GetObject() { IList objectNamesForType = GetObjectNamesForType(typeof(T)); + + if (objectNamesForType.Count > 1) + { + IList autowireCandidates = new List(); + foreach (var objectName in objectNamesForType) + { + if (GetObjectDefinition(objectName).IsAutowireCandidate) + autowireCandidates.Add(objectName); + + } + if (autowireCandidates.Count > 0) + objectNamesForType = autowireCandidates; + } + if ((objectNamesForType == null) || (objectNamesForType.Count == 0)) { throw new NoSuchObjectDefinitionException(typeof(T).FullName, "Requested Type not Defined in the Context."); } - if (objectNamesForType.Count > 1) + if (objectNamesForType.Count == 1) { - throw new ObjectDefinitionStoreException(string.Format("More than one definition for {0} found in the Context.", typeof(T).FullName)); + return (T)GetObject(objectNamesForType[0]); + } + else if (objectNamesForType.Count == 0 && ParentObjectFactory != null) + { + return ParentObjectFactory.GetObject(); + } + else + { + throw new NoSuchObjectDefinitionException(typeof(T), "expected single bean but found " + + objectNamesForType.Count + ": " + StringUtils.ArrayToCommaDelimitedString(objectNamesForType)); } - - return (T)GetObject(objectNamesForType[0]); } /// diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/ObjectDefinitionBuilder.cs b/src/Spring/Spring.Core/Objects/Factory/Support/ObjectDefinitionBuilder.cs index 49e80bae..96b59a9f 100644 --- a/src/Spring/Spring.Core/Objects/Factory/Support/ObjectDefinitionBuilder.cs +++ b/src/Spring/Spring.Core/Objects/Factory/Support/ObjectDefinitionBuilder.cs @@ -336,6 +336,28 @@ namespace Spring.Objects.Factory.Support return this; } + /// + /// Sets the autowire candidate value for this definition. + /// + /// The autowire candidate value + /// + public ObjectDefinitionBuilder SetAutowireCandidate(bool autowireCandidate) + { + objectDefinition.IsAutowireCandidate = autowireCandidate; + return this; + } + + /// + /// Sets the primary value for this definition. + /// + /// If object is primary + /// + public ObjectDefinitionBuilder SetPrimary(bool primary) + { + objectDefinition.IsPrimary = primary; + return this; + } + /// /// Sets the dependency check mode for this definition. /// diff --git a/src/Spring/Spring.Core/Objects/Factory/Xml/DocumentDefaultsDefinition.cs b/src/Spring/Spring.Core/Objects/Factory/Xml/DocumentDefaultsDefinition.cs index eccaa27d..92243287 100644 --- a/src/Spring/Spring.Core/Objects/Factory/Xml/DocumentDefaultsDefinition.cs +++ b/src/Spring/Spring.Core/Objects/Factory/Xml/DocumentDefaultsDefinition.cs @@ -33,6 +33,7 @@ namespace Spring.Objects.Factory.Xml private string dependencyCheck; private string lazyInit; private string merge; + private string autowireCandidates; /// /// Gets or sets the autowire setting for the document that's currently parsed. @@ -73,5 +74,15 @@ namespace Spring.Objects.Factory.Xml get { return merge; } set { merge = value; } } + + /// + /// Gets or sets autowire candidates for the document that's currently parsed + /// + /// The Autowire Candidates + public string AutowireCandidates + { + get { return autowireCandidates; } + set { autowireCandidates = value; } + } } } diff --git a/src/Spring/Spring.Core/Objects/Factory/Xml/ObjectDefinitionConstants.cs b/src/Spring/Spring.Core/Objects/Factory/Xml/ObjectDefinitionConstants.cs index 51c9835e..fe2e7643 100644 --- a/src/Spring/Spring.Core/Objects/Factory/Xml/ObjectDefinitionConstants.cs +++ b/src/Spring/Spring.Core/Objects/Factory/Xml/ObjectDefinitionConstants.cs @@ -48,6 +48,12 @@ namespace Spring.Objects.Factory.Xml /// public const string TrueValue = "true"; + /// + /// Value of a boolean attribute that represents + /// . + /// + public const string FalseValue = "false"; + /// /// Signifies that a default value is to be applied. /// @@ -90,6 +96,11 @@ namespace Spring.Objects.Factory.Xml /// public const string DefaultAutowireAttribute = "default-autowire"; + /// + /// Specifies the default autowire candidates. + /// + public const string DefaultAutowireCandidatesAttribute = "default-autowire-candidates"; + /// /// Specifies the default collection merge mode. /// @@ -586,6 +597,11 @@ namespace Spring.Objects.Factory.Xml /// public const string AutowireAttribute = "autowire"; + /// + /// The autowiring mode for an individual object definition. + /// + public const string AutowireCandidateAttribute = "autowire-candidate"; + /// /// Attribute element to farther deifne the qualifier of an object /// diff --git a/src/Spring/Spring.Core/Objects/Factory/Xml/ObjectDefinitionParserHelper.cs b/src/Spring/Spring.Core/Objects/Factory/Xml/ObjectDefinitionParserHelper.cs index d4a64c38..7bdf0f1f 100644 --- a/src/Spring/Spring.Core/Objects/Factory/Xml/ObjectDefinitionParserHelper.cs +++ b/src/Spring/Spring.Core/Objects/Factory/Xml/ObjectDefinitionParserHelper.cs @@ -175,6 +175,20 @@ namespace Spring.Objects.Factory.Xml #endregion + ddd.AutowireCandidates = GetAttributeValue(root, ObjectDefinitionConstants.DefaultAutowireCandidatesAttribute); + + #region Instrumentation + + if (log.IsDebugEnabled) + { + log.Debug( + string.Format( + "Default autowire candidates '{0}'.", + ddd.AutowireCandidates)); + } + + #endregion + defaults = ddd; } diff --git a/src/Spring/Spring.Core/Objects/Factory/Xml/ObjectsNamespaceParser.cs b/src/Spring/Spring.Core/Objects/Factory/Xml/ObjectsNamespaceParser.cs index 2188fdf1..df704dcb 100644 --- a/src/Spring/Spring.Core/Objects/Factory/Xml/ObjectsNamespaceParser.cs +++ b/src/Spring/Spring.Core/Objects/Factory/Xml/ObjectsNamespaceParser.cs @@ -481,10 +481,24 @@ namespace Spring.Objects.Factory.Xml autowire = childParserContext.ParserHelper.Defaults.Autowire; } od.AutowireMode = GetAutowireMode(autowire); - string primary = GetAttributeValue(element, ObjectDefinitionConstants.PrimaryAttribute); - if (primary == null) + + string autowireCandidates = GetAttributeValue(element, ObjectDefinitionConstants.AutowireCandidateAttribute); + if (string.IsNullOrEmpty(autowireCandidates) || ObjectDefinitionConstants.DefaultValue.Equals(autowireCandidates)) { - primary = "false"; + if (!string.IsNullOrEmpty(childParserContext.ParserHelper.Defaults.AutowireCandidates)) + { + string[] patterns = childParserContext.ParserHelper.Defaults.AutowireCandidates.Split(','); + od.IsAutowireCandidate = PatternMatchUtils.SimpleMatch(patterns, id); + } + } + else + { + od.IsAutowireCandidate = ObjectDefinitionConstants.TrueValue.Equals(autowireCandidates); + } + string primary = GetAttributeValue(element, ObjectDefinitionConstants.PrimaryAttribute); + if (string.IsNullOrEmpty(primary)) + { + primary = ObjectDefinitionConstants.FalseValue; } od.IsPrimary = IsTrueStringValue(primary); string initMethodName = GetAttributeValue(element, ObjectDefinitionConstants.InitMethodAttribute); diff --git a/src/Spring/Spring.Core/Objects/Factory/Xml/spring-objects-2.0.xsd b/src/Spring/Spring.Core/Objects/Factory/Xml/spring-objects-2.0.xsd index c5febc79..8f084e61 100644 --- a/src/Spring/Spring.Core/Objects/Factory/Xml/spring-objects-2.0.xsd +++ b/src/Spring/Spring.Core/Objects/Factory/Xml/spring-objects-2.0.xsd @@ -500,10 +500,25 @@ + Indicates whether or not this object should be considered when looking + for matching candidates to satisfy another object's autowiring requirements. + Note that this does not affect explicit references by name, which will get + resolved even if the specified bean is not marked as an autowire candidate. + --> + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/DefaultListableObjectFactoryTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/DefaultListableObjectFactoryTests.cs index c9b66d4f..94869bd5 100644 --- a/test/Spring/Spring.Core.Tests/Objects/Factory/DefaultListableObjectFactoryTests.cs +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/DefaultListableObjectFactoryTests.cs @@ -1608,6 +1608,36 @@ namespace Spring.Objects.Factory Assert.AreEqual(ExpectedAge, child.Age); } + [Test] + public void GetObjectByTypeWithAmbiguity() + { + DefaultListableObjectFactory lbf = new DefaultListableObjectFactory(); + RootObjectDefinition bd1 = new RootObjectDefinition(typeof(TestObject)); + RootObjectDefinition bd2 = new RootObjectDefinition(typeof(TestObject)); + lbf.RegisterObjectDefinition("bd1", bd1); + lbf.RegisterObjectDefinition("bd2", bd2); + + Assert.That(delegate { lbf.GetObject(); }, Throws.Exception.TypeOf()); + } + + [Test] + public void GetObjectByTypeFiltersOutNonAutowireCandidates() + { + DefaultListableObjectFactory lbf = new DefaultListableObjectFactory(); + RootObjectDefinition bd1 = new RootObjectDefinition(typeof(TestObject)); + RootObjectDefinition bd2 = new RootObjectDefinition(typeof(TestObject)); + RootObjectDefinition na1 = new RootObjectDefinition(typeof(TestObject)); + na1.IsAutowireCandidate = false; + + lbf.RegisterObjectDefinition("bd1", bd1); + lbf.RegisterObjectDefinition("na1", na1); + TestObject actual = lbf.GetObject(); // na1 was filtered + Assert.That(lbf.GetObject("bd1", typeof(TestObject)), Is.SameAs(actual)); + + lbf.RegisterObjectDefinition("bd2", bd2); + Assert.That(delegate { lbf.GetObject(); }, Throws.Exception.TypeOf()); + } + [Test] public void GetObjectDefinitionResolvesAliases() { diff --git a/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/XmlObjectDefinitionReaderTests.cs b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/XmlObjectDefinitionReaderTests.cs index 45aabde2..5fe77be8 100644 --- a/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/XmlObjectDefinitionReaderTests.cs +++ b/test/Spring/Spring.Core.Tests/Objects/Factory/Xml/XmlObjectDefinitionReaderTests.cs @@ -354,5 +354,37 @@ namespace Spring.Objects.Factory.Xml Assert.AreEqual("test1", od2.DependsOn[0]); Assert.AreEqual(DependencyCheckingMode.Simple, od2.DependencyCheck); } + + [Test] + public void ParsesAutowireCandidate() + { + DefaultListableObjectFactory of = new DefaultListableObjectFactory(); + XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(of); + reader.LoadObjectDefinitions(new StringResource( +@" + + + + + + + +")); + var od = (AbstractObjectDefinition)of.GetObjectDefinition("test1"); + Assert.That(od.IsAutowireCandidate, Is.True, "No attribute set should default to true"); + + od = (AbstractObjectDefinition)of.GetObjectDefinition("test2"); + Assert.That(od.IsAutowireCandidate, Is.False, "Specifically attribute set to false should set to false"); + + od = (AbstractObjectDefinition)of.GetObjectDefinition("test3"); + Assert.That(od.IsAutowireCandidate, Is.True, "Specifically attribute set to true should set to false"); + + od = (AbstractObjectDefinition)of.GetObjectDefinition("test4"); + Assert.That(od.IsAutowireCandidate, Is.True, "Attribute set to default should check pattern and return true"); + + od = (AbstractObjectDefinition)of.GetObjectDefinition("test5"); + Assert.That(od.IsAutowireCandidate, Is.False, "Attribute set to default should check pattern and return false"); + } + } } \ No newline at end of file