Skip to content

Commit 7b2eebe

Browse files
committed
ResolvableType-based matching consistently respects generic factory method return type (even for pre-initialized raw singleton instance)
Issue: SPR-17524 (cherry picked from commit ebbe14c)
1 parent cf31f02 commit 7b2eebe

File tree

2 files changed

+180
-6
lines changed

2 files changed

+180
-6
lines changed

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

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -507,12 +507,21 @@ else if (typeToMatch.hasGenerics() && containsBeanDefinition(beanName)) {
507507
// Generics potentially only match on the target class, not on the proxy...
508508
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
509509
Class<?> targetType = mbd.getTargetType();
510-
if (targetType != null && targetType != ClassUtils.getUserClass(beanInstance) &&
511-
typeToMatch.isAssignableFrom(targetType)) {
510+
if (targetType != null && targetType != ClassUtils.getUserClass(beanInstance)) {
512511
// Check raw class match as well, making sure it's exposed on the proxy.
513512
Class<?> classToMatch = typeToMatch.resolve();
514-
return (classToMatch == null || classToMatch.isInstance(beanInstance));
513+
if (classToMatch != null && !classToMatch.isInstance(beanInstance)) {
514+
return false;
515+
}
516+
if (typeToMatch.isAssignableFrom(targetType)) {
517+
return true;
518+
}
515519
}
520+
ResolvableType resolvableType = mbd.targetType;
521+
if (resolvableType == null) {
522+
resolvableType = mbd.factoryMethodReturnType;
523+
}
524+
return (resolvableType != null && typeToMatch.isAssignableFrom(resolvableType));
516525
}
517526
}
518527
return false;
@@ -1359,6 +1368,7 @@ public void clearMetadataCache() {
13591368
@Nullable
13601369
protected Class<?> resolveBeanClass(final RootBeanDefinition mbd, String beanName, final Class<?>... typesToMatch)
13611370
throws CannotLoadBeanClassException {
1371+
13621372
try {
13631373
if (mbd.hasBeanClass()) {
13641374
return mbd.getBeanClass();

spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java

Lines changed: 167 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,10 @@ public void genericsBasedInjectionWithEarlyGenericsMatching() {
570570
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
571571
assertEquals(1, beanNames.length);
572572
assertEquals("stringRepo", beanNames[0]);
573+
574+
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
575+
assertEquals(1, beanNames.length);
576+
assertEquals("stringRepo", beanNames[0]);
573577
}
574578

575579
@Test
@@ -584,6 +588,78 @@ public void genericsBasedInjectionWithLateGenericsMatching() {
584588
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
585589
assertEquals(1, beanNames.length);
586590
assertEquals("stringRepo", beanNames[0]);
591+
592+
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
593+
assertEquals(1, beanNames.length);
594+
assertEquals("stringRepo", beanNames[0]);
595+
}
596+
597+
@Test
598+
public void genericsBasedInjectionWithEarlyGenericsMatchingAndRawFactoryMethod() {
599+
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawFactoryMethodRepositoryConfiguration.class));
600+
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
601+
602+
String[] beanNames = beanFactory.getBeanNamesForType(Repository.class);
603+
assertTrue(ObjectUtils.containsElement(beanNames, "stringRepo"));
604+
605+
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
606+
assertEquals(0, beanNames.length);
607+
608+
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
609+
assertEquals(0, beanNames.length);
610+
}
611+
612+
@Test
613+
public void genericsBasedInjectionWithLateGenericsMatchingAndRawFactoryMethod() {
614+
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawFactoryMethodRepositoryConfiguration.class));
615+
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
616+
beanFactory.preInstantiateSingletons();
617+
618+
String[] beanNames = beanFactory.getBeanNamesForType(Repository.class);
619+
assertTrue(ObjectUtils.containsElement(beanNames, "stringRepo"));
620+
621+
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
622+
assertEquals(1, beanNames.length);
623+
assertEquals("stringRepo", beanNames[0]);
624+
625+
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
626+
assertEquals(1, beanNames.length);
627+
assertEquals("stringRepo", beanNames[0]);
628+
}
629+
630+
@Test
631+
public void genericsBasedInjectionWithEarlyGenericsMatchingAndRawInstance() {
632+
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawInstanceRepositoryConfiguration.class));
633+
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
634+
635+
String[] beanNames = beanFactory.getBeanNamesForType(Repository.class);
636+
assertTrue(ObjectUtils.containsElement(beanNames, "stringRepo"));
637+
638+
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
639+
assertEquals(1, beanNames.length);
640+
assertEquals("stringRepo", beanNames[0]);
641+
642+
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
643+
assertEquals(1, beanNames.length);
644+
assertEquals("stringRepo", beanNames[0]);
645+
}
646+
647+
@Test
648+
public void genericsBasedInjectionWithLateGenericsMatchingAndRawInstance() {
649+
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawInstanceRepositoryConfiguration.class));
650+
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
651+
beanFactory.preInstantiateSingletons();
652+
653+
String[] beanNames = beanFactory.getBeanNamesForType(Repository.class);
654+
assertTrue(ObjectUtils.containsElement(beanNames, "stringRepo"));
655+
656+
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
657+
assertEquals(1, beanNames.length);
658+
assertEquals("stringRepo", beanNames[0]);
659+
660+
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
661+
assertEquals(1, beanNames.length);
662+
assertEquals("stringRepo", beanNames[0]);
587663
}
588664

