Skip to content

Commit cf479bf

Browse files
committed
Consistent throwing of BeanCreationExceptions (and reflection exceptions)
Issue: SPR-14883 (cherry picked from commit b42d731)
1 parent 1f12811 commit cf479bf

File tree

10 files changed

+204
-102
lines changed

10 files changed

+204
-102
lines changed

spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java

Lines changed: 55 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ public int getOrder() {
216216
}
217217

218218
@Override
219-
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
219+
public void setBeanFactory(BeanFactory beanFactory) {
220220
if (!(beanFactory instanceof ConfigurableListableBeanFactory)) {
221221
throw new IllegalArgumentException(
222222
"AutowiredAnnotationBeanPostProcessor requires a ConfigurableListableBeanFactory");
@@ -234,36 +234,53 @@ public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, C
234234
}
235235

236236
@Override
237-
public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, final String beanName) throws BeansException {
237+
public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, final String beanName)
238+
throws BeanCreationException {
239+
240+
// Let's check for lookup methods here..
238241
if (!this.lookupMethodsChecked.contains(beanName)) {
239-
ReflectionUtils.doWithMethods(beanClass, new ReflectionUtils.MethodCallback() {
240-
@Override
241-
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
242-
Lookup lookup = method.getAnnotation(Lookup.class);
243-
if (lookup != null) {
244-
LookupOverride override = new LookupOverride(method, lookup.value());
245-
try {
246-
RootBeanDefinition mbd = (RootBeanDefinition) beanFactory.getMergedBeanDefinition(beanName);
247-
mbd.getMethodOverrides().addOverride(override);
248-
}
249-
catch (NoSuchBeanDefinitionException ex) {
250-
throw new BeanCreationException(beanName,
251-
"Cannot apply @Lookup to beans without corresponding bean definition");
242+
try {
243+
ReflectionUtils.doWithMethods(beanClass, new ReflectionUtils.MethodCallback() {
244+
@Override
245+
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
246+
Lookup lookup = method.getAnnotation(Lookup.class);
247+
if (lookup != null) {
248+
LookupOverride override = new LookupOverride(method, lookup.value());
249+
try {
250+
RootBeanDefinition mbd = (RootBeanDefinition) beanFactory.getMergedBeanDefinition(beanName);
251+
mbd.getMethodOverrides().addOverride(override);
252+
}
253+
catch (NoSuchBeanDefinitionException ex) {
254+
throw new BeanCreationException(beanName,
255+
"Cannot apply @Lookup to beans without corresponding bean definition");
256+
}
252257
}
253258
}
254-
}
255-
});
259+
});
260+
}
261+
catch (IllegalStateException ex) {
262+
throw new BeanCreationException(beanName, "Lookup method resolution failed", ex);
263+
}
256264
this.lookupMethodsChecked.add(beanName);
257265
}
258266

259267
// Quick check on the concurrent map first, with minimal locking.
260268
Constructor<?>[] candidateConstructors = this.candidateConstructorsCache.get(beanClass);
261269
if (candidateConstructors == null) {
270+
// Fully synchronized resolution now...
262271
synchronized (this.candidateConstructorsCache) {
263272
candidateConstructors = this.candidateConstructorsCache.get(beanClass);
264273
if (candidateConstructors == null) {
265-
Constructor<?>[] rawCandidates = beanClass.getDeclaredConstructors();
266-
List<Constructor<?>> candidates = new ArrayList<>(rawCandidates.length);
274+
Constructor<?>[] rawCandidates;
275+
try {
276+
rawCandidates = beanClass.getDeclaredConstructors();
277+
}
278+
catch (Throwable ex) {
279+
throw new BeanCreationException(beanName,
280+
"Resolution of declared constructors on bean Class [" + beanClass.getName() +
281+
"] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex);
282+
}
283+
List<Constructor<?>> candidates = new ArrayList<Constructor<?>>(rawCandidates.length);
267284
Constructor<?> requiredConstructor = null;
268285
Constructor<?> defaultConstructor = null;
269286
for (Constructor<?> candidate : rawCandidates) {
@@ -316,9 +333,9 @@ else if (candidate.getParameterCount() == 0) {
316333
}
317334
else if (candidates.size() == 1 && logger.isWarnEnabled()) {
318335
logger.warn("Inconsistent constructor declaration on bean with name '" + beanName +
319-
"': single autowire-marked constructor flagged as optional - this constructor " +
320-
"is effectively required since there is no default constructor to fall back to: " +
321-
candidates.get(0));
336+
"': single autowire-marked constructor flagged as optional - " +
337+
"this constructor is effectively required since there is no " +
338+
"default constructor to fall back to: " + candidates.get(0));
322339
}
323340
}
324341
candidateConstructors = candidates.toArray(new Constructor<?>[candidates.size()]);
@@ -357,9 +374,9 @@ public PropertyValues postProcessPropertyValues(
357374
* 'Native' processing method for direct calls with an arbitrary target instance,
358375
* resolving all of its fields and methods which are annotated with {@code @Autowired}.
359376
* @param bean the target instance to process
360-
* @throws BeansException if autowiring failed
377+
* @throws BeanCreationException if autowiring failed
361378
*/
362-
public void processInjection(Object bean) throws BeansException {
379+
public void processInjection(Object bean) throws BeanCreationException {
363380
Class<?> clazz = bean.getClass();
364381
InjectionMetadata metadata = findAutowiringMetadata(clazz.getName(), clazz, null);
365382
try {
@@ -369,7 +386,8 @@ public void processInjection(Object bean) throws BeansException {
369386
throw ex;
370387
}
371388
catch (Throwable ex) {
372-
throw new BeanCreationException("Injection of autowired dependencies failed for class [" + clazz + "]", ex);
389+
throw new BeanCreationException(
390+
"Injection of autowired dependencies failed for class [" + clazz + "]", ex);
373391
}
374392
}
375393

@@ -405,8 +423,7 @@ private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
405423
Class<?> targetClass = clazz;
406424

407425
do {
408-
final LinkedList<InjectionMetadata.InjectedElement> currElements =
409-
new LinkedList<>();
426+
final LinkedList<InjectionMetadata.InjectedElement> currElements = new LinkedList<>();
410427

411428
ReflectionUtils.doWithLocalFields(targetClass, new ReflectionUtils.FieldCallback() {
412429
@Override
@@ -442,7 +459,8 @@ public void doWith(Method method) throws IllegalArgumentException, IllegalAccess
442459
}
443460
if (method.getParameterCount() == 0) {
444461
if (logger.isWarnEnabled()) {
445-
logger.warn("Autowired annotation should be used on methods with parameters: " + method);
462+
logger.warn("Autowired annotation should only be used on methods with parameters: " +
463+
method);
446464
}
447465
}
448466
boolean required = determineRequiredStatus(ann);
@@ -625,15 +643,15 @@ protected void inject(Object bean, String beanName, PropertyValues pvs) throws T
625643
Class<?>[] paramTypes = method.getParameterTypes();
626644
arguments = new Object[paramTypes.length];
627645
DependencyDescriptor[] descriptors = new DependencyDescriptor[paramTypes.length];
628-
Set<String> autowiredBeanNames = new LinkedHashSet<>(paramTypes.length);
646+
Set<String> autowiredBeans = new LinkedHashSet<>(paramTypes.length);
629647
TypeConverter typeConverter = beanFactory.getTypeConverter();
630648
for (int i = 0; i < arguments.length; i++) {
631649
MethodParameter methodParam = new MethodParameter(method, i);
632650
DependencyDescriptor currDesc = new DependencyDescriptor(methodParam, this.required);
633651
currDesc.setContainingClass(bean.getClass());
634652
descriptors[i] = currDesc;
635653
try {
636-
Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeanNames, typeConverter);
654+
Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeans, typeConverter);
637655
if (arg == null && !this.required) {
638656
arguments = null;
639657
break;
@@ -651,9 +669,9 @@ protected void inject(Object bean, String beanName, PropertyValues pvs) throws T
651669
for (int i = 0; i < arguments.length; i++) {
652670
this.cachedMethodArguments[i] = descriptors[i];
653671
}
654-
registerDependentBeans(beanName, autowiredBeanNames);
655-
if (autowiredBeanNames.size() == paramTypes.length) {
656-
Iterator<String> it = autowiredBeanNames.iterator();
672+
registerDependentBeans(beanName, autowiredBeans);
673+
if (autowiredBeans.size() == paramTypes.length) {
674+
Iterator<String> it = autowiredBeans.iterator();
657675
for (int i = 0; i < paramTypes.length; i++) {
658676
String autowiredBeanName = it.next();
659677
if (beanFactory.containsBean(autowiredBeanName)) {
@@ -702,19 +720,19 @@ private Object[] resolveCachedArguments(String beanName) {
702720
@SuppressWarnings("serial")
703721
private static class ShortcutDependencyDescriptor extends DependencyDescriptor {
704722

705-
private final String shortcutName;
723+
private final String shortcut;
706724

707725
private final Class<?> requiredType;
708726

709-
public ShortcutDependencyDescriptor(DependencyDescriptor original, String shortcutName, Class<?> requiredType) {
727+
public ShortcutDependencyDescriptor(DependencyDescriptor original, String shortcut, Class<?> requiredType) {
710728
super(original);
711-
this.shortcutName = shortcutName;
729+
this.shortcut = shortcut;
712730
this.requiredType = requiredType;
713731
}
714732

715733
@Override
716734
public Object resolveShortcut(BeanFactory beanFactory) {
717-
return resolveCandidate(this.shortcutName, this.requiredType, beanFactory);
735+
return resolveCandidate(this.shortcut, this.requiredType, beanFactory);
718736
}
719737
}
720738

spring-beans/src/main/java/org/springframework/beans/factory/annotation/RequiredAnnotationBeanPostProcessor.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,7 @@ public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, C
140140

141141
@Override
142142
public PropertyValues postProcessPropertyValues(
143-
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName)
144-
throws BeansException {
143+
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
145144

146145
if (!this.validatedBeanNames.contains(beanName)) {
147146
if (!shouldSkip(this.beanFactory, beanName)) {

spring-beans/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessorAdapter.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -66,8 +66,7 @@ public boolean postProcessAfterInstantiation(Object bean, String beanName) throw
6666

6767
@Override
6868
public PropertyValues postProcessPropertyValues(
69-
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName)
70-
throws BeansException {
69+
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
7170

7271
return pvs;
7372
}

spring-beans/src/main/java/org/springframework/beans/factory/config/Scope.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -68,6 +68,7 @@ public interface Scope {
6868
* @param objectFactory the {@link ObjectFactory} to use to create the scoped
6969
* object if it is not present in the underlying storage mechanism
7070
* @return the desired object (never {@code null})
71+
* @throws IllegalStateException if the underlying scope is not currently active
7172
*/
7273
Object get(String name, ObjectFactory<?> objectFactory);
7374

@@ -84,6 +85,7 @@ public interface Scope {
8485
* removing an object.
8586
* @param name the name of the object to remove
8687
* @return the removed object, or {@code null} if no object was present
88+
* @throws IllegalStateException if the underlying scope is not currently active
8789
* @see #registerDestructionCallback
8890
*/
8991
Object remove(String name);
@@ -112,6 +114,7 @@ public interface Scope {
112114
* so it can safely be executed without an enclosing try-catch block.
113115
* Furthermore, the Runnable will usually be serializable, provided
114116
* that its target object is serializable as well.
117+
* @throws IllegalStateException if the underlying scope is not currently active
115118
* @see org.springframework.beans.factory.DisposableBean
116119
* @see org.springframework.beans.factory.support.AbstractBeanDefinition#getDestroyMethodName()
117120
* @see DestructionAwareBeanPostProcessor
@@ -123,6 +126,7 @@ public interface Scope {
123126
* E.g. the HttpServletRequest object for key "request".
124127
* @param key the contextual key
125128
* @return the corresponding object, or {@code null} if none found
129+
* @throws IllegalStateException if the underlying scope is not currently active
126130
*/
127131
Object resolveContextualObject(String key);
128132

@@ -139,6 +143,7 @@ public interface Scope {
139143
* underlying storage mechanism has no obvious candidate for such an ID.
140144
* @return the conversation ID, or {@code null} if there is no
141145
* conversation ID for the current scope
146+
* @throws IllegalStateException if the underlying scope is not currently active
142147
*/
143148
String getConversationId();
144149

spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java

Lines changed: 44 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -479,11 +479,25 @@ protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] ar
479479
"BeanPostProcessor before instantiation of bean failed", ex);
480480
}
481481

482-
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
483-
if (logger.isDebugEnabled()) {
484-
logger.debug("Finished creating instance of bean '" + beanName + "'");
482+
try {
483+
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
484+
if (logger.isDebugEnabled()) {
485+
logger.debug("Finished creating instance of bean '" + beanName + "'");
486+
}
487+
return beanInstance;
488+
}
489+
catch (BeanCreationException ex) {
490+
// A previously detected exception with proper bean creation context already...
491+
throw ex;
492+
}
493+
catch (ImplicitlyAppearedSingletonException ex) {
494+
// An IllegalStateException to be communicated up to DefaultSingletonBeanRegistry...
495+
throw ex;
496+
}
497+
catch (Throwable ex) {
498+
throw new BeanCreationException(
499+
mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
485500
}
486-
return beanInstance;
487501
}
488502

489503
/**
@@ -500,7 +514,9 @@ protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] ar
500514
* @see #instantiateUsingFactoryMethod
501515
* @see #autowireConstructor
502516
*/
503-
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
517+
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
518+
throws BeanCreationException {
519+
504520
// Instantiate the bean.
505521
BeanWrapper instanceWrapper = null;
506522
if (mbd.isSingleton()) {
@@ -515,7 +531,13 @@ protected Object doCreateBean(final String beanName, final RootBeanDefinition mb
515531
// Allow post-processors to modify the merged bean definition.
516532
synchronized (mbd.postProcessingLock) {
517533
if (!mbd.postProcessed) {
518-
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
534+
try {
535+
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
536+
}
537+
catch (Throwable ex) {
538+
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
539+
"Post-processing of bean type [" + beanType.getName() + "] failed", ex);
540+
}
519541
mbd.postProcessed = true;
520542
}
521543
}
@@ -550,7 +572,8 @@ public Object getObject() throws BeansException {
550572
throw (BeanCreationException) ex;
551573
}
552574
else {
553-
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
575+
throw new BeanCreationException(
576+
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
554577
}
555578
}
556579

@@ -586,7 +609,8 @@ else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
586609
registerDisposableBeanIfNecessary(beanName, bean, mbd);
587610
}
588611
catch (BeanDefinitionValidationException ex) {
589-
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
612+
throw new BeanCreationException(
613+
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
590614
}
591615

592616
return exposedObject;
@@ -773,10 +797,11 @@ class Holder { Class<?> value = null; }
773797
ReflectionUtils.doWithMethods(fbClass,
774798
new ReflectionUtils.MethodCallback() {
775799
@Override
776-
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
800+
public void doWith(Method method) {
777801
if (method.getName().equals(factoryMethodName) &&
778802
FactoryBean.class.isAssignableFrom(method.getReturnType())) {
779-
objectType.value = GenericTypeResolver.resolveReturnTypeArgument(method, FactoryBean.class);
803+
objectType.value = GenericTypeResolver.resolveReturnTypeArgument(
804+
method, FactoryBean.class);
780805
}
781806
}
782807
});
@@ -933,18 +958,12 @@ private FactoryBean<?> getNonSingletonFactoryBeanForTypeCheck(String beanName, R
933958
protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName)
934959
throws BeansException {
935960

936-
try {
937-
for (BeanPostProcessor bp : getBeanPostProcessors()) {
938-
if (bp instanceof MergedBeanDefinitionPostProcessor) {
939-
MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
940-
bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);
941-
}
961+
for (BeanPostProcessor bp : getBeanPostProcessors()) {
962+
if (bp instanceof MergedBeanDefinitionPostProcessor) {
963+
MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
964+
bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);
942965
}
943966
}
944-
catch (Exception ex) {
945-
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
946-
"Post-processing failed of bean type [" + beanType + "] failed", ex);
947-
}
948967
}
949968

950969
/**
@@ -1107,7 +1126,8 @@ public Object run() {
11071126
return bw;
11081127
}
11091128
catch (Throwable ex) {
1110-
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
1129+
throw new BeanCreationException(
1130+
mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
11111131
}
11121132
}
11131133

@@ -1659,7 +1679,9 @@ public Object run() throws Exception {
16591679
* methods with arguments.
16601680
* @see #invokeInitMethods
16611681
*/
1662-
protected void invokeCustomInitMethod(String beanName, final Object bean, RootBeanDefinition mbd) throws Throwable {
1682+
protected void invokeCustomInitMethod(String beanName, final Object bean, RootBeanDefinition mbd)
1683+
throws Throwable {
1684+
16631685
String initMethodName = mbd.getInitMethodName();
16641686
final Method initMethod = (mbd.isNonPublicAccessAllowed() ?
16651687
BeanUtils.findMethod(bean.getClass(), initMethodName) :

0 commit comments

Comments
 (0)