-
-
Notifications
You must be signed in to change notification settings - Fork 375
Description
It seems that as of M2, if you use VariablePlaceholderConfigurer in combination with any child context, your app is now broken. There is no work-around other than removing all child contexts or removing all VariablePlaceholderConfigurers.
[This][https://github.com/SpringSource/spring-net/blob/d8ff77d99941a2cf46e300c2f8dd080d58f7d18d/src/Spring/Spring.Core/Objects/Factory/Support/DefaultListableObjectFactory.cs] change to DefaultListableObjectFactory broke VariablePlaceholderConfigurer.
in VariablePlaceholderConfigurer.cs:
IList<string> objectDefinitionNames = factory.GetObjectDefinitionNames();
for (int i = 0; i < objectDefinitionNames.Count; ++i)
{
string name = objectDefinitionNames[i];
IObjectDefinition definition = factory.GetObjectDefinition( name );
try
{
visitor.VisitObjectDefinition( definition );
You can see that it uses GetObjectDefinitionNames() to list all of the definition names and then GetObjectDefinition() to get the definitions. GetObjectDefinitionNames() USED to only return definitions in the current context, but now it returns definitions in parent contexts as well. At a glance, I can't see why this was done, except it simplified (and I suppose obsoleted) ObjectFactoryUtils.ObjectNamesForTypeIncludingAncestors.
The problem now, though, is that there is no obvious way to retrieve the names of the definitions in the objectfactory without including the parents. A further problem seems to be that the related GetObjectDefinitionNames(Type type) method does NOT respect hierarchy, so now the two identically-named methods are very inconsistent in behaviour. And, of course, any code that behaves like the VariablePlaceholderConfigurer is now broken when using ancestor contexts.
My suggestions, in order of preference would be:
- Roll back the change to the behaviour of DefaultListableObjectFactory.GetObjectDefinitionNames() to the old one and add an overloaded method that takes a bool argument, just like GetObjectDefinition() does. Modify ObjectFactoryUtils.ObjectNamesForTypeIncludingAncestors to call the overloaded version. I do not yet know what other new code relies on the new behaviour of GetObjectDefinitionNames(), if any.
- Modify VariablePlaceholderConfigurer to ignore null values returned from GetObjectDefinition(). (Obviously this is probably a terrible idea).
I'm going to assume that the first option is the way to go, examine any calls to GetObjectDefinitionNames and make sure that none of them rely on the new behaviour. I would write a test that demonstrates the broken behaviour, but I'm not sure if it's even possible to construct a parent/child context with the integration test support, which is what's required in order to demonstrate the problem.
EDIT:
I've found the following additional code that will break if using the >=M2 version of DefaultListableObjectFactory:
ConfigurationClassPostProcessors.EnhanceConfigurationClasses: NRE on line 133
PropertyPlaceholderConfigurer.ProcessProperties: NRE within call on line 243
VariablePlaceholderConfigurer.ProcessProperties: NRE within call on line 256
Another question now presents itself:
Is it necessary to change the IConfigurableListableObjectFactory interface to provide the option of including ancestors? Is it necessary to modify GetObjectNamesForType and related methods to include those options? All of those methods call GetObjectDefinitionNames() and therefore now all behave differently than they used to, though it's reasonable to assume that the old behaviour was incorrect in some cases, especially since DoGetObjectNamesForType explicitly requests object definitions from ancestors.
Considering all of this, it MIGHT actually be better at this point to modify the three above broken classes to ignore null returns from GetObjectDefinition() since otherwise the effects of fixing this are wider and harder to predict and test.
Incidentally, this does appear to demonstrate a weakness in the testing that can be done when working in child contexts.