From a0bd13ceb1585660599a5ff6648e09c5ec5d5cbd Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 8 Apr 2024 10:16:48 +0200 Subject: [PATCH] Do not extract FactoryBean generic in case of targetType mismatch Closes gh-32489 --- ...ricTypeAwareAutowireCandidateResolver.java | 5 +- .../support/BeanFactoryGenericsTests.java | 819 ++++++++---------- 2 files changed, 387 insertions(+), 437 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/GenericTypeAwareAutowireCandidateResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/GenericTypeAwareAutowireCandidateResolver.java index 3a3c84b11a1a..72ab84d4f8e8 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/GenericTypeAwareAutowireCandidateResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/GenericTypeAwareAutowireCandidateResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -108,7 +108,8 @@ protected boolean checkGenericTypeMatch(BeanDefinitionHolder bdHolder, Dependenc Class resolvedClass = targetType.resolve(); if (resolvedClass != null && FactoryBean.class.isAssignableFrom(resolvedClass)) { Class typeToBeMatched = dependencyType.resolve(); - if (typeToBeMatched != null && !FactoryBean.class.isAssignableFrom(typeToBeMatched)) { + if (typeToBeMatched != null && !FactoryBean.class.isAssignableFrom(typeToBeMatched) && + !typeToBeMatched.isAssignableFrom(resolvedClass)) { targetType = targetType.getGeneric(); if (descriptor.fallbackMatchAllowed()) { // Matching the Class-based type determination for FactoryBean diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanFactoryGenericsTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanFactoryGenericsTests.java index 0efebed42ac9..2daf769109a2 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanFactoryGenericsTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanFactoryGenericsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,24 +16,23 @@ package org.springframework.beans.factory.support; -import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import java.net.MalformedURLException; import java.net.URI; import java.net.URL; -import java.util.AbstractCollection; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.stream.Collectors; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.beans.factory.BeanCreationException; +import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.NoUniqueBeanDefinitionException; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.config.TypedStringValue; @@ -49,11 +48,10 @@ import org.springframework.core.annotation.Order; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.UrlResource; -import org.springframework.core.testfixture.EnabledForTestGroups; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.springframework.core.testfixture.TestGroup.LONG_RUNNING; +import static org.assertj.core.api.Assertions.entry; /** * @author Juergen Hoeller @@ -64,276 +62,241 @@ class BeanFactoryGenericsTests { @Test - void testGenericSetProperty() { + void genericSetProperty() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); - RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); - Set input = new HashSet<>(); - input.add("4"); - input.add("5"); - rbd.getPropertyValues().add("integerSet", input); + RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class); + bd.getPropertyValues().add("integerSet", Set.of("4", "5")); + bf.registerBeanDefinition("genericBean", bd); - bf.registerBeanDefinition("genericBean", rbd); GenericBean gb = (GenericBean) bf.getBean("genericBean"); - - assertThat(gb.getIntegerSet().contains(4)).isTrue(); - assertThat(gb.getIntegerSet().contains(5)).isTrue(); + assertThat(gb.getIntegerSet()).containsExactlyInAnyOrder(4, 5); } @Test - void testGenericListProperty() throws Exception { + void genericListProperty() throws Exception { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); - RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); - List input = new ArrayList<>(); - input.add("http://localhost:8080"); - input.add("http://localhost:9090"); - rbd.getPropertyValues().add("resourceList", input); + RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class); + List input = List.of("http://localhost:8080", "http://localhost:9090"); + bd.getPropertyValues().add("resourceList", input); + bf.registerBeanDefinition("genericBean", bd); - bf.registerBeanDefinition("genericBean", rbd); GenericBean gb = (GenericBean) bf.getBean("genericBean"); - - assertThat(gb.getResourceList().get(0)).isEqualTo(new UrlResource("http://localhost:8080")); - assertThat(gb.getResourceList().get(1)).isEqualTo(new UrlResource("http://localhost:9090")); + assertThat(gb.getResourceList()) + .containsExactly(new UrlResource("http://localhost:8080"), new UrlResource("http://localhost:9090")); } @Test - void testGenericListPropertyWithAutowiring() throws Exception { + void genericListPropertyWithAutowiring() throws Exception { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); bf.registerSingleton("resource1", new UrlResource("http://localhost:8080")); bf.registerSingleton("resource2", new UrlResource("http://localhost:9090")); - RootBeanDefinition rbd = new RootBeanDefinition(GenericIntegerBean.class); - rbd.setAutowireMode(RootBeanDefinition.AUTOWIRE_BY_TYPE); - bf.registerBeanDefinition("genericBean", rbd); - GenericIntegerBean gb = (GenericIntegerBean) bf.getBean("genericBean"); + RootBeanDefinition bd = new RootBeanDefinition(GenericIntegerBean.class); + bd.setAutowireMode(RootBeanDefinition.AUTOWIRE_BY_TYPE); + bf.registerBeanDefinition("genericBean", bd); - assertThat(gb.getResourceList().get(0)).isEqualTo(new UrlResource("http://localhost:8080")); - assertThat(gb.getResourceList().get(1)).isEqualTo(new UrlResource("http://localhost:9090")); + GenericIntegerBean gb = (GenericIntegerBean) bf.getBean("genericBean"); + assertThat(gb.getResourceList()) + .containsExactly(new UrlResource("http://localhost:8080"), new UrlResource("http://localhost:9090")); } @Test - void testGenericListPropertyWithInvalidElementType() { + void genericListPropertyWithInvalidElementType() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); - RootBeanDefinition rbd = new RootBeanDefinition(GenericIntegerBean.class); - - List input = new ArrayList<>(); - input.add(1); - rbd.getPropertyValues().add("testBeanList", input); - - bf.registerBeanDefinition("genericBean", rbd); - assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() -> - bf.getBean("genericBean")) - .withMessageContaining("genericBean") - .withMessageContaining("testBeanList[0]") - .withMessageContaining(TestBean.class.getName()) - .withMessageContaining("Integer"); + + RootBeanDefinition bd = new RootBeanDefinition(GenericIntegerBean.class); + bd.getPropertyValues().add("testBeanList", List.of(1)); + bf.registerBeanDefinition("genericBean", bd); + + assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() -> bf.getBean("genericBean")) + .withMessageContaining("genericBean") + .withMessageContaining("testBeanList[0]") + .withMessageContaining(TestBean.class.getName()) + .withMessageContaining("Integer"); } @Test - void testGenericListPropertyWithOptionalAutowiring() { + void genericListPropertyWithOptionalAutowiring() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); - RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); - rbd.setAutowireMode(RootBeanDefinition.AUTOWIRE_BY_TYPE); - bf.registerBeanDefinition("genericBean", rbd); - GenericBean gb = (GenericBean) bf.getBean("genericBean"); + RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class); + bd.setAutowireMode(RootBeanDefinition.AUTOWIRE_BY_TYPE); + bf.registerBeanDefinition("genericBean", bd); + GenericBean gb = (GenericBean) bf.getBean("genericBean"); assertThat(gb.getResourceList()).isNull(); } @Test - void testGenericMapProperty() { + void genericMapProperty() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); - RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); - Map input = new HashMap<>(); - input.put("4", "5"); - input.put("6", "7"); - rbd.getPropertyValues().add("shortMap", input); + RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class); + Map input = Map.of( + "4", "5", + "6", "7"); + bd.getPropertyValues().add("shortMap", input); + bf.registerBeanDefinition("genericBean", bd); - bf.registerBeanDefinition("genericBean", rbd); GenericBean gb = (GenericBean) bf.getBean("genericBean"); - assertThat(gb.getShortMap().get(Short.valueOf("4"))).isEqualTo(5); assertThat(gb.getShortMap().get(Short.valueOf("6"))).isEqualTo(7); } @Test - void testGenericListOfArraysProperty() { + void genericListOfArraysProperty() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); new XmlBeanDefinitionReader(bf).loadBeanDefinitions( new ClassPathResource("genericBeanTests.xml", getClass())); - GenericBean gb = (GenericBean) bf.getBean("listOfArrays"); - assertThat(gb.getListOfArrays()).hasSize(1); - String[] array = gb.getListOfArrays().get(0); - assertThat(array).hasSize(2); - assertThat(array[0]).isEqualTo("value1"); - assertThat(array[1]).isEqualTo("value2"); + GenericBean gb = (GenericBean) bf.getBean("listOfArrays"); + assertThat(gb.getListOfArrays()).containsExactly(new String[] {"value1", "value2"}); } - @Test - void testGenericSetConstructor() { + void genericSetConstructor() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); - RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); - Set input = new HashSet<>(); - input.add("4"); - input.add("5"); - rbd.getConstructorArgumentValues().addGenericArgumentValue(input); + RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class); + Set input = Set.of("4", "5"); + bd.getConstructorArgumentValues().addGenericArgumentValue(input); + bf.registerBeanDefinition("genericBean", bd); - bf.registerBeanDefinition("genericBean", rbd); GenericBean gb = (GenericBean) bf.getBean("genericBean"); - - assertThat(gb.getIntegerSet().contains(4)).isTrue(); - assertThat(gb.getIntegerSet().contains(5)).isTrue(); + assertThat(gb.getIntegerSet()).containsExactlyInAnyOrder(4, 5); } @Test - void testGenericSetConstructorWithAutowiring() { + void genericSetConstructorWithAutowiring() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); bf.registerSingleton("integer1", 4); bf.registerSingleton("integer2", 5); - RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); - rbd.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR); - bf.registerBeanDefinition("genericBean", rbd); - GenericBean gb = (GenericBean) bf.getBean("genericBean"); + RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class); + bd.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR); + bf.registerBeanDefinition("genericBean", bd); - assertThat(gb.getIntegerSet().contains(4)).isTrue(); - assertThat(gb.getIntegerSet().contains(5)).isTrue(); + GenericBean gb = (GenericBean) bf.getBean("genericBean"); + assertThat(gb.getIntegerSet()).containsExactlyInAnyOrder(4, 5); } @Test - void testGenericSetConstructorWithOptionalAutowiring() { + void genericSetConstructorWithOptionalAutowiring() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); - RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); - rbd.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR); - bf.registerBeanDefinition("genericBean", rbd); - GenericBean gb = (GenericBean) bf.getBean("genericBean"); + RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class); + bd.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR); + bf.registerBeanDefinition("genericBean", bd); + GenericBean gb = (GenericBean) bf.getBean("genericBean"); assertThat(gb.getIntegerSet()).isNull(); } @Test - void testGenericSetListConstructor() throws Exception { + void genericSetListConstructor() throws Exception { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); - RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); - - Set input = new HashSet<>(); - input.add("4"); - input.add("5"); - List input2 = new ArrayList<>(); - input2.add("http://localhost:8080"); - input2.add("http://localhost:9090"); - rbd.getConstructorArgumentValues().addGenericArgumentValue(input); - rbd.getConstructorArgumentValues().addGenericArgumentValue(input2); - - bf.registerBeanDefinition("genericBean", rbd); - GenericBean gb = (GenericBean) bf.getBean("genericBean"); - assertThat(gb.getIntegerSet().contains(4)).isTrue(); - assertThat(gb.getIntegerSet().contains(5)).isTrue(); - assertThat(gb.getResourceList().get(0)).isEqualTo(new UrlResource("http://localhost:8080")); - assertThat(gb.getResourceList().get(1)).isEqualTo(new UrlResource("http://localhost:9090")); + RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class); + Set input1 = Set.of("4", "5"); + List input2 = List.of("http://localhost:8080", "http://localhost:9090"); + bd.getConstructorArgumentValues().addGenericArgumentValue(input1); + bd.getConstructorArgumentValues().addGenericArgumentValue(input2); + bf.registerBeanDefinition("genericBean", bd); + + GenericBean gb = (GenericBean) bf.getBean("genericBean"); + assertThat(gb.getIntegerSet()).containsExactlyInAnyOrder(4, 5); + assertThat(gb.getResourceList()) + .containsExactly(new UrlResource("http://localhost:8080"), new UrlResource("http://localhost:9090")); } @Test - void testGenericSetListConstructorWithAutowiring() throws Exception { + void genericSetListConstructorWithAutowiring() throws Exception { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); bf.registerSingleton("integer1", 4); bf.registerSingleton("integer2", 5); bf.registerSingleton("resource1", new UrlResource("http://localhost:8080")); bf.registerSingleton("resource2", new UrlResource("http://localhost:9090")); - RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); - rbd.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR); - bf.registerBeanDefinition("genericBean", rbd); - GenericBean gb = (GenericBean) bf.getBean("genericBean"); + RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class); + bd.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR); + bf.registerBeanDefinition("genericBean", bd); - assertThat(gb.getIntegerSet().contains(4)).isTrue(); - assertThat(gb.getIntegerSet().contains(5)).isTrue(); - assertThat(gb.getResourceList().get(0)).isEqualTo(new UrlResource("http://localhost:8080")); - assertThat(gb.getResourceList().get(1)).isEqualTo(new UrlResource("http://localhost:9090")); + GenericBean gb = (GenericBean) bf.getBean("genericBean"); + assertThat(gb.getIntegerSet()).containsExactlyInAnyOrder(4, 5); + assertThat(gb.getResourceList()) + .containsExactly(new UrlResource("http://localhost:8080"), new UrlResource("http://localhost:9090")); } @Test - void testGenericSetListConstructorWithOptionalAutowiring() throws Exception { + void genericSetListConstructorWithOptionalAutowiring() throws Exception { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); bf.registerSingleton("resource1", new UrlResource("http://localhost:8080")); bf.registerSingleton("resource2", new UrlResource("http://localhost:9090")); - RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); - rbd.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR); - bf.registerBeanDefinition("genericBean", rbd); - GenericBean gb = (GenericBean) bf.getBean("genericBean"); + RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class); + bd.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR); + bf.registerBeanDefinition("genericBean", bd); + GenericBean gb = (GenericBean) bf.getBean("genericBean"); assertThat(gb.getIntegerSet()).isNull(); assertThat(gb.getResourceList()).isNull(); } @Test - void testGenericSetMapConstructor() { + void genericSetMapConstructor() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); - RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); - - Set input = new HashSet<>(); - input.add("4"); - input.add("5"); - Map input2 = new HashMap<>(); - input2.put("4", "5"); - input2.put("6", "7"); - rbd.getConstructorArgumentValues().addGenericArgumentValue(input); - rbd.getConstructorArgumentValues().addGenericArgumentValue(input2); - - bf.registerBeanDefinition("genericBean", rbd); - GenericBean gb = (GenericBean) bf.getBean("genericBean"); - assertThat(gb.getIntegerSet().contains(4)).isTrue(); - assertThat(gb.getIntegerSet().contains(5)).isTrue(); + RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class); + Set input1 = Set.of("4", "5"); + Map input2 = Map.of( + "4", "5", + "6", "7"); + bd.getConstructorArgumentValues().addGenericArgumentValue(input1); + bd.getConstructorArgumentValues().addGenericArgumentValue(input2); + bf.registerBeanDefinition("genericBean", bd); + + GenericBean gb = (GenericBean) bf.getBean("genericBean"); + assertThat(gb.getIntegerSet()).containsExactlyInAnyOrder(4, 5); assertThat(gb.getShortMap().get(Short.valueOf("4"))).isEqualTo(5); assertThat(gb.getShortMap().get(Short.valueOf("6"))).isEqualTo(7); } @Test - void testGenericMapResourceConstructor() throws Exception { + void genericMapResourceConstructor() throws Exception { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); - RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); - Map input = new HashMap<>(); - input.put("4", "5"); - input.put("6", "7"); - rbd.getConstructorArgumentValues().addGenericArgumentValue(input); - rbd.getConstructorArgumentValues().addGenericArgumentValue("http://localhost:8080"); + RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class); + Map input = Map.of( + "4", "5", + "6", "7"); + bd.getConstructorArgumentValues().addGenericArgumentValue(input); + bd.getConstructorArgumentValues().addGenericArgumentValue("http://localhost:8080"); + bf.registerBeanDefinition("genericBean", bd); - bf.registerBeanDefinition("genericBean", rbd); GenericBean gb = (GenericBean) bf.getBean("genericBean"); - assertThat(gb.getShortMap().get(Short.valueOf("4"))).isEqualTo(5); assertThat(gb.getShortMap().get(Short.valueOf("6"))).isEqualTo(7); - assertThat(gb.getResourceList().get(0)).isEqualTo(new UrlResource("http://localhost:8080")); + assertThat(gb.getResourceList()).containsExactly(new UrlResource("http://localhost:8080")); } @Test - void testGenericMapMapConstructor() { + void genericMapMapConstructor() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); - RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); - - Map input = new HashMap<>(); - input.put("1", "0"); - input.put("2", "3"); - Map input2 = new HashMap<>(); - input2.put("4", "5"); - input2.put("6", "7"); - rbd.getConstructorArgumentValues().addGenericArgumentValue(input); - rbd.getConstructorArgumentValues().addGenericArgumentValue(input2); - - bf.registerBeanDefinition("genericBean", rbd); - GenericBean gb = (GenericBean) bf.getBean("genericBean"); + RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class); + Map input1 = Map.of( + "1", "0", + "2", "3"); + Map input2 = Map.of( + "4", "5", + "6", "7"); + bd.getConstructorArgumentValues().addGenericArgumentValue(input1); + bd.getConstructorArgumentValues().addGenericArgumentValue(input2); + bf.registerBeanDefinition("genericBean", bd); + + GenericBean gb = (GenericBean) bf.getBean("genericBean"); assertThat(gb.getShortMap()).isNotSameAs(gb.getPlainMap()); assertThat(gb.getPlainMap()).hasSize(2); assertThat(gb.getPlainMap().get("1")).isEqualTo("0"); @@ -344,19 +307,18 @@ void testGenericMapMapConstructor() { } @Test - void testGenericMapMapConstructorWithSameRefAndConversion() { + void genericMapMapConstructorWithSameRefAndConversion() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); - RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); - Map input = new HashMap<>(); - input.put("1", "0"); - input.put("2", "3"); - rbd.getConstructorArgumentValues().addGenericArgumentValue(input); - rbd.getConstructorArgumentValues().addGenericArgumentValue(input); + RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class); + Map input = Map.of( + "1", "0", + "2", "3"); + bd.getConstructorArgumentValues().addGenericArgumentValue(input); + bd.getConstructorArgumentValues().addGenericArgumentValue(input); + bf.registerBeanDefinition("genericBean", bd); - bf.registerBeanDefinition("genericBean", rbd); GenericBean gb = (GenericBean) bf.getBean("genericBean"); - assertThat(gb.getShortMap()).isNotSameAs(gb.getPlainMap()); assertThat(gb.getPlainMap()).hasSize(2); assertThat(gb.getPlainMap().get("1")).isEqualTo("0"); @@ -367,19 +329,18 @@ void testGenericMapMapConstructorWithSameRefAndConversion() { } @Test - void testGenericMapMapConstructorWithSameRefAndNoConversion() { + void genericMapMapConstructorWithSameRefAndNoConversion() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); - RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); + RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class); Map input = new HashMap<>(); input.put((short) 1, 0); input.put((short) 2, 3); - rbd.getConstructorArgumentValues().addGenericArgumentValue(input); - rbd.getConstructorArgumentValues().addGenericArgumentValue(input); + bd.getConstructorArgumentValues().addGenericArgumentValue(input); + bd.getConstructorArgumentValues().addGenericArgumentValue(input); + bf.registerBeanDefinition("genericBean", bd); - bf.registerBeanDefinition("genericBean", rbd); GenericBean gb = (GenericBean) bf.getBean("genericBean"); - assertThat(gb.getShortMap()).isSameAs(gb.getPlainMap()); assertThat(gb.getShortMap()).hasSize(2); assertThat(gb.getShortMap().get(Short.valueOf("1"))).isEqualTo(0); @@ -387,150 +348,128 @@ void testGenericMapMapConstructorWithSameRefAndNoConversion() { } @Test - void testGenericMapWithKeyTypeConstructor() { + void genericMapWithKeyTypeConstructor() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); - RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); - Map input = new HashMap<>(); - input.put("4", "5"); - input.put("6", "7"); - rbd.getConstructorArgumentValues().addGenericArgumentValue(input); + RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class); + Map input = Map.of( + "4", "5", + "6", "7"); + bd.getConstructorArgumentValues().addGenericArgumentValue(input); + bf.registerBeanDefinition("genericBean", bd); - bf.registerBeanDefinition("genericBean", rbd); GenericBean gb = (GenericBean) bf.getBean("genericBean"); - assertThat(gb.getLongMap().get(4L)).isEqualTo("5"); assertThat(gb.getLongMap().get(6L)).isEqualTo("7"); } @Test - void testGenericMapWithCollectionValueConstructor() { + void genericMapWithCollectionValueConstructor() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); - bf.addPropertyEditorRegistrar(registry -> registry.registerCustomEditor(Number.class, new CustomNumberEditor(Integer.class, false))); - RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); - - Map> input = new HashMap<>(); - HashSet value1 = new HashSet<>(); - value1.add(1); - input.put("1", value1); - ArrayList value2 = new ArrayList<>(); - value2.add(Boolean.TRUE); - input.put("2", value2); - rbd.getConstructorArgumentValues().addGenericArgumentValue(Boolean.TRUE); - rbd.getConstructorArgumentValues().addGenericArgumentValue(input); - - bf.registerBeanDefinition("genericBean", rbd); - GenericBean gb = (GenericBean) bf.getBean("genericBean"); + bf.addPropertyEditorRegistrar(registry -> + registry.registerCustomEditor(Number.class, new CustomNumberEditor(Integer.class, false))); - assertThat(gb.getCollectionMap().get(1) instanceof HashSet).isTrue(); - assertThat(gb.getCollectionMap().get(2) instanceof ArrayList).isTrue(); - } + RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class); + Map> input = Map.of( + "1", Set.of(1), + "2", List.of(Boolean.TRUE)); + bd.getConstructorArgumentValues().addGenericArgumentValue(Boolean.TRUE); + bd.getConstructorArgumentValues().addGenericArgumentValue(input); + bf.registerBeanDefinition("genericBean", bd); + GenericBean gb = (GenericBean) bf.getBean("genericBean"); + assertThat(gb.getCollectionMap().get(1)).isInstanceOf(Set.class); + assertThat(gb.getCollectionMap().get(2)).isInstanceOf(List.class); + } @Test - void testGenericSetFactoryMethod() { + void genericSetFactoryMethod() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); - RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); - rbd.setFactoryMethodName("createInstance"); - Set input = new HashSet<>(); - input.add("4"); - input.add("5"); - rbd.getConstructorArgumentValues().addGenericArgumentValue(input); + RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class); + bd.setFactoryMethodName("createInstance"); + Set input = Set.of("4", "5"); + bd.getConstructorArgumentValues().addGenericArgumentValue(input); + bf.registerBeanDefinition("genericBean", bd); - bf.registerBeanDefinition("genericBean", rbd); GenericBean gb = (GenericBean) bf.getBean("genericBean"); - - assertThat(gb.getIntegerSet().contains(4)).isTrue(); - assertThat(gb.getIntegerSet().contains(5)).isTrue(); + assertThat(gb.getIntegerSet()).containsExactlyInAnyOrder(4, 5); } @Test - void testGenericSetListFactoryMethod() throws Exception { + void genericSetListFactoryMethod() throws Exception { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); - RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); - rbd.setFactoryMethodName("createInstance"); - - Set input = new HashSet<>(); - input.add("4"); - input.add("5"); - List input2 = new ArrayList<>(); - input2.add("http://localhost:8080"); - input2.add("http://localhost:9090"); - rbd.getConstructorArgumentValues().addGenericArgumentValue(input); - rbd.getConstructorArgumentValues().addGenericArgumentValue(input2); - - bf.registerBeanDefinition("genericBean", rbd); - GenericBean gb = (GenericBean) bf.getBean("genericBean"); - assertThat(gb.getIntegerSet().contains(4)).isTrue(); - assertThat(gb.getIntegerSet().contains(5)).isTrue(); - assertThat(gb.getResourceList().get(0)).isEqualTo(new UrlResource("http://localhost:8080")); - assertThat(gb.getResourceList().get(1)).isEqualTo(new UrlResource("http://localhost:9090")); + RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class); + bd.setFactoryMethodName("createInstance"); + Set input1 = Set.of("4", "5"); + List input2 = List.of("http://localhost:8080", "http://localhost:9090"); + bd.getConstructorArgumentValues().addGenericArgumentValue(input1); + bd.getConstructorArgumentValues().addGenericArgumentValue(input2); + bf.registerBeanDefinition("genericBean", bd); + + GenericBean gb = (GenericBean) bf.getBean("genericBean"); + assertThat(gb.getIntegerSet()).containsExactlyInAnyOrder(4, 5); + assertThat(gb.getResourceList()) + .containsExactly(new UrlResource("http://localhost:8080"), new UrlResource("http://localhost:9090")); } @Test - void testGenericSetMapFactoryMethod() { + void genericSetMapFactoryMethod() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); - RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); - rbd.setFactoryMethodName("createInstance"); - - Set input = new HashSet<>(); - input.add("4"); - input.add("5"); - Map input2 = new HashMap<>(); - input2.put("4", "5"); - input2.put("6", "7"); - rbd.getConstructorArgumentValues().addGenericArgumentValue(input); - rbd.getConstructorArgumentValues().addGenericArgumentValue(input2); - - bf.registerBeanDefinition("genericBean", rbd); - GenericBean gb = (GenericBean) bf.getBean("genericBean"); - assertThat(gb.getIntegerSet().contains(4)).isTrue(); - assertThat(gb.getIntegerSet().contains(5)).isTrue(); + RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class); + bd.setFactoryMethodName("createInstance"); + Set input1 = Set.of("4", "5"); + Map input2 = Map.of( + "4", "5", + "6", "7"); + bd.getConstructorArgumentValues().addGenericArgumentValue(input1); + bd.getConstructorArgumentValues().addGenericArgumentValue(input2); + bf.registerBeanDefinition("genericBean", bd); + + GenericBean gb = (GenericBean) bf.getBean("genericBean"); + assertThat(gb.getIntegerSet()).containsExactlyInAnyOrder(4, 5); assertThat(gb.getShortMap().get(Short.valueOf("4"))).isEqualTo(5); assertThat(gb.getShortMap().get(Short.valueOf("6"))).isEqualTo(7); } @Test - void testGenericMapResourceFactoryMethod() throws Exception { + void genericMapResourceFactoryMethod() throws Exception { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); - RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); - rbd.setFactoryMethodName("createInstance"); - Map input = new HashMap<>(); - input.put("4", "5"); - input.put("6", "7"); - rbd.getConstructorArgumentValues().addGenericArgumentValue(input); - rbd.getConstructorArgumentValues().addGenericArgumentValue("http://localhost:8080"); + RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class); + bd.setFactoryMethodName("createInstance"); + Map input = Map.of( + "4", "5", + "6", "7"); + bd.getConstructorArgumentValues().addGenericArgumentValue(input); + bd.getConstructorArgumentValues().addGenericArgumentValue("http://localhost:8080"); + bf.registerBeanDefinition("genericBean", bd); - bf.registerBeanDefinition("genericBean", rbd); GenericBean gb = (GenericBean) bf.getBean("genericBean"); - assertThat(gb.getShortMap().get(Short.valueOf("4"))).isEqualTo(5); assertThat(gb.getShortMap().get(Short.valueOf("6"))).isEqualTo(7); - assertThat(gb.getResourceList().get(0)).isEqualTo(new UrlResource("http://localhost:8080")); + assertThat(gb.getResourceList()).containsExactly(new UrlResource("http://localhost:8080")); } @Test - void testGenericMapMapFactoryMethod() { + void genericMapMapFactoryMethod() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); - RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); - rbd.setFactoryMethodName("createInstance"); - - Map input = new HashMap<>(); - input.put("1", "0"); - input.put("2", "3"); - Map input2 = new HashMap<>(); - input2.put("4", "5"); - input2.put("6", "7"); - rbd.getConstructorArgumentValues().addGenericArgumentValue(input); - rbd.getConstructorArgumentValues().addGenericArgumentValue(input2); - - bf.registerBeanDefinition("genericBean", rbd); - GenericBean gb = (GenericBean) bf.getBean("genericBean"); + RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class); + bd.setFactoryMethodName("createInstance"); + Map input1 = Map.of( + "1", "0", + "2", "3"); + Map input2 = Map.of( + "4", "5", + "6", "7"); + bd.getConstructorArgumentValues().addGenericArgumentValue(input1); + bd.getConstructorArgumentValues().addGenericArgumentValue(input2); + bf.registerBeanDefinition("genericBean", bd); + + GenericBean gb = (GenericBean) bf.getBean("genericBean"); assertThat(gb.getPlainMap().get("1")).isEqualTo("0"); assertThat(gb.getPlainMap().get("2")).isEqualTo("3"); assertThat(gb.getShortMap().get(Short.valueOf("4"))).isEqualTo(5); @@ -538,109 +477,104 @@ void testGenericMapMapFactoryMethod() { } @Test - void testGenericMapWithKeyTypeFactoryMethod() { + void genericMapWithKeyTypeFactoryMethod() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); - RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); - rbd.setFactoryMethodName("createInstance"); - Map input = new HashMap<>(); - input.put("4", "5"); - input.put("6", "7"); - rbd.getConstructorArgumentValues().addGenericArgumentValue(input); + RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class); + bd.setFactoryMethodName("createInstance"); + Map input = Map.of( + "4", "5", + "6", "7"); + bd.getConstructorArgumentValues().addGenericArgumentValue(input); + bf.registerBeanDefinition("genericBean", bd); - bf.registerBeanDefinition("genericBean", rbd); GenericBean gb = (GenericBean) bf.getBean("genericBean"); - assertThat(gb.getLongMap().get(Long.valueOf("4"))).isEqualTo("5"); assertThat(gb.getLongMap().get(Long.valueOf("6"))).isEqualTo("7"); } @Test - void testGenericMapWithCollectionValueFactoryMethod() { + void genericMapWithCollectionValueFactoryMethod() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); - bf.addPropertyEditorRegistrar(registry -> registry.registerCustomEditor(Number.class, new CustomNumberEditor(Integer.class, false))); - RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); - rbd.setFactoryMethodName("createInstance"); - - Map> input = new HashMap<>(); - HashSet value1 = new HashSet<>(); - value1.add(1); - input.put("1", value1); - ArrayList value2 = new ArrayList<>(); - value2.add(Boolean.TRUE); - input.put("2", value2); - rbd.getConstructorArgumentValues().addGenericArgumentValue(Boolean.TRUE); - rbd.getConstructorArgumentValues().addGenericArgumentValue(input); - - bf.registerBeanDefinition("genericBean", rbd); - GenericBean gb = (GenericBean) bf.getBean("genericBean"); + bf.addPropertyEditorRegistrar(registry -> + registry.registerCustomEditor(Number.class, new CustomNumberEditor(Integer.class, false))); + + RootBeanDefinition bd = new RootBeanDefinition(GenericBean.class); + bd.setFactoryMethodName("createInstance"); + Map> input = Map.of( + "1", Set.of(1), + "2", List.of(Boolean.TRUE)); + bd.getConstructorArgumentValues().addGenericArgumentValue(Boolean.TRUE); + bd.getConstructorArgumentValues().addGenericArgumentValue(input); + bf.registerBeanDefinition("genericBean", bd); - assertThat(gb.getCollectionMap().get(1) instanceof HashSet).isTrue(); - assertThat(gb.getCollectionMap().get(2) instanceof ArrayList).isTrue(); + GenericBean gb = (GenericBean) bf.getBean("genericBean"); + assertThat(gb.getCollectionMap().get(1)).isInstanceOf(Set.class); + assertThat(gb.getCollectionMap().get(2)).isInstanceOf(List.class); } @Test - void testGenericListBean() throws Exception { + void genericListBean() throws Exception { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); new XmlBeanDefinitionReader(bf).loadBeanDefinitions( new ClassPathResource("genericBeanTests.xml", getClass())); - List list = (List) bf.getBean("list"); - assertThat(list).hasSize(1); - assertThat(list.get(0)).isEqualTo(new URL("http://localhost:8080")); + + NamedUrlList list = bf.getBean("list", NamedUrlList.class); + assertThat(list).containsExactly(new URL("http://localhost:8080")); } @Test - void testGenericSetBean() throws Exception { + void genericSetBean() throws Exception { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); new XmlBeanDefinitionReader(bf).loadBeanDefinitions( new ClassPathResource("genericBeanTests.xml", getClass())); - Set set = (Set) bf.getBean("set"); - assertThat(set).hasSize(1); - assertThat(set.iterator().next()).isEqualTo(new URL("http://localhost:8080")); + + NamedUrlSet set = bf.getBean("set", NamedUrlSet.class); + assertThat(set).containsExactly(new URL("http://localhost:8080")); } @Test - void testGenericMapBean() throws Exception { + void genericMapBean() throws Exception { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); new XmlBeanDefinitionReader(bf).loadBeanDefinitions( new ClassPathResource("genericBeanTests.xml", getClass())); - Map map = (Map) bf.getBean("map"); - assertThat(map).hasSize(1); - assertThat(map.keySet().iterator().next()).isEqualTo(10); - assertThat(map.values().iterator().next()).isEqualTo(new URL("http://localhost:8080")); + + NamedUrlMap map = bf.getBean("map", NamedUrlMap.class); + assertThat(map).containsExactly(entry(10, new URL("http://localhost:8080"))); } @Test - void testGenericallyTypedIntegerBean() { + void genericallyTypedIntegerBean() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); new XmlBeanDefinitionReader(bf).loadBeanDefinitions( new ClassPathResource("genericBeanTests.xml", getClass())); + GenericIntegerBean gb = (GenericIntegerBean) bf.getBean("integerBean"); assertThat(gb.getGenericProperty()).isEqualTo(10); - assertThat(gb.getGenericListProperty().get(0)).isEqualTo(20); - assertThat(gb.getGenericListProperty().get(1)).isEqualTo(30); + assertThat(gb.getGenericListProperty()).containsExactly(20, 30); } @Test - void testGenericallyTypedSetOfIntegerBean() { + void genericallyTypedSetOfIntegerBean() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); new XmlBeanDefinitionReader(bf).loadBeanDefinitions( new ClassPathResource("genericBeanTests.xml", getClass())); + GenericSetOfIntegerBean gb = (GenericSetOfIntegerBean) bf.getBean("setOfIntegerBean"); - assertThat(gb.getGenericProperty().iterator().next()).isEqualTo(10); - assertThat(gb.getGenericListProperty().get(0).iterator().next()).isEqualTo(20); - assertThat(gb.getGenericListProperty().get(1).iterator().next()).isEqualTo(30); + assertThat(gb.getGenericProperty()).singleElement().isEqualTo(10); + assertThat(gb.getGenericListProperty()).satisfiesExactly( + zero -> assertThat(zero).containsExactly(20), + first -> assertThat(first).containsExactly(30)); } @Test - @EnabledForTestGroups(LONG_RUNNING) - void testSetBean() throws Exception { + void setBean() throws Exception { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); new XmlBeanDefinitionReader(bf).loadBeanDefinitions( new ClassPathResource("genericBeanTests.xml", getClass())); - UrlSet us = (UrlSet) bf.getBean("setBean"); - assertThat(us).hasSize(1); - assertThat(us.iterator().next()).isEqualTo(new URL("https://www.springframework.org")); + + UrlSet urlSet = bf.getBean("setBean", UrlSet.class); + assertThat(urlSet).containsExactly(new URL("https://www.springframework.org")); } /** @@ -655,27 +589,27 @@ void testSetBean() throws Exception { */ @Test void parameterizedStaticFactoryMethod() { - RootBeanDefinition rbd = new RootBeanDefinition(getClass()); - rbd.setFactoryMethodName("createMockitoMock"); - rbd.getConstructorArgumentValues().addGenericArgumentValue(Runnable.class); + RootBeanDefinition bd = new RootBeanDefinition(getClass()); + bd.setFactoryMethodName("createMockitoMock"); + bd.getConstructorArgumentValues().addGenericArgumentValue(Runnable.class); - assertRunnableMockFactory(rbd); + assertRunnableMockFactory(bd); } @Test void parameterizedStaticFactoryMethodWithWrappedClassName() { - RootBeanDefinition rbd = new RootBeanDefinition(); - rbd.setBeanClassName(getClass().getName()); - rbd.setFactoryMethodName("createMockitoMock"); + RootBeanDefinition bd = new RootBeanDefinition(); + bd.setBeanClassName(getClass().getName()); + bd.setFactoryMethodName("createMockitoMock"); // TypedStringValue is used as an equivalent to an XML-defined argument String - rbd.getConstructorArgumentValues().addGenericArgumentValue(new TypedStringValue(Runnable.class.getName())); + bd.getConstructorArgumentValues().addGenericArgumentValue(new TypedStringValue(Runnable.class.getName())); - assertRunnableMockFactory(rbd); + assertRunnableMockFactory(bd); } - private void assertRunnableMockFactory(RootBeanDefinition rbd) { + private void assertRunnableMockFactory(RootBeanDefinition bd) { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); - bf.registerBeanDefinition("mock", rbd); + bf.registerBeanDefinition("mock", bd); assertThat(bf.isTypeMatch("mock", Runnable.class)).isTrue(); assertThat(bf.getType("mock")).isEqualTo(Runnable.class); @@ -698,14 +632,14 @@ private void assertRunnableMockFactory(RootBeanDefinition rbd) { void parameterizedInstanceFactoryMethod() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); - RootBeanDefinition rbd = new RootBeanDefinition(MocksControl.class); - bf.registerBeanDefinition("mocksControl", rbd); + RootBeanDefinition bd1 = new RootBeanDefinition(MocksControl.class); + bf.registerBeanDefinition("mocksControl", bd1); - rbd = new RootBeanDefinition(); - rbd.setFactoryBeanName("mocksControl"); - rbd.setFactoryMethodName("createMock"); - rbd.getConstructorArgumentValues().addGenericArgumentValue(Runnable.class); - bf.registerBeanDefinition("mock", rbd); + RootBeanDefinition bd2 = new RootBeanDefinition(); + bd2.setFactoryBeanName("mocksControl"); + bd2.setFactoryMethodName("createMock"); + bd2.getConstructorArgumentValues().addGenericArgumentValue(Runnable.class); + bf.registerBeanDefinition("mock", bd2); assertThat(bf.isTypeMatch("mock", Runnable.class)).isTrue(); assertThat(bf.isTypeMatch("mock", Runnable.class)).isTrue(); @@ -719,14 +653,14 @@ void parameterizedInstanceFactoryMethod() { void parameterizedInstanceFactoryMethodWithNonResolvedClassName() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); - RootBeanDefinition rbd = new RootBeanDefinition(MocksControl.class); - bf.registerBeanDefinition("mocksControl", rbd); + RootBeanDefinition bd1 = new RootBeanDefinition(MocksControl.class); + bf.registerBeanDefinition("mocksControl", bd1); - rbd = new RootBeanDefinition(); - rbd.setFactoryBeanName("mocksControl"); - rbd.setFactoryMethodName("createMock"); - rbd.getConstructorArgumentValues().addGenericArgumentValue(Runnable.class.getName()); - bf.registerBeanDefinition("mock", rbd); + RootBeanDefinition bd2 = new RootBeanDefinition(); + bd2.setFactoryBeanName("mocksControl"); + bd2.setFactoryMethodName("createMock"); + bd2.getConstructorArgumentValues().addGenericArgumentValue(Runnable.class.getName()); + bf.registerBeanDefinition("mock", bd2); assertThat(bf.isTypeMatch("mock", Runnable.class)).isTrue(); assertThat(bf.isTypeMatch("mock", Runnable.class)).isTrue(); @@ -740,14 +674,14 @@ void parameterizedInstanceFactoryMethodWithNonResolvedClassName() { void parameterizedInstanceFactoryMethodWithInvalidClassName() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); - RootBeanDefinition rbd = new RootBeanDefinition(MocksControl.class); - bf.registerBeanDefinition("mocksControl", rbd); + RootBeanDefinition bd1 = new RootBeanDefinition(MocksControl.class); + bf.registerBeanDefinition("mocksControl", bd1); - rbd = new RootBeanDefinition(); - rbd.setFactoryBeanName("mocksControl"); - rbd.setFactoryMethodName("createMock"); - rbd.getConstructorArgumentValues().addGenericArgumentValue("x"); - bf.registerBeanDefinition("mock", rbd); + RootBeanDefinition rbd2 = new RootBeanDefinition(); + rbd2.setFactoryBeanName("mocksControl"); + rbd2.setFactoryMethodName("createMock"); + rbd2.getConstructorArgumentValues().addGenericArgumentValue("x"); + bf.registerBeanDefinition("mock", rbd2); assertThat(bf.isTypeMatch("mock", Runnable.class)).isFalse(); assertThat(bf.isTypeMatch("mock", Runnable.class)).isFalse(); @@ -761,14 +695,14 @@ void parameterizedInstanceFactoryMethodWithInvalidClassName() { void parameterizedInstanceFactoryMethodWithIndexedArgument() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); - RootBeanDefinition rbd = new RootBeanDefinition(MocksControl.class); - bf.registerBeanDefinition("mocksControl", rbd); + RootBeanDefinition bd1 = new RootBeanDefinition(MocksControl.class); + bf.registerBeanDefinition("mocksControl", bd1); - rbd = new RootBeanDefinition(); - rbd.setFactoryBeanName("mocksControl"); - rbd.setFactoryMethodName("createMock"); - rbd.getConstructorArgumentValues().addIndexedArgumentValue(0, Runnable.class); - bf.registerBeanDefinition("mock", rbd); + RootBeanDefinition bd2 = new RootBeanDefinition(); + bd2.setFactoryBeanName("mocksControl"); + bd2.setFactoryMethodName("createMock"); + bd2.getConstructorArgumentValues().addIndexedArgumentValue(0, Runnable.class); + bf.registerBeanDefinition("mock", bd2); assertThat(bf.isTypeMatch("mock", Runnable.class)).isTrue(); assertThat(bf.isTypeMatch("mock", Runnable.class)).isTrue(); @@ -783,14 +717,14 @@ void parameterizedInstanceFactoryMethodWithTempClassLoader() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); bf.setTempClassLoader(new OverridingClassLoader(getClass().getClassLoader())); - RootBeanDefinition rbd = new RootBeanDefinition(MocksControl.class); - bf.registerBeanDefinition("mocksControl", rbd); + RootBeanDefinition bd1 = new RootBeanDefinition(MocksControl.class); + bf.registerBeanDefinition("mocksControl", bd1); - rbd = new RootBeanDefinition(); - rbd.setFactoryBeanName("mocksControl"); - rbd.setFactoryMethodName("createMock"); - rbd.getConstructorArgumentValues().addGenericArgumentValue(Runnable.class); - bf.registerBeanDefinition("mock", rbd); + RootBeanDefinition bd2 = new RootBeanDefinition(); + bd2.setFactoryBeanName("mocksControl"); + bd2.setFactoryMethodName("createMock"); + bd2.getConstructorArgumentValues().addGenericArgumentValue(Runnable.class); + bf.registerBeanDefinition("mock", bd2); assertThat(bf.isTypeMatch("mock", Runnable.class)).isTrue(); assertThat(bf.isTypeMatch("mock", Runnable.class)).isTrue(); @@ -801,7 +735,7 @@ void parameterizedInstanceFactoryMethodWithTempClassLoader() { } @Test - void testGenericMatchingWithBeanNameDifferentiation() { + void genericMatchingWithBeanNameDifferentiation() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); bf.setAutowireCandidateResolver(new GenericTypeAwareAutowireCandidateResolver()); @@ -817,15 +751,13 @@ void testGenericMatchingWithBeanNameDifferentiation() { String[] numberStoreNames = bf.getBeanNamesForType(ResolvableType.forClass(NumberStore.class)); String[] doubleStoreNames = bf.getBeanNamesForType(ResolvableType.forClassWithGenerics(NumberStore.class, Double.class)); String[] floatStoreNames = bf.getBeanNamesForType(ResolvableType.forClassWithGenerics(NumberStore.class, Float.class)); - assertThat(numberStoreNames).hasSize(2); - assertThat(numberStoreNames[0]).isEqualTo("doubleStore"); - assertThat(numberStoreNames[1]).isEqualTo("floatStore"); + assertThat(numberStoreNames).containsExactly("doubleStore", "floatStore"); assertThat(doubleStoreNames).isEmpty(); assertThat(floatStoreNames).isEmpty(); } @Test - void testGenericMatchingWithFullTypeDifferentiation() { + void genericMatchingWithFullTypeDifferentiation() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE); bf.setAutowireCandidateResolver(new GenericTypeAwareAutowireCandidateResolver()); @@ -840,19 +772,17 @@ void testGenericMatchingWithFullTypeDifferentiation() { new RootBeanDefinition(NumberBean.class, RootBeanDefinition.AUTOWIRE_CONSTRUCTOR, false)); NumberBean nb = bf.getBean(NumberBean.class); - assertThat(nb.getDoubleStore()).isSameAs(bf.getBean("store1")); - assertThat(nb.getFloatStore()).isSameAs(bf.getBean("store2")); + NumberStore store1 = bf.getBean("store1", NumberStore.class); + assertThat(nb.getDoubleStore()).isSameAs(store1); + NumberStore store2 = bf.getBean("store2", NumberStore.class); + assertThat(nb.getFloatStore()).isSameAs(store2); String[] numberStoreNames = bf.getBeanNamesForType(ResolvableType.forClass(NumberStore.class)); String[] doubleStoreNames = bf.getBeanNamesForType(ResolvableType.forClassWithGenerics(NumberStore.class, Double.class)); String[] floatStoreNames = bf.getBeanNamesForType(ResolvableType.forClassWithGenerics(NumberStore.class, Float.class)); - assertThat(numberStoreNames).hasSize(2); - assertThat(numberStoreNames[0]).isEqualTo("store1"); - assertThat(numberStoreNames[1]).isEqualTo("store2"); - assertThat(doubleStoreNames).hasSize(1); - assertThat(doubleStoreNames[0]).isEqualTo("store1"); - assertThat(floatStoreNames).hasSize(1); - assertThat(floatStoreNames[0]).isEqualTo("store2"); + assertThat(numberStoreNames).containsExactly("store1", "store2"); + assertThat(doubleStoreNames).containsExactly("store1"); + assertThat(floatStoreNames).containsExactly("store2"); ObjectProvider> numberStoreProvider = bf.getBeanProvider(ResolvableType.forClass(NumberStore.class)); ObjectProvider> doubleStoreProvider = bf.getBeanProvider(ResolvableType.forClassWithGenerics(NumberStore.class, Double.class)); @@ -860,64 +790,40 @@ void testGenericMatchingWithFullTypeDifferentiation() { assertThatExceptionOfType(NoUniqueBeanDefinitionException.class).isThrownBy(numberStoreProvider::getObject); assertThatExceptionOfType(NoUniqueBeanDefinitionException.class).isThrownBy(numberStoreProvider::getIfAvailable); assertThat(numberStoreProvider.getIfUnique()).isNull(); - assertThat(doubleStoreProvider.getObject()).isSameAs(bf.getBean("store1")); - assertThat(doubleStoreProvider.getIfAvailable()).isSameAs(bf.getBean("store1")); - assertThat(doubleStoreProvider.getIfUnique()).isSameAs(bf.getBean("store1")); - assertThat(floatStoreProvider.getObject()).isSameAs(bf.getBean("store2")); - assertThat(floatStoreProvider.getIfAvailable()).isSameAs(bf.getBean("store2")); - assertThat(floatStoreProvider.getIfUnique()).isSameAs(bf.getBean("store2")); + assertThat(doubleStoreProvider.getObject()).isSameAs(store1); + assertThat(doubleStoreProvider.getIfAvailable()).isSameAs(store1); + assertThat(doubleStoreProvider.getIfUnique()).isSameAs(store1); + assertThat(floatStoreProvider.getObject()).isSameAs(store2); + assertThat(floatStoreProvider.getIfAvailable()).isSameAs(store2); + assertThat(floatStoreProvider.getIfUnique()).isSameAs(store2); List> resolved = new ArrayList<>(); for (NumberStore instance : numberStoreProvider) { resolved.add(instance); } - assertThat(resolved).hasSize(2); - assertThat(resolved.get(0)).isSameAs(bf.getBean("store1")); - assertThat(resolved.get(1)).isSameAs(bf.getBean("store2")); - - resolved = numberStoreProvider.stream().toList(); - assertThat(resolved).hasSize(2); - assertThat(resolved.get(0)).isSameAs(bf.getBean("store1")); - assertThat(resolved.get(1)).isSameAs(bf.getBean("store2")); - - resolved = numberStoreProvider.orderedStream().toList(); - assertThat(resolved).hasSize(2); - assertThat(resolved.get(0)).isSameAs(bf.getBean("store2")); - assertThat(resolved.get(1)).isSameAs(bf.getBean("store1")); + assertThat(resolved).containsExactly(store1, store2); + assertThat(numberStoreProvider.stream()).containsExactly(store1, store2); + assertThat(numberStoreProvider.orderedStream()).containsExactly(store2, store1); resolved = new ArrayList<>(); for (NumberStore instance : doubleStoreProvider) { resolved.add(instance); } - assertThat(resolved).hasSize(1); - assertThat(resolved.contains(bf.getBean("store1"))).isTrue(); - - resolved = doubleStoreProvider.stream().collect(Collectors.toList()); - assertThat(resolved).hasSize(1); - assertThat(resolved.contains(bf.getBean("store1"))).isTrue(); - - resolved = doubleStoreProvider.orderedStream().collect(Collectors.toList()); - assertThat(resolved).hasSize(1); - assertThat(resolved.contains(bf.getBean("store1"))).isTrue(); + assertThat(resolved).containsExactly(store1); + assertThat(doubleStoreProvider.stream()).singleElement().isEqualTo(store1); + assertThat(doubleStoreProvider.orderedStream()).singleElement().isEqualTo(store1); resolved = new ArrayList<>(); for (NumberStore instance : floatStoreProvider) { resolved.add(instance); } - assertThat(resolved).hasSize(1); - assertThat(resolved.contains(bf.getBean("store2"))).isTrue(); - - resolved = floatStoreProvider.stream().collect(Collectors.toList()); - assertThat(resolved).hasSize(1); - assertThat(resolved.contains(bf.getBean("store2"))).isTrue(); - - resolved = floatStoreProvider.orderedStream().collect(Collectors.toList()); - assertThat(resolved).hasSize(1); - assertThat(resolved.contains(bf.getBean("store2"))).isTrue(); + assertThat(resolved).containsExactly(store2); + assertThat(floatStoreProvider.stream()).singleElement().isEqualTo(store2); + assertThat(floatStoreProvider.orderedStream()).singleElement().isEqualTo(store2); } @Test - void testGenericMatchingWithUnresolvedOrderedStream() { + void genericMatchingWithUnresolvedOrderedStream() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE); bf.setAutowireCandidateResolver(new GenericTypeAwareAutowireCandidateResolver()); @@ -930,10 +836,25 @@ void testGenericMatchingWithUnresolvedOrderedStream() { bf.registerBeanDefinition("store2", bd2); ObjectProvider> numberStoreProvider = bf.getBeanProvider(ResolvableType.forClass(NumberStore.class)); - List> resolved = numberStoreProvider.orderedStream().toList(); - assertThat(resolved).hasSize(2); - assertThat(resolved.get(0)).isSameAs(bf.getBean("store2")); - assertThat(resolved.get(1)).isSameAs(bf.getBean("store1")); + assertThat(numberStoreProvider.orderedStream()).containsExactly( + bf.getBean("store2", NumberStore.class), bf.getBean("store1", NumberStore.class)); + } + + @Test // gh-32489 + void genericMatchingAgainstFactoryBeanClass() { + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + bf.setAutowireCandidateResolver(new GenericTypeAwareAutowireCandidateResolver()); + + RootBeanDefinition bd = new RootBeanDefinition(MyFactoryBean.class); + // Replicate org.springframework.data.repository.config.RepositoryConfigurationDelegate#registerRepositoriesIn + // behavior of setting targetType, required to hit other branch in + // org.springframework.beans.factory.support.GenericTypeAwareAutowireCandidateResolver.checkGenericTypeMatch + bd.setTargetType(ResolvableType.forClassWithGenerics(MyFactoryBean.class, String.class)); + bf.registerBeanDefinition("myFactoryBean", bd); + bf.registerBeanDefinition("myFactoryBeanHolder", + new RootBeanDefinition(MyFactoryBeanHolder.class, AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR, false)); + + assertThat(bf.getBean(MyFactoryBeanHolder.class).factoryBeans).contains(bf.getBean(MyFactoryBean.class)); } @@ -999,7 +920,7 @@ public static class MocksControl { @SuppressWarnings("unchecked") public T createMock(Class toMock) { return (T) Proxy.newProxyInstance(BeanFactoryGenericsTests.class.getClassLoader(), new Class[] {toMock}, - (InvocationHandler) (proxy, method, args) -> { + (proxy, method, args) -> { throw new UnsupportedOperationException("mocked!"); }); } @@ -1052,4 +973,32 @@ public static NumberStore newFloatStore() { } } + + public interface MyGenericInterfaceForFactoryBeans { + } + + + public static class MyFactoryBean implements FactoryBean, MyGenericInterfaceForFactoryBeans { + + @Override + public T getObject() { + throw new UnsupportedOperationException(); + } + + @Override + public Class getObjectType() { + return String.class; + } + } + + + public static class MyFactoryBeanHolder { + + List> factoryBeans; // Requested type is not a FactoryBean type + + public MyFactoryBeanHolder(List> factoryBeans) { + this.factoryBeans = factoryBeans; + } + } + }