diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java index 55bcd6728d0..a63a42cda0c 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java @@ -216,7 +216,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"); @@ -234,36 +234,53 @@ 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 - public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException { - Lookup lookup = method.getAnnotation(Lookup.class); - if (lookup != null) { - LookupOverride override = new LookupOverride(method, lookup.value()); - try { - RootBeanDefinition mbd = (RootBeanDefinition) beanFactory.getMergedBeanDefinition(beanName); - mbd.getMethodOverrides().addOverride(override); - } - catch (NoSuchBeanDefinitionException ex) { - throw new BeanCreationException(beanName, - "Cannot apply @Lookup to beans without corresponding bean definition"); + try { + ReflectionUtils.doWithMethods(beanClass, new ReflectionUtils.MethodCallback() { + @Override + public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException { + Lookup lookup = method.getAnnotation(Lookup.class); + if (lookup != null) { + LookupOverride override = new LookupOverride(method, lookup.value()); + try { + RootBeanDefinition mbd = (RootBeanDefinition) beanFactory.getMergedBeanDefinition(beanName); + mbd.getMethodOverrides().addOverride(override); + } + catch (NoSuchBeanDefinitionException ex) { + throw new BeanCreationException(beanName, + "Cannot apply @Lookup to beans without corresponding bean definition"); + } } } - } - }); + }); + } + catch (IllegalStateException ex) { + throw new BeanCreationException(beanName, "Lookup method resolution failed", ex); + } this.lookupMethodsChecked.add(beanName); } // 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(); - List> candidates = new ArrayList<>(rawCandidates.length); + 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> candidates = new ArrayList>(rawCandidates.length); Constructor requiredConstructor = null; Constructor defaultConstructor = null; for (Constructor candidate : rawCandidates) { @@ -316,9 +333,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()]); @@ -357,9 +374,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 { @@ -369,7 +386,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); } } @@ -405,8 +423,7 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean Class targetClass = clazz; do { - final LinkedList currElements = - new LinkedList<>(); + final LinkedList currElements = new LinkedList<>(); ReflectionUtils.doWithLocalFields(targetClass, new ReflectionUtils.FieldCallback() { @Override @@ -442,7 +459,8 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean } if (method.getParameterCount() == 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); @@ -625,7 +643,7 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean Class[] paramTypes = method.getParameterTypes(); arguments = new Object[paramTypes.length]; DependencyDescriptor[] descriptors = new DependencyDescriptor[paramTypes.length]; - Set autowiredBeanNames = new LinkedHashSet<>(paramTypes.length); + Set autowiredBeans = new LinkedHashSet<>(paramTypes.length); TypeConverter typeConverter = beanFactory.getTypeConverter(); for (int i = 0; i < arguments.length; i++) { MethodParameter methodParam = new MethodParameter(method, i); @@ -633,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; @@ -651,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 it = autowiredBeanNames.iterator(); + registerDependentBeans(beanName, autowiredBeans); + if (autowiredBeans.size() == paramTypes.length) { + Iterator it = autowiredBeans.iterator(); for (int i = 0; i < paramTypes.length; i++) { String autowiredBeanName = it.next(); if (beanFactory.containsBean(autowiredBeanName)) { @@ -702,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); } } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/RequiredAnnotationBeanPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/RequiredAnnotationBeanPostProcessor.java index e024deb6e0b..b2cedcb271d 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/RequiredAnnotationBeanPostProcessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/RequiredAnnotationBeanPostProcessor.java @@ -140,8 +140,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)) { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessorAdapter.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessorAdapter.java index 3e02846fcb5..3f7e1746b70 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessorAdapter.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessorAdapter.java @@ -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; } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/Scope.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/Scope.java index 2911a123a1b..4e313c7919c 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/Scope.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/Scope.java @@ -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. @@ -68,6 +68,7 @@ public interface Scope { * @param objectFactory the {@link ObjectFactory} to use to create the scoped * object if it is not present in the underlying storage mechanism * @return the desired object (never {@code null}) + * @throws IllegalStateException if the underlying scope is not currently active */ Object get(String name, ObjectFactory objectFactory); @@ -84,6 +85,7 @@ public interface Scope { * removing an object. * @param name the name of the object to remove * @return the removed object, or {@code null} if no object was present + * @throws IllegalStateException if the underlying scope is not currently active * @see #registerDestructionCallback */ Object remove(String name); @@ -112,6 +114,7 @@ public interface Scope { * so it can safely be executed without an enclosing try-catch block. * Furthermore, the Runnable will usually be serializable, provided * that its target object is serializable as well. + * @throws IllegalStateException if the underlying scope is not currently active * @see org.springframework.beans.factory.DisposableBean * @see org.springframework.beans.factory.support.AbstractBeanDefinition#getDestroyMethodName() * @see DestructionAwareBeanPostProcessor @@ -123,6 +126,7 @@ public interface Scope { * E.g. the HttpServletRequest object for key "request". * @param key the contextual key * @return the corresponding object, or {@code null} if none found + * @throws IllegalStateException if the underlying scope is not currently active */ Object resolveContextualObject(String key); @@ -139,6 +143,7 @@ public interface Scope { * underlying storage mechanism has no obvious candidate for such an ID. * @return the conversation ID, or {@code null} if there is no * conversation ID for the current scope + * @throws IllegalStateException if the underlying scope is not currently active */ String getConversationId(); diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java index b7871d7a6a0..cffbc85f809 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java @@ -479,11 +479,25 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac "BeanPostProcessor before instantiation of bean failed", ex); } - Object beanInstance = doCreateBean(beanName, mbdToUse, args); - if (logger.isDebugEnabled()) { - logger.debug("Finished creating instance of bean '" + beanName + "'"); + 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 (ImplicitlyAppearedSingletonException ex) { + // An IllegalStateException to be communicated up to DefaultSingletonBeanRegistry... + throw ex; + } + catch (Throwable ex) { + throw new BeanCreationException( + mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex); } - return beanInstance; } /** @@ -500,7 +514,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 +531,13 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac // Allow post-processors to modify the merged bean definition. synchronized (mbd.postProcessingLock) { if (!mbd.postProcessed) { - applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); + try { + applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); + } + catch (Throwable ex) { + throw new BeanCreationException(mbd.getResourceDescription(), beanName, + "Post-processing of bean type [" + beanType.getName() + "] failed", ex); + } mbd.postProcessed = true; } } @@ -550,7 +572,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 +609,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 +797,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,18 +958,12 @@ 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; - bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName); - } + for (BeanPostProcessor bp : getBeanPostProcessors()) { + if (bp instanceof MergedBeanDefinitionPostProcessor) { + MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp; + bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName); } } - catch (Exception ex) { - throw new BeanCreationException(mbd.getResourceDescription(), beanName, - "Post-processing failed of bean type [" + beanType + "] failed", ex); - } } /** @@ -1107,7 +1126,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 +1679,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) : diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java index 560a9ac9f69..1f4cc19f1e1 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java @@ -285,13 +285,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); } } @@ -458,19 +458,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() { @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 { @@ -1051,11 +1051,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 @@ -1236,12 +1236,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"); } } @@ -1357,12 +1358,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)) { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java index 009bebf78fd..1fb4c1e6bd2 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java @@ -375,8 +375,7 @@ class ConstructorResolver { "factory-bean '" + factoryBeanName + "' (or a BeanPostProcessor involved) returned null"); } if (mbd.isSingleton() && this.beanFactory.containsSingleton(beanName)) { - throw new IllegalStateException("About-to-be-created singleton instance implicitly appeared " + - "through the creation of the factory bean that its bean definition points to"); + throw new ImplicitlyAppearedSingletonException(); } factoryClass = factoryBean.getClass(); isStatic = false; diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java index 152a40c6793..077c7804ee8 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java @@ -214,7 +214,7 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements if (singletonObject == null) { if (this.singletonsCurrentlyInDestruction) { throw new BeanCreationNotAllowedException(beanName, - "Singleton bean creation not allowed while the singletons of this factory are in destruction " + + "Singleton bean creation not allowed while singletons of this factory are in destruction " + "(Do not request a bean from a BeanFactory in a destroy method implementation!)"); } if (logger.isDebugEnabled()) { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/ImplicitlyAppearedSingletonException.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/ImplicitlyAppearedSingletonException.java new file mode 100644 index 00000000000..328108757e2 --- /dev/null +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/ImplicitlyAppearedSingletonException.java @@ -0,0 +1,32 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.beans.factory.support; + +/** + * Internal exception to be propagated from {@link ConstructorResolver}. + * + * @since 5.0 + */ +@SuppressWarnings("serial") +class ImplicitlyAppearedSingletonException extends IllegalStateException { + + public ImplicitlyAppearedSingletonException() { + super("About-to-be-created singleton instance implicitly appeared through the " + + "creation of the factory bean that its bean definition points to"); + } + +} diff --git a/spring-core/src/main/java/org/springframework/util/ReflectionUtils.java b/spring-core/src/main/java/org/springframework/util/ReflectionUtils.java index 934e92dca3a..adeb68e9e5e 100644 --- a/spring-core/src/main/java/org/springframework/util/ReflectionUtils.java +++ b/spring-core/src/main/java/org/springframework/util/ReflectionUtils.java @@ -499,6 +499,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) { @@ -520,6 +521,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) { @@ -534,6 +536,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. @@ -563,6 +566,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 methods = new ArrayList<>(32); @@ -580,6 +584,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 methods = new ArrayList<>(32); @@ -620,26 +625,34 @@ 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) { + Assert.notNull(clazz, "Class must not be null"); Method[] result = declaredMethodsCache.get(clazz); if (result == null) { - Method[] declaredMethods = clazz.getDeclaredMethods(); - List defaultMethods = findConcreteMethodsOnInterfaces(clazz); - if (defaultMethods != null) { - result = new Method[declaredMethods.length + defaultMethods.size()]; - System.arraycopy(declaredMethods, 0, result, 0, declaredMethods.length); - int index = declaredMethods.length; - for (Method defaultMethod : defaultMethods) { - result[index] = defaultMethod; - index++; + try { + Method[] declaredMethods = clazz.getDeclaredMethods(); + List defaultMethods = findConcreteMethodsOnInterfaces(clazz); + if (defaultMethods != null) { + result = new Method[declaredMethods.length + defaultMethods.size()]; + System.arraycopy(declaredMethods, 0, result, 0, declaredMethods.length); + int index = declaredMethods.length; + for (Method defaultMethod : defaultMethods) { + result[index] = defaultMethod; + index++; + } + } + else { + result = declaredMethods; } + declaredMethodsCache.put(clazz, (result.length == 0 ? NO_METHODS : result)); } - else { - result = declaredMethods; + catch (Throwable ex) { + throw new IllegalStateException("Failed to introspect Class [" + clazz + + "] from ClassLoader [" + clazz.getClassLoader() + "]", ex); } - declaredMethodsCache.put(clazz, (result.length == 0 ? NO_METHODS : result)); } return result; } @@ -665,6 +678,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) { @@ -683,6 +697,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); @@ -694,6 +709,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. @@ -721,13 +737,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) { + Assert.notNull(clazz, "Class must not be null"); Field[] result = declaredFieldsCache.get(clazz); if (result == null) { - result = clazz.getDeclaredFields(); - declaredFieldsCache.put(clazz, (result.length == 0 ? NO_FIELDS : result)); + 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; } @@ -736,6 +760,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) {