SPRNET-881 Add XML configuration option for default-autowire-candidates/autowire-candidate and modify IObjectFactory.GetObject<T>() to return requested object with the use of autowire-candidate filter

This commit is contained in:
Thomas Trageser
2012-09-27 16:24:42 +01:00
parent eb59b8f26e
commit f02b3244ec
12 changed files with 295 additions and 48 deletions

View File

@@ -260,6 +260,37 @@ namespace Spring.Objects.Factory
#endif
object this[string name] { get; }
/// <summary>
/// Return an instance (possibly shared or independent) of the given object name.
/// </summary>
/// <remarks>
/// <para>
/// This method allows an object factory to be used as a replacement for the
/// Singleton or Prototype design pattern.
/// </para>
/// <para>
/// 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.
/// </para>
/// <para>
/// Will ask the parent factory if the object cannot be found in this factory
/// instance.
/// </para>
/// </remarks>
/// <typeparam name="T">The type of the object to return.</typeparam>
/// <returns>The instance of the object.</returns>
/// <exception cref="Spring.Objects.Factory.NoSuchObjectDefinitionException">
/// If there's no such object definition.
/// </exception>
/// <exception cref="Spring.Objects.Factory.ObjectDefinitionStoreException">
/// If there is more than a single object of the requested type defined in the factory.
/// </exception>
/// <exception cref="Spring.Objects.ObjectsException">
/// If the object could not be created.
/// </exception>
T GetObject<T>();
/// <summary>
/// Return an instance (possibly shared or independent) of the given object name.
/// </summary>

View File

@@ -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);

View File

@@ -1917,6 +1917,38 @@ namespace Spring.Objects.Factory.Support
return GetObjectInternal(name, null, null, false);
}
/// <summary>
/// Return an instance (possibly shared or independent) of the given object name.
/// </summary>
/// <remarks>
/// <para>
/// This method allows an object factory to be used as a replacement for the
/// Singleton or Prototype design pattern.
/// </para>
/// <para>
/// 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.
/// </para>
/// <para>
/// Will ask the parent factory if the object cannot be found in this factory
/// instance.
/// </para>
/// </remarks>
/// <typeparam name="T">The type of the object to return.</typeparam>
/// <returns>The instance of the object.</returns>
/// <exception cref="Spring.Objects.Factory.NoSuchObjectDefinitionException">
/// If there's no such object definition.
/// </exception>
/// <exception cref="Spring.Objects.Factory.ObjectDefinitionStoreException">
/// If there is more than a single object of the requested type defined in the factory.
/// </exception>
/// <exception cref="Spring.Objects.ObjectsException">
/// If the object could not be created.
/// </exception>
public abstract T GetObject<T>();
/// <summary>
/// Return an instance (possibly shared or independent) of the given object name.
/// </summary>

View File

@@ -949,20 +949,41 @@ namespace Spring.Objects.Factory.Support
/// <exception cref="Spring.Objects.ObjectsException">
/// If the object could not be created.
/// </exception>
public T GetObject<T>()
public override T GetObject<T>()
{
IList<string> objectNamesForType = GetObjectNamesForType(typeof(T));
if (objectNamesForType.Count > 1)
{
IList<string> autowireCandidates = new List<string>();
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<T>();
}
else
{
throw new NoSuchObjectDefinitionException(typeof(T), "expected single bean but found " +
objectNamesForType.Count + ": " + StringUtils.ArrayToCommaDelimitedString(objectNamesForType));
}
return (T)GetObject(objectNamesForType[0]);
}
/// <summary>

View File

@@ -336,6 +336,28 @@ namespace Spring.Objects.Factory.Support
return this;
}
/// <summary>
/// Sets the autowire candidate value for this definition.
/// </summary>
/// <param name="autowireCandidate">The autowire candidate value</param>
/// <returns></returns>
public ObjectDefinitionBuilder SetAutowireCandidate(bool autowireCandidate)
{
objectDefinition.IsAutowireCandidate = autowireCandidate;
return this;
}
/// <summary>
/// Sets the primary value for this definition.
/// </summary>
/// <param name="primary">If object is primary</param>
/// <returns></returns>
public ObjectDefinitionBuilder SetPrimary(bool primary)
{
objectDefinition.IsPrimary = primary;
return this;
}
/// <summary>
/// Sets the dependency check mode for this definition.
/// </summary>

View File

@@ -33,6 +33,7 @@ namespace Spring.Objects.Factory.Xml
private string dependencyCheck;
private string lazyInit;
private string merge;
private string autowireCandidates;
/// <summary>
/// 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; }
}
/// <summary>
/// Gets or sets autowire candidates for the document that's currently parsed
/// </summary>
/// <value>The Autowire Candidates</value>
public string AutowireCandidates
{
get { return autowireCandidates; }
set { autowireCandidates = value; }
}
}
}

View File

