Skip to content

Commit bc3e585

Browse files
committed
Detect a FactoryBean type match even if predictBeanType returned a non-FactoryBean type
This change should be the final piece in the puzzle to let SmartInstantiationAwareBeanPostProcessor's predictBeanType predict a FactoryBean-produced type, effectively as a semantic alternative to its postProcessBeforeInstantiation-related role. Issue: SPR-10517
1 parent 25e29b8 commit bc3e585

File tree

2 files changed

+56
-38
lines changed

2 files changed

+56
-38
lines changed

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

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -521,26 +521,32 @@ else if (containsSingleton(beanName) && !containsBeanDefinition(beanName)) {
521521
}
522522
}
523523

524-
Class<?> beanClass = predictBeanType(beanName, mbd, typesToMatch);
525-
if (beanClass == null) {
524+
Class<?> beanType = predictBeanType(beanName, mbd, typesToMatch);
525+
if (beanType == null) {
526526
return false;
527527
}
528528

529529
// Check bean class whether we're dealing with a FactoryBean.
530-
if (FactoryBean.class.isAssignableFrom(beanClass)) {
530+
if (FactoryBean.class.isAssignableFrom(beanType)) {
531531
if (!BeanFactoryUtils.isFactoryDereference(name)) {
532532
// If it's a FactoryBean, we want to look at what it creates, not the factory class.
533-
Class<?> type = getTypeForFactoryBean(beanName, mbd);
534-
return (type != null && typeToMatch.isAssignableFrom(type));
535-
}
536-
else {
537-
return typeToMatch.isAssignableFrom(beanClass);
533+
beanType = getTypeForFactoryBean(beanName, mbd);
534+
if (beanType == null) {
535+
return false;
536+
}
538537
}
539538
}
540-
else {
541-
return !BeanFactoryUtils.isFactoryDereference(name) &&
542-
typeToMatch.isAssignableFrom(beanClass);
539+
else if (BeanFactoryUtils.isFactoryDereference(name)) {
540+
// Special case: A SmartInstantiationAwareBeanPostProcessor returned a non-FactoryBean
541+
// type but we nevertheless are being asked to dereference a FactoryBean...
542+
// Let's check the original bean class and proceed with it if it is a FactoryBean.
543+
beanType = predictBeanType(beanName, mbd, FactoryBean.class);
544+
if (beanType == null || !FactoryBean.class.isAssignableFrom(beanType)) {
545+
return false;
546+
}
543547
}
548+
549+
return typeToMatch.isAssignableFrom(beanType);
544550
}
545551
}
546552

@@ -1377,8 +1383,8 @@ protected Class<?> predictBeanType(String beanName, RootBeanDefinition mbd, Clas
13771383
* @param mbd the corresponding bean definition
13781384
*/
13791385
protected boolean isFactoryBean(String beanName, RootBeanDefinition mbd) {
1380-
Class<?> beanClass = predictBeanType(beanName, mbd, FactoryBean.class);
1381-
return (beanClass != null && FactoryBean.class.isAssignableFrom(beanClass));
1386+
Class<?> beanType = predictBeanType(beanName, mbd, FactoryBean.class);
1387+
return (beanType != null && FactoryBean.class.isAssignableFrom(beanType));
13821388
}
13831389

13841390
/**

spring-beans/src/test/java/org/springframework/beans/factory/support/Spr8954Tests.java

Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -16,59 +16,59 @@
1616

1717
package org.springframework.beans.factory.support;
1818

19-
import static org.hamcrest.CoreMatchers.*;
20-
import static org.junit.Assert.*;
21-
19+
import java.util.Arrays;
2220
import java.util.Map;
2321

22+
import org.junit.Before;
2423
import org.junit.Test;
2524

26-
import org.springframework.beans.BeansException;
2725
import org.springframework.beans.factory.FactoryBean;
26+
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
2827
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
29-
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
30-
import org.springframework.beans.factory.support.RootBeanDefinition;
28+
29+
import static org.hamcrest.CoreMatchers.*;
30+
import static org.junit.Assert.*;
3131

3232
/**
3333
* Unit tests for SPR-8954, in which a custom {@link InstantiationAwareBeanPostProcessor}
3434
* forces the predicted type of a FactoryBean, effectively preventing retrieval of the
3535
* bean from calls to #getBeansOfType(FactoryBean.class). The implementation of
36-
* {@link AbstractBeanFactory#isFactoryBean(String, RootBeanDefinition)} now ensures
37-
* that not only the predicted bean type is considered, but also the original bean
38-
* definition's beanClass.
36+
* {@link AbstractBeanFactory#isFactoryBean(String, RootBeanDefinition)} now ensures that
37+
* not only the predicted bean type is considered, but also the original bean definition's
38+
* beanClass.
3939
*
4040
* @author Chris Beams
4141
* @author Oliver Gierke
4242
*/
4343
public class Spr8954Tests {
4444

45-
@Test
46-
public void repro() {
47-
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
45+
private DefaultListableBeanFactory bf;
46+
47+
@Before
48+
public void setUp() {
49+
bf = new DefaultListableBeanFactory();
4850
bf.registerBeanDefinition("foo", new RootBeanDefinition(FooFactoryBean.class));
4951
bf.addBeanPostProcessor(new PredictingBPP());
52+
}
5053

54+
@Test
55+
public void repro() {
5156
assertThat(bf.getBean("foo"), instanceOf(Foo.class));
5257
assertThat(bf.getBean("&foo"), instanceOf(FooFactoryBean.class));
53-
5458
assertThat(bf.isTypeMatch("&foo", FactoryBean.class), is(true));
5559

5660
@SuppressWarnings("rawtypes")
5761
Map<String, FactoryBean> fbBeans = bf.getBeansOfType(FactoryBean.class);
58-
assertThat(1, equalTo(fbBeans.size()));
59-
assertThat("&foo", equalTo(fbBeans.keySet().iterator().next()));
62+
assertThat(fbBeans.size(), is(1));
63+
assertThat(fbBeans.keySet(), hasItem("&foo"));
6064

6165
Map<String, AnInterface> aiBeans = bf.getBeansOfType(AnInterface.class);
62-
assertThat(1, equalTo(aiBeans.size()));
63-
assertThat("&foo", equalTo(aiBeans.keySet().iterator().next()));
66+
assertThat(aiBeans.size(), is(1));
67+
assertThat(aiBeans.keySet(), hasItem("&foo"));
6468
}
6569

6670
@Test
6771
public void findsBeansByTypeIfNotInstantiated() {
68-
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
69-
bf.registerBeanDefinition("foo", new RootBeanDefinition(FooFactoryBean.class));
70-
bf.addBeanPostProcessor(new PredictingBPP());
71-
7272
assertThat(bf.isTypeMatch("&foo", FactoryBean.class), is(true));
7373

7474
@SuppressWarnings("rawtypes")
@@ -77,8 +77,21 @@ public void findsBeansByTypeIfNotInstantiated() {
7777
assertThat("&foo", equalTo(fbBeans.keySet().iterator().next()));
7878

7979
Map<String, AnInterface> aiBeans = bf.getBeansOfType(AnInterface.class);
80-
assertThat(1, equalTo(aiBeans.size()));
81-
assertThat("&foo", equalTo(aiBeans.keySet().iterator().next()));
80+
assertThat(aiBeans.size(), is(1));
81+
assertThat(aiBeans.keySet(), hasItem("&foo"));
82+
}
83+
84+
/**
85+
* SPR-10517
86+
*/
87+
@Test
88+
public void findsFactoryBeanNameByTypeWithoutInstantiation() {
89+
String[] names = bf.getBeanNamesForType(AnInterface.class, false, false);
90+
assertThat(Arrays.asList(names), hasItem("&foo"));
91+
92+
Map<String, AnInterface> beans = bf.getBeansOfType(AnInterface.class, false, false);
93+
assertThat(beans.size(), is(1));
94+
assertThat(beans.keySet(), hasItem("&foo"));
8295
}
8396

8497

@@ -116,8 +129,7 @@ static class PredictingBPP extends InstantiationAwareBeanPostProcessorAdapter {
116129

117130
@Override
118131
public Class<?> predictBeanType(Class<?> beanClass, String beanName) {
119-
return FactoryBean.class.isAssignableFrom(beanClass) ?
120-
PredictedType.class : null;
132+
return FactoryBean.class.isAssignableFrom(beanClass) ? PredictedType.class : null;
121133
}
122134
}
123135

0 commit comments

Comments
 (0)