提交 8a5c2a6a 编写于 作者: C Chris Beams

+ Added tests for custom @Bean naming and aliasing

+ Eliminated BeanDefinitionRegistrar and BeanRegistrar types
+ Simplified ConfigurationEnhancer logic
+ Updated JavaDoc for ConfigurationModel and related classes
+ Updated JavaDoc for all ASM visitors
上级 2bbc4e48
/*
* 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.config.java.support;
import java.lang.reflect.Method;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
/**
* Registers bean definition(s) for a particular method, usually based on its annotation
* metadata.
*
* <h3>Constraints</h3> Implementations must have only a default constructor, or explicitly
* declare a no-arg constructor.
*
* @see BeanMethod
*
* @author Chris Beams
*/
// TODO: SJC-242 document FactoryMethodHandler
// TODO: SJC-242 odd that the api here uses both ModelMethod and java.lang.reflect.Member
// TODO: SJC-242 document that there must be a no-arg ctor
interface BeanDefinitionRegistrar {
/**
* Determines whether this registrar is capable of handling <var>method</var>.
*/
// TODO: rename to supports() in alignment with Validator nomenclature
boolean accepts(Method method);
/**
* Registers any bean definitions for <var>method</var> with <var>registry</var>.
*/
void register(BeanMethod method, BeanDefinitionRegistry registry);
}
......@@ -38,6 +38,10 @@ import org.springframework.util.Assert;
* Represents a {@link Configuration} class method marked with the {@link Bean} annotation.
*
* @author Chris Beams
* @see ConfigurationClass
* @see ConfigurationModel
* @see ConfigurationParser
* @see ConfigurationModelBeanDefinitionReader
*/
final class BeanMethod implements BeanMetadataElement {
......
......@@ -24,16 +24,12 @@ import net.sf.cglib.proxy.MethodProxy;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.config.java.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert;
/**
......@@ -41,17 +37,16 @@ import org.springframework.util.Assert;
* handling of bean semantics such as scoping and AOP proxying.
*
* @author Chris Beams
* @since 3.0
* @see Bean
* @see BeanRegistrar
*/
class BeanMethodInterceptor implements BeanFactoryAware, MethodInterceptor {
protected final Log log = LogFactory.getLog(this.getClass());
protected DefaultListableBeanFactory beanFactory;
class BeanMethodInterceptor implements MethodInterceptor {
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
Assert.isInstanceOf(DefaultListableBeanFactory.class, beanFactory);
this.beanFactory = (DefaultListableBeanFactory) beanFactory;
private static final Log log = LogFactory.getLog(BeanMethodInterceptor.class);
private final DefaultListableBeanFactory beanFactory;
public BeanMethodInterceptor(DefaultListableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
/**
......@@ -60,20 +55,18 @@ class BeanMethodInterceptor implements BeanFactoryAware, MethodInterceptor {
*/
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// determine the name of the bean
String beanName;
// by default the bean name is the name of the @Bean-annotated method
String beanName = method.getName();
// check to see if the user has explicitly set the bean name
Bean bean = method.getAnnotation(Bean.class);
if(bean != null && bean.name().length > 1)
if(bean != null && bean.name().length > 0)
beanName = bean.name()[0];
// if not, simply return the name of the method as the bean name
else
beanName = method.getName();
// determine whether this bean is a scoped-proxy
Scope scope = AnnotationUtils.findAnnotation(method, Scope.class);
boolean isScopedProxy = (scope != null && scope.proxyMode() != ScopedProxyMode.NO);
String scopedBeanName = BeanRegistrar.resolveHiddenScopedProxyBeanName(beanName);
String scopedBeanName = ConfigurationModelBeanDefinitionReader.resolveHiddenScopedProxyBeanName(beanName);
if (isScopedProxy && beanFactory.isCurrentlyInCreation(scopedBeanName))
beanName = scopedBeanName;
......
/*
* 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.config.java.support;
import static java.lang.String.*;
import static org.springframework.util.StringUtils.*;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.framework.autoproxy.AutoProxyUtils;
import org.springframework.aop.scope.ScopedProxyFactoryBean;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
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.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert;
class BeanRegistrar implements BeanDefinitionRegistrar {
private static final Log logger = LogFactory.getLog(BeanRegistrar.class);
/** Prefix used when registering the target object for a scoped proxy. */
private static final String TARGET_NAME_PREFIX = "scopedTarget.";
/**
* Ensures that <var>member</var> is a method and is annotated (directly or indirectly)
* with {@link Bean @Bean}.
*/
public boolean accepts(Method method) {
return AnnotationUtils.findAnnotation(method, Bean.class) != null;
}
public void register(BeanMethod method, BeanDefinitionRegistry registry) {
RootBeanDefinition beanDef = new ConfigurationClassBeanDefinition();
ConfigurationClass configClass = method.getDeclaringClass();
beanDef.setFactoryBeanName(configClass.getBeanName());
beanDef.setFactoryMethodName(method.getName());
Bean bean = method.getRequiredAnnotation(Bean.class);
// TODO: prune defaults
//Configuration defaults = configClass.getMetadata();
// consider scoping
Scope scope = method.getAnnotation(Scope.class);
if(scope != null)
beanDef.setScope(scope.value());
// TODO: prune autowiring
// // consider autowiring
// if (bean.autowire() != AnnotationUtils.getDefaultValue(Bean.class, "autowire"))
// beanDef.setAutowireMode(bean.autowire().value());
// else if (defaults.defaultAutowire() != AnnotationUtils.getDefaultValue(Configuration.class,
// "defaultAutowire"))
// beanDef.setAutowireMode(defaults.defaultAutowire().value());
// consider name and any aliases
ArrayList<String> names = new ArrayList<String>(Arrays.asList(bean.name()));
String beanName = (names.size() > 0) ? names.remove(0) : method.getName();
for (String alias : bean.name())
registry.registerAlias(beanName, alias);
// has this already been overriden (i.e.: via XML)?
if (containsBeanDefinitionIncludingAncestry(beanName, registry)) {
BeanDefinition existingBeanDef = getBeanDefinitionIncludingAncestry(beanName, registry);
// is the existing bean definition one that was created by JavaConfig?
if (!(existingBeanDef instanceof ConfigurationClassBeanDefinition)) {
// no -> then it's an external override, probably XML
// TODO: Prune this
// // ensure that overriding is ok
// if (bean.allowOverriding() == false) {
// UsageError error = configClass.new IllegalBeanOverrideError(null, method);
// throw new MalformedConfigurationException(error);
// }
// overriding is legal, return immediately
logger.info(format("Skipping loading bean definition for %s: a definition for bean "
+ "'%s' already exists. This is likely due to an override in XML.", method, beanName));
return;
}
}
// TODO: re-enable for Lazy support
// // is this bean marked as primary for disambiguation?
// if (bean.primary() == Primary.TRUE)
// beanDef.setPrimary(true);
//
// // is this bean lazily instantiated?
// if ((bean.lazy() == Lazy.TRUE)
// || ((bean.lazy() == Lazy.UNSPECIFIED) && (defaults.defaultLazy() == Lazy.TRUE)))
// beanDef.setLazyInit(true);
// does this bean have a custom init-method specified?
String initMethodName = bean.initMethod();
if (hasText(initMethodName))
beanDef.setInitMethodName(initMethodName);
// does this bean have a custom destroy-method specified?
String destroyMethodName = bean.destroyMethod();
if (hasText(destroyMethodName))
beanDef.setDestroyMethodName(destroyMethodName);
// is this method annotated with @Scope(scopedProxy=...)?
if (scope != null && scope.proxyMode() != ScopedProxyMode.NO) {
RootBeanDefinition targetDef = beanDef;
// Create a scoped proxy definition for the original bean name,
// "hiding" the target bean in an internal target definition.
String targetBeanName = resolveHiddenScopedProxyBeanName(beanName);
RootBeanDefinition scopedProxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class);
scopedProxyDefinition.getPropertyValues().addPropertyValue("targetBeanName", targetBeanName);
if (scope.proxyMode() == ScopedProxyMode.TARGET_CLASS)
targetDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
// ScopedFactoryBean's "proxyTargetClass" default is TRUE, so we
// don't need to set it explicitly here.
else
scopedProxyDefinition.getPropertyValues().addPropertyValue("proxyTargetClass", Boolean.FALSE);
// The target bean should be ignored in favor of the scoped proxy.
targetDef.setAutowireCandidate(false);
// Register the target bean as separate bean in the factory
registry.registerBeanDefinition(targetBeanName, targetDef);
// replace the original bean definition with the target one
beanDef = scopedProxyDefinition;
}
// TODO: re-enable for @Meta support
// does this bean method have any @Meta annotations?
// for (Meta meta : bean.meta())
// beanDef.addMetadataAttribute(new BeanMetadataAttribute(meta.key(),
// meta.value()));
if (bean.dependsOn().length > 0)
beanDef.setDependsOn(bean.dependsOn());
logger.info(format("Registering bean definition for @Bean method %s.%s()",
configClass.getName(), beanName));
registry.registerBeanDefinition(beanName, beanDef);
}
private boolean containsBeanDefinitionIncludingAncestry(String beanName, BeanDefinitionRegistry registry) {
try {
getBeanDefinitionIncludingAncestry(beanName, registry);
return true;
} catch (NoSuchBeanDefinitionException ex) {
return false;
}
}
private BeanDefinition getBeanDefinitionIncludingAncestry(String beanName, BeanDefinitionRegistry registry) {
if(!(registry instanceof ConfigurableListableBeanFactory)) {
return registry.getBeanDefinition(beanName);
}
ConfigurableListableBeanFactory clbf = (ConfigurableListableBeanFactory) registry;
do {
if (clbf.containsBeanDefinition(beanName))
return registry.getBeanDefinition(beanName);
BeanFactory parent = clbf.getParentBeanFactory();
if (parent == null) {
clbf = null;
} else if (parent instanceof ConfigurableListableBeanFactory) {
clbf = (ConfigurableListableBeanFactory) parent;
// TODO: re-enable
// } else if (parent instanceof AbstractApplicationContext) {
// clbf = ((AbstractApplicationContext) parent).getBeanFactory();
} else {
throw new IllegalStateException("unknown parent type: " + parent.getClass().getName());
}
} while (clbf != null);
throw new NoSuchBeanDefinitionException(format("No bean definition matching name '%s' "
+ "could be found in %s or its ancestry", beanName, registry));
}
/**
* Return the <i>hidden</i> name based on a scoped proxy bean name.
*
* @param originalBeanName the scope proxy bean name as declared in the
* Configuration-annotated class
*
* @return the internally-used <i>hidden</i> bean name
*/
public static String resolveHiddenScopedProxyBeanName(String originalBeanName) {
Assert.hasText(originalBeanName);
return TARGET_NAME_PREFIX.concat(originalBeanName);
}
}
/**
* {@link RootBeanDefinition} marker subclass used to signify that a bean definition created
* by JavaConfig as opposed to any other configuration source. Used in bean overriding cases
* where it's necessary to determine whether the bean definition was created externally
* (e.g. via XML).
*/
@SuppressWarnings("serial")
class ConfigurationClassBeanDefinition extends RootBeanDefinition {
}
......@@ -29,7 +29,7 @@ import org.springframework.util.Assert;
/**
* Abstract representation of a user-defined {@link Configuration @Configuration} class.
* Represents a user-defined {@link Configuration @Configuration} class.
* Includes a set of {@link Bean} methods, including all such methods defined in the
* ancestry of the class, in a 'flattened-out' manner. Note that each {@link BeanMethod}
* representation contains source information about where it was originally detected
......
......@@ -27,15 +27,16 @@ import org.springframework.asm.AnnotationVisitor;
import org.springframework.asm.ClassAdapter;
import org.springframework.asm.Label;
import org.springframework.asm.MethodAdapter;
import org.springframework.asm.MethodVisitor;
import org.springframework.asm.Opcodes;
import org.springframework.config.java.Bean;
import org.springframework.config.java.Configuration;
/**
* Visits a single method declared in a given {@link Configuration} class. Determines
* whether the method is a {@link Bean} method and if so, adds it to the
* {@link ConfigurationClass}.
* ASM {@link MethodVisitor} that visits a single method declared in a given
* {@link Configuration} class. Determines whether the method is a {@link Bean}
* method and if so, adds it to the {@link ConfigurationClass}.
*
* @author Chris Beams
*/
......
......@@ -25,6 +25,7 @@ import java.util.Stack;
import org.springframework.asm.AnnotationVisitor;
import org.springframework.asm.ClassAdapter;
import org.springframework.asm.ClassReader;
import org.springframework.asm.ClassVisitor;
import org.springframework.asm.MethodVisitor;
import org.springframework.asm.Opcodes;
import org.springframework.beans.factory.parsing.Location;
......@@ -36,10 +37,12 @@ import org.springframework.core.io.ClassPathResource;
/**
* Visits a {@link Configuration} class, populating a {@link ConfigurationClass} instance
* with information gleaned along the way.
* ASM {@link ClassVisitor} that visits a {@link Configuration} class, populating a
* {@link ConfigurationClass} instance with information gleaned along the way.
*
* @author Chris Beams
* @see ConfigurationParser
* @see ConfigurationClass
*/
class ConfigurationClassVisitor extends ClassAdapter {
......
......@@ -17,29 +17,27 @@ package org.springframework.config.java.support;
import static java.lang.String.*;
import static org.springframework.config.java.support.Util.*;
import static org.springframework.util.Assert.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashSet;
import net.sf.cglib.core.DefaultGeneratorStrategy;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import net.sf.cglib.proxy.NoOp;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.asm.ClassAdapter;
import org.springframework.asm.ClassReader;
import org.springframework.asm.ClassWriter;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.config.java.Bean;
import org.springframework.config.java.Configuration;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert;
/**
......@@ -54,59 +52,30 @@ class ConfigurationEnhancer {
private static final Log log = LogFactory.getLog(ConfigurationEnhancer.class);
private final ArrayList<Class<? extends Callback>> callbackTypes = new ArrayList<Class<? extends Callback>>();
private final LinkedHashSet<BeanDefinitionRegistrar> registrars = new LinkedHashSet<BeanDefinitionRegistrar>();
private final ArrayList<Callback> callbackInstances = new ArrayList<Callback>();
private final CallbackFilter callbackFilter = new CallbackFilter() {
public int accept(Method candidateMethod) {
Iterator<BeanDefinitionRegistrar> iter = registrars.iterator();
for (int i = 0; iter.hasNext(); i++)
if (iter.next().accepts(candidateMethod))
return i;
throw new IllegalStateException(
format("No registrar is capable of handling method [%s]. "
+ "Perhaps you forgot to add a catch-all registrar?",
candidateMethod.getName()));
}
};
private final ArrayList<Class<? extends Callback>> callbackTypes = new ArrayList<Class<? extends Callback>>();
private final CallbackFilter callbackFilter;
/**
* Creates a new {@link ConfigurationEnhancer} instance.
*/
public ConfigurationEnhancer(DefaultListableBeanFactory beanFactory) {
notNull(beanFactory, "beanFactory must be non-null");
registrars.add(new BeanRegistrar());
BeanMethodInterceptor beanMethodInterceptor = new BeanMethodInterceptor();
beanMethodInterceptor.setBeanFactory(beanFactory);
callbackInstances.add(beanMethodInterceptor);
// add no-op default registrar and method interceptor
registrars.add(new BeanDefinitionRegistrar() {
public boolean accepts(Method method) {
return true;
}
Assert.notNull(beanFactory, "beanFactory must be non-null");
public void register(BeanMethod method, BeanDefinitionRegistry registry) {
// no-op
}
});
callbackInstances.add(new MethodInterceptor() {
callbackInstances.add(new BeanMethodInterceptor(beanFactory));
callbackInstances.add(NoOp.INSTANCE);
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
return null;
}
});
for (Callback callback : callbackInstances)
callbackTypes.add(callback.getClass());
// set up the callback filter to return the index of the BeanMethodInterceptor when
// handling a @Bean-annotated method; otherwise, return index of the NoOp callback.
callbackFilter = new CallbackFilter() {
public int accept(Method candidateMethod) {
return (AnnotationUtils.findAnnotation(candidateMethod, Bean.class) != null) ? 0 : 1;
}
};
}
......
......@@ -19,26 +19,22 @@ import static java.lang.String.*;
import java.util.ArrayList;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.parsing.ProblemReporter;
import org.springframework.config.java.Configuration;
/**
* An abstract representation of a set of user-provided "Configuration classes", usually but
* not necessarily annotated with {@link Configuration @Configuration}. The model is
* populated with a
* {@link org.springframework.config.java.support.ConfigurationParser}
* implementation which may be reflection-based or ASM-based. Once a model has been
* populated, it can then be rendered out to a set of BeanDefinitions. The model provides an
* important layer of indirection between the complexity of parsing a set of classes and the
* complexity of representing the contents of those classes as BeanDefinitions.
*
* <p>
* Interface follows the builder pattern for method chaining.
* </p>
* Represents the set of all user-defined {@link Configuration} classes. Once this model
* is populated using a {@link ConfigurationParser}, it can be rendered out to a set of
* {@link BeanDefinition} objects. This model provides an important layer of indirection
* between the complexity of parsing a set of classes and the complexity of representing
* the contents of those classes as BeanDefinitions.
*
* @author Chris Beams
* @see org.springframework.config.java.support.ConfigurationParser
* @see ConfigurationClass
* @see ConfigurationParser
* @see ConfigurationModelBeanDefinitionReader
*/
final class ConfigurationModel {
......
......@@ -16,17 +16,31 @@
package org.springframework.config.java.support;
import static java.lang.String.*;
import static org.springframework.util.StringUtils.*;
import java.util.ArrayList;
import java.util.Arrays;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.framework.autoproxy.AutoProxyUtils;
import org.springframework.aop.scope.ScopedProxyFactoryBean;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
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.GenericBeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.support.SimpleBeanDefinitionRegistry;
import org.springframework.config.java.Bean;
import org.springframework.config.java.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
/**
......@@ -38,6 +52,8 @@ import org.springframework.core.io.Resource;
* {@link Resource}.
*
* @author Chris Beams
* @see ConfigurationModel
* @see AbstractConfigurationClassProcessor#processConfigBeanDefinitions()
*/
class ConfigurationModelBeanDefinitionReader {
......@@ -46,12 +62,6 @@ class ConfigurationModelBeanDefinitionReader {
private BeanDefinitionRegistry registry;
/**
* Creates a new {@link ConfigurationModelBeanDefinitionReader} instance.
*/
public ConfigurationModelBeanDefinitionReader() {
}
/**
* Reads {@code model}, registering bean definitions with {@link #registry} based on
* its contents.
......@@ -60,7 +70,7 @@ class ConfigurationModelBeanDefinitionReader {
*/
public BeanDefinitionRegistry loadBeanDefinitions(ConfigurationModel model) {
registry = new SimpleBeanDefinitionRegistry();
for (ConfigurationClass configClass : model.getAllConfigurationClasses())
loadBeanDefinitionsForConfigurationClass(configClass);
......@@ -76,11 +86,6 @@ class ConfigurationModelBeanDefinitionReader {
for (BeanMethod method : configClass.getBeanMethods())
loadBeanDefinitionsForModelMethod(method);
// Annotation[] pluginAnnotations = configClass.getPluginAnnotations();
// Arrays.sort(pluginAnnotations, new PluginComparator());
// for (Annotation annotation : pluginAnnotations)
// loadBeanDefinitionsForExtensionAnnotation(beanDefs, annotation);
}
/**
......@@ -117,40 +122,162 @@ class ConfigurationModelBeanDefinitionReader {
* {@link #registry} based on its contents.
*/
private void loadBeanDefinitionsForModelMethod(BeanMethod method) {
new BeanRegistrar().register(method, registry);
RootBeanDefinition beanDef = new ConfigurationClassBeanDefinition();
ConfigurationClass configClass = method.getDeclaringClass();
beanDef.setFactoryBeanName(configClass.getBeanName());
beanDef.setFactoryMethodName(method.getName());
Bean bean = method.getRequiredAnnotation(Bean.class);
// TODO: prune defaults
//Configuration defaults = configClass.getMetadata();
// consider scoping
Scope scope = method.getAnnotation(Scope.class);
if(scope != null)
beanDef.setScope(scope.value());
// consider name and any aliases
ArrayList<String> names = new ArrayList<String>(Arrays.asList(bean.name()));
String beanName = (names.size() > 0) ? names.remove(0) : method.getName();
for (String alias : bean.name())
registry.registerAlias(beanName, alias);
// has this already been overriden (i.e.: via XML)?
if (containsBeanDefinitionIncludingAncestry(beanName, registry)) {
BeanDefinition existingBeanDef = getBeanDefinitionIncludingAncestry(beanName, registry);
// is the existing bean definition one that was created by JavaConfig?
if (!(existingBeanDef instanceof ConfigurationClassBeanDefinition)) {
// no -> then it's an external override, probably XML
// overriding is legal, return immediately
log.info(format("Skipping loading bean definition for %s: a definition for bean "
+ "'%s' already exists. This is likely due to an override in XML.", method, beanName));
return;
}
}
// TODO: re-enable for Lazy support
// // is this bean marked as primary for disambiguation?
// if (bean.primary() == Primary.TRUE)
// beanDef.setPrimary(true);
//
// // is this bean lazily instantiated?
// if ((bean.lazy() == Lazy.TRUE)
// || ((bean.lazy() == Lazy.UNSPECIFIED) && (defaults.defaultLazy() == Lazy.TRUE)))
// beanDef.setLazyInit(true);
// does this bean have a custom init-method specified?
String initMethodName = bean.initMethod();
if (hasText(initMethodName))
beanDef.setInitMethodName(initMethodName);
// does this bean have a custom destroy-method specified?
String destroyMethodName = bean.destroyMethod();
if (hasText(destroyMethodName))
beanDef.setDestroyMethodName(destroyMethodName);
// is this method annotated with @Scope(scopedProxy=...)?
if (scope != null && scope.proxyMode() != ScopedProxyMode.NO) {
RootBeanDefinition targetDef = beanDef;
// Create a scoped proxy definition for the original bean name,
// "hiding" the target bean in an internal target definition.
String targetBeanName = resolveHiddenScopedProxyBeanName(beanName);
RootBeanDefinition scopedProxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class);
scopedProxyDefinition.getPropertyValues().addPropertyValue("targetBeanName", targetBeanName);
if (scope.proxyMode() == ScopedProxyMode.TARGET_CLASS)
targetDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
// ScopedFactoryBean's "proxyTargetClass" default is TRUE, so we
// don't need to set it explicitly here.
else
scopedProxyDefinition.getPropertyValues().addPropertyValue("proxyTargetClass", Boolean.FALSE);
// The target bean should be ignored in favor of the scoped proxy.
targetDef.setAutowireCandidate(false);
// Register the target bean as separate bean in the factory
registry.registerBeanDefinition(targetBeanName, targetDef);
// replace the original bean definition with the target one
beanDef = scopedProxyDefinition;
}
if (bean.dependsOn().length > 0)
beanDef.setDependsOn(bean.dependsOn());
log.info(format("Registering bean definition for @Bean method %s.%s()",
configClass.getName(), beanName));
registry.registerBeanDefinition(beanName, beanDef);
}
// @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();
// }
// }
private boolean containsBeanDefinitionIncludingAncestry(String beanName, BeanDefinitionRegistry registry) {
try {
getBeanDefinitionIncludingAncestry(beanName, registry);
return true;
} catch (NoSuchBeanDefinitionException ex) {
return false;
}
}
private BeanDefinition getBeanDefinitionIncludingAncestry(String beanName, BeanDefinitionRegistry registry) {
if(!(registry instanceof ConfigurableListableBeanFactory))
return registry.getBeanDefinition(beanName);
ConfigurableListableBeanFactory clbf = (ConfigurableListableBeanFactory) registry;
do {
if (clbf.containsBeanDefinition(beanName))
return registry.getBeanDefinition(beanName);
BeanFactory parent = clbf.getParentBeanFactory();
if (parent == null) {
clbf = null;
} else if (parent instanceof ConfigurableListableBeanFactory) {
clbf = (ConfigurableListableBeanFactory) parent;
// TODO: re-enable
// } else if (parent instanceof AbstractApplicationContext) {
// clbf = ((AbstractApplicationContext) parent).getBeanFactory();
} else {
throw new IllegalStateException("unknown parent type: " + parent.getClass().getName());
}
} while (clbf != null);
throw new NoSuchBeanDefinitionException(
format("No bean definition matching name '%s' " +
"could be found in %s or its ancestry", beanName, registry));
}
/**
* Return the <i>hidden</i> name based on a scoped proxy bean name.
*
* @param originalBeanName the scope proxy bean name as declared in the
* Configuration-annotated class
*
* @return the internally-used <i>hidden</i> bean name
*/
public static String resolveHiddenScopedProxyBeanName(String originalBeanName) {
Assert.hasText(originalBeanName);
return TARGET_NAME_PREFIX.concat(originalBeanName);
}
/** Prefix used when registering the target object for a scoped proxy. */
private static final String TARGET_NAME_PREFIX = "scopedTarget.";
}
/**
* {@link RootBeanDefinition} marker subclass used to signify that a bean definition created
* by JavaConfig as opposed to any other configuration source. Used in bean overriding cases
* where it's necessary to determine whether the bean definition was created externally
* (e.g. via XML).
*/
@SuppressWarnings("serial")
class ConfigurationClassBeanDefinition extends RootBeanDefinition {
}
......@@ -46,9 +46,11 @@ public class ConfigurationParser {
private final ClassLoader classLoader;
/**
* Creates a new parser instance that will be used to populate <var>model</var>.
* @param model model to be populated by each successive call to
* {@link #parse(Object, String)}
* Creates a new {@link ConfigurationParser} instance that will be used to populate a
* {@link ConfigurationModel}.
*
* @param model model to be populated by each successive call to {@link #parse}
* @see #getConfigurationModel()
*/
public ConfigurationParser(ProblemReporter problemReporter, ClassLoader classLoader) {
this.model = new ConfigurationModel();
......@@ -77,6 +79,9 @@ public class ConfigurationParser {
model.add(configClass);
}
/**
* Returns the current {@link ConfigurationModel}, to be called after {@link #parse}.
*/
public ConfigurationModel getConfigurationModel() {
return model;
}
......
......@@ -25,7 +25,7 @@ import org.springframework.util.Assert;
/**
* Stack used for detecting circular use of the {@link Import} annotation.
* {@link Stack} used for detecting circular use of the {@link Import} annotation.
*
* @author Chris Beams
* @see Import
......
......@@ -24,7 +24,7 @@ import org.springframework.util.ClassUtils;
/**
* Representation of a class, free from java reflection,
* Represents a class, free from java reflection,
* populated by {@link ConfigurationParser}.
*
* @author Chris Beams
......
......@@ -25,6 +25,14 @@ import java.util.ArrayList;
import org.springframework.asm.AnnotationVisitor;
/**
* ASM {@link AnnotationVisitor} that visits any annotation array values while populating
* a new {@link MutableAnnotation} instance.
*
* @author Chris Beams
* @see MutableAnnotation
* @see MutableAnnotationUtils
*/
class MutableAnnotationArrayVisitor extends AnnotationAdapter {
private final ArrayList<Object> values = new ArrayList<Object>();
......
......@@ -30,6 +30,14 @@ import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
/**
* Handles calls to {@link MutableAnnotation} attribute methods at runtime. Essentially
* emulates what JDK annotation dynamic proxies do.
*
* @author Chris Beams
* @see MutableAnnotation
* @see MutableAnnotationUtils
*/
final class MutableAnnotationInvocationHandler implements InvocationHandler {
private final Class<? extends Annotation> annoType;
......
......@@ -27,7 +27,13 @@ import org.springframework.util.Assert;
/**
* Populates a given {@link MutableAnnotation} instance with its attributes.
* ASM {@link AnnotationVisitor} that populates a given {@link MutableAnnotation} instance
* with its attributes.
*
* @author Chris Beams
* @see MutableAnnotation
* @see MutableAnnotationInvocationHandler
* @see MutableAnnotationUtils
*/
class MutableAnnotationVisitor implements AnnotationVisitor {
......
......@@ -156,15 +156,10 @@ public class BeanMethodTests {
@Test
public void sessionInterfaceScopedProxiesAreLegal() {
Scope scope = PrototypeInterfaceProxy.class.getAnnotation(Scope.class);
Scope scope = SessionInterfaceProxy.class.getAnnotation(Scope.class);
BeanMethod beanMethod = new BeanMethod(beanName, 0, returnType, beanAnno, scope);
beanMethod.setDeclaringClass(declaringClass);
try {
beanMethod.validate(problemReporter);
fail("should have failed due to prototype with scoped proxy");
} catch (Exception ex) {
assertTrue(ex.getMessage().contains("cannot be created for singleton/prototype beans"));
}
beanMethod.validate(problemReporter); // should validate without problems - it's legal
}
@Scope(value=SINGLETON, proxyMode=INTERFACES)
......
......@@ -6,6 +6,7 @@ import static org.springframework.beans.factory.support.BeanDefinitionBuilder.*;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
......@@ -39,8 +40,7 @@ public class BasicTests {
for (Class<?> configClass : configClasses) {
String configBeanName = configClass.getName();
factory.registerBeanDefinition(configBeanName, rootBeanDefinition(configClass)
.getBeanDefinition());
factory.registerBeanDefinition(configBeanName, rootBeanDefinition(configClass).getBeanDefinition());
}
new ConfigurationClassPostProcessor().postProcessBeanFactory(factory);
......@@ -50,6 +50,51 @@ public class BasicTests {
return factory;
}
@Test
public void customBeanNameIsRespected() {
BeanFactory factory = initBeanFactory(ConfigWithBeanWithCustomName.class);
assertSame(factory.getBean("customName"), ConfigWithBeanWithCustomName.testBean);
// method name should not be registered
try {
factory.getBean("methodName");
fail("bean should not have been registered with 'methodName'");
} catch (NoSuchBeanDefinitionException ex) { /* expected */ }
}
@Configuration
static class ConfigWithBeanWithCustomName {
static TestBean testBean = new TestBean();
@Bean(name="customName")
public TestBean methodName() {
return testBean;
}
}
@Test
public void aliasesAreRespected() {
BeanFactory factory = initBeanFactory(ConfigWithBeanWithAliases.class);
assertSame(factory.getBean("name1"), ConfigWithBeanWithAliases.testBean);
String[] aliases = factory.getAliases("name1");
for(String alias : aliases)
assertSame(factory.getBean(alias), ConfigWithBeanWithAliases.testBean);
// method name should not be registered
try {
factory.getBean("methodName");
fail("bean should not have been registered with 'methodName'");
} catch (NoSuchBeanDefinitionException ex) { /* expected */ }
}
@Configuration
static class ConfigWithBeanWithAliases {
static TestBean testBean = new TestBean();
@Bean(name={"name1", "alias1", "alias2", "alias3"})
public TestBean methodName() {
return testBean;
}
}
@Test(expected=BeanDefinitionParsingException.class)
public void testFinalBeanMethod() {
initBeanFactory(ConfigWithFinalBean.class);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册