Compare commits

...

7 Commits

Author SHA1 Message Date
Moritz Halbritter fa77e2057c Finish! 2025-05-20 18:30:52 +02:00
Moritz Halbritter 7626d5ccb3 Exercise 8 2025-05-20 18:30:52 +02:00
Moritz Halbritter 72de788456 Exercise 7 2025-05-20 18:30:52 +02:00
Moritz Halbritter 559600ca77 Exercise 6 2025-05-20 18:30:52 +02:00
Moritz Halbritter d1cdd9e001 Exercise 5 2025-05-20 18:30:52 +02:00
Moritz Halbritter c469b2a940 Exercise 4 2025-05-20 18:30:52 +02:00
Moritz Halbritter 1e2e977dda Exercise 3 2025-05-20 18:30:52 +02:00
10 changed files with 244 additions and 19 deletions

View File

@ -17,25 +17,22 @@
<dependencies>
<dependency>
<groupId>com.workshop</groupId>
<artifactId>library-autoconfigure</artifactId>
<artifactId>library-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.workshop</groupId>
<artifactId>library-api</artifactId>
</dependency>
<dependency>
<groupId>com.workshop</groupId>
<artifactId>library-stdout</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<artifactId>library-slf4j</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-properties-migrator</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>

View File

@ -7,6 +7,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
public class WorkshopApplication {
public static void main(String[] args) {
System.setProperty("my.custom.condition", "true");
SpringApplication.run(WorkshopApplication.class, args);
}
}

View File

@ -1 +1,3 @@
debug=false
debug=true
workshop.greeting.text=Gude
workshop.greeting.type=none

View File

