Skip to content

Commit e9f7c35

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 cf8479c commit e9f7c35

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
@@ -509,12 +509,21 @@ else if (typeToMatch.hasGenerics() && containsBeanDefinition(beanName)) {
509509
// Generics potentially only match on the target class, not on the proxy...
510510
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
511511
Class<?> targetType = mbd.getTargetType();
512-
if (targetType != null && targetType != ClassUtils.getUserClass(beanInstance) &&
513-
typeToMatch.isAssignableFrom(targetType)) {
512+
if (targetType != null && targetType != ClassUtils.getUserClass(beanInstance)) {
514513
// Check raw class match as well, making sure it's exposed on the proxy.
515514
Class<?> classToMatch = typeToMatch.resolve();
516-
return (classToMatch == null || classToMatch.isInstance(beanInstance));
515+
if (classToMatch != null && !classToMatch.isInstance(beanInstance)) {
516+
return false;
517+
}
518+
if (typeToMatch.isAssignableFrom(targetType)) {
519+
return true;
520+
}
517521
}
522+
ResolvableType resolvableType = mbd.targetType;
523+
if (resolvableType == null) {
524+
resolvableType = mbd.factoryMethodReturnType;
525+
}
526+
return (resolvableType != null && typeToMatch.isAssignableFrom(resolvableType));
518527
}
519528
}
520529
return false;
@@ -1363,6 +1372,7 @@ public void clearMetadataCache() {
13631372
*/
13641373
protected Class<?> resolveBeanClass(final RootBeanDefinition mbd, String beanName, final Class<?>... typesToMatch)
13651374
throws CannotLoadBeanClassException {
1375+
13661376
try {
13671377
if (mbd.hasBeanClass()) {
13681378
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
@@ -544,6 +544,10 @@ public void genericsBasedInjectionWithEarlyGenericsMatching() {
544544
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
545545
assertEquals(1, beanNames.length);
546546
assertEquals("stringRepo", beanNames[0]);
547+
548+
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
549+
assertEquals(1, beanNames.length);
550+
assertEquals("stringRepo", beanNames[0]);
547551
}
548552

549553
@Test
@@ -558,6 +562,78 @@ public void genericsBasedInjectionWithLateGenericsMatching() {
558562
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
559563
assertEquals(1, beanNames.length);
560564
assertEquals("stringRepo", beanNames[0]);
565+
566+
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
567+
assertEquals(1, beanNames.length);
568+
assertEquals("stringRepo", beanNames[0]);
569+
}
570+
571+
@Test
572+
public void genericsBasedInjectionWithEarlyGenericsMatchingAndRawFactoryMethod() {
573+
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawFactoryMethodRepositoryConfiguration.class));
574+
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
575+
576+
String[] beanNames = beanFactory.getBeanNamesForType(Repository.class);
577+
assertTrue(ObjectUtils.containsElement(beanNames, "stringRepo"));
578+
579+
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
580+
assertEquals(0, beanNames.length);
581+
582+
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
583+
assertEquals(0, beanNames.length);
584+
}
585+
586+
@Test
587+
public void genericsBasedInjectionWithLateGenericsMatchingAndRawFactoryMethod() {
588+
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawFactoryMethodRepositoryConfiguration.class));
589+
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
590+
beanFactory.preInstantiateSingletons();
591+
592+
String[] beanNames = beanFactory.getBeanNamesForType(Repository.class);
593+
assertTrue(ObjectUtils.containsElement(beanNames, "stringRepo"));
594+
595+
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
596+
assertEquals(1, beanNames.length);
597+
assertEquals("stringRepo", beanNames[0]);
598+
599+
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
600+
assertEquals(1, beanNames.length);
601+
assertEquals("stringRepo", beanNames[0]);
602+
}
603+
604+
@Test
605+
public void genericsBasedInjectionWithEarlyGenericsMatchingAndRawInstance() {
606+
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawInstanceRepositoryConfiguration.class));
607+
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
608+
609+
String[] beanNames = beanFactory.getBeanNamesForType(Repository.class);
610+
assertTrue(ObjectUtils.containsElement(beanNames, "stringRepo"));
611+
612+
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
613+
assertEquals(1, beanNames.length);
614+
assertEquals("stringRepo", beanNames[0]);
615+
616+
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
617+
assertEquals(1, beanNames.length);
618+
assertEquals("stringRepo", beanNames[0]);
619+
}
620+
621+
@Test
622+
public void genericsBasedInjectionWithLateGenericsMatchingAndRawInstance() {
623+
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawInstanceRepositoryConfiguration.class));
624+
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
625+
beanFactory.preInstantiateSingletons();
626+
627+
String[] beanNames = beanFactory.getBeanNamesForType(Repository.class);
628+
assertTrue(ObjectUtils.containsElement(beanNames, "stringRepo"));
629+
630+
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
631+
assertEquals(1, beanNames.length);
632+
assertEquals("stringRepo", beanNames[0]);
633+
634+
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
635+
assertEquals(1, beanNames.length);
636+
assertEquals("stringRepo", beanNames[0]);
561637
}
562638

563639
@Test
@@ -577,6 +653,10 @@ public void genericsBasedInjectionWithEarlyGenericsMatchingOnCglibProxy() {
577653
assertEquals(1, beanNames.length);
578654
assertEquals("stringRepo", beanNames[0]);
579655

656+
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
657+
assertEquals(1, beanNames.length);
658+
assertEquals("stringRepo", beanNames[0]);
659+
580660
assertTrue(AopUtils.isCglibProxy(beanFactory.getBean("stringRepo")));
581661
}
582662

@@ -598,12 +678,16 @@ public void genericsBasedInjectionWithLateGenericsMatchingOnCglibProxy() {
598678
assertEquals(1, beanNames.length);
599679
assertEquals("stringRepo", beanNames[0]);
600680

681+
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
682+
assertEquals(1, beanNames.length);
683+
assertEquals("stringRepo", beanNames[0]);
684+
601685
assertTrue(AopUtils.isCglibProxy(beanFactory.getBean("stringRepo")));
602686
}
603687

604688
@Test
605689
public void genericsBasedInjectionWithLateGenericsMatchingOnCglibProxyAndRawFactoryMethod() {
606-
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawRepositoryConfiguration.class));
690+
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawFactoryMethodRepositoryConfiguration.class));
607691
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
608692
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
609693
autoProxyCreator.setProxyTargetClass(true);
@@ -619,6 +703,35 @@ public void genericsBasedInjectionWithLateGenericsMatchingOnCglibProxyAndRawFact
619703
assertEquals(1, beanNames.length);
620704
assertEquals("stringRepo", beanNames[0]);
621705

706+
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
707+
assertEquals(1, beanNames.length);
708+
assertEquals("stringRepo", beanNames[0]);
709+
710+
assertTrue(AopUtils.isCglibProxy(beanFactory.getBean("stringRepo")));
711+
}
712+
713+
@Test
714+
public void genericsBasedInjectionWithLateGenericsMatchingOnCglibProxyAndRawInstance() {
715+
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawInstanceRepositoryConfiguration.class));
716+
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
717+
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
718+
autoProxyCreator.setProxyTargetClass(true);
719+
autoProxyCreator.setBeanFactory(beanFactory);
720+
beanFactory.addBeanPostProcessor(autoProxyCreator);
721+
beanFactory.registerSingleton("traceInterceptor", new DefaultPointcutAdvisor(new SimpleTraceInterceptor()));
722+
beanFactory.preInstantiateSingletons();
723+
724+
String[] beanNames = beanFactory.getBeanNamesForType(Repository.class);
725+
assertTrue(ObjectUtils.containsElement(beanNames, "stringRepo"));
726+
727+
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
728+
assertEquals(1, beanNames.length);
729+
assertEquals("stringRepo", beanNames[0]);
730+
731+
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
732+
assertEquals(1, beanNames.length);
733+
assertEquals("stringRepo", beanNames[0]);
734+
622735
assertTrue(AopUtils.isCglibProxy(beanFactory.getBean("stringRepo")));
623736
}
624737

@@ -638,6 +751,10 @@ public void genericsBasedInjectionWithEarlyGenericsMatchingOnJdkProxy() {
638751
assertEquals(1, beanNames.length);
639752
assertEquals("stringRepo", beanNames[0]);
640753

754+
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(RepositoryInterface.class, String.class));
755+
assertEquals(1, beanNames.length);
756+
assertEquals("stringRepo", beanNames[0]);
757+
641758
assertTrue(AopUtils.isJdkDynamicProxy(beanFactory.getBean("stringRepo")));
642759
}
643760

@@ -658,12 +775,16 @@ public void genericsBasedInjectionWithLateGenericsMatchingOnJdkProxy() {
658775
assertEquals(1, beanNames.length);
659776
assertEquals("stringRepo", beanNames[0]);
660777

778+
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(RepositoryInterface.class, String.class));
779+
assertEquals(1, beanNames.length);
780+
assertEquals("stringRepo", beanNames[0]);
781+
661782
assertTrue(AopUtils.isJdkDynamicProxy(beanFactory.getBean("stringRepo")));
662783
}
663784

664785
@Test
665786
public void genericsBasedInjectionWithLateGenericsMatchingOnJdkProxyAndRawFactoryMethod() {
666-
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawRepositoryConfiguration.class));
787+
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawFactoryMethodRepositoryConfiguration.class));
667788
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
668789
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
669790
autoProxyCreator.setBeanFactory(beanFactory);
@@ -678,6 +799,34 @@ public void genericsBasedInjectionWithLateGenericsMatchingOnJdkProxyAndRawFactor
678799
assertEquals(1, beanNames.length);
679800
assertEquals("stringRepo", beanNames[0]);
680801

802+
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(RepositoryInterface.class, String.class));
803+
assertEquals(1, beanNames.length);
804+
assertEquals("stringRepo", beanNames[0]);
805+
806+
assertTrue(AopUtils.isJdkDynamicProxy(beanFactory.getBean("stringRepo")));
807+
}
808+
809+
@Test
810+
public void genericsBasedInjectionWithLateGenericsMatchingOnJdkProxyAndRawInstance() {
811+
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawInstanceRepositoryConfiguration.class));
812+
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
813+
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
814+
autoProxyCreator.setBeanFactory(beanFactory);
815+
beanFactory.addBeanPostProcessor(autoProxyCreator);
816+
beanFactory.registerSingleton("traceInterceptor", new DefaultPointcutAdvisor(new SimpleTraceInterceptor()));
817+
beanFactory.preInstantiateSingletons();
818+
819+
String[] beanNames = beanFactory.getBeanNamesForType(RepositoryInterface.class);
820+
assertTrue(ObjectUtils.containsElement(beanNames, "stringRepo"));
821+
822+
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(RepositoryInterface.class, String.class));
823+
assertEquals(1, beanNames.length);
824+
assertEquals("stringRepo", beanNames[0]);
825+
826+
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(RepositoryInterface.class, String.class));
827+
assertEquals(1, beanNames.length);
828+
assertEquals("stringRepo", beanNames[0]);
829+
681830
assertTrue(AopUtils.isJdkDynamicProxy(beanFactory.getBean("stringRepo")));
682831
}
683832

@@ -980,7 +1129,7 @@ public String toString() {
9801129
}
9811130

9821131
@Configuration
983-
public static class RawRepositoryConfiguration {
1132+
public static class RawFactoryMethodRepositoryConfiguration {
9841133

9851134
@Bean
9861135
public Repository stringRepo() {
@@ -993,6 +1142,21 @@ public String toString() {
9931142
}
9941143
}
9951144

1145+
@Configuration
1146+
public static class RawInstanceRepositoryConfiguration {
1147+
1148+
@SuppressWarnings({"rawtypes", "unchecked"})
1149+
@Bean
1150+
public Repository<String> stringRepo() {
1151+
return new Repository() {
1152+
@Override
1153+
public String toString() {
1154+
return "Repository<String>";
1155+
}
1156+
};
1157+
}
1158+
}
1159+
9961160
@Configuration
9971161
public static class ScopedRepositoryConfiguration {
9981162

0 commit comments

Comments
 (0)