@@ -48,6 +48,12 @@ namespace Spring.Objects.Factory.Xml
/// </remarks>
public const string TrueValue = "true";
/// <summary>
/// Value of a boolean attribute that represents
/// <see langword="false"/>.
/// </summary>
public const string FalseValue = "false";
/// <summary>
/// Signifies that a default value is to be applied.
/// </summary>
@@ -90,6 +96,11 @@ namespace Spring.Objects.Factory.Xml
/// </summary>
public const string DefaultAutowireAttribute = "default-autowire";
/// <summary>
/// Specifies the default autowire candidates.
/// </summary>
public const string DefaultAutowireCandidatesAttribute = "default-autowire-candidates";
/// <summary>
/// Specifies the default collection merge mode.
/// </summary>
@@ -586,6 +597,11 @@ namespace Spring.Objects.Factory.Xml
/// </summary>
public const string AutowireAttribute = "autowire";
/// <summary>
/// The autowiring mode for an individual object definition.
/// </summary>
public const string AutowireCandidateAttribute = "autowire-candidate";
/// <summary>
/// Attribute element to farther deifne the qualifier of an object
/// </summary>

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -500,10 +500,25 @@
</xsd:simpleType>
</xsd:attribute>
<!--
Is this object the rpimary object if type resolving during Autowiring
-->
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.
-->
<xsd:attribute name="autowire-candidate" use="optional" default="default">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="default"/>
<xsd:enumeration value="true"/>
<xsd:enumeration value="false"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<!--
Is this object the rpimary object if type resolving during Autowiring
-->
<xsd:attribute name="primary" type="xsd:boolean" use="optional" default="false"/>
<!--
<!--
Optional attribute controlling whether to check whether all this
objects dependencies, expressed in its properties, are satisfied.
Default is no dependency checking.
@@ -553,42 +568,49 @@
<xsd:documentation>The document root. At least one object definition is required.</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:element name="description" type="description" minOccurs="0" maxOccurs="1"/>
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element name="import" type="importElement"/>
<xsd:element name="alias" type="aliasElement"/>
<xsd:element name="object" type="vanillaObject"/>
<xsd:any namespace="##other" processContents="strict"/>
</xsd:choice>
</xsd:sequence>
<!--
Default values for all object definitions. Can be overridden at
the "object" level. See those attribute definitions for details.
-->
<xsd:attribute name="default-lazy-init" type="xsd:boolean" use="optional" default="false"/>
<xsd:attribute name="default-merge" type="xsd:boolean" use="optional" default="false"/>
<xsd:attribute name="default-dependency-check" use="optional" default="none">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="none"/>
<xsd:enumeration value="objects"/>
<xsd:enumeration value="simple"/>
<xsd:enumeration value="all"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="default-autowire" use="optional" default="no">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="no"/>
<xsd:enumeration value="byName"/>
<xsd:enumeration value="byType"/>
<xsd:enumeration value="constructor"/>
<xsd:enumeration value="autodetect"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:sequence>
<xsd:element name="description" type="description"
minOccurs="0" maxOccurs="1" />
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element name="import" type="importElement" />
<xsd:element name="alias" type="aliasElement" />
<xsd:element name="object" type="vanillaObject" />
<xsd:any namespace="##other"
processContents="strict" />
</xsd:choice>
</xsd:sequence>
<!--
Default values for all object definitions. Can be overridden at
the "object" level. See those attribute definitions for details.
-->
<xsd:attribute name="default-lazy-init" type="xsd:boolean"
use="optional" default="false" />
<xsd:attribute name="default-merge" type="xsd:boolean"
use="optional" default="false" />
<xsd:attribute name="default-dependency-check"
use="optional" default="none">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="none" />
<xsd:enumeration value="objects" />
<xsd:enumeration value="simple" />
<xsd:enumeration value="all" />
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="default-autowire" use="optional"
default="no">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="no" />
<xsd:enumeration value="byName" />
<xsd:enumeration value="byType" />
<xsd:enumeration value="constructor" />
<xsd:enumeration value="autodetect" />
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="default-autowire-candidates" type="xsd:string" use="optional" />
</xsd:complexType>
</xsd:element>

View File

@@ -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<TestObject>(); }, Throws.Exception.TypeOf<NoSuchObjectDefinitionException>());
}
[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<TestObject>(); // na1 was filtered
Assert.That(lbf.GetObject("bd1", typeof(TestObject)), Is.SameAs(actual));
lbf.RegisterObjectDefinition("bd2", bd2);
Assert.That(delegate { lbf.GetObject<TestObject>(); }, Throws.Exception.TypeOf<NoSuchObjectDefinitionException>());
}
[Test]
public void GetObjectDefinitionResolvesAliases()
{

View File

@@ -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(
@"<?xml version='1.0' encoding='UTF-8' ?>
<objects xmlns='http://www.springframework.net' default-autowire-candidates='test1*,test4*'>
<object id='test1' type='Spring.Objects.TestObject, Spring.Core.Tests' />
<object id='test2' type='Spring.Objects.TestObject, Spring.Core.Tests' autowire-candidate='false' />
<object id='test3' type='Spring.Objects.TestObject, Spring.Core.Tests' autowire-candidate='true' />
<object id='test4' type='Spring.Objects.TestObject, Spring.Core.Tests' autowire-candidate='default' />
<object id='test5' type='Spring.Objects.TestObject, Spring.Core.Tests' autowire-candidate='default' />
</objects>
"));
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");
}
}
}