Compare commits

..

1 Commits
main ... v4.0.0

Author SHA1 Message Date
Phillip Webb 1c0e08b4c4 Release v4.0.0 2025-11-20 08:17:33 -08:00
14 changed files with 70 additions and 110 deletions

View File

@ -13,6 +13,6 @@
"@springio/asciidoctor-extensions": "1.0.0-alpha.17"
},
"config": {
"ui-bundle-url": "https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.24/ui-bundle.zip"
"ui-bundle-url": "https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.21/ui-bundle.zip"
}
}

View File

@ -230,18 +230,10 @@ class JavaConventions {
CoreJavadocOptions options = (CoreJavadocOptions) javadoc.getOptions();
options.source("17");
options.encoding("UTF-8");
addValuelessOption(options, "Xdoclint:none");
addValuelessOption(options, "quiet");
if (!javadoc.getName().contains("aggregated")) {
addValuelessOption(options, "-no-fonts");
}
options.addStringOption("Xdoclint:none", "-quiet");
});
}
private void addValuelessOption(CoreJavadocOptions options, String option) {
options.addMultilineMultiValueOption(option).setValue(List.of(Collections.emptyList()));
}
private void configureJavaConventions(Project project) {
if (!project.hasProperty("toolchainVersion")) {
JavaPluginExtension javaPluginExtension = project.getExtensions().getByType(JavaPluginExtension.class);

View File

@ -16,17 +16,17 @@
package org.springframework.boot.build.architecture;
import java.util.Collections;
import java.util.ArrayList;
import java.util.List;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.TaskProvider;
import org.gradle.api.tasks.compile.JavaCompile;
import org.gradle.language.base.plugins.LifecycleBasePlugin;
import org.jetbrains.kotlin.gradle.tasks.KotlinCompileTool;
import org.springframework.util.StringUtils;
@ -46,45 +46,28 @@ public class ArchitecturePlugin implements Plugin<Project> {
private void registerTasks(Project project, ArchitectureCheckExtension extension) {
JavaPluginExtension javaPluginExtension = project.getExtensions().getByType(JavaPluginExtension.class);
List<TaskProvider<ArchitectureCheck>> packageTangleChecks = new ArrayList<>();
for (SourceSet sourceSet : javaPluginExtension.getSourceSets()) {
registerArchitectureCheck(sourceSet, "java", project).configure((task) -> {
task.setClasses(project.files(project.getTasks()
.named(sourceSet.getCompileTaskName("java"), JavaCompile.class)
.flatMap((compile) -> compile.getDestinationDirectory())));
TaskProvider<ArchitectureCheck> checkPackageTangles = project.getTasks()
.register("checkArchitecture" + StringUtils.capitalize(sourceSet.getName()), ArchitectureCheck.class,
(task) -> {
task.getSourceSet().set(sourceSet.getName());
task.getCompileClasspath().from(sourceSet.getCompileClasspath());
task.setClasses(sourceSet.getOutput().getClassesDirs());
task.getResourcesDirectory().set(sourceSet.getOutput().getResourcesDir());
task.dependsOn(sourceSet.getProcessResourcesTaskName());
task.setDescription("Checks the architecture of the classes of the " + sourceSet.getName()
+ " source set.");
task.setGroup(LifecycleBasePlugin.VERIFICATION_GROUP);
task.getNullMarkedEnabled().set(extension.getNullMarked().getEnabled());
task.getNullMarkedIgnoredPackages().set(extension.getNullMarked().getIgnoredPackages());
});
project.getPlugins()
.withId("org.jetbrains.kotlin.jvm",
(kotlinPlugin) -> registerArchitectureCheck(sourceSet, "kotlin", project).configure((task) -> {
task.setClasses(project.files(project.getTasks()
.named(sourceSet.getCompileTaskName("kotlin"), KotlinCompileTool.class)
.flatMap((compile) -> compile.getDestinationDirectory())));
task.getNullMarkedEnabled().set(false);
task.getNullMarkedIgnoredPackages().set(Collections.emptySet());
}));
packageTangleChecks.add(checkPackageTangles);
}
if (!packageTangleChecks.isEmpty()) {
TaskProvider<Task> checkTask = project.getTasks().named(LifecycleBasePlugin.CHECK_TASK_NAME);
checkTask.configure((check) -> check.dependsOn(packageTangleChecks));
}
private TaskProvider<ArchitectureCheck> registerArchitectureCheck(SourceSet sourceSet, String language,
Project project) {
TaskProvider<ArchitectureCheck> checkArchitecture = project.getTasks()
.register(
"checkArchitecture"
+ StringUtils.capitalize(sourceSet.getName() + StringUtils.capitalize(language)),
ArchitectureCheck.class, (task) -> {
task.getSourceSet().set(sourceSet.getName());
task.getCompileClasspath().from(sourceSet.getCompileClasspath());
task.getResourcesDirectory().set(sourceSet.getOutput().getResourcesDir());
task.dependsOn(sourceSet.getProcessResourcesTaskName());
task.setDescription("Checks the architecture of the " + language + " classes of the "
+ sourceSet.getName() + " source set.");
task.setGroup(LifecycleBasePlugin.VERIFICATION_GROUP);
});
project.getTasks()
.named(LifecycleBasePlugin.CHECK_TASK_NAME)
.configure((check) -> check.dependsOn(checkArchitecture));
return checkArchitecture;
}
}

View File

@ -421,8 +421,6 @@ final class ArchitectureRules {
return ArchRuleDefinition.members()
.that()
.areDeclaredInClassesThat(areRegularAutoConfiguration())
.and()
.areDeclaredInClassesThat(areNotKotlinClasses())
.and(areNotDefaultConstructors())
.and(areNotConstants())
.and(dontOverridePublicMethods())
@ -442,18 +440,13 @@ final class ArchitectureRules {
}
static DescribedPredicate<JavaClass> areRegularAutoConfiguration() {
return DescribedPredicate.describe("are regular @AutoConfiguration",
return DescribedPredicate.describe("Regular @AutoConfiguration",
(javaClass) -> javaClass.isAnnotatedWith(AUTOCONFIGURATION_ANNOTATION)
&& !javaClass.getName().contains("TestAutoConfiguration") && !javaClass.isAnnotation());
}
static DescribedPredicate<JavaClass> areNotKotlinClasses() {
return DescribedPredicate.describe("are not Kotlin classes",
(javaClass) -> !javaClass.isAnnotatedWith("kotlin.Metadata"));
}
static DescribedPredicate<JavaClass> areTestAutoConfiguration() {
return DescribedPredicate.describe("are test @AutoConfiguration",
return DescribedPredicate.describe("Test @AutoConfiguration",
(javaClass) -> javaClass.isAnnotatedWith(AUTOCONFIGURATION_ANNOTATION)
&& javaClass.getName().contains("TestAutoConfiguration") && !javaClass.isAnnotation());
}

View File

@ -31,16 +31,12 @@ import org.springframework.util.ReflectionUtils;
/**
* Finds all configurations from auto-configurations (either nested configurations or
* imported ones) and checks that these classes don't contain public members.
* <p>
* Kotlin classes are ignored as Kotlin does not have package-private visibility and
* {@code internal} isn't a good substitute.
*
* @author Moritz Halbritter
*/
class AutoConfigurationChecker {
private final DescribedPredicate<JavaClass> isAutoConfiguration = ArchitectureRules.areRegularAutoConfiguration()
.and(ArchitectureRules.areNotKotlinClasses());
private final DescribedPredicate<JavaClass> isAutoConfiguration = ArchitectureRules.areRegularAutoConfiguration();
EvaluationResult check(JavaClasses javaClasses) {
AutoConfigurations autoConfigurations = new AutoConfigurations();

View File

@ -423,7 +423,7 @@ class ArchitectureCheckTests {
@Override
public String toString() {
return "checkArchitecture" + StringUtils.capitalize(this.sourceSetName) + "Java";
return "checkArchitecture" + StringUtils.capitalize(this.sourceSetName);
}
}

View File

@ -207,20 +207,41 @@ This file will not be packaged in your uber jar or your container.
[[howto.data-initialization.migration-tool.liquibase-tests]]
=== Use Liquibase for Test-only Migrations
If you want to create Liquibase migrations which populate your test database, you can leverage https://docs.liquibase.com/reference-guide/changelog-attributes/what-are-contexts[Liquibase contexts].
See also the related https://www.liquibase.com/blog/contexts-vs-labels[blog post].
If you want to create Liquibase migrations which populate your test database, you have to create a test changelog which also includes the production changelog.
In practical terms, this translates into adding a `context:@test` attribute to changesets containing test data, for example:
First, you need to configure Liquibase to use a different changelog when running the tests.
One way to do this is to create a Spring Boot `test` profile and put the Liquibase properties in there.
For that, create a file named `src/test/resources/application-test.properties` and put the following property in there:
[source,sql]
[configprops,yaml]
----
--liquibase formatted sql
--changeset alice:1 context:@test
insert into project (id, name) values (1, 'Spring Boot');
spring:
liquibase:
change-log: "classpath:/db/changelog/db.changelog-test.yaml"
----
And using `spring.liquibase.contexts=test` in environments where you would like changesets containing test data to be applied.
This configures Liquibase to use a different changelog when running in the `test` profile.
Now create the changelog file at `src/test/resources/db/changelog/db.changelog-test.yaml`:
[source,yaml]
----
databaseChangeLog:
- include:
file: classpath:/db/changelog/db.changelog-master.yaml
- changeSet:
runOrder: "last"
id: "test"
changes:
# Insert your changes here
----
This changelog will be used when the tests are run and it will not be packaged in your uber jar or your container.
It includes the production changelog and then declares a new changeset, whose `runOrder: last` setting specifies that it runs after all the production changesets have been run.
You can now use for example the https://docs.liquibase.com/change-types/insert.html[insert changeset] to insert data or the https://docs.liquibase.com/change-types/sql.html[sql changeset] to execute SQL directly.
The last thing to do is to configure Spring Boot to activate the `test` profile when running tests.
To do this, you can add the `@ActiveProfiles("test")` annotation to your javadoc:org.springframework.boot.test.context.SpringBootTest[format=annotation] annotated test classes.

View File

@ -142,7 +142,10 @@ spring:
Caches can be created on startup by setting the configprop:spring.cache.cache-names[] property.
If a custom javadoc:org.infinispan.configuration.cache.ConfigurationBuilder[] bean is defined, it is used to customize the caches.
For more details, see https://infinispan.org/docs/stable/titles/spring/spring.html[the documentation].
To be compatible with Spring Boot's Jakarta EE 9 baseline, Infinispan's `-jakarta` modules must be used.
For every module with a `-jakarta` variant, the variant must be used in place of the standard module.
For example, `infinispan-core-jakarta` and `infinispan-commons-jakarta` must be used in place of `infinispan-core` and `infinispan-commons` respectively.
[[io.caching.provider.couchbase]]

View File

@ -30,11 +30,11 @@ public final class MyAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(SomeService.class)
static class SomeServiceConfiguration {
public static class SomeServiceConfiguration {
@Bean
@ConditionalOnMissingBean
SomeService someService() {
public SomeService someService() {
return new SomeService();
}

View File

@ -16,11 +16,11 @@
package org.springframework.boot.docs.features.developingautoconfiguration.conditionannotations.beanconditions
import org.springframework.boot.autoconfigure.AutoConfiguration
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@AutoConfiguration
@Configuration(proxyBeanMethods = false)
class MyAutoConfiguration {
@Bean

View File

@ -16,13 +16,12 @@
package org.springframework.boot.docs.features.developingautoconfiguration.conditionannotations.classconditions
import org.springframework.boot.autoconfigure.AutoConfiguration
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@AutoConfiguration
@Configuration(proxyBeanMethods = false)
// Some conditions ...
class MyAutoConfiguration {

View File

@ -1,4 +1,4 @@
version=4.0.1-SNAPSHOT
version=4.0.0
latestVersion=true
spring.build-type=oss

View File

@ -32,7 +32,7 @@ dependencies {
}
tasks.configureEach {
if ("checkArchitectureMainJava".equals(it.name)) {
if ("checkArchitectureMain".equals(it.name)) {
prohibitObjectsRequireNonNull = false
}
}

View File

@ -13,24 +13,6 @@
"description": "Whether to create an AmqpAdmin bean.",
"defaultValue": true
},
{
"name": "spring.rabbitmq.listener.direct.retry.max-attempts",
"type": "java.lang.Long",
"deprecation": {
"level": "error",
"replacement": "spring.rabbitmq.listener.direct.retry.max-retries",
"since": "4.0.0"
}
},
{
"name": "spring.rabbitmq.listener.simple.retry.max-attempts",
"type": "java.lang.Long",
"deprecation": {
"level": "error",
"replacement": "spring.rabbitmq.listener.simple.retry.max-retries",
"since": "4.0.0"
}
},
{
"name": "spring.rabbitmq.listener.simple.transaction-size",
"type": "java.lang.Integer",
@ -55,15 +37,6 @@
"level": "error",
"since": "2.2.0"
}
},
{
"name": "spring.rabbitmq.template.retry.max-attempts",
"type": "java.lang.Long",
"deprecation": {
"level": "error",
"replacement": "spring.rabbitmq.template.retry.max-retries",
"since": "4.0.0"
}
}
]
}