diff --git a/spring-di-4/pom.xml b/spring-di-4/pom.xml index a486b19e517d..b9cd10dcf398 100644 --- a/spring-di-4/pom.xml +++ b/spring-di-4/pom.xml @@ -33,6 +33,14 @@ com.baeldung.registrypostprocessor.RegistryPostProcessorApplication + + org.apache.maven.plugins + maven-compiler-plugin + + 17 + 17 + + \ No newline at end of file diff --git a/spring-di-4/src/main/java/com/baeldung/autowiremultipleimplementations/candidates/GoodService.java b/spring-di-4/src/main/java/com/baeldung/autowiremultipleimplementations/candidates/GoodService.java new file mode 100644 index 000000000000..7f7208998dbd --- /dev/null +++ b/spring-di-4/src/main/java/com/baeldung/autowiremultipleimplementations/candidates/GoodService.java @@ -0,0 +1,6 @@ +package com.baeldung.autowiremultipleimplementations.candidates; + +public interface GoodService { + + String getHelloMessage(); +} diff --git a/spring-di-4/src/main/java/com/baeldung/autowiremultipleimplementations/candidates/GoodServiceA.java b/spring-di-4/src/main/java/com/baeldung/autowiremultipleimplementations/candidates/GoodServiceA.java new file mode 100644 index 000000000000..5815153fdca4 --- /dev/null +++ b/spring-di-4/src/main/java/com/baeldung/autowiremultipleimplementations/candidates/GoodServiceA.java @@ -0,0 +1,18 @@ +package com.baeldung.autowiremultipleimplementations.candidates; + +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Profile; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Service; + +@Service +@Profile("default") +@Qualifier("goodServiceA-custom-name") +@Order(3) +public class GoodServiceA implements GoodService { + + @Override + public String getHelloMessage() { + return "Hello from A!"; + } +} diff --git a/spring-di-4/src/main/java/com/baeldung/autowiremultipleimplementations/candidates/GoodServiceB.java b/spring-di-4/src/main/java/com/baeldung/autowiremultipleimplementations/candidates/GoodServiceB.java new file mode 100644 index 000000000000..daa3a2bf179b --- /dev/null +++ b/spring-di-4/src/main/java/com/baeldung/autowiremultipleimplementations/candidates/GoodServiceB.java @@ -0,0 +1,16 @@ +package com.baeldung.autowiremultipleimplementations.candidates; + +import org.springframework.context.annotation.Profile; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Service; + +@Service +@Profile("default") +@Order(2) +public class GoodServiceB implements GoodService { + + @Override + public String getHelloMessage() { + return "Hello from B!"; + } +} diff --git a/spring-di-4/src/main/java/com/baeldung/autowiremultipleimplementations/candidates/GoodServiceC.java b/spring-di-4/src/main/java/com/baeldung/autowiremultipleimplementations/candidates/GoodServiceC.java new file mode 100644 index 000000000000..9c8f799a37d9 --- /dev/null +++ b/spring-di-4/src/main/java/com/baeldung/autowiremultipleimplementations/candidates/GoodServiceC.java @@ -0,0 +1,18 @@ +package com.baeldung.autowiremultipleimplementations.candidates; + +import org.springframework.context.annotation.Primary; +import org.springframework.context.annotation.Profile; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Service; + +@Primary +@Service +@Profile("default") +@Order(1) +public class GoodServiceC implements GoodService { + + @Override + public String getHelloMessage() { + return "Hello from C!"; + } +} diff --git a/spring-di-4/src/main/java/com/baeldung/autowiremultipleimplementations/candidates/GoodServiceD.java b/spring-di-4/src/main/java/com/baeldung/autowiremultipleimplementations/candidates/GoodServiceD.java new file mode 100644 index 000000000000..8ebcdb474e70 --- /dev/null +++ b/spring-di-4/src/main/java/com/baeldung/autowiremultipleimplementations/candidates/GoodServiceD.java @@ -0,0 +1,13 @@ +package com.baeldung.autowiremultipleimplementations.candidates; + +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Service; + +@Service +@Profile("dev") +public class GoodServiceD implements GoodService { + + public String getHelloMessage() { + return "Hello from D!"; + } +} diff --git a/spring-di-4/src/main/java/com/baeldung/autowiremultipleimplementations/candidates/GoodServiceE.java b/spring-di-4/src/main/java/com/baeldung/autowiremultipleimplementations/candidates/GoodServiceE.java new file mode 100644 index 000000000000..45c652681ad3 --- /dev/null +++ b/spring-di-4/src/main/java/com/baeldung/autowiremultipleimplementations/candidates/GoodServiceE.java @@ -0,0 +1,14 @@ +package com.baeldung.autowiremultipleimplementations.candidates; + +import com.baeldung.autowiremultipleimplementations.condition.OnFeatureEnabledCondition; +import org.springframework.context.annotation.Conditional; +import org.springframework.stereotype.Service; + +@Service +@Conditional(OnFeatureEnabledCondition.class) +public class GoodServiceE implements GoodService { + + public String getHelloMessage() { + return "Hello from E!"; + } +} diff --git a/spring-di-4/src/main/java/com/baeldung/autowiremultipleimplementations/components/CollectionsAutowire.java b/spring-di-4/src/main/java/com/baeldung/autowiremultipleimplementations/components/CollectionsAutowire.java new file mode 100644 index 000000000000..ac2f15b2a807 --- /dev/null +++ b/spring-di-4/src/main/java/com/baeldung/autowiremultipleimplementations/components/CollectionsAutowire.java @@ -0,0 +1,35 @@ +package com.baeldung.autowiremultipleimplementations.components; + +import com.baeldung.autowiremultipleimplementations.candidates.GoodService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Map; +import java.util.Set; + +@Component +public class CollectionsAutowire { + + private final Set goodServices; + private final Map goodServiceMap; + + @Autowired + public CollectionsAutowire(Set goodServices, Map goodServiceMap) { + this.goodServices = goodServices; + this.goodServiceMap = goodServiceMap; + } + + public String hello() { + return goodServices.stream() + .map(GoodService::getHelloMessage) + .reduce((a, b) -> a + " " + b) + .orElse("No services available"); + } + + public String helloMap() { + return goodServiceMap.values().stream() + .map(GoodService::getHelloMessage) + .reduce((a, b) -> a + " " + b) + .orElse("No services available"); + } +} diff --git a/spring-di-4/src/main/java/com/baeldung/autowiremultipleimplementations/components/PrimaryAutowire.java b/spring-di-4/src/main/java/com/baeldung/autowiremultipleimplementations/components/PrimaryAutowire.java new file mode 100644 index 000000000000..741cff5467ad --- /dev/null +++ b/spring-di-4/src/main/java/com/baeldung/autowiremultipleimplementations/components/PrimaryAutowire.java @@ -0,0 +1,20 @@ +package com.baeldung.autowiremultipleimplementations.components; + +import com.baeldung.autowiremultipleimplementations.candidates.GoodService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class PrimaryAutowire { + + private final GoodService goodService; + + @Autowired + public PrimaryAutowire(GoodService goodService) { + this.goodService = goodService; + } + + public String hello() { + return goodService.getHelloMessage(); + } +} diff --git a/spring-di-4/src/main/java/com/baeldung/autowiremultipleimplementations/components/QualifierAutowire.java b/spring-di-4/src/main/java/com/baeldung/autowiremultipleimplementations/components/QualifierAutowire.java new file mode 100644 index 000000000000..bf5f8ec8e7aa --- /dev/null +++ b/spring-di-4/src/main/java/com/baeldung/autowiremultipleimplementations/components/QualifierAutowire.java @@ -0,0 +1,29 @@ +package com.baeldung.autowiremultipleimplementations.components; + +import com.baeldung.autowiremultipleimplementations.candidates.GoodService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Component; + +@Component +public class QualifierAutowire { + + private final GoodService goodServiceA; + private final GoodService goodServiceB; + private final GoodService goodServiceC; + + @Autowired + public QualifierAutowire(@Qualifier("goodServiceA-custom-name") GoodService niceServiceA, + @Qualifier("goodServiceB") GoodService niceServiceB, + GoodService goodServiceC) { + this.goodServiceA = niceServiceA; + this.goodServiceB = niceServiceB; + this.goodServiceC = goodServiceC; + } + + public String hello() { + return goodServiceA.getHelloMessage() + " " + + goodServiceB.getHelloMessage() + " " + + goodServiceC.getHelloMessage(); + } +} diff --git a/spring-di-4/src/main/java/com/baeldung/autowiremultipleimplementations/condition/OnFeatureEnabledCondition.java b/spring-di-4/src/main/java/com/baeldung/autowiremultipleimplementations/condition/OnFeatureEnabledCondition.java new file mode 100644 index 000000000000..f7ae0ab04edf --- /dev/null +++ b/spring-di-4/src/main/java/com/baeldung/autowiremultipleimplementations/condition/OnFeatureEnabledCondition.java @@ -0,0 +1,14 @@ +package com.baeldung.autowiremultipleimplementations.condition; + +import org.springframework.context.annotation.Condition; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.core.type.AnnotatedTypeMetadata; + +public class OnFeatureEnabledCondition implements Condition { + + @Override + public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { + String featureToggle = context.getEnvironment().getProperty("feature.toggle"); + return "enabled".equalsIgnoreCase(featureToggle); + } +} diff --git a/spring-di-4/src/test/java/com/baeldung/autowiremultipleimplementations/CollectionsAutowireUnitTest.java b/spring-di-4/src/test/java/com/baeldung/autowiremultipleimplementations/CollectionsAutowireUnitTest.java new file mode 100644 index 000000000000..c8eda9094469 --- /dev/null +++ b/spring-di-4/src/test/java/com/baeldung/autowiremultipleimplementations/CollectionsAutowireUnitTest.java @@ -0,0 +1,75 @@ +package com.baeldung.autowiremultipleimplementations; + +import com.baeldung.autowiremultipleimplementations.components.CollectionsAutowire; +import com.baeldung.autowiremultipleimplementations.candidates.*; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import java.lang.reflect.Field; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.*; + +@ExtendWith(SpringExtension.class) +@SpringBootTest(classes = {CollectionsAutowire.class, GoodServiceD.class, GoodServiceC.class, GoodServiceB.class, GoodServiceA.class}) +public class CollectionsAutowireUnitTest { + + @Autowired + private CollectionsAutowire collectionsAutowire; + + @Test + public void testSetAutowiring() throws NoSuchFieldException, IllegalAccessException { + Set rawServices = (Set) getPrivateField(collectionsAutowire, "goodServices"); + assertNotNull(rawServices, "Set of GoodService should not be null"); + assertFalse(rawServices.isEmpty(), "Set of GoodService should not be empty"); + + Set services = rawServices.stream() + .map(service -> (GoodService) service) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + + // Check for all specific types + assertTrue(services.stream().anyMatch(s -> s instanceof GoodServiceA), "Should contain GoodServiceA"); + assertTrue(services.stream().anyMatch(s -> s instanceof GoodServiceB), "Should contain GoodServiceB"); + assertTrue(services.stream().anyMatch(s -> s instanceof GoodServiceC), "Should contain GoodServiceC"); + + String actualMessage = collectionsAutowire.hello(); + assertTrue(actualMessage.contains("Hello from A!"), "Message should contain greeting from A"); + assertTrue(actualMessage.contains("Hello from B!"), "Message should contain greeting from B"); + assertTrue(actualMessage.contains("Hello from C!"), "Message should contain greeting from C"); + } + + @Test + public void testMapAutowiring() throws NoSuchFieldException, IllegalAccessException { + Map rawServiceMap = (Map) getPrivateField(collectionsAutowire, "goodServiceMap"); + assertNotNull(rawServiceMap, "Map of GoodService should not be null"); + assertFalse(rawServiceMap.isEmpty(), "Map of GoodService should not be empty"); + + Map serviceMap = rawServiceMap.entrySet().stream() + .collect(Collectors.toMap( + entry -> (String) entry.getKey(), + entry -> (GoodService) entry.getValue() + )); + + // Check keys and specific instances + assertTrue(serviceMap.containsKey("goodServiceA"), "Map should contain a key for GoodServiceA"); + assertTrue(serviceMap.containsKey("goodServiceB"), "Map should contain a key for GoodServiceB"); + assertTrue(serviceMap.containsKey("goodServiceC"), "Map should contain a key for GoodServiceC"); + + assertInstanceOf(GoodServiceA.class, serviceMap.get("goodServiceA"), "goodServiceA should be an instance of GoodServiceA"); + assertInstanceOf(GoodServiceB.class, serviceMap.get("goodServiceB"), "goodServiceB should be an instance of GoodServiceB"); + assertInstanceOf(GoodServiceC.class, serviceMap.get("goodServiceC"), "goodServiceC should be an instance of GoodServiceC"); + } + + private Object getPrivateField(Object object, String fieldName) throws NoSuchFieldException, IllegalAccessException { + Field field = object.getClass().getDeclaredField(fieldName); + field.setAccessible(true); + return field.get(object); + } +} diff --git a/spring-di-4/src/test/java/com/baeldung/autowiremultipleimplementations/ConditionalDisabledUnitTest.java b/spring-di-4/src/test/java/com/baeldung/autowiremultipleimplementations/ConditionalDisabledUnitTest.java new file mode 100644 index 000000000000..c4095915a64d --- /dev/null +++ b/spring-di-4/src/test/java/com/baeldung/autowiremultipleimplementations/ConditionalDisabledUnitTest.java @@ -0,0 +1,26 @@ +package com.baeldung.autowiremultipleimplementations; + +import com.baeldung.autowiremultipleimplementations.candidates.GoodService; +import com.baeldung.autowiremultipleimplementations.candidates.GoodServiceE; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.junit.jupiter.api.extension.ExtendWith; + +import static org.junit.jupiter.api.Assertions.assertNull; + +@ExtendWith(SpringExtension.class) +@SpringBootTest(classes = {GoodServiceE.class}) +@TestPropertySource(properties = {"feature.toggle=disabled"}) +public class ConditionalDisabledUnitTest { + + @Autowired(required = false) + private GoodService goodService; + + @Test + void testServiceWhenFeatureDisabled() { + assertNull(goodService, "GoodService should not be autowired when feature.toggle is disabled"); + } +} diff --git a/spring-di-4/src/test/java/com/baeldung/autowiremultipleimplementations/ConditionalEnabledUnitTest.java b/spring-di-4/src/test/java/com/baeldung/autowiremultipleimplementations/ConditionalEnabledUnitTest.java new file mode 100644 index 000000000000..d186aa678ece --- /dev/null +++ b/spring-di-4/src/test/java/com/baeldung/autowiremultipleimplementations/ConditionalEnabledUnitTest.java @@ -0,0 +1,27 @@ +package com.baeldung.autowiremultipleimplementations; + +import com.baeldung.autowiremultipleimplementations.candidates.GoodService; +import com.baeldung.autowiremultipleimplementations.candidates.GoodServiceE; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import static org.junit.jupiter.api.Assertions.*; + +@ExtendWith(SpringExtension.class) +@SpringBootTest(classes = {GoodServiceE.class}) +@TestPropertySource(properties = {"feature.toggle=enabled"}) +public class ConditionalEnabledUnitTest { + + @Autowired + private GoodService goodService; + + @Test + void testServiceWhenFeatureEnabled() { + assertNotNull(goodService, "GoodService should be autowired when feature.toggle is enabled"); + assertInstanceOf(GoodServiceE.class, goodService, "goodService should be an instance of GoodServiceE"); + } +} diff --git a/spring-di-4/src/test/java/com/baeldung/autowiremultipleimplementations/PrimaryAutowireUnitTest.java b/spring-di-4/src/test/java/com/baeldung/autowiremultipleimplementations/PrimaryAutowireUnitTest.java new file mode 100644 index 000000000000..491226174351 --- /dev/null +++ b/spring-di-4/src/test/java/com/baeldung/autowiremultipleimplementations/PrimaryAutowireUnitTest.java @@ -0,0 +1,38 @@ +package com.baeldung.autowiremultipleimplementations; + +import com.baeldung.autowiremultipleimplementations.components.PrimaryAutowire; +import com.baeldung.autowiremultipleimplementations.candidates.GoodService; +import com.baeldung.autowiremultipleimplementations.candidates.GoodServiceA; +import com.baeldung.autowiremultipleimplementations.candidates.GoodServiceB; +import com.baeldung.autowiremultipleimplementations.candidates.GoodServiceC; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.ApplicationContext; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import static org.junit.jupiter.api.Assertions.*; + +@SpringBootTest(classes = {PrimaryAutowire.class, GoodServiceC.class, GoodServiceB.class, GoodServiceA.class}) +@ExtendWith(SpringExtension.class) +public class PrimaryAutowireUnitTest { + + @Autowired + private ApplicationContext context; + + @Autowired + private PrimaryAutowire primaryAutowire; + + @Test + public void whenPrimaryServiceInjected_thenItShouldBeGoodServiceC() { + GoodService injectedService = context.getBean(GoodService.class); + + assertInstanceOf(GoodServiceC.class, injectedService); + + String expectedMessage = "Hello from C!"; // GoodServiceC returns this message + String actualMessage = primaryAutowire.hello(); + + assertEquals(expectedMessage, actualMessage); + } +} diff --git a/spring-di-4/src/test/java/com/baeldung/autowiremultipleimplementations/ProfilesAutowireUnitTest.java b/spring-di-4/src/test/java/com/baeldung/autowiremultipleimplementations/ProfilesAutowireUnitTest.java new file mode 100644 index 000000000000..028a4a92583e --- /dev/null +++ b/spring-di-4/src/test/java/com/baeldung/autowiremultipleimplementations/ProfilesAutowireUnitTest.java @@ -0,0 +1,29 @@ +package com.baeldung.autowiremultipleimplementations; + +import com.baeldung.autowiremultipleimplementations.components.PrimaryAutowire; +import com.baeldung.autowiremultipleimplementations.candidates.*; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import static org.junit.jupiter.api.Assertions.*; + +@ExtendWith(SpringExtension.class) +@SpringBootTest(classes = {PrimaryAutowire.class, GoodServiceD.class, GoodServiceC.class, + GoodServiceB.class, GoodServiceA.class}) +@ActiveProfiles("dev") +public class ProfilesAutowireUnitTest { + + @Autowired + private GoodService goodService; + + @Test + public void goodServiceDIsAutowiredCorrectly() { + assertNotNull(goodService, "GoodService should be autowired"); + assertInstanceOf(GoodServiceD.class, goodService, + "Autowired GoodService should be an instance of GoodServiceD under 'dev' profile"); + } +} diff --git a/spring-di-4/src/test/java/com/baeldung/autowiremultipleimplementations/QualifierAutowireUnitTest.java b/spring-di-4/src/test/java/com/baeldung/autowiremultipleimplementations/QualifierAutowireUnitTest.java new file mode 100644 index 000000000000..ab756ecea426 --- /dev/null +++ b/spring-di-4/src/test/java/com/baeldung/autowiremultipleimplementations/QualifierAutowireUnitTest.java @@ -0,0 +1,49 @@ +package com.baeldung.autowiremultipleimplementations; + +import com.baeldung.autowiremultipleimplementations.components.QualifierAutowire; +import com.baeldung.autowiremultipleimplementations.candidates.GoodService; +import com.baeldung.autowiremultipleimplementations.candidates.GoodServiceA; +import com.baeldung.autowiremultipleimplementations.candidates.GoodServiceB; +import com.baeldung.autowiremultipleimplementations.candidates.GoodServiceC; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import java.lang.reflect.Field; + +import static org.junit.jupiter.api.Assertions.*; + +@SpringBootTest(classes = {QualifierAutowire.class, GoodServiceC.class, GoodServiceB.class, GoodServiceA.class}) +@ExtendWith(SpringExtension.class) +public class QualifierAutowireUnitTest { + + @Autowired + private QualifierAutowire qualifierAutowire; + + @Test + public void testAutowiring() throws NoSuchFieldException, IllegalAccessException { + assertNotNull(qualifierAutowire, "QualifierAutowire should be autowired"); + + GoodService goodServiceA = getGoodServiceField("goodServiceA"); + GoodService goodServiceB = getGoodServiceField("goodServiceB"); + GoodService goodServiceC = getGoodServiceField("goodServiceC"); + + // Verify the types of the autowired services + assertInstanceOf(GoodServiceA.class, goodServiceA, "goodServiceA should be an instance of GoodServiceA"); + assertInstanceOf(GoodServiceB.class, goodServiceB, "goodServiceB should be an instance of GoodServiceB"); + assertInstanceOf(GoodServiceC.class, goodServiceC, "goodServiceC should be an instance of GoodServiceC"); + + // Check that the hello message is as expected + String expectedMessage = "Hello from A! Hello from B! Hello from C!"; + String actualMessage = qualifierAutowire.hello(); + assertEquals(expectedMessage, actualMessage, "Messages should be concatenated correctly from all services"); + } + + private GoodService getGoodServiceField(String fieldName) throws NoSuchFieldException, IllegalAccessException { + Field field = qualifierAutowire.getClass().getDeclaredField(fieldName); + field.setAccessible(true); + return (GoodService) field.get(qualifierAutowire); + } +}