+ 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
This commit is contained in:
parent
2bbc4e48ad
commit
8a5c2a6a56
|
|
@ -1,50 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2009 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.springframework.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.
|
* Represents a {@link Configuration} class method marked with the {@link Bean} annotation.
|
||||||
*
|
*
|
||||||
* @author Chris Beams
|
* @author Chris Beams
|
||||||
|
* @see ConfigurationClass
|
||||||
|
* @see ConfigurationModel
|
||||||
|
* @see ConfigurationParser
|
||||||
|
* @see ConfigurationModelBeanDefinitionReader
|
||||||
*/
|
*/
|
||||||
final class BeanMethod implements BeanMetadataElement {
|
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.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
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.config.ConfigurableBeanFactory;
|
||||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||||
import org.springframework.config.java.Bean;
|
import org.springframework.config.java.Bean;
|
||||||
import org.springframework.context.annotation.Scope;
|
import org.springframework.context.annotation.Scope;
|
||||||
import org.springframework.context.annotation.ScopedProxyMode;
|
import org.springframework.context.annotation.ScopedProxyMode;
|
||||||
import org.springframework.core.annotation.AnnotationUtils;
|
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.
|
* handling of bean semantics such as scoping and AOP proxying.
|
||||||
*
|
*
|
||||||
* @author Chris Beams
|
* @author Chris Beams
|
||||||
* @since 3.0
|
|
||||||
* @see Bean
|
* @see Bean
|
||||||
* @see BeanRegistrar
|
|
||||||
*/
|
*/
|
||||||
class BeanMethodInterceptor implements BeanFactoryAware, MethodInterceptor {
|
class BeanMethodInterceptor implements MethodInterceptor {
|
||||||
protected final Log log = LogFactory.getLog(this.getClass());
|
|
||||||
protected DefaultListableBeanFactory beanFactory;
|
|
||||||
|
|
||||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
private static final Log log = LogFactory.getLog(BeanMethodInterceptor.class);
|
||||||
Assert.isInstanceOf(DefaultListableBeanFactory.class, beanFactory);
|
|
||||||
this.beanFactory = (DefaultListableBeanFactory) beanFactory;
|
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 {
|
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
||||||
|
|
||||||
// determine the name of the bean
|
// by default the bean name is the name of the @Bean-annotated method
|
||||||
String beanName;
|
String beanName = method.getName();
|
||||||
|
|
||||||
// check to see if the user has explicitly set the bean name
|
// check to see if the user has explicitly set the bean name
|
||||||
Bean bean = method.getAnnotation(Bean.class);
|
Bean bean = method.getAnnotation(Bean.class);
|
||||||
if(bean != null && bean.name().length > 1)
|
if(bean != null && bean.name().length > 0)
|
||||||
beanName = bean.name()[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
|
// determine whether this bean is a scoped-proxy
|
||||||
Scope scope = AnnotationUtils.findAnnotation(method, Scope.class);
|
Scope scope = AnnotationUtils.findAnnotation(method, Scope.class);
|
||||||
boolean isScopedProxy = (scope != null && scope.proxyMode() != ScopedProxyMode.NO);
|
boolean isScopedProxy = (scope != null && scope.proxyMode() != ScopedProxyMode.NO);
|
||||||
String scopedBeanName = BeanRegistrar.resolveHiddenScopedProxyBeanName(beanName);
|
String scopedBeanName = ConfigurationModelBeanDefinitionReader.resolveHiddenScopedProxyBeanName(beanName);
|
||||||
if (isScopedProxy && beanFactory.isCurrentlyInCreation(scopedBeanName))
|
if (isScopedProxy && beanFactory.isCurrentlyInCreation(scopedBeanName))
|
||||||
beanName = scopedBeanName;
|
beanName = scopedBeanName;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,234 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2009 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.springframework.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
|
* 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}
|
* ancestry of the class, in a 'flattened-out' manner. Note that each {@link BeanMethod}
|
||||||
* representation contains source information about where it was originally detected
|
* 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.ClassAdapter;
|
||||||
import org.springframework.asm.Label;
|
import org.springframework.asm.Label;
|
||||||
import org.springframework.asm.MethodAdapter;
|
import org.springframework.asm.MethodAdapter;
|
||||||
|
import org.springframework.asm.MethodVisitor;
|
||||||
import org.springframework.asm.Opcodes;
|
import org.springframework.asm.Opcodes;
|
||||||
import org.springframework.config.java.Bean;
|
import org.springframework.config.java.Bean;
|
||||||
import org.springframework.config.java.Configuration;
|
import org.springframework.config.java.Configuration;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Visits a single method declared in a given {@link Configuration} class. Determines
|
* ASM {@link MethodVisitor} that visits a single method declared in a given
|
||||||
* whether the method is a {@link Bean} method and if so, adds it to the
|
* {@link Configuration} class. Determines whether the method is a {@link Bean}
|
||||||
* {@link ConfigurationClass}.
|
* method and if so, adds it to the {@link ConfigurationClass}.
|
||||||
*
|
*
|
||||||
* @author Chris Beams
|
* @author Chris Beams
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import java.util.Stack;
|
||||||
import org.springframework.asm.AnnotationVisitor;
|
import org.springframework.asm.AnnotationVisitor;
|
||||||
import org.springframework.asm.ClassAdapter;
|
import org.springframework.asm.ClassAdapter;
|
||||||
import org.springframework.asm.ClassReader;
|
import org.springframework.asm.ClassReader;
|
||||||
|
import org.springframework.asm.ClassVisitor;
|
||||||
import org.springframework.asm.MethodVisitor;
|
import org.springframework.asm.MethodVisitor;
|
||||||
import org.springframework.asm.Opcodes;
|
import org.springframework.asm.Opcodes;
|
||||||
import org.springframework.beans.factory.parsing.Location;
|
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
|
* ASM {@link ClassVisitor} that visits a {@link Configuration} class, populating a
|
||||||
* with information gleaned along the way.
|
* {@link ConfigurationClass} instance with information gleaned along the way.
|
||||||
*
|
*
|
||||||
* @author Chris Beams
|
* @author Chris Beams
|
||||||
|
* @see ConfigurationParser
|
||||||
|
* @see ConfigurationClass
|
||||||
*/
|
*/
|
||||||
class ConfigurationClassVisitor extends ClassAdapter {
|
class ConfigurationClassVisitor extends ClassAdapter {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,29 +17,27 @@ package org.springframework.config.java.support;
|
||||||
|
|
||||||
import static java.lang.String.*;
|
import static java.lang.String.*;
|
||||||
import static org.springframework.config.java.support.Util.*;
|
import static org.springframework.config.java.support.Util.*;
|
||||||
import static org.springframework.util.Assert.*;
|
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.LinkedHashSet;
|
|
||||||
|
|
||||||
import net.sf.cglib.core.DefaultGeneratorStrategy;
|
import net.sf.cglib.core.DefaultGeneratorStrategy;
|
||||||
import net.sf.cglib.proxy.Callback;
|
import net.sf.cglib.proxy.Callback;
|
||||||
import net.sf.cglib.proxy.CallbackFilter;
|
import net.sf.cglib.proxy.CallbackFilter;
|
||||||
import net.sf.cglib.proxy.Enhancer;
|
import net.sf.cglib.proxy.Enhancer;
|
||||||
import net.sf.cglib.proxy.MethodInterceptor;
|
import net.sf.cglib.proxy.NoOp;
|
||||||
import net.sf.cglib.proxy.MethodProxy;
|
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.springframework.asm.ClassAdapter;
|
import org.springframework.asm.ClassAdapter;
|
||||||
import org.springframework.asm.ClassReader;
|
import org.springframework.asm.ClassReader;
|
||||||
import org.springframework.asm.ClassWriter;
|
import org.springframework.asm.ClassWriter;
|
||||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
|
||||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||||
|
import org.springframework.config.java.Bean;
|
||||||
import org.springframework.config.java.Configuration;
|
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 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 ArrayList<Callback> callbackInstances = new ArrayList<Callback>();
|
||||||
|
private final ArrayList<Class<? extends Callback>> callbackTypes = new ArrayList<Class<? extends Callback>>();
|
||||||
private final CallbackFilter callbackFilter = new CallbackFilter() {
|
private final CallbackFilter 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()));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new {@link ConfigurationEnhancer} instance.
|
* Creates a new {@link ConfigurationEnhancer} instance.
|
||||||
*/
|
*/
|
||||||
public ConfigurationEnhancer(DefaultListableBeanFactory beanFactory) {
|
public ConfigurationEnhancer(DefaultListableBeanFactory beanFactory) {
|
||||||
notNull(beanFactory, "beanFactory must be non-null");
|
Assert.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) {
|
callbackInstances.add(new BeanMethodInterceptor(beanFactory));
|
||||||
return true;
|
callbackInstances.add(NoOp.INSTANCE);
|
||||||
}
|
|
||||||
|
|
||||||
public void register(BeanMethod method, BeanDefinitionRegistry registry) {
|
|
||||||
// no-op
|
|
||||||
}
|
|
||||||
});
|
|
||||||
callbackInstances.add(new MethodInterceptor() {
|
|
||||||
|
|
||||||
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
for (Callback callback : callbackInstances)
|
for (Callback callback : callbackInstances)
|
||||||
callbackTypes.add(callback.getClass());
|
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 java.util.ArrayList;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
import org.springframework.beans.factory.parsing.ProblemReporter;
|
import org.springframework.beans.factory.parsing.ProblemReporter;
|
||||||
import org.springframework.config.java.Configuration;
|
import org.springframework.config.java.Configuration;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An abstract representation of a set of user-provided "Configuration classes", usually but
|
* Represents the set of all user-defined {@link Configuration} classes. Once this model
|
||||||
* not necessarily annotated with {@link Configuration @Configuration}. The model is
|
* is populated using a {@link ConfigurationParser}, it can be rendered out to a set of
|
||||||
* populated with a
|
* {@link BeanDefinition} objects. This model provides an important layer of indirection
|
||||||
* {@link org.springframework.config.java.support.ConfigurationParser}
|
* between the complexity of parsing a set of classes and the complexity of representing
|
||||||
* implementation which may be reflection-based or ASM-based. Once a model has been
|
* the contents of those classes as BeanDefinitions.
|
||||||
* 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>
|
|
||||||
*
|
*
|
||||||
* @author Chris Beams
|
* @author Chris Beams
|
||||||
* @see org.springframework.config.java.support.ConfigurationParser
|
* @see ConfigurationClass
|
||||||
|
* @see ConfigurationParser
|
||||||
|
* @see ConfigurationModelBeanDefinitionReader
|
||||||
*/
|
*/
|
||||||
final class ConfigurationModel {
|
final class ConfigurationModel {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,17 +16,31 @@
|
||||||
package org.springframework.config.java.support;
|
package org.springframework.config.java.support;
|
||||||
|
|
||||||
import static java.lang.String.*;
|
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.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
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.AbstractBeanDefinition;
|
||||||
import org.springframework.beans.factory.support.BeanDefinitionReader;
|
import org.springframework.beans.factory.support.BeanDefinitionReader;
|
||||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||||
import org.springframework.beans.factory.support.GenericBeanDefinition;
|
import org.springframework.beans.factory.support.GenericBeanDefinition;
|
||||||
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
import org.springframework.beans.factory.support.SimpleBeanDefinitionRegistry;
|
import org.springframework.beans.factory.support.SimpleBeanDefinitionRegistry;
|
||||||
import org.springframework.config.java.Bean;
|
import org.springframework.config.java.Bean;
|
||||||
import org.springframework.config.java.Configuration;
|
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.core.io.Resource;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -38,6 +52,8 @@ import org.springframework.core.io.Resource;
|
||||||
* {@link Resource}.
|
* {@link Resource}.
|
||||||
*
|
*
|
||||||
* @author Chris Beams
|
* @author Chris Beams
|
||||||
|
* @see ConfigurationModel
|
||||||
|
* @see AbstractConfigurationClassProcessor#processConfigBeanDefinitions()
|
||||||
*/
|
*/
|
||||||
class ConfigurationModelBeanDefinitionReader {
|
class ConfigurationModelBeanDefinitionReader {
|
||||||
|
|
||||||
|
|
@ -46,12 +62,6 @@ class ConfigurationModelBeanDefinitionReader {
|
||||||
private BeanDefinitionRegistry registry;
|
private BeanDefinitionRegistry registry;
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new {@link ConfigurationModelBeanDefinitionReader} instance.
|
|
||||||
*/
|
|
||||||
public ConfigurationModelBeanDefinitionReader() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads {@code model}, registering bean definitions with {@link #registry} based on
|
* Reads {@code model}, registering bean definitions with {@link #registry} based on
|
||||||
* its contents.
|
* its contents.
|
||||||
|
|
@ -60,7 +70,7 @@ class ConfigurationModelBeanDefinitionReader {
|
||||||
*/
|
*/
|
||||||
public BeanDefinitionRegistry loadBeanDefinitions(ConfigurationModel model) {
|
public BeanDefinitionRegistry loadBeanDefinitions(ConfigurationModel model) {
|
||||||
registry = new SimpleBeanDefinitionRegistry();
|
registry = new SimpleBeanDefinitionRegistry();
|
||||||
|
|
||||||
for (ConfigurationClass configClass : model.getAllConfigurationClasses())
|
for (ConfigurationClass configClass : model.getAllConfigurationClasses())
|
||||||
loadBeanDefinitionsForConfigurationClass(configClass);
|
loadBeanDefinitionsForConfigurationClass(configClass);
|
||||||
|
|
||||||
|
|
@ -76,11 +86,6 @@ class ConfigurationModelBeanDefinitionReader {
|
||||||
|
|
||||||
for (BeanMethod method : configClass.getBeanMethods())
|
for (BeanMethod method : configClass.getBeanMethods())
|
||||||
loadBeanDefinitionsForModelMethod(method);
|
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.
|
* {@link #registry} based on its contents.
|
||||||
*/
|
*/
|
||||||
private void loadBeanDefinitionsForModelMethod(BeanMethod method) {
|
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 boolean containsBeanDefinitionIncludingAncestry(String beanName, BeanDefinitionRegistry registry) {
|
||||||
// private void loadBeanDefinitionsForExtensionAnnotation(Map<String, BeanDefinition> beanDefs, Annotation anno) {
|
try {
|
||||||
// // ExtensionAnnotationUtils.getRegistrarFor(anno).registerBeanDefinitionsWith(beanFactory);
|
getBeanDefinitionIncludingAncestry(beanName, registry);
|
||||||
// // there is a fixed assumption that in order for this annotation to have
|
return true;
|
||||||
// // been registered in the first place, it must be meta-annotated with @Plugin
|
} catch (NoSuchBeanDefinitionException ex) {
|
||||||
// // assert this as an invariant now
|
return false;
|
||||||
// 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 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;
|
private final ClassLoader classLoader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new parser instance that will be used to populate <var>model</var>.
|
* Creates a new {@link ConfigurationParser} instance that will be used to populate a
|
||||||
* @param model model to be populated by each successive call to
|
* {@link ConfigurationModel}.
|
||||||
* {@link #parse(Object, String)}
|
*
|
||||||
|
* @param model model to be populated by each successive call to {@link #parse}
|
||||||
|
* @see #getConfigurationModel()
|
||||||
*/
|
*/
|
||||||
public ConfigurationParser(ProblemReporter problemReporter, ClassLoader classLoader) {
|
public ConfigurationParser(ProblemReporter problemReporter, ClassLoader classLoader) {
|
||||||
this.model = new ConfigurationModel();
|
this.model = new ConfigurationModel();
|
||||||
|
|
@ -77,6 +79,9 @@ public class ConfigurationParser {
|
||||||
model.add(configClass);
|
model.add(configClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current {@link ConfigurationModel}, to be called after {@link #parse}.
|
||||||
|
*/
|
||||||
public ConfigurationModel getConfigurationModel() {
|
public ConfigurationModel getConfigurationModel() {
|
||||||
return model;
|
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
|
* @author Chris Beams
|
||||||
* @see Import
|
* @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}.
|
* populated by {@link ConfigurationParser}.
|
||||||
*
|
*
|
||||||
* @author Chris Beams
|
* @author Chris Beams
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,14 @@ import java.util.ArrayList;
|
||||||
import org.springframework.asm.AnnotationVisitor;
|
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 {
|
class MutableAnnotationArrayVisitor extends AnnotationAdapter {
|
||||||
|
|
||||||
private final ArrayList<Object> values = new ArrayList<Object>();
|
private final ArrayList<Object> values = new ArrayList<Object>();
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,14 @@ import org.springframework.util.ReflectionUtils;
|
||||||
import org.springframework.util.StringUtils;
|
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 {
|
final class MutableAnnotationInvocationHandler implements InvocationHandler {
|
||||||
|
|
||||||
private final Class<? extends Annotation> annoType;
|
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 {
|
class MutableAnnotationVisitor implements AnnotationVisitor {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -156,15 +156,10 @@ public class BeanMethodTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void sessionInterfaceScopedProxiesAreLegal() {
|
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 beanMethod = new BeanMethod(beanName, 0, returnType, beanAnno, scope);
|
||||||
beanMethod.setDeclaringClass(declaringClass);
|
beanMethod.setDeclaringClass(declaringClass);
|
||||||
try {
|
beanMethod.validate(problemReporter); // should validate without problems - it's legal
|
||||||
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"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Scope(value=SINGLETON, proxyMode=INTERFACES)
|
@Scope(value=SINGLETON, proxyMode=INTERFACES)
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import static org.springframework.beans.factory.support.BeanDefinitionBuilder.*;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.springframework.beans.factory.BeanFactory;
|
import org.springframework.beans.factory.BeanFactory;
|
||||||
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||||
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
|
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
||||||
|
|
@ -39,8 +40,7 @@ public class BasicTests {
|
||||||
|
|
||||||
for (Class<?> configClass : configClasses) {
|
for (Class<?> configClass : configClasses) {
|
||||||
String configBeanName = configClass.getName();
|
String configBeanName = configClass.getName();
|
||||||
factory.registerBeanDefinition(configBeanName, rootBeanDefinition(configClass)
|
factory.registerBeanDefinition(configBeanName, rootBeanDefinition(configClass).getBeanDefinition());
|
||||||
.getBeanDefinition());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(factory);
|
new ConfigurationClassPostProcessor().postProcessBeanFactory(factory);
|
||||||
|
|
@ -50,6 +50,51 @@ public class BasicTests {
|
||||||
return factory;
|
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)
|
@Test(expected=BeanDefinitionParsingException.class)
|
||||||
public void testFinalBeanMethod() {
|
public void testFinalBeanMethod() {
|
||||||
initBeanFactory(ConfigWithFinalBean.class);
|
initBeanFactory(ConfigWithFinalBean.class);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue