Consistent throwing of BeanCreationExceptions (and reflection exceptions)

Issue: SPR-14883
This commit is contained in:
Juergen Hoeller 2016-11-07 16:56:35 +01:00
parent fd7045adac
commit b42d731fc8
6 changed files with 138 additions and 82 deletions

View File

@ -220,7 +220,7 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
public void setBeanFactory(BeanFactory beanFactory) {
if (!(beanFactory instanceof ConfigurableListableBeanFactory)) {
throw new IllegalArgumentException(
"AutowiredAnnotationBeanPostProcessor requires a ConfigurableListableBeanFactory");
@ -238,7 +238,10 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
}
@Override
public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, final String beanName) throws BeansException {
public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, final String beanName)
throws BeanCreationException {
// Let's check for lookup methods here..
if (!this.lookupMethodsChecked.contains(beanName)) {
ReflectionUtils.doWithMethods(beanClass, new ReflectionUtils.MethodCallback() {
@Override
@ -263,10 +266,19 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
// Quick check on the concurrent map first, with minimal locking.
Constructor<?>[] candidateConstructors = this.candidateConstructorsCache.get(beanClass);
if (candidateConstructors == null) {
// Fully synchronized resolution now...
synchronized (this.candidateConstructorsCache) {
candidateConstructors = this.candidateConstructorsCache.get(beanClass);
if (candidateConstructors == null) {
Constructor<?>[] rawCandidates = beanClass.getDeclaredConstructors();
Constructor<?>[] rawCandidates;
try {
rawCandidates = beanClass.getDeclaredConstructors();
}
catch (Throwable ex) {
throw new BeanCreationException(beanName,
"Resolution of declared constructors on bean Class [" + beanClass.getName() +
"] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex);
}
List<Constructor<?>> candidates = new ArrayList<Constructor<?>>(rawCandidates.length);
Constructor<?> requiredConstructor = null;
Constructor<?> defaultConstructor = null;
@ -320,9 +332,9 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
}
else if (candidates.size() == 1 && logger.isWarnEnabled()) {
logger.warn("Inconsistent constructor declaration on bean with name '" + beanName +
"': single autowire-marked constructor flagged as optional - this constructor " +
"is effectively required since there is no default constructor to fall back to: " +
candidates.get(0));
"': single autowire-marked constructor flagged as optional - " +
"this constructor is effectively required since there is no " +
"default constructor to fall back to: " + candidates.get(0));
}
}
candidateConstructors = candidates.toArray(new Constructor<?>[candidates.size()]);
@ -342,7 +354,7 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
@Override
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
@ -361,9 +373,9 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
* 'Native' processing method for direct calls with an arbitrary target instance,
* resolving all of its fields and methods which are annotated with {@code @Autowired}.
* @param bean the target instance to process
* @throws BeansException if autowiring failed
* @throws BeanCreationException if autowiring failed
*/
public void processInjection(Object bean) throws BeansException {
public void processInjection(Object bean) throws BeanCreationException {
Class<?> clazz = bean.getClass();
InjectionMetadata metadata = findAutowiringMetadata(clazz.getName(), clazz, null);
try {
@ -373,7 +385,8 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException("Injection of autowired dependencies failed for class [" + clazz + "]", ex);
throw new BeanCreationException(
"Injection of autowired dependencies failed for class [" + clazz + "]", ex);
}
}
@ -446,7 +459,8 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
}
if (method.getParameterTypes().length == 0) {
if (logger.isWarnEnabled()) {
logger.warn("Autowired annotation should be used on methods with parameters: " + method);
logger.warn("Autowired annotation should only be used on methods with parameters: " +
method);
}
}
boolean required = determineRequiredStatus(ann);
@ -629,7 +643,7 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
Class<?>[] paramTypes = method.getParameterTypes();
arguments = new Object[paramTypes.length];
DependencyDescriptor[] descriptors = new DependencyDescriptor[paramTypes.length];
Set<String> autowiredBeanNames = new LinkedHashSet<String>(paramTypes.length);
Set<String> autowiredBeans = new LinkedHashSet<String>(paramTypes.length);
TypeConverter typeConverter = beanFactory.getTypeConverter();
for (int i = 0; i < arguments.length; i++) {
MethodParameter methodParam = new MethodParameter(method, i);
@ -637,7 +651,7 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
currDesc.setContainingClass(bean.getClass());
descriptors[i] = currDesc;
try {
Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeanNames, typeConverter);
Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeans, typeConverter);
if (arg == null && !this.required) {
arguments = null;
break;
@ -655,9 +669,9 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
for (int i = 0; i < arguments.length; i++) {
this.cachedMethodArguments[i] = descriptors[i];
}
registerDependentBeans(beanName, autowiredBeanNames);
if (autowiredBeanNames.size() == paramTypes.length) {
Iterator<String> it = autowiredBeanNames.iterator();
registerDependentBeans(beanName, autowiredBeans);
if (autowiredBeans.size() == paramTypes.length) {
Iterator<String> it = autowiredBeans.iterator();
for (int i = 0; i < paramTypes.length; i++) {
String autowiredBeanName = it.next();
if (beanFactory.containsBean(autowiredBeanName)) {
@ -706,19 +720,19 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
@SuppressWarnings("serial")
private static class ShortcutDependencyDescriptor extends DependencyDescriptor {
private final String shortcutName;
private final String shortcut;
private final Class<?> requiredType;
public ShortcutDependencyDescriptor(DependencyDescriptor original, String shortcutName, Class<?> requiredType) {
public ShortcutDependencyDescriptor(DependencyDescriptor original, String shortcut, Class<?> requiredType) {
super(original);
this.shortcutName = shortcutName;
this.shortcut = shortcut;
this.requiredType = requiredType;
}
@Override
public Object resolveShortcut(BeanFactory beanFactory) {
return resolveCandidate(this.shortcutName, this.requiredType, beanFactory);
return resolveCandidate(this.shortcut, this.requiredType, beanFactory);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2016 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.
@ -141,8 +141,7 @@ public class RequiredAnnotationBeanPostProcessor extends InstantiationAwareBeanP
@Override
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName)
throws BeansException {
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
if (!this.validatedBeanNames.contains(beanName)) {
if (!shouldSkip(this.beanFactory, beanName)) {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2016 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.
@ -66,8 +66,7 @@ public abstract class InstantiationAwareBeanPostProcessorAdapter implements Smar
@Override
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName)
throws BeansException {
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
return pvs;
}

View File

@ -479,12 +479,22 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
"BeanPostProcessor before instantiation of bean failed", ex);
}
try {
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isDebugEnabled()) {
logger.debug("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
catch (BeanCreationException ex) {
// A previously detected exception with proper bean creation context already...
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(
mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
}
}
/**
* Actually create the specified bean. Pre-creation processing has already happened
@ -500,7 +510,9 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
* @see #instantiateUsingFactoryMethod
* @see #autowireConstructor
*/
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
@ -515,7 +527,13 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
// Allow post-processors to modify the merged bean definition.
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing failed of bean type [" + beanType + "] failed", ex);
}
mbd.postProcessed = true;
}
}
@ -550,7 +568,8 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}
@ -586,7 +605,8 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
@ -773,10 +793,11 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
ReflectionUtils.doWithMethods(fbClass,
new ReflectionUtils.MethodCallback() {
@Override
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
public void doWith(Method method) {
if (method.getName().equals(factoryMethodName) &&
FactoryBean.class.isAssignableFrom(method.getReturnType())) {
objectType.value = GenericTypeResolver.resolveReturnTypeArgument(method, FactoryBean.class);
objectType.value = GenericTypeResolver.resolveReturnTypeArgument(
method, FactoryBean.class);
}
}
});
@ -933,7 +954,6 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName)
throws BeansException {
try {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof MergedBeanDefinitionPostProcessor) {
MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
@ -941,11 +961,6 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
}
}
}
catch (Exception ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing failed of bean type [" + beanType + "] failed", ex);
}
}
/**
* Apply before-instantiation post-processors, resolving whether there is a
@ -1107,7 +1122,8 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
return bw;
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
}
}
@ -1659,7 +1675,9 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
* methods with arguments.
* @see #invokeInitMethods
*/
protected void invokeCustomInitMethod(String beanName, final Object bean, RootBeanDefinition mbd) throws Throwable {
protected void invokeCustomInitMethod(String beanName, final Object bean, RootBeanDefinition mbd)
throws Throwable {
String initMethodName = mbd.getInitMethodName();
final Method initMethod = (mbd.isNonPublicAccessAllowed() ?
BeanUtils.findMethod(bean.getClass(), initMethodName) :

View File

@ -287,13 +287,13 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
// Guarantee initialization of beans that the current bean depends on.
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dependsOnBean : dependsOn) {
if (isDependent(beanName, dependsOnBean)) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dependsOnBean + "'");
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
registerDependentBean(dependsOnBean, beanName);
getBean(dependsOnBean);
registerDependentBean(dep, beanName);
getBean(dep);
}
}
@ -460,19 +460,19 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
return false;
}
if (isFactoryBean(beanName, mbd)) {
final FactoryBean<?> factoryBean = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
final FactoryBean<?> fb = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
if (System.getSecurityManager() != null) {
return AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
@Override
public Boolean run() {
return ((factoryBean instanceof SmartFactoryBean && ((SmartFactoryBean<?>) factoryBean).isPrototype()) ||
!factoryBean.isSingleton());
return ((fb instanceof SmartFactoryBean && ((SmartFactoryBean<?>) fb).isPrototype()) ||
!fb.isSingleton());
}
}, getAccessControlContext());
}
else {
return ((factoryBean instanceof SmartFactoryBean && ((SmartFactoryBean<?>) factoryBean).isPrototype()) ||
!factoryBean.isSingleton());
return ((fb instanceof SmartFactoryBean && ((SmartFactoryBean<?>) fb).isPrototype()) ||
!fb.isSingleton());
}
}
else {
@ -1053,11 +1053,11 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
* Destroy the given bean instance (usually a prototype instance
* obtained from this factory) according to the given bean definition.
* @param beanName the name of the bean definition
* @param beanInstance the bean instance to destroy
* @param bean the bean instance to destroy
* @param mbd the merged bean definition
*/
protected void destroyBean(String beanName, Object beanInstance, RootBeanDefinition mbd) {
new DisposableBeanAdapter(beanInstance, beanName, mbd, getBeanPostProcessors(), getAccessControlContext()).destroy();
protected void destroyBean(String beanName, Object bean, RootBeanDefinition mbd) {
new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), getAccessControlContext()).destroy();
}
@Override
@ -1238,12 +1238,13 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
pbd = getMergedBeanDefinition(parentBeanName);
}
else {
if (getParentBeanFactory() instanceof ConfigurableBeanFactory) {
pbd = ((ConfigurableBeanFactory) getParentBeanFactory()).getMergedBeanDefinition(parentBeanName);
BeanFactory parent = getParentBeanFactory();
if (parent instanceof ConfigurableBeanFactory) {
pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);
}
else {
throw new NoSuchBeanDefinitionException(bd.getParentName(),
"Parent name '" + bd.getParentName() + "' is equal to bean name '" + beanName +
throw new NoSuchBeanDefinitionException(parentBeanName,
"Parent name '" + parentBeanName + "' is equal to bean name '" + beanName +
"': cannot be resolved without an AbstractBeanFactory parent");
}
}
@ -1359,12 +1360,14 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
catch (ClassNotFoundException ex) {
throw new CannotLoadBeanClassException(mbd.getResourceDescription(), beanName, mbd.getBeanClassName(), ex);
}
catch (LinkageError err) {
throw new CannotLoadBeanClassException(mbd.getResourceDescription(), beanName, mbd.getBeanClassName(), err);
catch (LinkageError ex) {
throw new CannotLoadBeanClassException(mbd.getResourceDescription(), beanName, mbd.getBeanClassName(), ex);
}
}
private Class<?> doResolveBeanClass(RootBeanDefinition mbd, Class<?>... typesToMatch) throws ClassNotFoundException {
private Class<?> doResolveBeanClass(RootBeanDefinition mbd, Class<?>... typesToMatch)
throws ClassNotFoundException {
ClassLoader beanClassLoader = getBeanClassLoader();
ClassLoader classLoaderToUse = beanClassLoader;
if (!ObjectUtils.isEmpty(typesToMatch)) {

View File

@ -483,6 +483,7 @@ public abstract class ReflectionUtils {
* @param clazz the class to introspect
* @param mc the callback to invoke for each method
* @since 4.2
* @throws IllegalStateException if introspection fails
* @see #doWithMethods
*/
public static void doWithLocalMethods(Class<?> clazz, MethodCallback mc) {
@ -504,6 +505,7 @@ public abstract class ReflectionUtils {
* twice, unless excluded by a {@link MethodFilter}.
* @param clazz the class to introspect
* @param mc the callback to invoke for each method
* @throws IllegalStateException if introspection fails
* @see #doWithMethods(Class, MethodCallback, MethodFilter)
*/
public static void doWithMethods(Class<?> clazz, MethodCallback mc) {
@ -518,6 +520,7 @@ public abstract class ReflectionUtils {
* @param clazz the class to introspect
* @param mc the callback to invoke for each method
* @param mf the filter that determines the methods to apply the callback to
* @throws IllegalStateException if introspection fails
*/
public static void doWithMethods(Class<?> clazz, MethodCallback mc, MethodFilter mf) {
// Keep backing up the inheritance hierarchy.
@ -547,6 +550,7 @@ public abstract class ReflectionUtils {
* Get all declared methods on the leaf class and all superclasses.
* Leaf class methods are included first.
* @param leafClass the class to introspect
* @throws IllegalStateException if introspection fails
*/
public static Method[] getAllDeclaredMethods(Class<?> leafClass) {
final List<Method> methods = new ArrayList<Method>(32);
@ -564,6 +568,7 @@ public abstract class ReflectionUtils {
* Leaf class methods are included first and while traversing the superclass hierarchy
* any methods found with signatures matching a method already included are filtered out.
* @param leafClass the class to introspect
* @throws IllegalStateException if introspection fails
*/
public static Method[] getUniqueDeclaredMethods(Class<?> leafClass) {
final List<Method> methods = new ArrayList<Method>(32);
@ -604,11 +609,13 @@ public abstract class ReflectionUtils {
* interfaces, since those are effectively to be treated just like declared methods.
* @param clazz the class to introspect
* @return the cached array of methods
* @throws IllegalStateException if introspection fails
* @see Class#getDeclaredMethods()
*/
private static Method[] getDeclaredMethods(Class<?> clazz) {
Method[] result = declaredMethodsCache.get(clazz);
if (result == null) {
try {
Method[] declaredMethods = clazz.getDeclaredMethods();
List<Method> defaultMethods = findConcreteMethodsOnInterfaces(clazz);
if (defaultMethods != null) {
@ -625,6 +632,11 @@ public abstract class ReflectionUtils {
}
declaredMethodsCache.put(clazz, (result.length == 0 ? NO_METHODS : result));
}
catch (Throwable ex) {
throw new IllegalStateException("Failed to introspect Class [" + clazz +
"] from ClassLoader [" + clazz.getClassLoader() + "]", ex);
}
}
return result;
}
@ -649,6 +661,7 @@ public abstract class ReflectionUtils {
* @param clazz the target class to analyze
* @param fc the callback to invoke for each field
* @since 4.2
* @throws IllegalStateException if introspection fails
* @see #doWithFields
*/
public static void doWithLocalFields(Class<?> clazz, FieldCallback fc) {
@ -667,6 +680,7 @@ public abstract class ReflectionUtils {
* class hierarchy to get all declared fields.
* @param clazz the target class to analyze
* @param fc the callback to invoke for each field
* @throws IllegalStateException if introspection fails
*/
public static void doWithFields(Class<?> clazz, FieldCallback fc) {
doWithFields(clazz, fc, null);
@ -678,6 +692,7 @@ public abstract class ReflectionUtils {
* @param clazz the target class to analyze
* @param fc the callback to invoke for each field
* @param ff the filter that determines the fields to apply the callback to
* @throws IllegalStateException if introspection fails
*/
public static void doWithFields(Class<?> clazz, FieldCallback fc, FieldFilter ff) {
// Keep backing up the inheritance hierarchy.
@ -705,14 +720,21 @@ public abstract class ReflectionUtils {
* in order to avoid the JVM's SecurityManager check and defensive array copying.
* @param clazz the class to introspect
* @return the cached array of fields
* @throws IllegalStateException if introspection fails
* @see Class#getDeclaredFields()
*/
private static Field[] getDeclaredFields(Class<?> clazz) {
Field[] result = declaredFieldsCache.get(clazz);
if (result == null) {
try {
result = clazz.getDeclaredFields();
declaredFieldsCache.put(clazz, (result.length == 0 ? NO_FIELDS : result));
}
catch (Throwable ex) {
throw new IllegalStateException("Failed to introspect Class [" + clazz +
"] from ClassLoader [" + clazz.getClassLoader() + "]", ex);
}
}
return result;
}
@ -720,6 +742,7 @@ public abstract class ReflectionUtils {
* Given the source object and the destination, which must be the same class
* or a subclass, copy all fields, including inherited fields. Designed to
* work on objects with public no-arg constructors.
* @throws IllegalStateException if introspection fails
*/
public static void shallowCopyFieldState(final Object src, final Object dest) {
if (src == null) {