Skip to content

Commit ebbe14c

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

File tree

2 files changed

+179
-6
lines changed

2 files changed

+179
-6
lines changed

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

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -510,12 +510,21 @@ else if (typeToMatch.hasGenerics() && containsBeanDefinition(beanName)) {
510510
// Generics potentially only match on the target class, not on the proxy...
511511
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
512512
Class<?> targetType = mbd.getTargetType();
513-
if (targetType != null && targetType != ClassUtils.getUserClass(beanInstance) &&
514-
typeToMatch.isAssignableFrom(targetType)) {
513+
if (targetType != null && targetType != ClassUtils.getUserClass(beanInstance)) {
515514
// Check raw class match as well, making sure it's exposed on the proxy.
516515
Class<?> classToMatch = typeToMatch.resolve();
517-
return (classToMatch == null || classToMatch.isInstance(beanInstance));
516+
if (classToMatch != null && !classToMatch.isInstance(beanInstance)) {
517+
return false;
518+
}
519+
if (typeToMatch.isAssignableFrom(targetType)) {
520+
return true;
521+
}
522+
}
523+
ResolvableType resolvableType = mbd.targetType;
524+
if (resolvableType == null) {
525+
resolvableType = mbd.factoryMethodReturnType;
518526
}
527+
return (resolvableType != null && typeToMatch.isAssignableFrom(resolvableType));
519528
}
520529
}
521530
return false;

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
@@ -571,6 +571,10 @@ public void genericsBasedInjectionWithEarlyGenericsMatching() {
571571
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
572572
assertEquals(1, beanNames.length);
573573
assertEquals("stringRepo", beanNames[0]);
574+
575+
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
576+
assertEquals(1, beanNames.length);
577+
assertEquals("stringRepo", beanNames[0]);
574578
}
575579

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

590666
@Test
@@ -604,6 +680,10 @@ public void genericsBasedInjectionWithEarlyGenericsMatchingOnCglibProxy() {
604680
assertEquals(1, beanNames.length);
605681
assertEquals("stringRepo", beanNames[0]);
606682

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

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

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

631715
@Test
632716
public void genericsBasedInjectionWithLateGenericsMatchingOnCglibProxyAndRawFactoryMethod() {
633-
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawRepositoryConfiguration.class));
717+
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawFactoryMethodRepositoryConfiguration.class));
634718
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
635719
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
636720
autoProxyCreator.setProxyTargetClass(true);
@@ -646,6 +730,35 @@ public void genericsBasedInjectionWithLateGenericsMatchingOnCglibProxyAndRawFact
646730
assertEquals(1, beanNames.length);
647731
assertEquals("stringRepo", beanNames[0]);
648732

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

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

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

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

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

691812
@Test
692813
public void genericsBasedInjectionWithLateGenericsMatchingOnJdkProxyAndRawFactoryMethod() {
693-
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawRepositoryConfiguration.class));
814+
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawFactoryMethodRepositoryConfiguration.class));
694815
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
695816
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
696817
autoProxyCreator.setBeanFactory(beanFactory);
@@ -705,6 +826,34 @@ public void genericsBasedInjectionWithLateGenericsMatchingOnJdkProxyAndRawFactor
705826
assertEquals(1, beanNames.length);
706827
assertEquals("stringRepo", beanNames[0]);
707828

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

@@ -1125,7 +1274,7 @@ public String toString() {
11251274
}
11261275

11271276
@Configuration
1128-
public static class RawRepositoryConfiguration {
1277+
public static class RawFactoryMethodRepositoryConfiguration {
11291278

11301279
@Bean
11311280
public Repository stringRepo() {
@@ -1138,6 +1287,21 @@ public String toString() {
11381287
}
11391288
}
11401289

1290+
@Configuration
1291+
public static class RawInstanceRepositoryConfiguration {
1292+
1293+
@SuppressWarnings({"rawtypes", "unchecked"})
1294+
@Bean
1295+
public Repository<String> stringRepo() {
1296+
return new Repository() {
1297+
@Override
1298+
public String toString() {
1299+
return "Repository<String>";
1300+
}
1301+
};
1302+
}
1303+
}
1304+
11411305
@Configuration
11421306
public static class ScopedRepositoryConfiguration {
11431307

0 commit comments

Comments
 (0)