589665
@Test
@@ -603,6 +679,10 @@ public void genericsBasedInjectionWithEarlyGenericsMatchingOnCglibProxy() {
603679
assertEquals(1, beanNames.length);
604680
assertEquals("stringRepo", beanNames[0]);
605681

682+
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
683+
assertEquals(1, beanNames.length);
684+
assertEquals("stringRepo", beanNames[0]);
685+
606686
assertTrue(AopUtils.isCglibProxy(beanFactory.getBean("stringRepo")));
607687
}
608688

@@ -624,12 +704,16 @@ public void genericsBasedInjectionWithLateGenericsMatchingOnCglibProxy() {
624704
assertEquals(1, beanNames.length);
625705
assertEquals("stringRepo", beanNames[0]);
626706

707+
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
708+
assertEquals(1, beanNames.length);
709+
assertEquals("stringRepo", beanNames[0]);
710+
627711
assertTrue(AopUtils.isCglibProxy(beanFactory.getBean("stringRepo")));
628712
}
629713

630714
@Test
631715
public void genericsBasedInjectionWithLateGenericsMatchingOnCglibProxyAndRawFactoryMethod() {
632-
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawRepositoryConfiguration.class));
716+
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawFactoryMethodRepositoryConfiguration.class));
633717
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
634718
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
635719
autoProxyCreator.setProxyTargetClass(true);
@@ -645,6 +729,35 @@ public void genericsBasedInjectionWithLateGenericsMatchingOnCglibProxyAndRawFact
645729
assertEquals(1, beanNames.length);
646730
assertEquals("stringRepo", beanNames[0]);
647731

732+
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
733+
assertEquals(1, beanNames.length);
734+
assertEquals("stringRepo", beanNames[0]);
735+
736+
assertTrue(AopUtils.isCglibProxy(beanFactory.getBean("stringRepo")));
737+
}
738+
739+
@Test
740+
public void genericsBasedInjectionWithLateGenericsMatchingOnCglibProxyAndRawInstance() {
741+
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawInstanceRepositoryConfiguration.class));
742+
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
743+
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
744+
autoProxyCreator.setProxyTargetClass(true);
745+
autoProxyCreator.setBeanFactory(beanFactory);
746+
beanFactory.addBeanPostProcessor(autoProxyCreator);
747+
beanFactory.registerSingleton("traceInterceptor", new DefaultPointcutAdvisor(new SimpleTraceInterceptor()));
748+
beanFactory.preInstantiateSingletons();
749+
750+
String[] beanNames = beanFactory.getBeanNamesForType(Repository.class);
751+
assertTrue(ObjectUtils.containsElement(beanNames, "stringRepo"));
752+
753+
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
754+
assertEquals(1, beanNames.length);
755+
assertEquals("stringRepo", beanNames[0]);
756+
757+
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
758+
assertEquals(1, beanNames.length);
759+
assertEquals("stringRepo", beanNames[0]);
760+
648761
assertTrue(AopUtils.isCglibProxy(beanFactory.getBean("stringRepo")));
649762
}
650763

