revised support for annotated factory methods (merged @FactoryMethod functionality into JavaConfig facility)

This commit is contained in:
Juergen Hoeller
2009-04-19 23:45:31 +00:00
parent 736169aa2a
commit 14bd475519
84 changed files with 2411 additions and 3458 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2007 the original author or authors.
* Copyright 2002-2009 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.
@@ -34,14 +34,6 @@ import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
*/
public enum Autowire {
/**
* Constant that indicates that autowiring information was not specified.
* In some cases it may be necessary to specify autowiring status,
* but merely confirm that this should be inherited from an enclosing
* container definition scope.
*/
INHERITED(-1),
/**
* Constant that indicates no autowiring at all.
*/

View File

@@ -54,7 +54,6 @@ import org.springframework.core.GenericTypeResolver;
import org.springframework.core.MethodParameter;
import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
@@ -102,8 +101,7 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
protected final Log logger = LogFactory.getLog(AutowiredAnnotationBeanPostProcessor.class);
@SuppressWarnings("unchecked")
private Class<? extends Annotation>[] autowiredAnnotationTypes =
new Class[] {Autowired.class, Qualifier.class, Value.class};
private Class<? extends Annotation>[] autowiredAnnotationTypes = new Class[] {Autowired.class, Value.class};
private String requiredParameterName = "required";
@@ -317,29 +315,19 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
});
ReflectionUtils.doWithMethods(clazz, new ReflectionUtils.MethodCallback() {
public void doWith(Method method) {
if (!isFactoryMethod(method)) {
Annotation annotation = findAutowiredAnnotation(method);
if (annotation != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
if (Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException("Autowired annotation is not supported on static methods");
}
if (method.getParameterTypes().length == 0) {
throw new IllegalStateException("Autowired annotation requires at least one argument: " + method);
}
boolean required = determineRequiredStatus(annotation);
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method);
newMetadata.addInjectedMethod(new AutowiredMethodElement(method, required, pd));
Annotation annotation = findAutowiredAnnotation(method);
if (annotation != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
if (Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException("Autowired annotation is not supported on static methods");
}
if (method.getParameterTypes().length == 0) {
throw new IllegalStateException("Autowired annotation requires at least one argument: " + method);
}
boolean required = determineRequiredStatus(annotation);
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method);
newMetadata.addInjectedMethod(new AutowiredMethodElement(method, required, pd));
}
}
private boolean isFactoryMethod(Method method) {
if (AnnotationUtils.findAnnotation(method, FactoryMethod.class)!= null) {
return true;
} else {
return false;
}
}
});
metadata = newMetadata;
this.injectionMetadataCache.put(clazz, metadata);
@@ -429,7 +417,7 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
Field field = (Field) this.member;
try {
Object value = null;
Object value;
if (this.cached) {
if (this.cachedFieldValue instanceof DependencyDescriptor) {
DependencyDescriptor descriptor = (DependencyDescriptor) this.cachedFieldValue;

View File

@@ -1,36 +0,0 @@
/*
* Copyright 2002-2009 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.
*/
package org.springframework.beans.factory.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Marks a method as being a factory-method of the class. Use during component scanning
* to create a bean definition that has factory-bean and factory-method metadata
*
* @author Mark Pollack
* @since 3.0
* @see RequiredAnnotationBeanPostProcessor
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface FactoryMethod {
}

View File

@@ -17,6 +17,7 @@
package org.springframework.beans.factory.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@@ -25,9 +26,9 @@ import org.springframework.beans.SimpleTypeConverter;
import org.springframework.beans.TypeConverter;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.AutowireCandidateQualifier;
import org.springframework.beans.factory.support.AutowireCandidateResolver;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert;
@@ -134,8 +135,14 @@ public class QualifierAnnotationAutowireCandidateResolver implements AutowireCan
return true;
}
boolean match = checkQualifiers(bdHolder, descriptor.getAnnotations());
if (match && descriptor.getMethodParameter() != null) {
match = checkQualifiers(bdHolder, descriptor.getMethodParameter().getAnnotations());
if (match) {
MethodParameter methodParam = descriptor.getMethodParameter();
if (methodParam != null) {
Method method = methodParam.getMethod();
if (method == null || void.class.equals(method.getReturnType())) {
match = checkQualifiers(bdHolder, methodParam.getAnnotations());
}
}
}
return match;
}
@@ -178,15 +185,21 @@ public class QualifierAnnotationAutowireCandidateResolver implements AutowireCan
BeanDefinitionHolder bdHolder, Annotation annotation, TypeConverter typeConverter) {
Class<? extends Annotation> type = annotation.annotationType();
AbstractBeanDefinition bd = (AbstractBeanDefinition) bdHolder.getBeanDefinition();
RootBeanDefinition bd = (RootBeanDefinition) bdHolder.getBeanDefinition();
AutowireCandidateQualifier qualifier = bd.getQualifier(type.getName());
if (qualifier == null) {
qualifier = bd.getQualifier(ClassUtils.getShortName(type));
}
if (qualifier == null && bd.hasBeanClass()) {
// look for matching annotation on the target class
Class<?> beanClass = bd.getBeanClass();
Annotation targetAnnotation = beanClass.getAnnotation(type);
if (qualifier == null) {
Annotation targetAnnotation = null;
if (bd.getFactoryMethodForIntrospection() != null) {
targetAnnotation = bd.getFactoryMethodForIntrospection().getAnnotation(type);
}
if (targetAnnotation == null && bd.hasBeanClass()) {
// look for matching annotation on the target class
Class<?> beanClass = bd.getBeanClass();
targetAnnotation = beanClass.getAnnotation(type);
}
if (targetAnnotation != null && targetAnnotation.equals(annotation)) {
return true;
}

View File

@@ -1,45 +0,0 @@
/*
* 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.
*/
package org.springframework.beans.factory.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Marker annotation identical in functionality with &lt;aop:scoped-proxy/&gt; tag. Provides a smart
* proxy backed by a scoped bean, which can be injected into object instances (usually singletons)
* allowing the same reference to be held while delegating method invocations to the backing, scoped
* beans.
*
* @author Costin Leau
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ScopedProxy {
/**
* Use CGLib-based class proxies (true) or JDK interface-based (false).
*
* Default is CGLib (true).
* @return
*/
boolean proxyTargetClass() default true;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 the original author or authors.
* Copyright 2002-2009 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.
@@ -98,7 +98,10 @@ public interface AutowireCapableBeanFactory extends BeanFactory {
* through introspection of the bean class.
* @see #createBean
* @see #autowire
* @deprecated as of Spring 3.0: If you are using mixed autowiring strategies,
* prefer annotation-based autowiring for clearer demarcation of autowiring needs.
*/
@Deprecated
int AUTOWIRE_AUTODETECT = 4;
@@ -181,7 +184,6 @@ public interface AutowireCapableBeanFactory extends BeanFactory {
* @see #AUTOWIRE_BY_NAME
* @see #AUTOWIRE_BY_TYPE
* @see #AUTOWIRE_CONSTRUCTOR
* @see #AUTOWIRE_AUTODETECT
*/
Object createBean(Class beanClass, int autowireMode, boolean dependencyCheck) throws BeansException;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 the original author or authors.
* Copyright 2002-2009 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.
@@ -143,6 +143,30 @@ public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
*/
void setScope(String scope);
/**
* Return whether this bean should be lazily initialized, i.e. not
* eagerly instantiated on startup. Only applicable to a singleton bean.
*/
boolean isLazyInit();
/**
* Set whether this bean should be lazily initialized.
* <p>If <code>false</code>, the bean will get instantiated on startup by bean
* factories that perform eager initialization of singletons.
*/
void setLazyInit(boolean lazyInit);
/**
* Return the bean names that this bean depends on.
*/
String[] getDependsOn();
/**
* Set the names of the beans that this bean depends on being initialized.
* The bean factory will guarantee that these beans get initialized first.
*/
void setDependsOn(String[] dependsOn);
/**
* Return whether this bean is a candidate for getting autowired into some other bean.
*/
@@ -153,6 +177,20 @@ public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
*/
void setAutowireCandidate(boolean autowireCandidate);
/**
* Return whether this bean is a primary autowire candidate.
* If this value is true for exactly one bean among multiple
* matching candidates, it will serve as a tie-breaker.
*/
boolean isPrimary();
/**
* Set whether this bean is a primary autowire candidate.
* <p>If this value is true for exactly one bean among multiple
* matching candidates, it will serve as a tie-breaker.
*/
void setPrimary(boolean primary);
/**
* Return the constructor argument values for this bean.
@@ -180,12 +218,6 @@ public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
*/
boolean isAbstract();
/**
* Return whether this bean should be lazily initialized, that is, not
* eagerly instantiated on startup.
*/
boolean isLazyInit();
/**
* Get the role hint for this <code>BeanDefinition</code>. The role hint
* provides tools with an indication of the importance of a particular

View File

@@ -524,7 +524,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
@Override
protected Class predictBeanType(String beanName, RootBeanDefinition mbd, Class[] typesToMatch) {
Class beanClass = null;
Class beanClass;
if (mbd.getFactoryMethodName() != null) {
beanClass = getTypeForFactoryMethod(beanName, mbd, typesToMatch);
}
@@ -562,7 +562,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
* @see #createBean
*/
protected Class getTypeForFactoryMethod(String beanName, RootBeanDefinition mbd, Class[] typesToMatch) {
Class factoryClass = null;
Class factoryClass;
boolean isStatic = true;
String factoryBeanName = mbd.getFactoryBeanName();
@@ -1203,7 +1203,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
}
MutablePropertyValues mpvs = null;
List<PropertyValue> original = null;
List<PropertyValue> original;
if (pvs instanceof MutablePropertyValues) {
mpvs = (MutablePropertyValues) pvs;

View File

@@ -16,9 +16,9 @@
package org.springframework.beans.factory.support;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
@@ -83,7 +83,10 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
* Constant that indicates determining an appropriate autowire strategy
* through introspection of the bean class.
* @see #setAutowireMode
* @deprecated as of Spring 3.0: If you are using mixed autowiring strategies,
* use annotation-based autowiring for clearer demarcation of autowiring needs.
*/
@Deprecated
public static final int AUTOWIRE_AUTODETECT = AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT;
@@ -134,11 +137,11 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
private boolean autowireCandidate = true;
private boolean primary = false;
private final Map<String, AutowireCandidateQualifier> qualifiers =
new LinkedHashMap<String, AutowireCandidateQualifier>();
private boolean primary = false;
private ConstructorArgumentValues constructorArgumentValues;
private MutablePropertyValues propertyValues;
@@ -317,8 +320,8 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
*/
public void applyDefaults(BeanDefinitionDefaults defaults) {
setLazyInit(defaults.isLazyInit());
setDependencyCheck(defaults.getDependencyCheck());
setAutowireMode(defaults.getAutowireMode());
setDependencyCheck(defaults.getDependencyCheck());
setInitMethodName(defaults.getInitMethodName());
setEnforceInitMethod(false);
setDestroyMethodName(defaults.getDestroyMethodName());
@@ -336,7 +339,7 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
/**
* Specify the class for this bean.
*/
public void setBeanClass(Class beanClass) {
public void setBeanClass(Class<?> beanClass) {
this.beanClass = beanClass;
}
@@ -346,7 +349,7 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
* @throws IllegalStateException if the bean definition does not define a bean class,
* or a specified bean class name has not been resolved into an actual Class
*/
public Class getBeanClass() throws IllegalStateException {
public Class<?> getBeanClass() throws IllegalStateException {
Object beanClassObject = this.beanClass;
if (beanClassObject == null) {
throw new IllegalStateException("No bean class specified on bean definition");
@@ -556,7 +559,7 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
/**
* Set the names of the beans that this bean depends on being initialized.
* The bean factory will guarantee that these beans get initialized before.
* The bean factory will guarantee that these beans get initialized first.
* <p>Note that dependencies are normally expressed through bean properties or
* constructor arguments. This property should just be necessary for other kinds
* of dependencies like statics (*ugh*) or database preparation on startup.
@@ -586,6 +589,24 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
return this.autowireCandidate;
}
/**
* Set whether this bean is a primary autowire candidate.
* If this value is true for exactly one bean among multiple
* matching candidates, it will serve as a tie-breaker.
*/
public void setPrimary(boolean primary) {
this.primary = primary;
}
/**
* Return whether this bean is a primary autowire candidate.
* If this value is true for exactly one bean among multiple
* matching candidates, it will serve as a tie-breaker.
*/
public boolean isPrimary() {
return this.primary;
}
/**
* Register a qualifier to be used for autowire candidate resolution,
* keyed by the qualifier's type name.
@@ -626,24 +647,6 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
this.qualifiers.putAll(source.qualifiers);
}
/**
* Set whether this bean is a primary autowire candidate.
* If this value is true for exactly one bean among multiple
* matching candidates, it will serve as a tie-breaker.
*/
public void setPrimary(boolean primary) {
this.primary = primary;
}
/**
* Return whether this bean is a primary autowire candidate.
* If this value is true for exactly one bean among multiple
* matching candidates, it will serve as a tie-breaker.
*/
public boolean isPrimary() {
return this.primary;
}
/**
* Specify constructor argument values for this bean.

View File

@@ -59,6 +59,28 @@ abstract class AutowireUtils {
});
}
/**
* Sort the given factory methods, preferring public methods and "greedy" ones
* with a maximum of arguments. The result will contain public methods first,
* with decreasing number of arguments, then non-public methods, again with
* decreasing number of arguments.
* @param factoryMethods the factory method array to sort
*/
public static void sortFactoryMethods(Method[] factoryMethods) {
Arrays.sort(factoryMethods, new Comparator<Method>() {
public int compare(Method fm1, Method fm2) {
boolean p1 = Modifier.isPublic(fm1.getModifiers());
boolean p2 = Modifier.isPublic(fm2.getModifiers());
if (p1 != p2) {
return (p1 ? -1 : 1);
}
int c1pl = fm1.getParameterTypes().length;
int c2pl = fm2.getParameterTypes().length;
return (new Integer(c1pl)).compareTo(c2pl) * -1;
}
});
}
/**
* Determine whether the given bean property is excluded from dependency checks.
* <p>This implementation excludes properties defined by CGLIB.

View File

@@ -20,6 +20,7 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
@@ -45,6 +46,7 @@ import org.springframework.core.MethodParameter;
import org.springframework.util.MethodInvoker;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ClassUtils;
/**
* Helper class for resolving constructors and factory methods.
@@ -284,6 +286,10 @@ class ConstructorResolver {
}
else {
// It's a static factory method on the bean class.
if (!mbd.hasBeanClass()) {
throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,
"bean definition declares neither a bean class nor a factory-bean reference");
}
factoryBean = null;
factoryClass = mbd.getBeanClass();
isStatic = true;
@@ -309,7 +315,18 @@ class ConstructorResolver {
if (factoryMethodToUse == null) {
// Need to determine the factory method...
// Try all methods with this name to see if they match the given arguments.
Method[] candidates = ReflectionUtils.getAllDeclaredMethods(factoryClass);
factoryClass = ClassUtils.getUserClass(factoryClass);
Method[] rawCandidates = ReflectionUtils.getAllDeclaredMethods(factoryClass);
List<Method> candidateSet = new ArrayList<Method>();
for (Method candidate : rawCandidates) {
if (Modifier.isStatic(candidate.getModifiers()) == isStatic &&
candidate.getName().equals(mbd.getFactoryMethodName())) {
candidateSet.add(candidate);
}
}
Method[] candidates = candidateSet.toArray(new Method[candidateSet.size()]);
AutowireUtils.sortFactoryMethods(candidates);
boolean autowiring = (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR);
int minTypeDiffWeight = Integer.MAX_VALUE;
ConstructorArgumentValues resolvedValues = null;
@@ -332,10 +349,7 @@ class ConstructorResolver {
Method candidate = candidates[i];
Class[] paramTypes = candidate.getParameterTypes();
if (Modifier.isStatic(candidate.getModifiers()) == isStatic &&
candidate.getName().equals(mbd.getFactoryMethodName()) &&
paramTypes.length >= minNrOfArgs) {
if (paramTypes.length >= minNrOfArgs) {
ArgumentsHolder args;
if (resolvedValues != null) {

View File

@@ -17,7 +17,9 @@
package org.springframework.beans.factory.support;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
@@ -47,7 +49,9 @@ import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
/**
@@ -428,6 +432,32 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
*/
protected boolean isAutowireCandidate(String beanName, RootBeanDefinition mbd, DependencyDescriptor descriptor) {
resolveBeanClass(mbd, beanName);
// TODO: the following is duplicating the factory method resolution algorithm in
// ConstructorResolver quite a bit...
if (mbd.getFactoryMethodName() != null && mbd.factoryMethodForIntrospection == null) {
Class factoryClass;
if (mbd.getFactoryBeanName() != null) {
factoryClass = getType(mbd.getFactoryBeanName());
}
else {
factoryClass = mbd.getBeanClass();
}
factoryClass = ClassUtils.getUserClass(factoryClass);
Method[] candidates = ReflectionUtils.getAllDeclaredMethods(factoryClass);
Method uniqueCandidate = null;
for (Method candidate : candidates) {
if (candidate.getName().equals(mbd.getFactoryMethodName())) {
if (uniqueCandidate == null) {
uniqueCandidate = candidate;
}
else if (!Arrays.equals(uniqueCandidate.getParameterTypes(), candidate.getParameterTypes())) {
uniqueCandidate = null;
break;
}
}
}
mbd.factoryMethodForIntrospection = uniqueCandidate;
}
return getAutowireCandidateResolver().isAutowireCandidate(
new BeanDefinitionHolder(mbd, beanName, getAliases(beanName)), descriptor);
}

View File

@@ -17,6 +17,7 @@
package org.springframework.beans.factory.support;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
@@ -51,6 +52,8 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
private final Set<String> externallyManagedDestroyMethods = Collections.synchronizedSet(new HashSet<String>());
volatile Method factoryMethodForIntrospection;
/** Package-visible field for caching the resolved constructor or factory method */
volatile Object resolvedConstructorOrFactoryMethod;
@@ -218,6 +221,10 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
}
}
public Method getFactoryMethodForIntrospection() {
return this.factoryMethodForIntrospection;
}
public void registerExternallyManagedConfigMember(Member configMember) {
this.externallyManagedConfigMembers.add(configMember);