Browse Source

Stop using @ConditionalOnClass on @Bean methods

See gh-47429

Signed-off-by: Dmytro Nosan <dimanosan@gmail.com>
pull/47902/head
Dmytro Nosan 2 months ago committed by Phillip Webb
parent
commit
ba88ec7ed1
  1. 11
      buildSrc/src/main/java/org/springframework/boot/build/architecture/ArchitectureCheck.java
  2. 9
      buildSrc/src/main/java/org/springframework/boot/build/architecture/ArchitectureRules.java
  3. 81
      buildSrc/src/test/java/org/springframework/boot/build/architecture/ArchitectureCheckTests.java
  4. 36
      buildSrc/src/test/java/org/springframework/boot/build/architecture/annotations/TestConditionalOnClass.java
  5. 30
      buildSrc/src/test/java/org/springframework/boot/build/architecture/conditionalonclass/OnBeanMethod.java
  6. 27
      spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/audit/AuditAutoConfiguration.java
  7. 4
      spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/jackson/JacksonEndpointAutoConfiguration.java
  8. 1
      spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/servlet/SecurityRequestMatchersManagementContextConfiguration.java
  9. 33
      spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/servlet/ServletManagementChildContextConfiguration.java
  10. 52
      spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration.java
  11. 13
      spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/hateoas/HypermediaAutoConfiguration.java
  12. 25
      spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarReactiveAutoConfiguration.java
  13. 20
      spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerFactoryAutoConfiguration.java
  14. 30
      spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryAutoConfiguration.java

11
buildSrc/src/main/java/org/springframework/boot/build/architecture/ArchitectureCheck.java

