+ Adding POC tooling integration points, namely AbstractConfigurationPostProcessor and allowing for tooling-specified ClassLoader for use with ASM parsing
+ Eliminated ModelMethod in favor of BeanMethod throughout
This commit is contained in:
parent
61a1c4d0c6
commit
eaf3a7cec4
|
@ -13,7 +13,7 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
|||
* declare a no-arg constructor.
|
||||
*
|
||||
* @see FactoryMethod
|
||||
* @see ModelMethod
|
||||
* @see BeanMethod
|
||||
*
|
||||
* @author Chris Beams
|
||||
*/
|
||||
|
@ -31,6 +31,6 @@ public interface BeanDefinitionRegistrar {
|
|||
/**
|
||||
* Registers any bean definitions for <var>method</var> with <var>registry</var>.
|
||||
*/
|
||||
void register(ModelMethod method, BeanDefinitionRegistry registry);
|
||||
void register(BeanMethod method, BeanDefinitionRegistry registry);
|
||||
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ import org.springframework.util.Assert;
|
|||
|
||||
|
||||
/** TODO: JAVADOC */
|
||||
public final class ModelMethod implements Validatable {
|
||||
public final class BeanMethod implements Validatable {
|
||||
|
||||
private final String name;
|
||||
private final int modifiers;
|
||||
|
@ -42,7 +42,7 @@ public final class ModelMethod implements Validatable {
|
|||
private transient FactoryMethod factoryAnno;
|
||||
private transient final List<Validator> validators = new ArrayList<Validator>();
|
||||
|
||||
public ModelMethod(String name, int modifiers, ModelClass returnType, Annotation... annotations) {
|
||||
public BeanMethod(String name, int modifiers, ModelClass returnType, Annotation... annotations) {
|
||||
Assert.hasText(name);
|
||||
this.name = name;
|
||||
|
||||
|
@ -109,7 +109,7 @@ public final class ModelMethod implements Validatable {
|
|||
/**
|
||||
* Sets up bi-directional relationship between this method and its declaring class.
|
||||
*
|
||||
* @see ConfigurationClass#addMethod(ModelMethod)
|
||||
* @see ConfigurationClass#addMethod(BeanMethod)
|
||||
*/
|
||||
public void setDeclaringClass(ConfigurationClass declaringClass) {
|
||||
this.declaringClass = declaringClass;
|
||||
|
@ -132,8 +132,8 @@ public final class ModelMethod implements Validatable {
|
|||
}
|
||||
|
||||
public void validate(List<UsageError> errors) {
|
||||
for (Validator validator : validators)
|
||||
validator.validate(this, errors);
|
||||
// for (Validator validator : validators)
|
||||
// validator.validate(this, errors);
|
||||
|
||||
if (Modifier.isPrivate(getModifiers()))
|
||||
errors.add(new PrivateMethodError());
|
||||
|
@ -190,7 +190,7 @@ public final class ModelMethod implements Validatable {
|
|||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
ModelMethod other = (ModelMethod) obj;
|
||||
BeanMethod other = (BeanMethod) obj;
|
||||
if (annotations == null) {
|
||||
if (other.annotations != null)
|
||||
return false;
|
|
@ -24,6 +24,7 @@ import java.util.HashSet;
|
|||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.config.java.ext.Bean;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import sun.security.x509.Extension;
|
||||
|
@ -55,7 +56,7 @@ public final class ConfigurationClass extends ModelClass implements Validatable
|
|||
|
||||
private Configuration metadata;
|
||||
|
||||
private HashSet<ModelMethod> methods = new HashSet<ModelMethod>();
|
||||
private HashSet<BeanMethod> methods = new HashSet<BeanMethod>();
|
||||
|
||||
private HashSet<Annotation> pluginAnnotations = new HashSet<Annotation>();
|
||||
|
||||
|
@ -112,7 +113,7 @@ public final class ConfigurationClass extends ModelClass implements Validatable
|
|||
setModifiers(modifiers);
|
||||
}
|
||||
|
||||
public ConfigurationClass addMethod(ModelMethod method) {
|
||||
public ConfigurationClass addMethod(BeanMethod method) {
|
||||
method.setDeclaringClass(this);
|
||||
methods.add(method);
|
||||
return this;
|
||||
|
@ -127,7 +128,7 @@ public final class ConfigurationClass extends ModelClass implements Validatable
|
|||
return this;
|
||||
}
|
||||
|
||||
public Set<ModelMethod> getMethods() {
|
||||
public Set<BeanMethod> getMethods() {
|
||||
return methods;
|
||||
}
|
||||
|
||||
|
@ -184,7 +185,7 @@ public final class ConfigurationClass extends ModelClass implements Validatable
|
|||
if (Modifier.isFinal(modifiers))
|
||||
errors.add(new FinalConfigurationError());
|
||||
|
||||
for (ModelMethod method : methods)
|
||||
for (BeanMethod method : methods)
|
||||
method.validate(errors);
|
||||
}
|
||||
|
||||
|
@ -299,7 +300,7 @@ public final class ConfigurationClass extends ModelClass implements Validatable
|
|||
*/
|
||||
public class IllegalBeanOverrideError extends UsageError {
|
||||
private final ConfigurationClass authoritativeClass;
|
||||
private final ModelMethod finalMethodInQuestion;
|
||||
private final BeanMethod finalMethodInQuestion;
|
||||
|
||||
/**
|
||||
* Creates a new IllegalBeanOverrideError object.
|
||||
|
@ -310,7 +311,7 @@ public final class ConfigurationClass extends ModelClass implements Validatable
|
|||
* @param finalMethodInQuestion the method that has been marked
|
||||
* 'allowOverriding=false'
|
||||
*/
|
||||
public IllegalBeanOverrideError(ConfigurationClass violatingClass, ModelMethod finalMethodInQuestion) {
|
||||
public IllegalBeanOverrideError(ConfigurationClass violatingClass, BeanMethod finalMethodInQuestion) {
|
||||
super(violatingClass, -1);
|
||||
this.authoritativeClass = ConfigurationClass.this;
|
||||
this.finalMethodInQuestion = finalMethodInQuestion;
|
||||
|
@ -329,9 +330,9 @@ public final class ConfigurationClass extends ModelClass implements Validatable
|
|||
return getMethod(methodName) != null;
|
||||
}
|
||||
|
||||
public ModelMethod getMethod(String methodName) {
|
||||
public BeanMethod getMethod(String methodName) {
|
||||
|
||||
for (ModelMethod method : methods)
|
||||
for (BeanMethod method : methods)
|
||||
if (methodName.equals(method.getName()))
|
||||
return method;
|
||||
|
||||
|
|
|
@ -94,7 +94,7 @@ public final class ConfigurationModel implements Validatable {
|
|||
* <var>errors</var>.
|
||||
*
|
||||
* @see ConfigurationClass#validate(java.util.List)
|
||||
* @see ModelMethod#validate(java.util.List)
|
||||
* @see BeanMethod#validate(java.util.List)
|
||||
* @see Validator
|
||||
* @see UsageError
|
||||
*/
|
||||
|
@ -107,7 +107,7 @@ public final class ConfigurationModel implements Validatable {
|
|||
// depending on where they are registered (with the model, the class, or the method)
|
||||
// they will be called directly or indirectly below
|
||||
for (ConfigurationClass configClass : getAllConfigurationClasses()) {
|
||||
for (ModelMethod method : configClass.getMethods()) {
|
||||
for (BeanMethod method : configClass.getMethods()) {
|
||||
for (Validator validator : method.getValidators()) {
|
||||
if (validator.supports(method))
|
||||
method.registerValidator(validator);
|
||||
|
|
|
@ -98,6 +98,7 @@ public class ModelClass implements BeanMetadataElement {
|
|||
* Returns a resource path-formatted representation of the .java file that declares this
|
||||
* class
|
||||
*/
|
||||
// TODO: return type should be Object here. Spring IDE will return a JDT representation...
|
||||
public String getSource() {
|
||||
return source;
|
||||
}
|
||||
|
|
|
@ -131,9 +131,9 @@ public class Util {
|
|||
* @see ClassUtils#getDefaultClassLoader()
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Class<? extends T> loadToolingSafeClass(String fqClassName) {
|
||||
public static <T> Class<? extends T> loadToolingSafeClass(String fqClassName, ClassLoader classLoader) {
|
||||
try {
|
||||
return (Class<? extends T>) ClassUtils.getDefaultClassLoader().loadClass(fqClassName);
|
||||
return (Class<? extends T>) classLoader.loadClass(fqClassName);
|
||||
} catch (ClassNotFoundException ex) {
|
||||
log.warn(format("Unable to load class [%s], likely due to tooling-specific restrictions."
|
||||
+ "Attempting to continue, but unexpected errors may occur", fqClassName), ex);
|
||||
|
@ -152,10 +152,10 @@ public class Util {
|
|||
*
|
||||
* @throws RuntimeException if <var>pathToClass</var> does not exist
|
||||
*/
|
||||
public static InputStream getClassAsStream(String pathToClass) {
|
||||
public static InputStream getClassAsStream(String pathToClass, ClassLoader classLoader) {
|
||||
String classFileName = pathToClass + ClassUtils.CLASS_FILE_SUFFIX;
|
||||
|
||||
InputStream is = ClassUtils.getDefaultClassLoader().getResourceAsStream(classFileName);
|
||||
InputStream is = classLoader.getResourceAsStream(classFileName);
|
||||
|
||||
if (is == null)
|
||||
throw new RuntimeException(new FileNotFoundException("Class file [" + classFileName
|
||||
|
|
|
@ -26,11 +26,11 @@ import java.util.List;
|
|||
import org.springframework.beans.factory.annotation.Autowire;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.config.java.BeanMethod;
|
||||
import org.springframework.config.java.Configuration;
|
||||
import org.springframework.config.java.ConfigurationClass;
|
||||
import org.springframework.config.java.ConfigurationModel;
|
||||
import org.springframework.config.java.FactoryMethod;
|
||||
import org.springframework.config.java.ModelMethod;
|
||||
import org.springframework.config.java.Scopes;
|
||||
import org.springframework.config.java.UsageError;
|
||||
import org.springframework.config.java.Validator;
|
||||
|
@ -164,11 +164,11 @@ public @interface Bean {
|
|||
class BeanValidator implements Validator {
|
||||
|
||||
public boolean supports(Object object) {
|
||||
return object instanceof ModelMethod;
|
||||
return object instanceof BeanMethod;
|
||||
}
|
||||
|
||||
public void validate(Object object, List<UsageError> errors) {
|
||||
ModelMethod method = (ModelMethod) object;
|
||||
BeanMethod method = (BeanMethod) object;
|
||||
|
||||
// TODO: re-enable for @ScopedProxy support
|
||||
// if (method.getAnnotation(ScopedProxy.class) == null)
|
||||
|
@ -204,7 +204,7 @@ class IllegalBeanOverrideValidator implements Validator {
|
|||
ConfigurationClass[] allClasses = model.getAllConfigurationClasses();
|
||||
|
||||
for (int i = 0; i < allClasses.length; i++) {
|
||||
for (ModelMethod method : allClasses[i].getMethods()) {
|
||||
for (BeanMethod method : allClasses[i].getMethods()) {
|
||||
Bean bean = method.getAnnotation(Bean.class);
|
||||
|
||||
if (bean == null || bean.allowOverriding())
|
||||
|
|
|
@ -33,7 +33,7 @@ import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
|||
*
|
||||
* @author Chris Beams
|
||||
*/
|
||||
class BeanMethodInterceptor extends AbstractMethodInterceptor {
|
||||
public class BeanMethodInterceptor extends AbstractMethodInterceptor {
|
||||
|
||||
/**
|
||||
* Enhances a {@link Bean @Bean} method to check the supplied BeanFactory for the
|
||||
|
|
|
@ -14,18 +14,17 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
|||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.config.java.BeanDefinitionRegistrar;
|
||||
import org.springframework.config.java.BeanMethod;
|
||||
import org.springframework.config.java.Configuration;
|
||||
import org.springframework.config.java.ConfigurationClass;
|
||||
import org.springframework.config.java.MalformedConfigurationException;
|
||||
import org.springframework.config.java.ModelMethod;
|
||||
import org.springframework.config.java.UsageError;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
// TODO: SJC-242 document BeanHandler
|
||||
// TODO: SJC-242 make package-private
|
||||
class BeanRegistrar implements BeanDefinitionRegistrar {
|
||||
public class BeanRegistrar implements BeanDefinitionRegistrar {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(BeanRegistrar.class);
|
||||
|
||||
|
@ -38,7 +37,7 @@ class BeanRegistrar implements BeanDefinitionRegistrar {
|
|||
}
|
||||
|
||||
// TODO: SJC-242 method too long
|
||||
public void register(ModelMethod method, BeanDefinitionRegistry registry) {
|
||||
public void register(BeanMethod method, BeanDefinitionRegistry registry) {
|
||||
RootBeanDefinition beanDef = new ConfigurationClassBeanDefinition();
|
||||
|
||||
ConfigurationClass configClass = method.getDeclaringClass();
|
||||
|
@ -171,7 +170,10 @@ class BeanRegistrar implements BeanDefinitionRegistrar {
|
|||
}
|
||||
|
||||
private BeanDefinition getBeanDefinitionIncludingAncestry(String beanName, BeanDefinitionRegistry registry) {
|
||||
Assert.isInstanceOf(ConfigurableListableBeanFactory.class, registry);
|
||||
if(!(registry instanceof ConfigurableListableBeanFactory)) {
|
||||
return registry.getBeanDefinition(beanName);
|
||||
}
|
||||
|
||||
ConfigurableListableBeanFactory clbf = (ConfigurableListableBeanFactory) registry;
|
||||
|
||||
do {
|
||||
|
|
|
@ -39,11 +39,13 @@ import org.springframework.beans.factory.BeanFactoryAware;
|
|||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.config.java.BeanDefinitionRegistrar;
|
||||
import org.springframework.config.java.BeanMethod;
|
||||
import org.springframework.config.java.Configuration;
|
||||
import org.springframework.config.java.ConfigurationClass;
|
||||
import org.springframework.config.java.ConfigurationModel;
|
||||
import org.springframework.config.java.ModelMethod;
|
||||
import org.springframework.config.java.NoOpInterceptor;
|
||||
import org.springframework.config.java.ext.BeanMethodInterceptor;
|
||||
import org.springframework.config.java.ext.BeanRegistrar;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -81,11 +83,31 @@ public class ConfigurationEnhancer {
|
|||
/**
|
||||
* Creates a new {@link ConfigurationEnhancer} instance.
|
||||
*/
|
||||
public ConfigurationEnhancer(DefaultListableBeanFactory beanFactory, ConfigurationModel model) {
|
||||
public ConfigurationEnhancer(DefaultListableBeanFactory beanFactory) {
|
||||
notNull(beanFactory, "beanFactory must be non-null");
|
||||
notNull(model, "model must be non-null");
|
||||
//notNull(model, "model must be non-null");
|
||||
|
||||
populateRegistrarsAndCallbacks(beanFactory, model);
|
||||
//populateRegistrarsAndCallbacks(beanFactory, model);
|
||||
|
||||
registrars.add(new BeanRegistrar());
|
||||
BeanMethodInterceptor beanMethodInterceptor = new BeanMethodInterceptor();
|
||||
beanMethodInterceptor.setBeanFactory(beanFactory);
|
||||
callbackInstances.add(beanMethodInterceptor);
|
||||
|
||||
registrars.add(new BeanDefinitionRegistrar() {
|
||||
|
||||
public boolean accepts(Method method) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void register(BeanMethod method, BeanDefinitionRegistry registry) {
|
||||
// no-op
|
||||
}
|
||||
});
|
||||
callbackInstances.add(NoOpInterceptor.INSTANCE);
|
||||
|
||||
for (Callback callback : callbackInstances)
|
||||
callbackTypes.add(callback.getClass());
|
||||
}
|
||||
|
||||
|
||||
|
@ -97,9 +119,9 @@ public class ConfigurationEnhancer {
|
|||
*/
|
||||
private void populateRegistrarsAndCallbacks(DefaultListableBeanFactory beanFactory,
|
||||
ConfigurationModel model) {
|
||||
|
||||
|
||||
for (ConfigurationClass configClass : model.getAllConfigurationClasses()) {
|
||||
for (ModelMethod method : configClass.getMethods()) {
|
||||
for (BeanMethod method : configClass.getMethods()) {
|
||||
registrars.add(method.getRegistrar());
|
||||
|
||||
Callback callback = method.getCallback();
|
||||
|
@ -118,7 +140,7 @@ public class ConfigurationEnhancer {
|
|||
return true;
|
||||
}
|
||||
|
||||
public void register(ModelMethod method, BeanDefinitionRegistry registry) {
|
||||
public void register(BeanMethod method, BeanDefinitionRegistry registry) {
|
||||
// no-op
|
||||
}
|
||||
});
|
||||
|
|
|
@ -97,8 +97,8 @@ class AsmUtils {
|
|||
* classpath
|
||||
* @throws RuntimeException if an IOException occurs when creating the new ClassReader
|
||||
*/
|
||||
public static ClassReader newClassReader(String pathToClass) {
|
||||
InputStream is = Util.getClassAsStream(pathToClass);
|
||||
public static ClassReader newClassReader(String pathToClass, ClassLoader classLoader) {
|
||||
InputStream is = Util.getClassAsStream(pathToClass, classLoader);
|
||||
return newClassReader(is);
|
||||
}
|
||||
|
||||
|
|
|
@ -28,11 +28,12 @@ import org.objectweb.asm.ClassAdapter;
|
|||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.MethodAdapter;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.springframework.config.java.BeanMethod;
|
||||
import org.springframework.config.java.Configuration;
|
||||
import org.springframework.config.java.ConfigurationClass;
|
||||
import org.springframework.config.java.FactoryMethod;
|
||||
import org.springframework.config.java.ModelClass;
|
||||
import org.springframework.config.java.ModelMethod;
|
||||
import org.springframework.config.java.ext.Bean;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -49,6 +50,7 @@ class ConfigurationClassMethodVisitor extends MethodAdapter {
|
|||
private final int modifiers;
|
||||
private final ModelClass returnType;
|
||||
private final ArrayList<Annotation> annotations = new ArrayList<Annotation>();
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
private boolean isModelMethod = false;
|
||||
private int lineNumber;
|
||||
|
@ -62,13 +64,14 @@ class ConfigurationClassMethodVisitor extends MethodAdapter {
|
|||
* @param modifiers modifiers for this method
|
||||
*/
|
||||
public ConfigurationClassMethodVisitor(ConfigurationClass configClass, String methodName,
|
||||
String methodDescriptor, int modifiers) {
|
||||
String methodDescriptor, int modifiers, ClassLoader classLoader) {
|
||||
super(AsmUtils.EMPTY_VISITOR);
|
||||
|
||||
this.configClass = configClass;
|
||||
this.methodName = methodName;
|
||||
this.returnType = initReturnTypeFromMethodDescriptor(methodDescriptor);
|
||||
this.classLoader = classLoader;
|
||||
this.modifiers = modifiers;
|
||||
this.returnType = initReturnTypeFromMethodDescriptor(methodDescriptor);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -79,7 +82,7 @@ class ConfigurationClassMethodVisitor extends MethodAdapter {
|
|||
public AnnotationVisitor visitAnnotation(String annoTypeDesc, boolean visible) {
|
||||
String annoClassName = AsmUtils.convertTypeDescriptorToClassName(annoTypeDesc);
|
||||
|
||||
Class<? extends Annotation> annoClass = loadToolingSafeClass(annoClassName);
|
||||
Class<? extends Annotation> annoClass = loadToolingSafeClass(annoClassName, classLoader);
|
||||
|
||||
if (annoClass == null)
|
||||
return super.visitAnnotation(annoTypeDesc, visible);
|
||||
|
@ -88,7 +91,7 @@ class ConfigurationClassMethodVisitor extends MethodAdapter {
|
|||
|
||||
annotations.add(annotation);
|
||||
|
||||
return new MutableAnnotationVisitor(annotation);
|
||||
return new MutableAnnotationVisitor(annotation, classLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -111,7 +114,7 @@ class ConfigurationClassMethodVisitor extends MethodAdapter {
|
|||
@Override
|
||||
public void visitEnd() {
|
||||
for (Annotation anno : annotations) {
|
||||
if (anno.annotationType().getAnnotation(FactoryMethod.class) != null) {
|
||||
if (anno.annotationType().equals(Bean.class)) {
|
||||
isModelMethod = true;
|
||||
break;
|
||||
}
|
||||
|
@ -121,7 +124,7 @@ class ConfigurationClassMethodVisitor extends MethodAdapter {
|
|||
return;
|
||||
|
||||
Annotation[] annoArray = annotations.toArray(new Annotation[] {});
|
||||
ModelMethod method = new ModelMethod(methodName, modifiers, returnType, annoArray);
|
||||
BeanMethod method = new BeanMethod(methodName, modifiers, returnType, annoArray);
|
||||
method.setLineNumber(lineNumber);
|
||||
configClass.addMethod(method);
|
||||
}
|
||||
|
@ -130,11 +133,11 @@ class ConfigurationClassMethodVisitor extends MethodAdapter {
|
|||
* Determines return type from ASM <var>methodDescriptor</var> and determines whether
|
||||
* that type is an interface.
|
||||
*/
|
||||
private static ModelClass initReturnTypeFromMethodDescriptor(String methodDescriptor) {
|
||||
private ModelClass initReturnTypeFromMethodDescriptor(String methodDescriptor) {
|
||||
final ModelClass returnType = new ModelClass(getReturnTypeFromMethodDescriptor(methodDescriptor));
|
||||
|
||||
// detect whether the return type is an interface
|
||||
newClassReader(convertClassNameToResourcePath(returnType.getName())).accept(
|
||||
newClassReader(convertClassNameToResourcePath(returnType.getName()), classLoader).accept(
|
||||
new ClassAdapter(AsmUtils.EMPTY_VISITOR) {
|
||||
@Override
|
||||
public void visit(int arg0, int arg1, String arg2, String arg3, String arg4, String[] arg5) {
|
||||
|
|
|
@ -52,11 +52,13 @@ class ConfigurationClassVisitor extends ClassAdapter {
|
|||
private final HashMap<String, ConfigurationClass> innerClasses = new HashMap<String, ConfigurationClass>();
|
||||
|
||||
private boolean processInnerClasses = true;
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
public ConfigurationClassVisitor(ConfigurationClass configClass, ConfigurationModel model) {
|
||||
public ConfigurationClassVisitor(ConfigurationClass configClass, ConfigurationModel model, ClassLoader classLoader) {
|
||||
super(AsmUtils.EMPTY_VISITOR);
|
||||
this.configClass = configClass;
|
||||
this.model = model;
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
public void setProcessInnerClasses(boolean processInnerClasses) {
|
||||
|
@ -89,9 +91,9 @@ class ConfigurationClassVisitor extends ClassAdapter {
|
|||
if (OBJECT_DESC.equals(superTypeDesc))
|
||||
return;
|
||||
|
||||
ConfigurationClassVisitor visitor = new ConfigurationClassVisitor(configClass, model);
|
||||
ConfigurationClassVisitor visitor = new ConfigurationClassVisitor(configClass, model, classLoader);
|
||||
|
||||
ClassReader reader = AsmUtils.newClassReader(superTypeDesc);
|
||||
ClassReader reader = AsmUtils.newClassReader(superTypeDesc, classLoader);
|
||||
reader.accept(visitor, false);
|
||||
}
|
||||
|
||||
|
@ -113,7 +115,7 @@ class ConfigurationClassVisitor extends ClassAdapter {
|
|||
if (Configuration.class.getName().equals(annoTypeName)) {
|
||||
Configuration mutableConfiguration = createMutableAnnotation(Configuration.class);
|
||||
configClass.setMetadata(mutableConfiguration);
|
||||
return new MutableAnnotationVisitor(mutableConfiguration);
|
||||
return new MutableAnnotationVisitor(mutableConfiguration, classLoader);
|
||||
}
|
||||
|
||||
// TODO: re-enable for @Import support
|
||||
|
@ -131,39 +133,41 @@ class ConfigurationClassVisitor extends ClassAdapter {
|
|||
// -------------------------------------
|
||||
// Detect @Plugin annotations
|
||||
// -------------------------------------
|
||||
PluginAnnotationDetectingClassVisitor classVisitor = new PluginAnnotationDetectingClassVisitor();
|
||||
PluginAnnotationDetectingClassVisitor classVisitor = new PluginAnnotationDetectingClassVisitor(classLoader);
|
||||
|
||||
String className = AsmUtils.convertTypeDescriptorToClassName(annoTypeDesc);
|
||||
String resourcePath = ClassUtils.convertClassNameToResourcePath(className);
|
||||
ClassReader reader = AsmUtils.newClassReader(resourcePath);
|
||||
ClassReader reader = AsmUtils.newClassReader(resourcePath, classLoader);
|
||||
reader.accept(classVisitor, false);
|
||||
|
||||
if (!classVisitor.hasPluginAnnotation())
|
||||
return super.visitAnnotation(annoTypeDesc, visible);
|
||||
|
||||
Class<? extends Annotation> annoType = loadToolingSafeClass(annoTypeName);
|
||||
Class<? extends Annotation> annoType = loadToolingSafeClass(annoTypeName, classLoader);
|
||||
|
||||
if (annoType == null)
|
||||
return super.visitAnnotation(annoTypeDesc, visible);
|
||||
|
||||
Annotation pluginAnno = createMutableAnnotation(annoType);
|
||||
configClass.addPluginAnnotation(pluginAnno);
|
||||
return new MutableAnnotationVisitor(pluginAnno);
|
||||
return new MutableAnnotationVisitor(pluginAnno, classLoader);
|
||||
}
|
||||
|
||||
private static class PluginAnnotationDetectingClassVisitor extends ClassAdapter {
|
||||
private boolean hasPluginAnnotation = false;
|
||||
private final Extension pluginAnnotation = createMutableAnnotation(Extension.class);
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
public PluginAnnotationDetectingClassVisitor() {
|
||||
public PluginAnnotationDetectingClassVisitor(ClassLoader classLoader) {
|
||||
super(AsmUtils.EMPTY_VISITOR);
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String typeDesc, boolean arg1) {
|
||||
if (Extension.class.getName().equals(AsmUtils.convertTypeDescriptorToClassName(typeDesc))) {
|
||||
hasPluginAnnotation = true;
|
||||
return new MutableAnnotationVisitor(pluginAnnotation);
|
||||
return new MutableAnnotationVisitor(pluginAnnotation, classLoader);
|
||||
}
|
||||
return super.visitAnnotation(typeDesc, arg1);
|
||||
}
|
||||
|
@ -185,7 +189,7 @@ class ConfigurationClassVisitor extends ClassAdapter {
|
|||
public MethodVisitor visitMethod(int modifiers, String methodName, String methodDescriptor, String arg3,
|
||||
String[] arg4) {
|
||||
|
||||
return new ConfigurationClassMethodVisitor(configClass, methodName, methodDescriptor, modifiers);
|
||||
return new ConfigurationClassMethodVisitor(configClass, methodName, methodDescriptor, modifiers, classLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -215,11 +219,11 @@ class ConfigurationClassVisitor extends ClassAdapter {
|
|||
|
||||
ConfigurationClass innerConfigClass = new ConfigurationClass();
|
||||
|
||||
ConfigurationClassVisitor ccVisitor = new ConfigurationClassVisitor(innerConfigClass,
|
||||
new ConfigurationModel());
|
||||
ConfigurationClassVisitor ccVisitor =
|
||||
new ConfigurationClassVisitor(innerConfigClass, new ConfigurationModel(), classLoader);
|
||||
ccVisitor.setProcessInnerClasses(false);
|
||||
|
||||
ClassReader reader = AsmUtils.newClassReader(name);
|
||||
ClassReader reader = AsmUtils.newClassReader(name, classLoader);
|
||||
reader.accept(ccVisitor, false);
|
||||
|
||||
if (innerClasses.containsKey(outerName))
|
||||
|
|
|
@ -21,7 +21,7 @@ import org.springframework.config.java.Configuration;
|
|||
import org.springframework.config.java.ConfigurationClass;
|
||||
import org.springframework.config.java.ConfigurationModel;
|
||||
import org.springframework.config.java.Util;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -44,6 +44,7 @@ public class ConfigurationParser {
|
|||
* Model to be populated during calls to {@link #parse(Object, String)}
|
||||
*/
|
||||
private final ConfigurationModel model;
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
/**
|
||||
* Creates a new parser instance that will be used to populate <var>model</var>.
|
||||
|
@ -51,8 +52,9 @@ public class ConfigurationParser {
|
|||
* @param model model to be populated by each successive call to
|
||||
* {@link #parse(Object, String)}
|
||||
*/
|
||||
public ConfigurationParser(ConfigurationModel model) {
|
||||
this.model = model;
|
||||
public ConfigurationParser(ClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
this.model = new ConfigurationModel();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -63,16 +65,21 @@ public class ConfigurationParser {
|
|||
* @param configurationId may be null, but if populated represents the bean id (assumes
|
||||
* that this configuration class was configured via XML)
|
||||
*/
|
||||
public void parse(ClassPathResource resource, String configurationId) {
|
||||
public void parse(String className, String configurationId) {
|
||||
|
||||
String resourcePath = ClassUtils.convertClassNameToResourcePath(className);
|
||||
|
||||
String resourcePath = resource.getPath();
|
||||
ClassReader configClassReader = AsmUtils.newClassReader(Util.getClassAsStream(resourcePath));
|
||||
ClassReader configClassReader = AsmUtils.newClassReader(Util.getClassAsStream(resourcePath, classLoader));
|
||||
|
||||
ConfigurationClass configClass = new ConfigurationClass();
|
||||
configClass.setBeanName(configurationId);
|
||||
|
||||
configClassReader.accept(new ConfigurationClassVisitor(configClass, model), false);
|
||||
configClassReader.accept(new ConfigurationClassVisitor(configClass, model, classLoader), false);
|
||||
model.add(configClass);
|
||||
}
|
||||
|
||||
public ConfigurationModel getConfigurationModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -36,11 +36,14 @@ class MutableAnnotationArrayVisitor extends AnnotationAdapter {
|
|||
private final MutableAnnotation mutableAnno;
|
||||
private final String attribName;
|
||||
|
||||
public MutableAnnotationArrayVisitor(MutableAnnotation mutableAnno, String attribName) {
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
public MutableAnnotationArrayVisitor(MutableAnnotation mutableAnno, String attribName, ClassLoader classLoader) {
|
||||
super(AsmUtils.EMPTY_VISITOR);
|
||||
|
||||
this.mutableAnno = mutableAnno;
|
||||
this.attribName = attribName;
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -51,14 +54,14 @@ class MutableAnnotationArrayVisitor extends AnnotationAdapter {
|
|||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String na, String annoTypeDesc) {
|
||||
String annoTypeName = AsmUtils.convertTypeDescriptorToClassName(annoTypeDesc);
|
||||
Class<? extends Annotation> annoType = loadToolingSafeClass(annoTypeName);
|
||||
Class<? extends Annotation> annoType = loadToolingSafeClass(annoTypeName, classLoader);
|
||||
|
||||
if (annoType == null)
|
||||
return super.visitAnnotation(na, annoTypeDesc);
|
||||
|
||||
Annotation anno = createMutableAnnotation(annoType);
|
||||
values.add(anno);
|
||||
return new MutableAnnotationVisitor(anno);
|
||||
return new MutableAnnotationVisitor(anno, classLoader);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -37,6 +37,8 @@ class MutableAnnotationVisitor implements AnnotationVisitor {
|
|||
|
||||
protected final MutableAnnotation mutableAnno;
|
||||
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
/**
|
||||
* Creates a new {@link MutableAnnotationVisitor} instance that will populate the the
|
||||
* attributes of the given <var>mutableAnno</var>. Accepts {@link Annotation} instead of
|
||||
|
@ -49,13 +51,14 @@ class MutableAnnotationVisitor implements AnnotationVisitor {
|
|||
*
|
||||
* @see MutableAnnotationUtils#createMutableAnnotation(Class)
|
||||
*/
|
||||
public MutableAnnotationVisitor(Annotation mutableAnno) {
|
||||
public MutableAnnotationVisitor(Annotation mutableAnno, ClassLoader classLoader) {
|
||||
Assert.isInstanceOf(MutableAnnotation.class, mutableAnno, "annotation must be mutable");
|
||||
this.mutableAnno = (MutableAnnotation) mutableAnno;
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitArray(final String attribName) {
|
||||
return new MutableAnnotationArrayVisitor(mutableAnno, attribName);
|
||||
return new MutableAnnotationArrayVisitor(mutableAnno, attribName, classLoader);
|
||||
}
|
||||
|
||||
public void visit(String attribName, Object attribValue) {
|
||||
|
@ -65,7 +68,7 @@ class MutableAnnotationVisitor implements AnnotationVisitor {
|
|||
// the attribute type is Class -> load it and set it.
|
||||
String fqClassName = ((Type) attribValue).getClassName();
|
||||
|
||||
Class<?> classVal = loadToolingSafeClass(fqClassName);
|
||||
Class<?> classVal = loadToolingSafeClass(fqClassName, classLoader);
|
||||
|
||||
if (classVal == null)
|
||||
return;
|
||||
|
@ -82,7 +85,7 @@ class MutableAnnotationVisitor implements AnnotationVisitor {
|
|||
public void visitEnum(String attribName, String enumTypeDescriptor, String strEnumValue) {
|
||||
String enumClassName = AsmUtils.convertTypeDescriptorToClassName(enumTypeDescriptor);
|
||||
|
||||
Class<? extends Enum> enumClass = loadToolingSafeClass(enumClassName);
|
||||
Class<? extends Enum> enumClass = loadToolingSafeClass(enumClassName, classLoader);
|
||||
|
||||
if (enumClass == null)
|
||||
return;
|
||||
|
@ -93,7 +96,7 @@ class MutableAnnotationVisitor implements AnnotationVisitor {
|
|||
|
||||
public AnnotationVisitor visitAnnotation(String attribName, String attribAnnoTypeDesc) {
|
||||
String annoTypeName = AsmUtils.convertTypeDescriptorToClassName(attribAnnoTypeDesc);
|
||||
Class<? extends Annotation> annoType = loadToolingSafeClass(annoTypeName);
|
||||
Class<? extends Annotation> annoType = loadToolingSafeClass(annoTypeName, classLoader);
|
||||
|
||||
if (annoType == null)
|
||||
return AsmUtils.EMPTY_VISITOR.visitAnnotation(attribName, attribAnnoTypeDesc);
|
||||
|
@ -107,7 +110,7 @@ class MutableAnnotationVisitor implements AnnotationVisitor {
|
|||
throw new RuntimeException(ex);
|
||||
}
|
||||
|
||||
return new MutableAnnotationVisitor(anno);
|
||||
return new MutableAnnotationVisitor(anno, classLoader);
|
||||
}
|
||||
|
||||
public void visitEnd() {
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
package org.springframework.config.java.support;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.config.java.ConfigurationModel;
|
||||
import org.springframework.config.java.internal.parsing.ConfigurationParser;
|
||||
|
||||
public abstract class AbstractConfigurationClassProcessor {
|
||||
|
||||
protected abstract BeanDefinitionRegistry getConfigurationBeanDefinitions(boolean includeAbstractBeanDefs);
|
||||
|
||||
protected abstract ConfigurationParser createConfigurationParser();
|
||||
|
||||
protected abstract void validateModel(ConfigurationModel configModel);
|
||||
|
||||
protected BeanDefinitionRegistry processConfigBeanDefinitions() {
|
||||
BeanDefinitionRegistry configBeanDefs = getConfigurationBeanDefinitions(false);
|
||||
|
||||
if(configBeanDefs.getBeanDefinitionCount() == 0)
|
||||
return configBeanDefs; // nothing to do - don't waste any more cycles
|
||||
|
||||
ConfigurationModel configModel = createConfigurationModelFor(configBeanDefs);
|
||||
|
||||
validateModel(configModel);
|
||||
|
||||
return renderModelAsBeanDefinitions(configModel);
|
||||
}
|
||||
|
||||
private ConfigurationModel createConfigurationModelFor(BeanDefinitionRegistry configBeanDefinitions) {
|
||||
|
||||
ConfigurationParser parser = createConfigurationParser();
|
||||
|
||||
for(String beanName : configBeanDefinitions.getBeanDefinitionNames()) {
|
||||
BeanDefinition beanDef = configBeanDefinitions.getBeanDefinition(beanName);
|
||||
String className = beanDef.getBeanClassName();
|
||||
|
||||
parser.parse(className, beanName);
|
||||
}
|
||||
|
||||
return parser.getConfigurationModel();
|
||||
}
|
||||
|
||||
private BeanDefinitionRegistry renderModelAsBeanDefinitions(ConfigurationModel configModel) {
|
||||
return new ConfigurationModelBeanDefinitionReader().loadBeanDefinitions(configModel);
|
||||
}
|
||||
|
||||
}
|
|
@ -16,11 +16,6 @@
|
|||
package org.springframework.config.java.support;
|
||||
|
||||
import static java.lang.String.*;
|
||||
import static org.springframework.config.java.Util.*;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
@ -28,19 +23,16 @@ import org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostPr
|
|||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReader;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.GenericBeanDefinition;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.support.SimpleBeanDefinitionRegistry;
|
||||
import org.springframework.config.java.BeanMethod;
|
||||
import org.springframework.config.java.Configuration;
|
||||
import org.springframework.config.java.ConfigurationClass;
|
||||
import org.springframework.config.java.ConfigurationModel;
|
||||
import org.springframework.config.java.FactoryMethod;
|
||||
import org.springframework.config.java.ModelMethod;
|
||||
import org.springframework.config.java.plugin.Extension;
|
||||
import org.springframework.config.java.plugin.ExtensionAnnotationBeanDefinitionRegistrar;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -57,29 +49,28 @@ class ConfigurationModelBeanDefinitionReader {
|
|||
|
||||
private static final Log log = LogFactory.getLog(ConfigurationModelBeanDefinitionReader.class);
|
||||
|
||||
private final DefaultListableBeanFactory beanFactory;
|
||||
private BeanDefinitionRegistry registry;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link ConfigurationModelBeanDefinitionReader} instance.
|
||||
*/
|
||||
public ConfigurationModelBeanDefinitionReader(DefaultListableBeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
public ConfigurationModelBeanDefinitionReader() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads {@code model}, registering bean definitions with {@link #beanFactory} based on
|
||||
* Reads {@code model}, registering bean definitions with {@link #registry} based on
|
||||
* its contents.
|
||||
*
|
||||
* @return number of bean definitions generated
|
||||
*/
|
||||
public int loadBeanDefinitions(ConfigurationModel model) {
|
||||
int initialBeanDefCount = beanFactory.getBeanDefinitionCount();
|
||||
|
||||
public BeanDefinitionRegistry loadBeanDefinitions(ConfigurationModel model) {
|
||||
registry = new SimpleBeanDefinitionRegistry();
|
||||
|
||||
for (ConfigurationClass configClass : model.getAllConfigurationClasses())
|
||||
loadBeanDefinitionsForConfigurationClass(configClass);
|
||||
|
||||
return beanFactory.getBeanDefinitionCount() - initialBeanDefCount;
|
||||
return registry;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -90,21 +81,23 @@ class ConfigurationModelBeanDefinitionReader {
|
|||
private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass) {
|
||||
doLoadBeanDefinitionForConfigurationClass(configClass);
|
||||
|
||||
for (ModelMethod method : configClass.getMethods())
|
||||
for (BeanMethod method : configClass.getMethods())
|
||||
loadBeanDefinitionsForModelMethod(method);
|
||||
|
||||
Annotation[] pluginAnnotations = configClass.getPluginAnnotations();
|
||||
Arrays.sort(pluginAnnotations, new PluginComparator());
|
||||
for (Annotation annotation : pluginAnnotations)
|
||||
loadBeanDefinitionsForExtensionAnnotation(annotation);
|
||||
// Annotation[] pluginAnnotations = configClass.getPluginAnnotations();
|
||||
// Arrays.sort(pluginAnnotations, new PluginComparator());
|
||||
// for (Annotation annotation : pluginAnnotations)
|
||||
// loadBeanDefinitionsForExtensionAnnotation(beanDefs, annotation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the {@link Configuration} class itself as a bean definition.
|
||||
* @param beanDefs
|
||||
*/
|
||||
private void doLoadBeanDefinitionForConfigurationClass(ConfigurationClass configClass) {
|
||||
Configuration metadata = configClass.getMetadata();
|
||||
|
||||
// TODO: think about implications with annotation-config
|
||||
if (metadata.checkRequired() == true) {
|
||||
RootBeanDefinition requiredAnnotationPostProcessor = new RootBeanDefinition();
|
||||
Class<?> beanClass = RequiredAnnotationBeanPostProcessor.class;
|
||||
|
@ -112,7 +105,7 @@ class ConfigurationModelBeanDefinitionReader {
|
|||
requiredAnnotationPostProcessor.setBeanClass(beanClass);
|
||||
requiredAnnotationPostProcessor
|
||||
.setResourceDescription("ensures @Required methods have been invoked");
|
||||
beanFactory.registerBeanDefinition(beanName, requiredAnnotationPostProcessor);
|
||||
registry.registerBeanDefinition(beanName, requiredAnnotationPostProcessor);
|
||||
}
|
||||
|
||||
GenericBeanDefinition configBeanDef = new GenericBeanDefinition();
|
||||
|
@ -122,13 +115,13 @@ class ConfigurationModelBeanDefinitionReader {
|
|||
|
||||
// consider the case where it's already been defined (probably in XML)
|
||||
// and potentially has PropertyValues and ConstructorArgs)
|
||||
if (beanFactory.containsBeanDefinition(configBeanName)) {
|
||||
if (registry.containsBeanDefinition(configBeanName)) {
|
||||
if (log.isInfoEnabled())
|
||||
log.info(format(
|
||||
"Copying property and constructor arg values from existing bean definition for "
|
||||
+ "@Configuration class %s to new bean definition", configBeanName));
|
||||
AbstractBeanDefinition existing = (AbstractBeanDefinition) beanFactory
|
||||
.getBeanDefinition(configBeanName);
|
||||
AbstractBeanDefinition existing =
|
||||
(AbstractBeanDefinition) registry.getBeanDefinition(configBeanName);
|
||||
configBeanDef.setPropertyValues(existing.getPropertyValues());
|
||||
configBeanDef.setConstructorArgumentValues(existing.getConstructorArgumentValues());
|
||||
configBeanDef.setResource(existing.getResource());
|
||||
|
@ -137,52 +130,52 @@ class ConfigurationModelBeanDefinitionReader {
|
|||
if (log.isInfoEnabled())
|
||||
log.info(format("Registering bean definition for @Configuration class %s", configBeanName));
|
||||
|
||||
beanFactory.registerBeanDefinition(configBeanName, configBeanDef);
|
||||
registry.registerBeanDefinition(configBeanName, configBeanDef);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads a particular {@link ModelMethod}, registering bean definitions with
|
||||
* {@link #beanFactory} based on its contents.
|
||||
* Reads a particular {@link BeanMethod}, registering bean definitions with
|
||||
* {@link #registry} based on its contents.
|
||||
*
|
||||
* @see FactoryMethod
|
||||
*/
|
||||
private void loadBeanDefinitionsForModelMethod(ModelMethod method) {
|
||||
method.getRegistrar().register(method, beanFactory);
|
||||
private void loadBeanDefinitionsForModelMethod(BeanMethod method) {
|
||||
method.getRegistrar().register(method, registry);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void loadBeanDefinitionsForExtensionAnnotation(Annotation anno) {
|
||||
// ExtensionAnnotationUtils.getRegistrarFor(anno).registerBeanDefinitionsWith(beanFactory);
|
||||
// there is a fixed assumption that in order for this annotation to have
|
||||
// been registered in the first place, it must be meta-annotated with @Plugin
|
||||
// assert this as an invariant now
|
||||
Class<?> annoClass = anno.getClass();
|
||||
Extension extensionAnno = AnnotationUtils.findAnnotation(annoClass, Extension.class);
|
||||
Assert.isTrue(extensionAnno != null, format("%s annotation is not annotated as a @%s", annoClass,
|
||||
Extension.class.getSimpleName()));
|
||||
|
||||
Class<? extends ExtensionAnnotationBeanDefinitionRegistrar> extHandlerClass = extensionAnno.handler();
|
||||
|
||||
ExtensionAnnotationBeanDefinitionRegistrar extHandler = getInstance(extHandlerClass);
|
||||
extHandler.handle(anno, beanFactory);
|
||||
}
|
||||
|
||||
private static class PluginComparator implements Comparator<Annotation> {
|
||||
public int compare(Annotation a1, Annotation a2) {
|
||||
Integer i1 = getOrder(a1);
|
||||
Integer i2 = getOrder(a2);
|
||||
return i1.compareTo(i2);
|
||||
}
|
||||
|
||||
private Integer getOrder(Annotation a) {
|
||||
Extension plugin = a.annotationType().getAnnotation(Extension.class);
|
||||
if (plugin == null)
|
||||
throw new IllegalArgumentException("annotation was not annotated with @Plugin: "
|
||||
+ a.annotationType());
|
||||
return plugin.order();
|
||||
}
|
||||
}
|
||||
// @SuppressWarnings("unchecked")
|
||||
// private void loadBeanDefinitionsForExtensionAnnotation(Map<String, BeanDefinition> beanDefs, Annotation anno) {
|
||||
// // ExtensionAnnotationUtils.getRegistrarFor(anno).registerBeanDefinitionsWith(beanFactory);
|
||||
// // there is a fixed assumption that in order for this annotation to have
|
||||
// // been registered in the first place, it must be meta-annotated with @Plugin
|
||||
// // assert this as an invariant now
|
||||
// Class<?> annoClass = anno.getClass();
|
||||
// Extension extensionAnno = AnnotationUtils.findAnnotation(annoClass, Extension.class);
|
||||
// Assert.isTrue(extensionAnno != null, format("%s annotation is not annotated as a @%s", annoClass,
|
||||
// Extension.class.getSimpleName()));
|
||||
//
|
||||
// Class<? extends ExtensionAnnotationBeanDefinitionRegistrar> extHandlerClass = extensionAnno.handler();
|
||||
//
|
||||
// ExtensionAnnotationBeanDefinitionRegistrar extHandler = getInstance(extHandlerClass);
|
||||
// extHandler.handle(anno, beanFactory);
|
||||
// }
|
||||
//
|
||||
// private static class PluginComparator implements Comparator<Annotation> {
|
||||
// public int compare(Annotation a1, Annotation a2) {
|
||||
// Integer i1 = getOrder(a1);
|
||||
// Integer i2 = getOrder(a2);
|
||||
// return i1.compareTo(i2);
|
||||
// }
|
||||
//
|
||||
// private Integer getOrder(Annotation a) {
|
||||
// Extension plugin = a.annotationType().getAnnotation(Extension.class);
|
||||
// if (plugin == null)
|
||||
// throw new IllegalArgumentException("annotation was not annotated with @Plugin: "
|
||||
// + a.annotationType());
|
||||
// return plugin.order();
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -15,10 +15,10 @@
|
|||
*/
|
||||
package org.springframework.config.java.support;
|
||||
|
||||
import static org.springframework.config.java.Util.*;
|
||||
import static java.lang.String.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
@ -27,6 +27,7 @@ import org.springframework.beans.factory.config.BeanDefinition;
|
|||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.config.java.Configuration;
|
||||
import org.springframework.config.java.ConfigurationModel;
|
||||
|
@ -35,17 +36,21 @@ import org.springframework.config.java.UsageError;
|
|||
import org.springframework.config.java.internal.enhancement.ConfigurationEnhancer;
|
||||
import org.springframework.config.java.internal.parsing.ConfigurationParser;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.core.type.classreading.MetadataReader;
|
||||
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
/**
|
||||
* {@link BeanFactoryPostProcessor} used for bootstrapping {@link Configuration
|
||||
* @Configuration} beans from Spring XML files.
|
||||
* @Configuration} beans. Usually used in conjunction with Spring XML files.
|
||||
*/
|
||||
public class ConfigurationPostProcessor implements Ordered, BeanFactoryPostProcessor {
|
||||
public class ConfigurationPostProcessor extends AbstractConfigurationClassProcessor
|
||||
implements Ordered, BeanFactoryPostProcessor {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(ConfigurationPostProcessor.class);
|
||||
private DefaultListableBeanFactory beanFactory;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -55,58 +60,102 @@ public class ConfigurationPostProcessor implements Ordered, BeanFactoryPostProce
|
|||
public int getOrder() {
|
||||
return Ordered.HIGHEST_PRECEDENCE;
|
||||
}
|
||||
|
||||
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory clBeanFactory) throws BeansException {
|
||||
Assert.isInstanceOf(DefaultListableBeanFactory.class, clBeanFactory);
|
||||
beanFactory = (DefaultListableBeanFactory) clBeanFactory;
|
||||
|
||||
BeanDefinitionRegistry factoryBeanDefs = processConfigBeanDefinitions();
|
||||
|
||||
for(String beanName : factoryBeanDefs.getBeanDefinitionNames())
|
||||
beanFactory.registerBeanDefinition(beanName, factoryBeanDefs.getBeanDefinition(beanName));
|
||||
|
||||
enhanceConfigurationClasses();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConfigurationParser createConfigurationParser() {
|
||||
return new ConfigurationParser(beanFactory.getBeanClassLoader());
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches <var>beanFactory</var> for any {@link Configuration} classes in order to
|
||||
* parse and enhance them. Also registers any {@link BeanPostProcessor} objects
|
||||
* necessary to fulfill JavaConfig requirements.
|
||||
* @return map of all non-abstract {@link BeanDefinition}s in the enclosing {@link #beanFactory}
|
||||
*/
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory clBeanFactory) throws BeansException {
|
||||
if (!(clBeanFactory instanceof DefaultListableBeanFactory))
|
||||
throw new IllegalStateException("beanFactory must be of type "
|
||||
+ DefaultListableBeanFactory.class.getSimpleName());
|
||||
|
||||
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) clBeanFactory;
|
||||
|
||||
ConfigurationModel model = new ConfigurationModel();
|
||||
|
||||
parseAnyConfigurationClasses(beanFactory, model);
|
||||
|
||||
enhanceAnyConfigurationClasses(beanFactory, model);
|
||||
}
|
||||
|
||||
private void parseAnyConfigurationClasses(DefaultListableBeanFactory beanFactory, ConfigurationModel model) {
|
||||
|
||||
// linked map is important for maintaining predictable ordering of configuration
|
||||
// classes.
|
||||
// this is important in bean / value override situations.
|
||||
LinkedHashMap<String, ClassPathResource> configClassResources = new LinkedHashMap<String, ClassPathResource>();
|
||||
|
||||
@Override
|
||||
protected BeanDefinitionRegistry getConfigurationBeanDefinitions(boolean includeAbstractBeanDefs) {
|
||||
|
||||
BeanDefinitionRegistry configBeanDefs = new DefaultListableBeanFactory();
|
||||
|
||||
for (String beanName : beanFactory.getBeanDefinitionNames()) {
|
||||
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
|
||||
if (beanDef.isAbstract())
|
||||
|
||||
if (beanDef.isAbstract() && !includeAbstractBeanDefs)
|
||||
continue;
|
||||
|
||||
if (isConfigClass(beanDef)) {
|
||||
String path = ClassUtils.convertClassNameToResourcePath(beanDef.getBeanClassName());
|
||||
configClassResources.put(beanName, new ClassPathResource(path));
|
||||
}
|
||||
if (isConfigClass(beanDef))
|
||||
configBeanDefs.registerBeanDefinition(beanName, beanDef);
|
||||
}
|
||||
|
||||
ConfigurationModelBeanDefinitionReader modelBeanDefinitionReader = new ConfigurationModelBeanDefinitionReader(
|
||||
beanFactory);
|
||||
ConfigurationParser parser = new ConfigurationParser(model);
|
||||
|
||||
for (String id : configClassResources.keySet())
|
||||
parser.parse(configClassResources.get(id), id);
|
||||
|
||||
|
||||
return configBeanDefs;
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Searches <var>beanFactory</var> for any {@link Configuration} classes in order to
|
||||
// * parse and enhance them. Also registers any {@link BeanPostProcessor} objects
|
||||
// * necessary to fulfill JavaConfig requirements.
|
||||
// */
|
||||
// public void postProcessBeanFactory2(ConfigurableListableBeanFactory clBeanFactory) throws BeansException {
|
||||
// Assert.isInstanceOf(DefaultListableBeanFactory.class, clBeanFactory);
|
||||
// DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) clBeanFactory;
|
||||
//
|
||||
// ConfigurationModel model = parseAnyConfigurationClasses(beanFactory);
|
||||
//
|
||||
// enhanceAnyConfigurationClasses(beanFactory, model);
|
||||
// }
|
||||
//
|
||||
// private ConfigurationModel parseAnyConfigurationClasses(DefaultListableBeanFactory beanFactory) {
|
||||
//
|
||||
// // linked map is important for maintaining predictable ordering of configuration
|
||||
// // classes.
|
||||
// // this is important in bean / value override situations.
|
||||
// LinkedHashMap<String, ClassPathResource> configClassResources = new LinkedHashMap<String, ClassPathResource>();
|
||||
//
|
||||
// for (String beanName : beanFactory.getBeanDefinitionNames()) {
|
||||
// BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
|
||||
// if (beanDef.isAbstract())
|
||||
// continue;
|
||||
//
|
||||
// if (isConfigClass(beanDef)) {
|
||||
// String path = ClassUtils.convertClassNameToResourcePath(beanDef.getBeanClassName());
|
||||
// configClassResources.put(beanName, new ClassPathResource(path));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// ConfigurationModelBeanDefinitionReader modelBeanDefinitionReader = new ConfigurationModelBeanDefinitionReader(
|
||||
// beanFactory);
|
||||
// ConfigurationParser parser = new ConfigurationParser(ClassUtils.getDefaultClassLoader());
|
||||
//
|
||||
// for (String id : configClassResources.keySet())
|
||||
// parser.parse(configClassResources.get(id).getPath(), id);
|
||||
//
|
||||
// ConfigurationModel model = parser.getConfigurationModel();
|
||||
//
|
||||
// ArrayList<UsageError> errors = new ArrayList<UsageError>();
|
||||
// model.validate(errors);
|
||||
// if (errors.size() > 0)
|
||||
// throw new MalformedConfigurationException(errors.toArray(new UsageError[] {}));
|
||||
//
|
||||
// modelBeanDefinitionReader.loadBeanDefinitions(model);
|
||||
//
|
||||
// return model;
|
||||
// }
|
||||
|
||||
@Override
|
||||
protected void validateModel(ConfigurationModel model) {
|
||||
ArrayList<UsageError> errors = new ArrayList<UsageError>();
|
||||
model.validate(errors);
|
||||
if (errors.size() > 0)
|
||||
throw new MalformedConfigurationException(errors.toArray(new UsageError[] {}));
|
||||
|
||||
modelBeanDefinitionReader.loadBeanDefinitions(model);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -118,55 +167,43 @@ public class ConfigurationPostProcessor implements Ordered, BeanFactoryPostProce
|
|||
* @see ConfigurationEnhancer
|
||||
* @see BeanFactoryPostProcessor
|
||||
*/
|
||||
private void enhanceAnyConfigurationClasses(DefaultListableBeanFactory beanFactory,
|
||||
ConfigurationModel model) {
|
||||
private void enhanceConfigurationClasses() {
|
||||
|
||||
ConfigurationEnhancer enhancer = new ConfigurationEnhancer(beanFactory, model);
|
||||
|
||||
int configClassesEnhanced = 0;
|
||||
|
||||
for (String beanName : beanFactory.getBeanDefinitionNames()) {
|
||||
ConfigurationEnhancer enhancer = new ConfigurationEnhancer(beanFactory);
|
||||
|
||||
BeanDefinitionRegistry configBeanDefs = getConfigurationBeanDefinitions(true);
|
||||
|
||||
for(String beanName : configBeanDefs.getBeanDefinitionNames()) {
|
||||
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
|
||||
|
||||
if (!isConfigClass(beanDef))
|
||||
continue;
|
||||
|
||||
String configClassName = beanDef.getBeanClassName();
|
||||
|
||||
String enhancedClassName = enhancer.enhance(configClassName);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger
|
||||
.debug(String
|
||||
.format(
|
||||
"Replacing bean definition '%s' existing class name '%s' with enhanced class name '%s'",
|
||||
beanName, configClassName, enhancedClassName));
|
||||
logger.debug(format("Replacing bean definition '%s' existing class name '%s' with enhanced class name '%s'",
|
||||
beanName, configClassName, enhancedClassName));
|
||||
|
||||
beanDef.setBeanClassName(enhancedClassName);
|
||||
|
||||
configClassesEnhanced++;
|
||||
}
|
||||
|
||||
if (configClassesEnhanced == 0)
|
||||
logger.warn("Found no @Configuration class BeanDefinitions within " + beanFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the class for <var>beanDef</var> is a {@link Configuration}
|
||||
* -annotated class. Returns false if <var>beanDef</var> has no class specified.
|
||||
* <p>
|
||||
* Note: the classloading used within should not be problematic or interfere with
|
||||
* tooling in any way. BeanFactoryPostProcessing happens only during actual runtime
|
||||
* processing via {@link JavaConfigApplicationContext} or via XML using
|
||||
* {@link ConfigurationPostProcessor}. In any case, tooling (Spring IDE) will hook in at
|
||||
* a lower level than this class and thus never encounter this classloading. Should this
|
||||
* become problematic, it would not be too difficult to replace the following with ASM
|
||||
* logic that traverses the class hierarchy in order to find whether the class is
|
||||
* directly or indirectly annotated with {@link Configuration}.
|
||||
*/
|
||||
private static boolean isConfigClass(BeanDefinition beanDef) {
|
||||
|
||||
String className = beanDef.getBeanClassName();
|
||||
return className != null && loadRequiredClass(className).isAnnotationPresent(Configuration.class);
|
||||
|
||||
if(className == null)
|
||||
return false;
|
||||
|
||||
try {
|
||||
MetadataReader metadataReader = new SimpleMetadataReaderFactory().getMetadataReader(className);
|
||||
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
|
||||
return annotationMetadata.hasAnnotation(Configuration.class.getName());
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,4 +11,5 @@
|
|||
|
||||
<bean class="test.basic.AutowiredConfigurationTests$AutowiredConfig"/>
|
||||
<bean class="test.basic.AutowiredConfigurationTests$ColorConfig"/>
|
||||
|
||||
</beans>
|
||||
|
|
|
@ -10,6 +10,7 @@ import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostP
|
|||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.config.java.Configuration;
|
||||
import org.springframework.config.java.MalformedConfigurationException;
|
||||
import org.springframework.config.java.Scopes;
|
||||
import org.springframework.config.java.ext.Bean;
|
||||
import org.springframework.config.java.support.ConfigurationPostProcessor;
|
||||
|
@ -100,4 +101,63 @@ public class BasicTests {
|
|||
return new TestBean("bar");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void legalBeanOverriding() {
|
||||
{
|
||||
BeanFactory factory = initBeanFactory(ConfigWithBeanThatAllowsOverriding.class, ConfigWithBeanOverride.class);
|
||||
|
||||
TestBean testBean = factory.getBean("testBean", TestBean.class);
|
||||
|
||||
assertThat(testBean.getName(), equalTo("overridden"));
|
||||
}
|
||||
|
||||
// now try it the other way around - order matters!
|
||||
|
||||
{
|
||||
BeanFactory factory = initBeanFactory(ConfigWithBeanOverride.class, ConfigWithBeanThatAllowsOverriding.class);
|
||||
|
||||
TestBean testBean = factory.getBean("testBean", TestBean.class);
|
||||
|
||||
assertThat(testBean.getName(), equalTo("original"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test(expected=MalformedConfigurationException.class)
|
||||
public void illegalBeanOverriding() {
|
||||
initBeanFactory(ConfigWithBeanThatDisallowsOverriding.class, ConfigWithBeanOverride.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void illegalBeanOverriding2() {
|
||||
// should be okay when the class that disallows overriding is the one doing the overriding
|
||||
initBeanFactory(ConfigWithBeanOverride.class, ConfigWithBeanThatDisallowsOverriding.class);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ConfigWithBeanThatAllowsOverriding {
|
||||
@Bean
|
||||
public TestBean testBean() {
|
||||
return new TestBean("original");
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ConfigWithBeanThatDisallowsOverriding {
|
||||
@Bean(allowOverriding = false)
|
||||
public TestBean testBean() {
|
||||
return new TestBean("original");
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ConfigWithBeanOverride {
|
||||
@Bean
|
||||
public TestBean testBean() {
|
||||
return new TestBean("overridden");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -10,4 +10,8 @@
|
|||
<bean class="org.springframework.config.java.support.ConfigurationPostProcessor"/>
|
||||
|
||||
<bean class="test.basic.AutowiredConfigurationTests$ValueConfig"/>
|
||||
|
||||
<!--
|
||||
<context:component-scan base-package="test.basic.value"/>
|
||||
-->
|
||||
</beans>
|
||||
|
|
Loading…
Reference in New Issue