@@ -664,6 +777,10 @@ public void genericsBasedInjectionWithEarlyGenericsMatchingOnJdkProxy() {
664777
assertEquals(1, beanNames.length);
665778
assertEquals("stringRepo", beanNames[0]);
666779

780+
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(RepositoryInterface.class, String.class));
781+
assertEquals(1, beanNames.length);
782+
assertEquals("stringRepo", beanNames[0]);
783+
667784
assertTrue(AopUtils.isJdkDynamicProxy(beanFactory.getBean("stringRepo")));
668785
}
669786

@@ -684,12 +801,16 @@ public void genericsBasedInjectionWithLateGenericsMatchingOnJdkProxy() {
684801
assertEquals(1, beanNames.length);
685802
assertEquals("stringRepo", beanNames[0]);
686803

804+
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(RepositoryInterface.class, String.class));
805+
assertEquals(1, beanNames.length);
806+
assertEquals("stringRepo", beanNames[0]);
807+
687808
assertTrue(AopUtils.isJdkDynamicProxy(beanFactory.getBean("stringRepo")));
688809
}
689810

690811
@Test
691812
public void genericsBasedInjectionWithLateGenericsMatchingOnJdkProxyAndRawFactoryMethod() {
692-
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawRepositoryConfiguration.class));
813+
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawFactoryMethodRepositoryConfiguration.class));
693814
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
694815
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
695816
autoProxyCreator.setBeanFactory(beanFactory);
@@ -704,6 +825,34 @@ public void genericsBasedInjectionWithLateGenericsMatchingOnJdkProxyAndRawFactor
704825
assertEquals(1, beanNames.length);
705826
assertEquals("stringRepo", beanNames[0]);
706827

828+
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(RepositoryInterface.class, String.class));
829+
assertEquals(1, beanNames.length);
830+
assertEquals("stringRepo", beanNames[0]);
831+
832+
assertTrue(AopUtils.isJdkDynamicProxy(beanFactory.getBean("stringRepo")));
833+
}
834+
835+
@Test
836+
public void genericsBasedInjectionWithLateGenericsMatchingOnJdkProxyAndRawInstance() {
837+
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawInstanceRepositoryConfiguration.class));
838+
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
839+
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
840+
autoProxyCreator.setBeanFactory(beanFactory);
841+
beanFactory.addBeanPostProcessor(autoProxyCreator);
842+
beanFactory.registerSingleton("traceInterceptor", new DefaultPointcutAdvisor(new SimpleTraceInterceptor()));
843+
beanFactory.preInstantiateSingletons();
844+
845+
String[] beanNames = beanFactory.getBeanNamesForType(RepositoryInterface.class);
846+
assertTrue(ObjectUtils.containsElement(beanNames, "stringRepo"));
847+
848+
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(RepositoryInterface.class, String.class));
849+
assertEquals(1, beanNames.length);
850+
assertEquals("stringRepo", beanNames[0]);
851+
852+
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(RepositoryInterface.class, String.class));
853+
assertEquals(1, beanNames.length);
854+
assertEquals("stringRepo", beanNames[0]);
855+
707856
assertTrue(AopUtils.isJdkDynamicProxy(beanFactory.getBean("stringRepo")));
708857
}
709858

@@ -1073,7 +1222,7 @@ public String toString() {
10731222
}
10741223

10751224
@Configuration
1076-
public static class RawRepositoryConfiguration {
1225+
public static class RawFactoryMethodRepositoryConfiguration {
10771226

10781227
@Bean
10791228
public Repository stringRepo() {
@@ -1086,6 +1235,21 @@ public String toString() {
10861235
}
10871236
}
10881237

1238+
@Configuration
1239+
public static class RawInstanceRepositoryConfiguration {
1240+
1241+
@SuppressWarnings({"rawtypes", "unchecked"})
1242+
@Bean
1243+
public Repository<String> stringRepo() {
1244+
return new Repository() {
1245+
@Override
1246+
public String toString() {
1247+
return "Repository<String>";
1248+
}
1249+
};
1250+
}
1251+
}
1252+
10891253
@Configuration
10901254
public static class ScopedRepositoryConfiguration {
10911255

0 commit comments

Comments
 (0)