container refresh modifications to interact with ConfigurationClassPostProcessor as required

This commit is contained in:
sbohlen
2010-11-15 20:35:54 +00:00
parent 324486e86a
commit 55b169d06c
9 changed files with 320 additions and 95 deletions

View File

@@ -0,0 +1,92 @@
#region License
/*
* Copyright <20> 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
{
/// <summary>
/// Intercepts calls to methods within the configuration object
/// </summary>
/// <author>Mark Pollack</author>
/// <author>Erich Eichinger</author>
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
}
}

View File

@@ -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)
{ }
}

View File

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

View File

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

View File

@@ -39,6 +39,7 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Context\Advice\SpringObjectMethodInterceptor.cs" />
<Compile Include="Context\Attributes\ConfigurationAttribute.cs" />
<Compile Include="Context\Attributes\ConfigurationClass.cs" />
<Compile Include="Context\Attributes\ConfigurationClassMethod.cs" />

View File

@@ -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
/// </note>
/// </remarks>
/// <exception cref="ObjectsException">In the case of errors.</exception>
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();

View File

@@ -18,7 +18,8 @@ namespace Spring.Objects.Factory.Parsing
/// <param name="source"></param>
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;
}

View File

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

View File

@@ -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<Parent>());
Assert.That(_ctx[typeof(Child).Name], Is.TypeOf<Child>());
Assert.That(_ctx[typeof(SingletonParent).Name], Is.TypeOf<SingletonParent>());
Assert.That(_ctx[typeof(PrototypeChild).Name], Is.TypeOf<PrototypeChild>());
}
[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 { }
}