@ -24,9 +24,41 @@
<artifactId>library-stdout</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.workshop</groupId>
<artifactId>library-slf4j</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,22 +1,43 @@
package com.workshop.magic.config;
import com.workshop.magic.service.GreetingService;
import com.workshop.magic.service.slf4j.BeepGreetingService;
import com.workshop.magic.service.slf4j.LoggerGreetingService;
import com.workshop.magic.service.stdout.StdOutGreetingService;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
@AutoConfiguration
@ConditionalOnClass(GreetingService.class)
@EnableConfigurationProperties(GreetingProperties.class)
public class GreetingAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnClass(StdOutGreetingService.class)
GreetingService stdOutGreetingService() {
return new StdOutGreetingService();
@ConditionalOnProperty(name = "workshop.greeting.type", havingValue = "stdout", matchIfMissing = true)
GreetingService stdOutGreetingService(GreetingProperties properties) {
return new StdOutGreetingService(properties.getPrefix());
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnClass(LoggerGreetingService.class)
@ConditionalOnProperty(name = "workshop.greeting.type", havingValue = "logger")
GreetingService slf4jGreetingService(GreetingProperties properties) {
return new LoggerGreetingService(properties.getPrefix());
}
@Bean
@ConditionalOnMissingBean
@MyCustomCondition
@ConditionalOnClass(BeepGreetingService.class)
GreetingService beepGreetingService(GreetingProperties properties) {
return new BeepGreetingService(properties.getPrefix());
}
}

View File

@ -1,15 +1,21 @@
package com.workshop.magic.config;
public class GreetingProperties {
private String text = "Hello";
private Type type = Type.STDOUT;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.DeprecatedConfigurationProperty;
@ConfigurationProperties(prefix = "workshop.greeting")
public class GreetingProperties {
private Type type = Type.STDOUT;
private String prefix = "Hello";
@DeprecatedConfigurationProperty(replacement = "workshop.greeting.prefix")
@Deprecated
public String getText() {
return this.text;
return this.prefix;
}
public void setText(String text) {
this.text = text;
this.prefix = text;
}
public Type getType() {
@ -20,6 +26,14 @@ public class GreetingProperties {
this.type = type;
}
public String getPrefix() {
return this.prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public enum Type {
STDOUT,
LOGGER,

View File

@ -0,0 +1,16 @@
package com.workshop.magic.config;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Conditional;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({OnCustomCondition.class})
@interface MyCustomCondition {
}

View File

@ -0,0 +1,22 @@
package com.workshop.magic.config;
import java.util.Locale;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
class OnCustomCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
String value = System.getProperty("my.custom.condition");
if (value == null) {
return ConditionOutcome.noMatch("No 'my.custom.condition' system property found");
}
if (value.toLowerCase(Locale.ROOT).equals("true")) {
return ConditionOutcome.match("'my.custom.condition' system property is true");
}
return ConditionOutcome.noMatch("'my.custom.condition' system property is '%s'".formatted(value));
}
}

View File

@ -0,0 +1,101 @@
package com.workshop.magic.config;
import com.workshop.magic.service.GreetingService;
import com.workshop.magic.service.slf4j.BeepGreetingService;
import com.workshop.magic.service.slf4j.LoggerGreetingService;
import com.workshop.magic.service.stdout.StdOutGreetingService;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import static org.assertj.core.api.Assertions.assertThat;
class GreetingAutoConfigurationTest {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(GreetingAutoConfiguration.class));
@Test
void shouldProvideStdOutGreetingServiceByDefault() {
this.contextRunner.run(context -> {
assertThat(context).hasSingleBean(StdOutGreetingService.class);
});
}
@Test
void shouldProvideStdOutGreetingServiceWhenPropertyIsSet() {
this.contextRunner
.withPropertyValues("workshop.greeting.type=stdout")
.run(context -> {
assertThat(context).hasSingleBean(StdOutGreetingService.class);
});
}
@Test
void shouldProvideLoggerGreetingServiceWhenPropertyIsSet() {
this.contextRunner
.withPropertyValues("workshop.greeting.type=logger")
.run(context -> {
assertThat(context).hasSingleBean(LoggerGreetingService.class);
});
}
@Test
void shouldBackOffIfGreetingServiceIsDefinedByUser() {
this.contextRunner
.withBean(GreetingService.class, UserGreetingService::new)
.run(context -> {
assertThat(context).hasSingleBean(GreetingService.class);
assertThat(context).hasSingleBean(UserGreetingService.class);
});
}
@Test
void shouldNotUseStdOutGreetingServiceIfNotOnClasspath() {
this.contextRunner
.withPropertyValues("workshop.greeting.type=stdout")
.withClassLoader(new FilteredClassLoader(StdOutGreetingService.class))
.run(context -> {
assertThat(context).doesNotHaveBean(GreetingService.class);
});
}
@Test
void shouldNotUseLoggerGreetingServiceIfNotOnClasspath() {
this.contextRunner
.withPropertyValues("workshop.greeting.type=logger")
.withClassLoader(new FilteredClassLoader(LoggerGreetingService.class))
.run(context -> {
assertThat(context).doesNotHaveBean(GreetingService.class);
});
}
@Test
void shouldProvideBeepGreetingServiceIfSystemPropertyIsSet() {
this.contextRunner
.withPropertyValues("workshop.greeting.type=none")
.withSystemProperties("my.custom.condition=true")
.run(context -> {
assertThat(context).hasSingleBean(BeepGreetingService.class);
});
}
@Test
void shouldNotUseBeepGreetingServiceIfNotOnClasspath() {
this.contextRunner
.withPropertyValues("workshop.greeting.type=none")
.withSystemProperties("my.custom.condition=true")
.withClassLoader(new FilteredClassLoader(BeepGreetingService.class))
.run(context -> {
assertThat(context).doesNotHaveBean(GreetingService.class);
});
}
private static class UserGreetingService implements GreetingService {
@Override
public void greet(String name) {
System.out.println("UserGreetingService: Hello " + name);
}
}
}

View File

@ -12,4 +12,23 @@
<artifactId>library-spring-boot-starter</artifactId>
</project>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.workshop</groupId>
<artifactId>library-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>com.workshop</groupId>
<artifactId>library-api</artifactId>
</dependency>
<dependency>
<groupId>com.workshop</groupId>
<artifactId>library-stdout</artifactId>
</dependency>
</dependencies>
</project>