@ -70,15 +70,19 @@ import org.gradle.api.tasks.VerificationException; @@ -70,15 +70,19 @@ import org.gradle.api.tasks.VerificationException;
*/
public abstract class ArchitectureCheck extends DefaultTask {
private static final String CONDITIONAL_ON_CLASS_ANNOTATION = "org.springframework.boot.autoconfigure.condition.ConditionalOnClass";
private FileCollection classes;
public ArchitectureCheck() {
getOutputDirectory().convention(getProject().getLayout().getBuildDirectory().dir(getName()));
getConditionalOnClassAnnotation().convention(CONDITIONAL_ON_CLASS_ANNOTATION);
getRules().addAll(getProhibitObjectsRequireNonNull().convention(true)
.map(whenTrue(ArchitectureRules::noClassesShouldCallObjectsRequireNonNull)));
getRules().addAll(ArchitectureRules.standard());
getRules().addAll(whenMainSources(
() -> Collections.singletonList(ArchitectureRules.allBeanMethodsShouldReturnNonPrivateType())));
getRules().addAll(whenMainSources(() -> List
.of(ArchitectureRules.allBeanMethodsShouldReturnNonPrivateType(), ArchitectureRules
.allBeanMethodsShouldNotHaveConditionalOnClassAnnotation(getConditionalOnClassAnnotation().get()))));
getRuleDescriptions().set(getRules().map(this::asDescriptions));
}
@ -186,4 +190,7 @@ public abstract class ArchitectureCheck extends DefaultTask { @@ -186,4 +190,7 @@ public abstract class ArchitectureCheck extends DefaultTask {
@Input // Use descriptions as input since rules aren't serializable
abstract ListProperty<String> getRuleDescriptions();
@Internal
abstract Property<String> getConditionalOnClassAnnotation();
}

9
buildSrc/src/main/java/org/springframework/boot/build/architecture/ArchitectureRules.java

@ -108,6 +108,15 @@ final class ArchitectureRules { @@ -108,6 +108,15 @@ final class ArchitectureRules {
.allowEmptyShould(true);
}
static ArchRule allBeanMethodsShouldNotHaveConditionalOnClassAnnotation(String annotationName) {
return methodsThatAreAnnotatedWith("org.springframework.context.annotation.Bean").should()
.notBeAnnotatedWith(annotationName)
.because("@ConditionalOnClass on @Bean methods is ineffective - it doesn't prevent "
+ "the method signature from being loaded. Such condition need to be placed"
+ " on a @Configuration class, allowing the condition to back off before the type is loaded.")
.allowEmptyShould(true);
}
private static ArchRule allPackagesShouldBeFreeOfTangles() {
return SlicesRuleDefinition.slices().matching("(**)").should().beFreeOfCycles();
}

81
buildSrc/src/test/java/org/springframework/boot/build/architecture/ArchitectureCheckTests.java

@ -26,6 +26,7 @@ import java.util.LinkedHashMap; @@ -26,6 +26,7 @@ import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.UnaryOperator;
import org.gradle.api.tasks.SourceSet;
import org.gradle.testkit.runner.BuildResult;
@ -39,6 +40,7 @@ import org.junit.jupiter.api.io.TempDir; @@ -39,6 +40,7 @@ import org.junit.jupiter.api.io.TempDir;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.springframework.boot.build.architecture.annotations.TestConditionalOnClass;
import org.springframework.util.ClassUtils;
import org.springframework.util.FileSystemUtils;
import org.springframework.util.StringUtils;
@ -180,7 +182,7 @@ class ArchitectureCheckTests { @@ -180,7 +182,7 @@ class ArchitectureCheckTests {
void whenClassCallsObjectsRequireNonNullWithMessageAndProhibitObjectsRequireNonNullIsFalseShouldSucceedAndWriteEmptyReport(
Task task) throws IOException {
prepareTask(task, "objects/requireNonNullWithString");
build(this.gradleBuild.withProhibitObjectsRequireNonNull(task, false), task);
build(this.gradleBuild.withProhibitObjectsRequireNonNull(false), task);
}
@ParameterizedTest(name = "{0}")
@ -195,7 +197,7 @@ class ArchitectureCheckTests { @@ -195,7 +197,7 @@ class ArchitectureCheckTests {
void whenClassCallsObjectsRequireNonNullWithSupplierAndProhibitObjectsRequireNonNullIsFalseShouldSucceedAndWriteEmptyReport(
Task task) throws IOException {
prepareTask(task, "objects/requireNonNullWithSupplier");
build(this.gradleBuild.withProhibitObjectsRequireNonNull(task, false), task);
build(this.gradleBuild.withProhibitObjectsRequireNonNull(false), task);
}
@ParameterizedTest(name = "{0}")
@ -295,6 +297,25 @@ class ArchitectureCheckTests { @@ -295,6 +297,25 @@ class ArchitectureCheckTests {
"should not have a value that is the same as the type of the method's first parameter");
}
@Test
void whenConditionalOnClassUsedOnBeanMethodsWithMainSourcesShouldFailAndWriteReport() throws IOException {
prepareTask(Task.CHECK_ARCHITECTURE_MAIN, "conditionalonclass", "annotations");
GradleBuild gradleBuild = this.gradleBuild.withDependencies(SPRING_CONTEXT)
.withConditionalOnClassAnnotation(TestConditionalOnClass.class.getName());
buildAndFail(gradleBuild, Task.CHECK_ARCHITECTURE_MAIN,
"because @ConditionalOnClass on @Bean methods is ineffective - it doesn't prevent"
+ " the method signature from being loaded. Such condition need to be placed"
+ " on a @Configuration class, allowing the condition to back off before the type is loaded");
}
@Test
void whenConditionalOnClassUsedOnBeanMethodsWithTestSourcesShouldSucceedAndWriteEmptyReport() throws IOException {
prepareTask(Task.CHECK_ARCHITECTURE_TEST, "conditionalonclass", "annotations");
GradleBuild gradleBuild = this.gradleBuild.withDependencies(SPRING_CONTEXT)
.withConditionalOnClassAnnotation(TestConditionalOnClass.class.getName());
build(gradleBuild, Task.CHECK_ARCHITECTURE_TEST);
}
private void prepareTask(Task task, String... sourceDirectories) throws IOException {
for (String sourceDirectory : sourceDirectories) {
FileSystemUtils.copyRecursively(
@ -310,7 +331,7 @@ class ArchitectureCheckTests { @@ -310,7 +331,7 @@ class ArchitectureCheckTests {
private void build(GradleBuild gradleBuild, Task task) throws IOException {
try {
BuildResult buildResult = gradleBuild.build(task.toString());
assertThat(buildResult.taskPaths(TaskOutcome.SUCCESS)).contains(":" + task);
assertThat(buildResult.taskPaths(TaskOutcome.SUCCESS)).as(buildResult.getOutput()).contains(":" + task);
assertThat(task.getFailureReport(gradleBuild.getProjectDir())).isEmpty();
}
catch (UnexpectedBuildFailure ex) {
@ -326,7 +347,7 @@ class ArchitectureCheckTests { @@ -326,7 +347,7 @@ class ArchitectureCheckTests {
private void buildAndFail(GradleBuild gradleBuild, Task task, String... messages) throws IOException {
try {
BuildResult buildResult = gradleBuild.buildAndFail(task.toString());
assertThat(buildResult.taskPaths(TaskOutcome.FAILED)).contains(":" + task);
assertThat(buildResult.taskPaths(TaskOutcome.FAILED)).as(buildResult.getOutput()).contains(":" + task);
assertThat(task.getFailureReport(gradleBuild.getProjectDir())).contains(messages);
}
catch (UnexpectedBuildSuccess ex) {
@ -371,7 +392,7 @@ class ArchitectureCheckTests { @@ -371,7 +392,7 @@ class ArchitectureCheckTests {
private final Set<String> dependencies = new LinkedHashSet<>();
private final Map<Task, Boolean> prohibitObjectsRequireNonNull = new LinkedHashMap<>();
private final Map<Task, TaskConfiguration> taskConfigurations = new LinkedHashMap<>();
private GradleBuild(Path projectDir) {
this.projectDir = projectDir;
@ -381,12 +402,28 @@ class ArchitectureCheckTests { @@ -381,12 +402,28 @@ class ArchitectureCheckTests {
return this.projectDir;
}
GradleBuild withProhibitObjectsRequireNonNull(Task task, boolean prohibitObjectsRequireNonNull) {
this.prohibitObjectsRequireNonNull.put(task, prohibitObjectsRequireNonNull);
GradleBuild withProhibitObjectsRequireNonNull(Boolean prohibitObjectsRequireNonNull) {
for (Task task : Task.values()) {
configureTask(task, (configuration) -> configuration
.withProhibitObjectsRequireNonNull(prohibitObjectsRequireNonNull));
}
return this;
}
GradleBuild withConditionalOnClassAnnotation(String annotationName) {
for (Task task : Task.values()) {
configureTask(task, (configuration) -> configuration.withConditionalOnClassAnnotation(annotationName));
}
return this;
}
private void configureTask(Task task, UnaryOperator<TaskConfiguration> configurer) {
this.taskConfigurations.computeIfAbsent(task, (key) -> new TaskConfiguration(null, null));
this.taskConfigurations.compute(task, (key, value) -> configurer.apply(value));
}
GradleBuild withDependencies(String... dependencies) {
this.dependencies.clear();
this.dependencies.addAll(Arrays.asList(dependencies));
return this;
}
@ -415,15 +452,22 @@ class ArchitectureCheckTests { @@ -415,15 +452,22 @@ class ArchitectureCheckTests {
if (!this.dependencies.isEmpty()) {
buildFile.append("dependencies {\n");
for (String dependency : this.dependencies) {
buildFile.append(" implementation '%s'\n".formatted(dependency));
buildFile.append("\n implementation ").append(StringUtils.quote(dependency));
}
buildFile.append("}\n");
}
this.prohibitObjectsRequireNonNull.forEach((task, prohibitObjectsRequireNonNull) -> buildFile.append(task)
.append(" {\n")
.append(" prohibitObjectsRequireNonNull = ")
.append(prohibitObjectsRequireNonNull)
.append("\n}\n\n"));
this.taskConfigurations.forEach((task, configuration) -> {
buildFile.append(task).append(" {");
if (configuration.conditionalOnClassAnnotation() != null) {
buildFile.append("\n conditionalOnClassAnnotation = ")
.append(StringUtils.quote(configuration.conditionalOnClassAnnotation()));
}
if (configuration.prohibitObjectsRequireNonNull() != null) {
buildFile.append("\n prohibitObjectsRequireNonNull = ")
.append(configuration.prohibitObjectsRequireNonNull());
}
buildFile.append("\n}\n");
});
Files.writeString(this.projectDir.resolve("build.gradle"), buildFile, StandardCharsets.UTF_8);
return GradleRunner.create()
.withProjectDir(this.projectDir.toFile())
@ -431,6 +475,17 @@ class ArchitectureCheckTests { @@ -431,6 +475,17 @@ class ArchitectureCheckTests {
.withPluginClasspath();
}
private record TaskConfiguration(Boolean prohibitObjectsRequireNonNull, String conditionalOnClassAnnotation) {
private TaskConfiguration withConditionalOnClassAnnotation(String annotationName) {
return new TaskConfiguration(this.prohibitObjectsRequireNonNull, annotationName);
}
private TaskConfiguration withProhibitObjectsRequireNonNull(Boolean prohibitObjectsRequireNonNull) {
return new TaskConfiguration(prohibitObjectsRequireNonNull, this.conditionalOnClassAnnotation);
}
}
}
}

36
buildSrc/src/test/java/org/springframework/boot/build/architecture/annotations/TestConditionalOnClass.java

@ -0,0 +1,36 @@ @@ -0,0 +1,36 @@
/*
* Copyright 2012-present 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.build.architecture.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* {@code @ConditionalOnClass} analogue for architecture checks.
*
*/
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface TestConditionalOnClass {
Class<?>[] value() default {};
String[] name() default {};
}

30
buildSrc/src/test/java/org/springframework/boot/build/architecture/conditionalonclass/OnBeanMethod.java

@ -0,0 +1,30 @@ @@ -0,0 +1,30 @@
/*
* Copyright 2012-present 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.build.architecture.conditionalonclass;
import org.springframework.boot.build.architecture.annotations.TestConditionalOnClass;
import org.springframework.context.annotation.Bean;
class OnBeanMethod {
@Bean
@TestConditionalOnClass(String.class)
String helloWorld() {
return "Hello World";
}
}

27
spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/audit/AuditAutoConfiguration.java

@ -31,6 +31,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -31,6 +31,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* {@link EnableAutoConfiguration Auto-configuration} for {@link AuditEvent}s.
@ -50,18 +51,28 @@ public class AuditAutoConfiguration { @@ -50,18 +51,28 @@ public class AuditAutoConfiguration {
return new AuditListener(auditEventRepository);
}
@Bean
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = "org.springframework.security.authentication.event.AbstractAuthenticationEvent")
@ConditionalOnMissingBean(AbstractAuthenticationAuditListener.class)
public AuthenticationAuditListener authenticationAuditListener() {
return new AuthenticationAuditListener();
static class AuthenticationAuditConfiguration {
@Bean
@ConditionalOnMissingBean(AbstractAuthenticationAuditListener.class)
AuthenticationAuditListener authenticationAuditListener() {
return new AuthenticationAuditListener();
}
}
@Bean
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = "org.springframework.security.access.event.AbstractAuthorizationEvent")
@ConditionalOnMissingBean(AbstractAuthorizationAuditListener.class)
public AuthorizationAuditListener authorizationAuditListener() {
return new AuthorizationAuditListener();
static class AuthorizationAuditConfiguration {
@Bean
@ConditionalOnMissingBean(AbstractAuthorizationAuditListener.class)
AuthorizationAuditListener authorizationAuditListener() {
return new AuthorizationAuditListener();
}
}
}

4
spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/jackson/JacksonEndpointAutoConfiguration.java

@ -36,12 +36,12 @@ import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; @@ -36,12 +36,12 @@ import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
* @since 3.0.0
*/
@AutoConfiguration(after = JacksonAutoConfiguration.class)
@ConditionalOnClass({ ObjectMapper.class, Jackson2ObjectMapperBuilder.class })
public class JacksonEndpointAutoConfiguration {
@Bean
@ConditionalOnProperty(name = "management.endpoints.jackson.isolated-object-mapper", matchIfMissing = true)
@ConditionalOnClass({ ObjectMapper.class, Jackson2ObjectMapperBuilder.class })
public EndpointObjectMapper endpointObjectMapper() {
EndpointObjectMapper endpointObjectMapper() {
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json()
.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,
SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS)

1
spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/servlet/SecurityRequestMatchersManagementContextConfiguration.java

@ -53,7 +53,6 @@ public class SecurityRequestMatchersManagementContextConfiguration { @@ -53,7 +53,6 @@ public class SecurityRequestMatchersManagementContextConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnClass(DispatcherServlet.class)
public RequestMatcherProvider requestMatcherProvider(DispatcherServletPath servletPath) {
return new AntPathRequestMatcherProvider(servletPath::getRelativePath);
}

33
spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/servlet/ServletManagementChildContextConfiguration.java

@ -81,22 +81,37 @@ class ServletManagementChildContextConfiguration { @@ -81,22 +81,37 @@ class ServletManagementChildContextConfiguration {
return new ServletManagementWebServerFactoryCustomizer(beanFactory);
}
@Bean
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = "io.undertow.Undertow")
UndertowAccessLogCustomizer undertowManagementAccessLogCustomizer() {
return new UndertowAccessLogCustomizer();
static class UndertowConfiguration {
@Bean
UndertowAccessLogCustomizer undertowManagementAccessLogCustomizer() {
return new UndertowAccessLogCustomizer();
}
}
@Bean
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = "org.apache.catalina.valves.AccessLogValve")
TomcatAccessLogCustomizer tomcatManagementAccessLogCustomizer() {
return new TomcatAccessLogCustomizer();
static class TomcatConfiguration {
@Bean
TomcatAccessLogCustomizer tomcatManagementAccessLogCustomizer() {
return new TomcatAccessLogCustomizer();
}
}
@Bean
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = "org.eclipse.jetty.server.Server")
JettyAccessLogCustomizer jettyManagementAccessLogCustomizer() {
return new JettyAccessLogCustomizer();
static class JettyConfiguration {
@Bean
JettyAccessLogCustomizer jettyManagementAccessLogCustomizer() {
return new JettyAccessLogCustomizer();
}
}
@Configuration(proxyBeanMethods = false)

52
spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration.java

@ -144,24 +144,6 @@ public class FlywayAutoConfiguration { @@ -144,24 +144,6 @@ public class FlywayAutoConfiguration {
return new PropertiesFlywayConnectionDetails(this.properties);
}
@Bean
@ConditionalOnClass(name = "org.flywaydb.database.sqlserver.SQLServerConfigurationExtension")
SqlServerFlywayConfigurationCustomizer sqlServerFlywayConfigurationCustomizer() {
return new SqlServerFlywayConfigurationCustomizer(this.properties);
}
@Bean
@ConditionalOnClass(name = "org.flywaydb.database.oracle.OracleConfigurationExtension")
OracleFlywayConfigurationCustomizer oracleFlywayConfigurationCustomizer() {
return new OracleFlywayConfigurationCustomizer(this.properties);
}
@Bean
@ConditionalOnClass(name = "org.flywaydb.database.postgresql.PostgreSQLConfigurationExtension")
PostgresqlFlywayConfigurationCustomizer postgresqlFlywayConfigurationCustomizer() {
return new PostgresqlFlywayConfigurationCustomizer(this.properties);
}
@Bean
Flyway flyway(FlywayConnectionDetails connectionDetails, ResourceLoader resourceLoader,
ObjectProvider<DataSource> dataSource, @FlywayDataSource ObjectProvider<DataSource> flywayDataSource,
@ -355,6 +337,40 @@ public class FlywayAutoConfiguration { @@ -355,6 +337,40 @@ public class FlywayAutoConfiguration {
return new FlywayMigrationInitializer(flyway, migrationStrategy.getIfAvailable());
}
@ConditionalOnClass(name = "org.flywaydb.database.sqlserver.SQLServerConfigurationExtension")
@Configuration(proxyBeanMethods = false)
static class SqlServerConfiguration {
@Bean
SqlServerFlywayConfigurationCustomizer sqlServerFlywayConfigurationCustomizer(FlywayProperties properties) {
return new SqlServerFlywayConfigurationCustomizer(properties);
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = "org.flywaydb.database.oracle.OracleConfigurationExtension")
static class OracleConfiguration {
@Bean
OracleFlywayConfigurationCustomizer oracleFlywayConfigurationCustomizer(FlywayProperties properties) {
return new OracleFlywayConfigurationCustomizer(properties);
}
}
@ConditionalOnClass(name = "org.flywaydb.database.postgresql.PostgreSQLConfigurationExtension")
@Configuration(proxyBeanMethods = false)
static class PostgresqlConfiguration {
@Bean
PostgresqlFlywayConfigurationCustomizer postgresqlFlywayConfigurationCustomizer(
FlywayProperties properties) {
return new PostgresqlFlywayConfigurationCustomizer(properties);
}
}
}
private static class LocationResolver {

13
spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/hateoas/HypermediaAutoConfiguration.java

@ -57,13 +57,18 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl @@ -57,13 +57,18 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl
@EnableConfigurationProperties(HateoasProperties.class)
public class HypermediaAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = "com.fasterxml.jackson.databind.ObjectMapper")
@ConditionalOnProperty(prefix = "spring.hateoas", name = "use-hal-as-default-json-media-type",
matchIfMissing = true)
HalConfiguration applicationJsonHalConfiguration() {
return new HalConfiguration().withMediaType(MediaType.APPLICATION_JSON);
static class JacksonJsonHalConfiguration {
@Bean
@ConditionalOnMissingBean
HalConfiguration applicationJsonHalConfiguration() {
return new HalConfiguration().withMediaType(MediaType.APPLICATION_JSON);
}
}
@Configuration(proxyBeanMethods = false)

25
spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/pulsar/PulsarReactiveAutoConfiguration.java

@ -87,16 +87,6 @@ public class PulsarReactiveAutoConfiguration { @@ -87,16 +87,6 @@ public class PulsarReactiveAutoConfiguration {
return AdaptedReactivePulsarClientFactory.create(pulsarClient);
}
@Bean
@ConditionalOnMissingBean(ProducerCacheProvider.class)
@ConditionalOnClass(CaffeineShadedProducerCacheProvider.class)
@ConditionalOnProperty(name = "spring.pulsar.producer.cache.enabled", havingValue = "true", matchIfMissing = true)
CaffeineShadedProducerCacheProvider reactivePulsarProducerCacheProvider() {
PulsarProperties.Producer.Cache properties = this.properties.getProducer().getCache();
return new CaffeineShadedProducerCacheProvider(properties.getExpireAfterAccess(), Duration.ofMinutes(10),
properties.getMaximumSize(), properties.getInitialCapacity());
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(name = "spring.pulsar.producer.cache.enabled", havingValue = "true", matchIfMissing = true)
@ -205,6 +195,21 @@ public class PulsarReactiveAutoConfiguration { @@ -205,6 +195,21 @@ public class PulsarReactiveAutoConfiguration {
return new ReactivePulsarTemplate<>(reactivePulsarSenderFactory, schemaResolver, topicResolver);
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(CaffeineShadedProducerCacheProvider.class)
@ConditionalOnProperty(name = "spring.pulsar.producer.cache.enabled", havingValue = "true", matchIfMissing = true)
static class PulsarCacheConfiguration {
@Bean
@ConditionalOnMissingBean(ProducerCacheProvider.class)
CaffeineShadedProducerCacheProvider reactivePulsarProducerCacheProvider(PulsarProperties pulsarProperties) {
PulsarProperties.Producer.Cache properties = pulsarProperties.getProducer().getCache();
return new CaffeineShadedProducerCacheProvider(properties.getExpireAfterAccess(), Duration.ofMinutes(10),
properties.getMaximumSize(), properties.getInitialCapacity());
}
}
@Configuration(proxyBeanMethods = false)
@EnableReactivePulsar
@ConditionalOnMissingBean(

20
spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerFactoryAutoConfiguration.java

@ -36,6 +36,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties @@ -36,6 +36,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.boot.ssl.SslBundles;
import org.springframework.boot.web.server.WebServerFactoryCustomizerBeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.Ordered;
@ -69,13 +70,6 @@ public class ReactiveWebServerFactoryAutoConfiguration { @@ -69,13 +70,6 @@ public class ReactiveWebServerFactoryAutoConfiguration {
return new ReactiveWebServerFactoryCustomizer(serverProperties, sslBundles.getIfAvailable());
}
@Bean
@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
public TomcatReactiveWebServerFactoryCustomizer tomcatReactiveWebServerFactoryCustomizer(
ServerProperties serverProperties) {
return new TomcatReactiveWebServerFactoryCustomizer(serverProperties);
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(value = "server.forward-headers-strategy", havingValue = "framework")
@ -83,6 +77,18 @@ public class ReactiveWebServerFactoryAutoConfiguration { @@ -83,6 +77,18 @@ public class ReactiveWebServerFactoryAutoConfiguration {
return new ForwardedHeaderTransformer();
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
static class TomcatConfiguration {
@Bean
TomcatReactiveWebServerFactoryCustomizer tomcatReactiveWebServerFactoryCustomizer(
ServerProperties serverProperties) {
return new TomcatReactiveWebServerFactoryCustomizer(serverProperties);
}
}
/**
* Registers a {@link WebServerFactoryCustomizerBeanPostProcessor}. Registered via
* {@link ImportBeanDefinitionRegistrar} for early registration.

30
spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryAutoConfiguration.java

@ -81,11 +81,16 @@ public class ServletWebServerFactoryAutoConfiguration { @@ -81,11 +81,16 @@ public class ServletWebServerFactoryAutoConfiguration {
cookieSameSiteSuppliers.orderedStream().toList(), sslBundles.getIfAvailable());
}
@Bean
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
ServerProperties serverProperties) {
return new TomcatServletWebServerFactoryCustomizer(serverProperties);
static class TomcatConfiguration {
@Bean
TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
ServerProperties serverProperties) {
return new TomcatServletWebServerFactoryCustomizer(serverProperties);
}
}
@Configuration(proxyBeanMethods = false)
@ -93,12 +98,6 @@ public class ServletWebServerFactoryAutoConfiguration { @@ -93,12 +98,6 @@ public class ServletWebServerFactoryAutoConfiguration {
@ConditionalOnMissingFilterBean(ForwardedHeaderFilter.class)
static class ForwardedHeaderFilterConfiguration {
@Bean
@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
ForwardedHeaderFilterCustomizer tomcatForwardedHeaderFilterCustomizer(ServerProperties serverProperties) {
return (filter) -> filter.setRelativeRedirects(serverProperties.getTomcat().isUseRelativeRedirects());
}
@Bean
FilterRegistrationBean<ForwardedHeaderFilter> forwardedHeaderFilter(
ObjectProvider<ForwardedHeaderFilterCustomizer> customizerProvider) {
@ -110,6 +109,17 @@ public class ServletWebServerFactoryAutoConfiguration { @@ -110,6 +109,17 @@ public class ServletWebServerFactoryAutoConfiguration {
return registration;
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
static class TomcatConfiguration {
@Bean
ForwardedHeaderFilterCustomizer tomcatForwardedHeaderFilterCustomizer(ServerProperties serverProperties) {
return (filter) -> filter.setRelativeRedirects(serverProperties.getTomcat().isUseRelativeRedirects());
}
}
}
interface ForwardedHeaderFilterCustomizer {

Loading…
Cancel
Save