Compare commits

..

1 Commits

Author SHA1 Message Date
Spring Builds 0abadaa2f6 Release v6.0.10 2023-06-15 08:19:28 +00:00
1162 changed files with 13391 additions and 21759 deletions
@@ -1,33 +0,0 @@
name: Send notification
description: Sends a Google Chat message as a notification of the job's outcome
inputs:
webhook-url:
description: 'Google Chat Webhook URL'
required: true
status:
description: 'Status of the job'
required: true
build-scan-url:
description: 'URL of the build scan to include in the notification'
run-name:
description: 'Name of the run to include in the notification'
default: ${{ format('{0} {1}', github.ref_name, github.job) }}
runs:
using: composite
steps:
- shell: bash
run: |
echo "BUILD_SCAN=${{ inputs.build-scan-url == '' && ' [build scan unavailable]' || format(' [<{0}|Build Scan>]', inputs.build-scan-url) }}" >> "$GITHUB_ENV"
echo "RUN_URL=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" >> "$GITHUB_ENV"
- shell: bash
if: ${{ inputs.status == 'success' }}
run: |
curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: "<${{ env.RUN_URL }}|${{ inputs.run-name }}> was successful ${{ env.BUILD_SCAN }}"}' || true
- shell: bash
if: ${{ inputs.status == 'failure' }}
run: |
curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: "<users/all> *<${{ env.RUN_URL }}|${{ inputs.run-name }}> failed* ${{ env.BUILD_SCAN }}"}' || true
- shell: bash
if: ${{ inputs.status == 'cancelled' }}
run: |
curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: "<${{ env.RUN_URL }}|${{ inputs.run-name }}> was cancelled"}' || true
+4 -6
View File
@@ -18,13 +18,11 @@ jobs:
pull-requests: write
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Set up Java
uses: actions/setup-java@v4
- uses: actions/checkout@v3
- uses: actions/setup-java@v3
with:
distribution: 'liberica'
java-version: 17
distribution: 'temurin'
java-version: '17'
- name: Download BackportBot
run: wget https://github.com/spring-io/backport-bot/releases/download/latest/backport-bot-0.0.1-SNAPSHOT.jar
- name: Backport
@@ -1,63 +0,0 @@
name: Build and deploy snapshot
on:
push:
branches:
- 6.0.x
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
build-and-deploy-snapshot:
if: ${{ github.repository == 'spring-projects/spring-framework' }}
name: Build and deploy snapshot
runs-on: ubuntu-latest
steps:
- name: Set up Java
uses: actions/setup-java@v4
with:
distribution: 'liberica'
java-version: 17
- name: Check out code
uses: actions/checkout@v4
- name: Set up Gradle
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
with:
cache-read-only: false
- name: Configure Gradle properties
shell: bash
run: |
mkdir -p $HOME/.gradle
echo 'systemProp.user.name=spring-builds+github' >> $HOME/.gradle/gradle.properties
echo 'systemProp.org.gradle.internal.launcher.welcomeMessageEnabled=false' >> $HOME/.gradle/gradle.properties
echo 'org.gradle.daemon=false' >> $HOME/.gradle/gradle.properties
echo 'org.gradle.daemon=4' >> $HOME/.gradle/gradle.properties
- name: Build and publish
id: build
env:
CI: 'true'
GRADLE_ENTERPRISE_URL: 'https://ge.spring.io'
DEVELOCITY_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
run: ./gradlew -PdeploymentRepository=$(pwd)/deployment-repository build publishAllPublicationsToDeploymentRepository
- name: Deploy
uses: spring-io/artifactory-deploy-action@v0.0.1
with:
uri: 'https://repo.spring.io'
username: ${{ secrets.ARTIFACTORY_USERNAME }}
password: ${{ secrets.ARTIFACTORY_PASSWORD }}
build-name: ${{ format('spring-framework-{0}', github.ref_name)}}
repository: 'libs-snapshot-local'
folder: 'deployment-repository'
signing-key: ${{ secrets.GPG_PRIVATE_KEY }}
signing-passphrase: ${{ secrets.GPG_PASSPHRASE }}
artifact-properties: |
/**/framework-docs-*.zip::zip.name=spring-framework,zip.deployed=false
/**/framework-docs-*-docs.zip::zip.type=docs
/**/framework-docs-*-dist.zip::zip.type=dist
/**/framework-docs-*-schema.zip::zip.type=schema
- name: Send notification
uses: ./.github/actions/send-notification
if: always()
with:
webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
status: ${{ job.status }}
build-scan-url: ${{ steps.build.outputs.build-scan-url }}
run-name: ${{ format('{0} | Linux | Java 17', github.ref_name) }}
-78
View File
@@ -1,78 +0,0 @@
name: CI
on:
push:
branches:
- 6.0.x
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
ci:
if: ${{ github.repository == 'spring-projects/spring-framework' }}
strategy:
matrix:
os:
- id: ubuntu-latest
name: Linux
java:
- version: 17
toolchain: false
- version: 21
toolchain: true
exclude:
- os:
name: Linux
java:
version: 17
name: '${{ matrix.os.name}} | Java ${{ matrix.java.version}}'
runs-on: ${{ matrix.os.id }}
steps:
- name: Set up Java
uses: actions/setup-java@v4
with:
distribution: 'liberica'
java-version: |
${{ matrix.java.version }}
${{ matrix.java.toolchain && '17' || '' }}
- name: Prepare Windows runner
if: ${{ runner.os == 'Windows' }}
run: |
git config --global core.autocrlf true
git config --global core.longPaths true
Stop-Service -name Docker
- name: Check out code
uses: actions/checkout@v4
- name: Set up Gradle
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
with:
cache-read-only: false
- name: Configure Gradle properties
shell: bash
run: |
mkdir -p $HOME/.gradle
echo 'systemProp.user.name=spring-builds+github' >> $HOME/.gradle/gradle.properties
echo 'systemProp.org.gradle.internal.launcher.welcomeMessageEnabled=false' >> $HOME/.gradle/gradle.properties
echo 'org.gradle.daemon=false' >> $HOME/.gradle/gradle.properties
echo 'org.gradle.daemon=4' >> $HOME/.gradle/gradle.properties
- name: Configure toolchain properties
if: ${{ matrix.java.toolchain }}
shell: bash
run: |
echo toolchainVersion=${{ matrix.java.version }} >> $HOME/.gradle/gradle.properties
echo systemProp.org.gradle.java.installations.auto-detect=false >> $HOME/.gradle/gradle.properties
echo systemProp.org.gradle.java.installations.auto-download=false >> $HOME/.gradle/gradle.properties
echo systemProp.org.gradle.java.installations.paths=${{ format('$JAVA_HOME_{0}_X64', matrix.java.version) }} >> $HOME/.gradle/gradle.properties
- name: Build
id: build
env:
CI: 'true'
GRADLE_ENTERPRISE_URL: 'https://ge.spring.io'
DEVELOCITY_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
run: ./gradlew check antora
- name: Send notification
uses: ./.github/actions/send-notification
if: always()
with:
webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
status: ${{ job.status }}
build-scan-url: ${{ steps.build.outputs.build-scan-url }}
run-name: ${{ format('{0} | {1} | Java {2}', github.ref_name, matrix.os.name, matrix.java.version) }}
+6 -8
View File
@@ -1,14 +1,12 @@
name: Deploy Docs
on:
push:
branches:
- 'main'
- '*.x'
- '!gh-pages'
tags:
- 'v*'
branches-ignore: [ gh-pages ]
tags: '**'
repository_dispatch:
types: request-build-reference # legacy
schedule:
- cron: '0 10 * * *' # Once per day at 10am UTC
workflow_dispatch:
permissions:
actions: write
@@ -17,8 +15,8 @@ jobs:
runs-on: ubuntu-latest
if: github.repository_owner == 'spring-projects'
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Checkout
uses: actions/checkout@v3
with:
ref: docs-build
fetch-depth: 1
@@ -9,5 +9,5 @@ jobs:
name: "Validation"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: gradle/wrapper-validation-action@v2
- uses: actions/checkout@v3
- uses: gradle/wrapper-validation-action@v1
+2 -3
View File
@@ -21,7 +21,8 @@ derby.log
/build
buildSrc/build
/spring-*/build
/framework-*/build
/framework-bom/build
/framework-docs/build
/integration-tests/build
/src/asciidoc/build
spring-test/test-output/
@@ -51,5 +52,3 @@ atlassian-ide-plugin.xml
.vscode/
cached-antora-playbook.yml
node_modules
+36
View File
@@ -0,0 +1,36 @@
Juergen Hoeller <jhoeller@vmware.com>
Juergen Hoeller <jhoeller@vmware.com> <jhoeller@pivotal.io>
Juergen Hoeller <jhoeller@vmware.com> <jhoeller@gopivotal.com>
Rossen Stoyanchev <rstoyanchev@vmware.com>
Rossen Stoyanchev <rstoyanchev@vmware.com> <rstoyanchev@pivotal.io>
Rossen Stoyanchev <rstoyanchev@vmware.com> <rstoyanchev@gopivotal.com>
Phillip Webb <pwebb@vmware.com>
Phillip Webb <pwebb@vmware.com> <pwebb@pivotal.io>
Phillip Webb <pwebb@vmware.com> <pwebb@gopivotal.com>
Chris Beams <cbeams@vmware.com>
Chris Beams <cbeams@vmware.com> <cbeams@pivotal.io>
Chris Beams <cbeams@vmware.com> <cbeams@gopivotal.com>
Arjen Poutsma <poutsmaa@vmware.com>
Arjen Poutsma <poutsmaa@vmware.com> <apoutsma@pivotal.io>
Arjen Poutsma <poutsmaa@vmware.com> <apoutsma@gopivotal.com>
Arjen Poutsma <poutsmaa@vmware.com> <poutsma@mac.com>
Arjen Poutsma <poutsmaa@vmware.com> <apoutsma@vmware.com>
Oliver Drotbohm <odrotbohm@vmware.com>
Oliver Drotbohm <odrotbohm@vmware.com> <ogierke@vmware.com>
Oliver Drotbohm <odrotbohm@vmware.com> <ogierke@pivotal.io>
Oliver Drotbohm <odrotbohm@vmware.com> <ogierke@gopivotal.com>
Dave Syer <dsyer@vmware.com>
Dave Syer <dsyer@vmware.com> <dsyer@pivotal.io>
Dave Syer <dsyer@vmware.com> <dsyer@gopivotal.com>
Dave Syer <dsyer@vmware.com> <david_syer@hotmail.com>
Andy Clement <aclement@vmware.com>
Andy Clement <aclement@vmware.com> <aclement@pivotal.io>
Andy Clement <aclement@vmware.com> <aclement@gopivotal.com>
Andy Clement <aclement@vmware.com> <andrew.clement@gmail.com>
Sam Brannen <sbrannen@vmware.com>
Sam Brannen <sbrannen@vmware.com> <sbrannen@pivotal.io>
Sam Brannen <sbrannen@vmware.com> <sam@sambrannen.com>
Simon Basle <sbasle@vmware.com>
Simon Baslé <sbasle@vmware.com>
<dmitry.katsubo@gmail.com> <dmitry.katsubo@gmai.com>
Nick Williams <nicholas@nicholaswilliams.net>
+1 -1
View File
@@ -1,3 +1,3 @@
# Enable auto-env through the sdkman_auto_env config
# Add key=value pairs of SDKs to use below
java=17.0.11-librca
java=17.0.7-librca
+1 -1
View File
@@ -1,4 +1,4 @@
# <img src="framework-docs/src/docs/spring-framework.png" width="80" height="80"> Spring Framework [![Build Status](https://github.com/spring-projects/spring-framework/actions/workflows/build-and-deploy-snapshot.yml/badge.svg?branch=6.0.x)](https://github.com/spring-projects/spring-framework/actions/workflows/build-and-deploy-snapshot.yml?query=branch%3A6.0.x) [![Revved up by Develocity](https://img.shields.io/badge/Revved%20up%20by-Develocity-06A0CE?logo=Gradle&labelColor=02303A)](https://ge.spring.io/scans?search.rootProjectNames=spring)
# <img src="framework-docs/src/docs/spring-framework.png" width="80" height="80"> Spring Framework [![Build Status](https://ci.spring.io/api/v1/teams/spring-framework/pipelines/spring-framework-6.0.x/jobs/build/badge)](https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-6.0.x?groups=Build") [![Revved up by Gradle Enterprise](https://img.shields.io/badge/Revved%20up%20by-Gradle%20Enterprise-06A0CE?logo=Gradle&labelColor=02303A)](https://ge.spring.io/scans?search.rootProjectNames=spring)
This is the home of the Spring Framework: the foundation for all [Spring projects](https://spring.io/projects). Collectively the Spring Framework and the family of Spring projects are often referred to simply as "Spring".
+11 -7
View File
@@ -3,12 +3,12 @@ plugins {
id 'io.freefair.aspectj' version '8.0.1' apply false
// kotlinVersion is managed in gradle.properties
id 'org.jetbrains.kotlin.plugin.serialization' version "${kotlinVersion}" apply false
id 'org.jetbrains.dokka' version '1.8.20'
id 'org.jetbrains.dokka' version '1.8.10'
id 'org.unbroken-dome.xjc' version '2.0.0' apply false
id 'com.github.ben-manes.versions' version '0.51.0'
id 'com.github.ben-manes.versions' version '0.46.0'
id 'com.github.johnrengelman.shadow' version '8.1.1' apply false
id 'de.undercouch.download' version '5.4.0'
id 'me.champeau.jmh' version '0.7.2' apply false
id 'me.champeau.jmh' version '0.7.0' apply false
}
ext {
@@ -75,7 +75,7 @@ configure([rootProject] + javaProjects) { project ->
}
checkstyle {
toolVersion = "10.12.7"
toolVersion = "10.12.0"
configDirectory.set(rootProject.file("src/checkstyle"))
}
@@ -106,14 +106,17 @@ configure([rootProject] + javaProjects) { project ->
// JSR-305 only used for non-required meta-annotations
compileOnly("com.google.code.findbugs:jsr305")
testCompileOnly("com.google.code.findbugs:jsr305")
checkstyle("io.spring.javaformat:spring-javaformat-checkstyle:0.0.42")
checkstyle("io.spring.javaformat:spring-javaformat-checkstyle:0.0.38")
}
ext.javadocLinks = [
"https://docs.oracle.com/en/java/javase/17/docs/api/",
"https://jakarta.ee/specifications/platform/9/apidocs/",
"https://docs.oracle.com/cd/E13222_01/wls/docs90/javadocs/", // CommonJ and weblogic.* packages
"https://www.ibm.com/docs/api/v1/content/SSEQTP_8.5.5/com.ibm.websphere.javadoc.doc/web/apidocs/", // com.ibm.*
"https://docs.jboss.org/jbossas/javadoc/4.0.5/connector/", // org.jboss.resource.*
"https://docs.jboss.org/hibernate/orm/5.6/javadocs/",
"https://eclipse.dev/aspectj/doc/released/aspectj5rt-api",
"https://www.eclipse.org/aspectj/doc/released/aspectj5rt-api/",
"https://www.quartz-scheduler.org/api/2.3.0/",
"https://www.javadoc.io/doc/com.fasterxml.jackson.core/jackson-core/2.14.1/",
"https://www.javadoc.io/doc/com.fasterxml.jackson.core/jackson-databind/2.14.1/",
@@ -143,6 +146,7 @@ configure(rootProject) {
description = "Spring Framework"
apply plugin: "io.spring.nohttp"
apply plugin: 'org.springframework.build.api-diff'
nohttp {
source.exclude "**/test-output/**"
@@ -151,7 +155,7 @@ configure(rootProject) {
def rootPath = file(rootDir).toPath()
def projectDirs = allprojects.collect { it.projectDir } + "${rootDir}/buildSrc"
projectDirs.forEach { dir ->
[ 'bin', 'build', 'out', '.settings', 'node_modules' ]
[ 'bin', 'build', 'out', '.settings' ]
.collect { rootPath.relativize(new File(dir, it).toPath()) }
.forEach { source.exclude "$it/**" }
[ '.classpath', '.project' ]
+15
View File
@@ -22,6 +22,21 @@ but doesn't affect the classpath of dependent projects.
This plugin does not provide a `provided` configuration, as the native `compileOnly` and `testCompileOnly`
configurations are preferred.
### API Diff
This plugin uses the [Gradle JApiCmp](https://github.com/melix/japicmp-gradle-plugin) plugin
to generate API Diff reports for each Spring Framework module. This plugin is applied once on the root
project and creates tasks in each framework module. Unlike previous versions of this part of the build,
there is no need for checking out a specific tag. The plugin will fetch the JARs we want to compare the
current working version with. You can generate the reports for all modules or a single module:
```
./gradlew apiDiff -PbaselineVersion=5.1.0.RELEASE
./gradlew :spring-core:apiDiff -PbaselineVersion=5.1.0.RELEASE
```
The reports are located under `build/reports/api-diff/$OLDVERSION_to_$NEWVERSION/`.
### RuntimeHints Java Agent
+6 -1
View File
@@ -19,11 +19,16 @@ ext {
dependencies {
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}")
implementation("org.jetbrains.kotlin:kotlin-compiler-embeddable:${kotlinVersion}")
implementation "org.gradle:test-retry-gradle-plugin:1.5.6"
implementation "me.champeau.gradle:japicmp-gradle-plugin:0.3.0"
implementation "org.gradle:test-retry-gradle-plugin:1.4.1"
}
gradlePlugin {
plugins {
apiDiffPlugin {
id = "org.springframework.build.api-diff"
implementationClass = "org.springframework.build.api.ApiDiffPlugin"
}
conventionsPlugin {
id = "org.springframework.build.conventions"
implementationClass = "org.springframework.build.ConventionsPlugin"
@@ -0,0 +1,140 @@
/*
* Copyright 2002-2019 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.build.api;
import java.io.File;
import java.net.URI;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.List;
import me.champeau.gradle.japicmp.JapicmpPlugin;
import me.champeau.gradle.japicmp.JapicmpTask;
import org.gradle.api.GradleException;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.plugins.JavaBasePlugin;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.publish.maven.plugins.MavenPublishPlugin;
import org.gradle.api.tasks.TaskProvider;
import org.gradle.jvm.tasks.Jar;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* {@link Plugin} that applies the {@code "japicmp-gradle-plugin"}
* and create tasks for all subprojects named {@code "spring-*"}, diffing the public API one by one
* and creating the reports in {@code "build/reports/api-diff/$OLDVERSION_to_$NEWVERSION/"}.
* <p>{@code "./gradlew apiDiff -PbaselineVersion=5.1.0.RELEASE"} will output the
* reports for the API diff between the baseline version and the current one for all modules.
* You can limit the report to a single module with
* {@code "./gradlew :spring-core:apiDiff -PbaselineVersion=5.1.0.RELEASE"}.
*
* @author Brian Clozel
*/
public class ApiDiffPlugin implements Plugin<Project> {
private static final Logger logger = LoggerFactory.getLogger(ApiDiffPlugin.class);
public static final String TASK_NAME = "apiDiff";
private static final String BASELINE_VERSION_PROPERTY = "baselineVersion";
private static final List<String> PACKAGE_INCLUDES = Collections.singletonList("org.springframework.*");
private static final URI SPRING_MILESTONE_REPOSITORY = URI.create("https://repo.spring.io/milestone");
@Override
public void apply(Project project) {
if (project.hasProperty(BASELINE_VERSION_PROPERTY) && project.equals(project.getRootProject())) {
project.getPluginManager().apply(JapicmpPlugin.class);
project.getPlugins().withType(JapicmpPlugin.class,
plugin -> applyApiDiffConventions(project));
}
}
private void applyApiDiffConventions(Project project) {
String baselineVersion = project.property(BASELINE_VERSION_PROPERTY).toString();
project.subprojects(subProject -> {
if (subProject.getName().startsWith("spring-")) {
createApiDiffTask(baselineVersion, subProject);
}
});
}
private void createApiDiffTask(String baselineVersion, Project project) {
if (isProjectEligible(project)) {
// Add Spring Milestone repository for generating diffs against previous milestones
project.getRootProject()
.getRepositories()
.maven(mavenArtifactRepository -> mavenArtifactRepository.setUrl(SPRING_MILESTONE_REPOSITORY));
JapicmpTask apiDiff = project.getTasks().create(TASK_NAME, JapicmpTask.class);
apiDiff.setDescription("Generates an API diff report with japicmp");
apiDiff.setGroup(JavaBasePlugin.DOCUMENTATION_GROUP);
apiDiff.setOldClasspath(createBaselineConfiguration(baselineVersion, project));
TaskProvider<Jar> jar = project.getTasks().withType(Jar.class).named("jar");
apiDiff.setNewArchives(project.getLayout().files(jar.get().getArchiveFile().get().getAsFile()));
apiDiff.setNewClasspath(getRuntimeClassPath(project));
apiDiff.setPackageIncludes(PACKAGE_INCLUDES);
apiDiff.setOnlyModified(true);
apiDiff.setIgnoreMissingClasses(true);
// Ignore Kotlin metadata annotations since they contain
// illegal HTML characters and fail the report generation
apiDiff.setAnnotationExcludes(Collections.singletonList("@kotlin.Metadata"));
apiDiff.setHtmlOutputFile(getOutputFile(baselineVersion, project));
apiDiff.dependsOn(project.getTasks().getByName("jar"));
}
}
private boolean isProjectEligible(Project project) {
return project.getPlugins().hasPlugin(JavaPlugin.class)
&& project.getPlugins().hasPlugin(MavenPublishPlugin.class);
}
private Configuration createBaselineConfiguration(String baselineVersion, Project project) {
String baseline = String.join(":",
project.getGroup().toString(), project.getName(), baselineVersion);
Dependency baselineDependency = project.getDependencies().create(baseline + "@jar");
Configuration baselineConfiguration = project.getRootProject().getConfigurations().detachedConfiguration(baselineDependency);
try {
// eagerly resolve the baseline configuration to check whether this is a new Spring module
baselineConfiguration.resolve();
return baselineConfiguration;
}
catch (GradleException exception) {
logger.warn("Could not resolve {} - assuming this is a new Spring module.", baseline);
}
return project.getRootProject().getConfigurations().detachedConfiguration();
}
private Configuration getRuntimeClassPath(Project project) {
return project.getConfigurations().getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME);
}
private File getOutputFile(String baseLineVersion, Project project) {
Path outDir = Paths.get(project.getRootProject().getBuildDir().getAbsolutePath(),
"reports", "api-diff",
baseLineVersion + "_to_" + project.getRootProject().getVersion());
return project.file(outDir.resolve(project.getName() + ".html").toString());
}
}
-2
View File
@@ -1,7 +1,5 @@
== Spring Framework Concourse pipeline
NOTE: CI is being migrated to GitHub Actions.
The Spring Framework uses https://concourse-ci.org/[Concourse] for its CI build and other automated tasks.
The Spring team has a dedicated Concourse instance available at https://ci.spring.io with a build pipeline
for https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-6.0.x[Spring Framework 6.0.x].
+2 -1
View File
@@ -1,4 +1,4 @@
FROM ubuntu:jammy-20240125
FROM ubuntu:jammy-20230425
ADD setup.sh /setup.sh
ADD get-jdk-url.sh /get-jdk-url.sh
@@ -6,5 +6,6 @@ RUN ./setup.sh
ENV JAVA_HOME /opt/openjdk/java17
ENV JDK17 /opt/openjdk/java17
ENV JDK20 /opt/openjdk/java20
ENV PATH $JAVA_HOME/bin:$PATH
+4 -1
View File
@@ -3,7 +3,10 @@ set -e
case "$1" in
java17)
echo "https://github.com/bell-sw/Liberica/releases/download/17.0.10%2B13/bellsoft-jdk17.0.10+13-linux-amd64.tar.gz"
echo "https://github.com/bell-sw/Liberica/releases/download/17.0.7+7/bellsoft-jdk17.0.7+7-linux-amd64.tar.gz"
;;
java20)
echo "https://github.com/bell-sw/Liberica/releases/download/20.0.1+10/bellsoft-jdk20.0.1+10-linux-amd64.tar.gz"
;;
*)
echo $"Unknown java version"
+1 -1
View File
@@ -20,7 +20,7 @@ curl https://raw.githubusercontent.com/spring-io/concourse-java-scripts/v0.0.4/c
mkdir -p /opt/openjdk
pushd /opt/openjdk > /dev/null
for jdk in java17
for jdk in java17 java20
do
JDK_URL=$( /get-jdk-url.sh $jdk )
mkdir $jdk
+1
View File
@@ -8,3 +8,4 @@ milestone: "6.0.x"
build-name: "spring-framework"
pipeline-name: "spring-framework"
concourse-url: "https://ci.spring.io"
task-timeout: 1h00m
+123 -15
View File
@@ -5,7 +5,9 @@ anchors:
password: ((github-ci-release-token))
branch: ((branch))
gradle-enterprise-task-params: &gradle-enterprise-task-params
DEVELOCITY_ACCESS_KEY: ((gradle_enterprise_secret_access_key))
GRADLE_ENTERPRISE_ACCESS_KEY: ((gradle_enterprise_secret_access_key))
GRADLE_ENTERPRISE_CACHE_USERNAME: ((gradle_enterprise_cache_user.username))
GRADLE_ENTERPRISE_CACHE_PASSWORD: ((gradle_enterprise_cache_user.password))
sonatype-task-params: &sonatype-task-params
SONATYPE_USERNAME: ((sonatype-username))
SONATYPE_PASSWORD: ((sonatype-password))
@@ -21,6 +23,14 @@ anchors:
docker-resource-source: &docker-resource-source
username: ((docker-hub-username))
password: ((docker-hub-password))
slack-fail-params: &slack-fail-params
text: >
:concourse-failed: <https://ci.spring.io/teams/${BUILD_TEAM_NAME}/pipelines/${BUILD_PIPELINE_NAME}/jobs/${BUILD_JOB_NAME}/builds/${BUILD_NAME}|${BUILD_PIPELINE_NAME} ${BUILD_JOB_NAME} failed!>
[$TEXT_FILE_CONTENT]
text_file: git-repo/build/build-scan-uri.txt
silent: true
icon_emoji: ":concourse:"
username: concourse-ci
changelog-task-params: &changelog-task-params
name: generated-changelog/tag
tag: generated-changelog/tag
@@ -35,7 +45,7 @@ resource_types:
source:
<<: *docker-resource-source
repository: concourse/registry-image-resource
tag: 1.8.0
tag: 1.5.0
- name: artifactory-resource
type: registry-image
source:
@@ -47,13 +57,19 @@ resource_types:
source:
<<: *docker-resource-source
repository: concourse/github-release-resource
tag: 1.8.0
tag: 1.5.5
- name: github-status-resource
type: registry-image
source:
<<: *docker-resource-source
repository: dpb587/github-status-resource
tag: master
- name: slack-notification
type: registry-image
source:
<<: *docker-resource-source
repository: cfcommunity/slack-notification-resource
tag: latest
resources:
- name: git-repo
type: git
@@ -74,6 +90,13 @@ resources:
<<: *docker-resource-source
repository: ((docker-hub-organization))/spring-framework-ci
tag: ((milestone))
- name: every-morning
type: time
icon: alarm
source:
start: 8:00 AM
stop: 9:00 AM
location: Europe/Vienna
- name: artifactory-repo
type: artifactory-resource
icon: package-variant
@@ -82,6 +105,27 @@ resources:
username: ((artifactory-username))
password: ((artifactory-password))
build_name: ((build-name))
- name: repo-status-build
type: github-status-resource
icon: eye-check-outline
source:
repository: ((github-repo-name))
access_token: ((github-ci-status-token))
branch: ((branch))
context: build
- name: repo-status-jdk20-build
type: github-status-resource
icon: eye-check-outline
source:
repository: ((github-repo-name))
access_token: ((github-ci-status-token))
branch: ((branch))
context: jdk20-build
- name: slack-alert
type: slack-notification
icon: slack
source:
url: ((slack-webhook-url))
- name: github-pre-release
type: github-release
icon: briefcase-download-outline
@@ -116,23 +160,37 @@ jobs:
- put: ci-image
params:
image: ci-image/image.tar
- name: stage-milestone
- name: build
serial: true
public: true
plan:
- get: ci-image
- get: git-repo
trigger: false
- task: stage
image: ci-image
file: git-repo/ci/tasks/stage-version.yml
params:
RELEASE_TYPE: M
<<: *gradle-enterprise-task-params
trigger: true
- put: repo-status-build
params: { state: "pending", commit: "git-repo" }
- do:
- task: build-project
image: ci-image
file: git-repo/ci/tasks/build-project.yml
privileged: true
timeout: ((task-timeout))
params:
<<: *build-project-task-params
on_failure:
do:
- put: repo-status-build
params: { state: "failure", commit: "git-repo" }
- put: slack-alert
params:
<<: *slack-fail-params
- put: repo-status-build
params: { state: "success", commit: "git-repo" }
- put: artifactory-repo
params: &artifactory-params
signing_key: ((signing-key))
signing_passphrase: ((signing-passphrase))
repo: libs-staging-local
repo: libs-snapshot-local
folder: distribution-repository
build_uri: "https://ci.spring.io/teams/${BUILD_TEAM_NAME}/pipelines/${BUILD_PIPELINE_NAME}/jobs/${BUILD_JOB_NAME}/builds/${BUILD_NAME}"
build_number: "${BUILD_PIPELINE_NAME}-${BUILD_JOB_NAME}-${BUILD_NAME}"
@@ -157,9 +215,55 @@ jobs:
- "/**/framework-docs-*-schema.zip"
properties:
"zip.type": "schema"
- put: git-repo
params:
repository: stage-git-repo
get_params:
threads: 8
- name: jdk20-build
serial: true
public: true
plan:
- get: ci-image
- get: git-repo
- get: every-morning
trigger: true
- put: repo-status-jdk20-build
params: { state: "pending", commit: "git-repo" }
- do:
- task: check-project
image: ci-image
file: git-repo/ci/tasks/check-project.yml
privileged: true
timeout: ((task-timeout))
params:
TEST_TOOLCHAIN: 20
<<: *build-project-task-params
on_failure:
do:
- put: repo-status-jdk20-build
params: { state: "failure", commit: "git-repo" }
- put: slack-alert
params:
<<: *slack-fail-params
- put: repo-status-jdk20-build
params: { state: "success", commit: "git-repo" }
- name: stage-milestone
serial: true
plan:
- get: ci-image
- get: git-repo
trigger: false
- task: stage
image: ci-image
file: git-repo/ci/tasks/stage-version.yml
params:
RELEASE_TYPE: M
<<: *gradle-enterprise-task-params
- put: artifactory-repo
params:
<<: *artifactory-params
repo: libs-staging-local
- put: git-repo
params:
repository: stage-git-repo
- name: promote-milestone
serial: true
plan:
@@ -201,6 +305,7 @@ jobs:
- put: artifactory-repo
params:
<<: *artifactory-params
repo: libs-staging-local
- put: git-repo
params:
repository: stage-git-repo
@@ -245,6 +350,7 @@ jobs:
- put: artifactory-repo
params:
<<: *artifactory-params
repo: libs-staging-local
- put: git-repo
params:
repository: stage-git-repo
@@ -289,6 +395,8 @@ jobs:
<<: *changelog-task-params
groups:
- name: "builds"
jobs: ["build", "jdk20-build"]
- name: "releases"
jobs: ["stage-milestone", "stage-rc", "stage-release", "promote-milestone", "promote-rc", "promote-release", "create-github-release"]
- name: "ci-images"
+8
View File
@@ -0,0 +1,8 @@
#!/bin/bash
set -e
source $(dirname $0)/common.sh
pushd git-repo > /dev/null
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false --no-daemon --max-workers=4 check
popd > /dev/null
+9
View File
@@ -0,0 +1,9 @@
#!/bin/bash
set -e
source $(dirname $0)/common.sh
repository=$(pwd)/distribution-repository
pushd git-repo > /dev/null
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false --no-daemon --max-workers=4 -PdeploymentRepository=${repository} build publishAllPublicationsToDeploymentRepository
popd > /dev/null
+9
View File
@@ -0,0 +1,9 @@
#!/bin/bash
set -e
source $(dirname $0)/common.sh
pushd git-repo > /dev/null
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false -Porg.gradle.java.installations.fromEnv=JDK17,JDK20 \
-PmainToolchain=${MAIN_TOOLCHAIN} -PtestToolchain=${TEST_TOOLCHAIN} --no-daemon --max-workers=4 check antora
popd > /dev/null
+19
View File
@@ -0,0 +1,19 @@
---
platform: linux
inputs:
- name: git-repo
caches:
- path: gradle
params:
BRANCH:
CI: true
GRADLE_ENTERPRISE_ACCESS_KEY:
GRADLE_ENTERPRISE_CACHE_USERNAME:
GRADLE_ENTERPRISE_CACHE_PASSWORD:
GRADLE_ENTERPRISE_URL: https://ge.spring.io
run:
path: bash
args:
- -ec
- |
${PWD}/git-repo/ci/scripts/build-pr.sh
+22
View File
@@ -0,0 +1,22 @@
---
platform: linux
inputs:
- name: git-repo
outputs:
- name: distribution-repository
- name: git-repo
caches:
- path: gradle
params:
BRANCH:
CI: true
GRADLE_ENTERPRISE_ACCESS_KEY:
GRADLE_ENTERPRISE_CACHE_USERNAME:
GRADLE_ENTERPRISE_CACHE_PASSWORD:
GRADLE_ENTERPRISE_URL: https://ge.spring.io
run:
path: bash
args:
- -ec
- |
${PWD}/git-repo/ci/scripts/build-project.sh
+24
View File
@@ -0,0 +1,24 @@
---
platform: linux
inputs:
- name: git-repo
outputs:
- name: distribution-repository
- name: git-repo
caches:
- path: gradle
params:
BRANCH:
CI: true
MAIN_TOOLCHAIN:
TEST_TOOLCHAIN:
GRADLE_ENTERPRISE_ACCESS_KEY:
GRADLE_ENTERPRISE_CACHE_USERNAME:
GRADLE_ENTERPRISE_CACHE_PASSWORD:
GRADLE_ENTERPRISE_URL: https://ge.spring.io
run:
path: bash
args:
- -ec
- |
${PWD}/git-repo/ci/scripts/check-project.sh
+1 -1
View File
@@ -4,7 +4,7 @@ image_resource:
type: registry-image
source:
repository: springio/concourse-release-scripts
tag: '0.4.0'
tag: '0.3.4'
username: ((docker-hub-username))
password: ((docker-hub-password))
inputs:
-39
View File
@@ -1,39 +0,0 @@
antora:
extensions:
- require: '@springio/antora-extensions'
root_component_name: 'framework'
site:
title: Spring Framework
url: https://docs.spring.io/spring-framework/reference
robots: allow
git:
ensure_git_suffix: false
content:
sources:
- url: https://github.com/spring-projects/spring-framework
# Refname matching:
# https://docs.antora.org/antora/latest/playbook/content-refname-matching/
branches: ['main', '{6..9}.+({0..9}).x']
tags: ['v{6..9}.+({0..9}).+({0..9})?(-{RC,M}*)', '!(v6.0.{0..8})', '!(v6.0.0-{RC,M}{0..9})']
start_path: framework-docs
asciidoc:
extensions:
- '@asciidoctor/tabs'
- '@springio/asciidoctor-extensions'
- '@springio/asciidoctor-extensions/include-code-extension'
attributes:
page-stackoverflow-url: https://stackoverflow.com/questions/tagged/spring
page-pagination: ''
hide-uri-scheme: '@'
tabs-sync-option: '@'
include-java: 'example$docs-src/main/java/org/springframework/docs'
urls:
latest_version_segment_strategy: redirect:to
latest_version_segment: ''
redirect_facility: httpd
runtime:
log:
failure_level: warn
ui:
bundle:
url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.15/ui-bundle.zip
+20 -3
View File
@@ -10,10 +10,27 @@ apply from: "${rootDir}/gradle/publications.gradle"
antora {
options = [clean: true, fetch: !project.gradle.startParameter.offline, stacktrace: true]
version = '3.2.0-alpha.2'
playbook = 'cached-antora-playbook.yml'
playbookProvider {
repository = 'spring-projects/spring-framework'
branch = 'docs-build'
path = 'lib/antora/templates/per-branch-antora-playbook.yml'
checkLocalBranch = true
}
options = ['--clean', '--stacktrace']
environment = [
'BUILD_REFNAME': 'HEAD',
'BUILD_VERSION': project.version,
'ALGOLIA_API_KEY': '82c7ead946afbac3cf98c32446154691',
'ALGOLIA_APP_ID': '244V8V9FGG',
'ALGOLIA_INDEX_NAME': 'framework-docs'
]
dependencies = [
'@antora/atlas-extension': '1.0.0-alpha.1',
'@antora/collector-extension': '1.0.0-alpha.3',
'@asciidoctor/tabs': '1.0.0-beta.3',
'@opendevise/antora-release-line-extension': '1.0.0-alpha.2',
'@springio/antora-extensions': '1.3.0',
'@springio/asciidoctor-extensions': '1.0.0-alpha.9'
]
}
+2 -2
View File
@@ -39,8 +39,8 @@
** xref:core/resources.adoc[]
** xref:core/validation.adoc[]
*** xref:core/validation/validator.adoc[]
*** xref:core/validation/beans-beans.adoc[]
*** xref:core/validation/conversion.adoc[]
*** xref:core/validation/beans-beans.adoc[]
*** xref:core/validation/convert.adoc[]
*** xref:core/validation/format.adoc[]
*** xref:core/validation/format-configuring-formatting-globaldatetimeformat.adoc[]
@@ -431,4 +431,4 @@
** xref:languages/groovy.adoc[]
** xref:languages/dynamic.adoc[]
* xref:appendix.adoc[]
* https://github.com/spring-projects/spring-framework/wiki[Wiki]
* https://github.com/spring-projects/spring-framework/wiki[Wiki]
@@ -168,7 +168,7 @@ Kotlin::
======
NOTE: Pooling stateless service objects is not usually necessary. We do not believe it should
be the default choice, as most stateless objects are naturally thread-safe, and instance
be the default choice, as most stateless objects are naturally thread safe, and instance
pooling is problematic if resources are cached.
Simpler pooling is available by using auto-proxying. You can set the `TargetSource` implementations
@@ -176,7 +176,7 @@ Kotlin::
@AfterReturning(
pointcut = "execution(* com.xyz.dao.*.*(..))",
returning = "retVal")
fun doAccessCheck(retVal: Any?) {
fun doAccessCheck(retVal: Any) {
// ...
}
}
@@ -448,7 +448,7 @@ Kotlin::
class AroundExample {
@Around("execution(* com.xyz..service.*.*(..))")
fun doBasicProfiling(pjp: ProceedingJoinPoint): Any? {
fun doBasicProfiling(pjp: ProceedingJoinPoint): Any {
// start stopwatch
val retVal = pjp.proceed()
// stop stopwatch
@@ -893,7 +893,7 @@ Kotlin::
"com.xyz.CommonPointcuts.inDataAccessLayer() && " +
"args(accountHolderNamePattern)") // <1>
fun preProcessQueryPattern(pjp: ProceedingJoinPoint,
accountHolderNamePattern: String): Any? {
accountHolderNamePattern: String): Any {
val newPattern = preProcess(accountHolderNamePattern)
return pjp.proceed(arrayOf<Any>(newPattern))
}
@@ -85,7 +85,7 @@ Kotlin::
}
@Around("com.xyz.CommonPointcuts.businessService()") // <1>
fun doConcurrentOperation(pjp: ProceedingJoinPoint): Any? {
fun doConcurrentOperation(pjp: ProceedingJoinPoint): Any {
var numAttempts = 0
var lockFailureException: PessimisticLockingFailureException
do {
@@ -173,7 +173,7 @@ Kotlin::
----
@Around("execution(* com.xyz..service.*.*(..)) && " +
"@annotation(com.xyz.service.Idempotent)")
fun doConcurrentOperation(pjp: ProceedingJoinPoint): Any? {
fun doConcurrentOperation(pjp: ProceedingJoinPoint): Any {
// ...
}
----
@@ -6,8 +6,8 @@ it until later.
By default, there is a single instance of each aspect within the application
context. AspectJ calls this the singleton instantiation model. It is possible to define
aspects with alternate lifecycles. Spring supports AspectJ's `perthis`, `pertarget`, and
`pertypewithin` instantiation models; `percflow` and `percflowbelow` are not currently
aspects with alternate lifecycles. Spring supports AspectJ's `perthis` and `pertarget`
instantiation models; `percflow`, `percflowbelow`, and `pertypewithin` are not currently
supported.
You can declare a `perthis` aspect by specifying a `perthis` clause in the `@Aspect`
@@ -154,6 +154,7 @@ Java::
----
package com.xyz;
@Aspect
public class Pointcuts {
@Pointcut("execution(public * *(..))")
@@ -178,6 +179,7 @@ Kotlin::
----
package com.xyz
@Aspect
class Pointcuts {
@Pointcut("execution(public * *(..))")
@@ -209,9 +211,9 @@ pointcut matching.
When working with enterprise applications, developers often have the need to refer to
modules of the application and particular sets of operations from within several aspects.
We recommend defining a dedicated class that captures commonly used _named pointcut_
expressions for this purpose. Such a class typically resembles the following
`CommonPointcuts` example (though what you name the class is up to you):
We recommend defining a dedicated aspect that captures commonly used _named pointcut_
expressions for this purpose. Such an aspect typically resembles the following
`CommonPointcuts` example (though what you name the aspect is up to you):
[tabs]
======
@@ -221,8 +223,10 @@ Java::
----
package com.xyz;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class CommonPointcuts {
/**
@@ -283,8 +287,10 @@ Kotlin::
----
package com.xyz
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Pointcut
@Aspect
class CommonPointcuts {
/**
@@ -340,9 +346,9 @@ Kotlin::
----
======
You can refer to the pointcuts defined in such a class anywhere you need a pointcut
expression by referencing the fully-qualified name of the class combined with the
`@Pointcut` method's name. For example, to make the service layer transactional, you
You can refer to the pointcuts defined in such an aspect anywhere you need a pointcut
expression by referencing the fully-qualified name of the `@Aspect` class combined with
the `@Pointcut` method's name. For example, to make the service layer transactional, you
could write the following which references the
`com.xyz.CommonPointcuts.businessService()` _named pointcut_:
@@ -435,7 +435,7 @@ Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
fun doBasicProfiling(pjp: ProceedingJoinPoint): Any? {
fun doBasicProfiling(pjp: ProceedingJoinPoint): Any {
// start stopwatch
val retVal = pjp.proceed()
// stop stopwatch
@@ -554,7 +554,7 @@ Kotlin::
class SimpleProfiler {
fun profile(call: ProceedingJoinPoint, name: String, age: Int): Any? {
fun profile(call: ProceedingJoinPoint, name: String, age: Int): Any {
val clock = StopWatch("Profiling for '$name' and '$age'")
try {
clock.start(call.toShortString())
@@ -890,7 +890,7 @@ Kotlin::
this.order = order
}
fun doConcurrentOperation(pjp: ProceedingJoinPoint): Any? {
fun doConcurrentOperation(pjp: ProceedingJoinPoint): Any {
var numAttempts = 0
var lockFailureException: PessimisticLockingFailureException
do {
@@ -493,7 +493,7 @@ Kotlin::
class ProfilingAspect {
@Around("methodsToBeProfiled()")
fun profile(pjp: ProceedingJoinPoint): Any? {
fun profile(pjp: ProceedingJoinPoint): Any {
val sw = StopWatch(javaClass.simpleName)
try {
sw.start(pjp.getSignature().getName())
@@ -828,6 +828,13 @@ The following table summarizes various `LoadTimeWeaver` implementations:
| Running in Red Hat's https://www.jboss.org/jbossas/[JBoss AS] or https://www.wildfly.org/[WildFly]
| `JBossLoadTimeWeaver`
| Running in IBM's https://www-01.ibm.com/software/webservers/appserv/was/[WebSphere]
| `WebSphereLoadTimeWeaver`
| Running in Oracle's
https://www.oracle.com/technetwork/middleware/weblogic/overview/index-085209.html[WebLogic]
| `WebLogicLoadTimeWeaver`
| JVM started with Spring `InstrumentationSavingAgent`
(`java -javaagent:path/to/spring-instrument.jar`)
| `InstrumentationLoadTimeWeaver`
@@ -942,11 +949,11 @@ when you use Spring's LTW support in environments such as application servers an
containers.
[[aop-aj-ltw-environments-tomcat-jboss-etc]]
==== Tomcat, JBoss, WildFly
==== Tomcat, JBoss, WebSphere, WebLogic
Tomcat and JBoss/WildFly provide a general app `ClassLoader` that is capable of local
instrumentation. Spring's native LTW may leverage those ClassLoader implementations
to provide AspectJ weaving.
Tomcat, JBoss/WildFly, IBM WebSphere Application Server and Oracle WebLogic Server all
provide a general app `ClassLoader` that is capable of local instrumentation. Spring's
native LTW may leverage those ClassLoader implementations to provide AspectJ weaving.
You can simply enable load-time weaving, as xref:core/aop/using-aspectj.adoc[described earlier].
Specifically, you do not need to modify the JVM launch script to add
`-javaagent:path/to/spring-instrument.jar`.
@@ -318,51 +318,6 @@ Java::
----
======
[[aot.bestpractices.jpa]]
=== JPA
The JPA persistence unit has to be known upfront for certain optimizations to apply. Consider the following basic example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Bean
LocalContainerEntityManagerFactoryBean customDBEntityManagerFactory(DataSource dataSource) {
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(dataSource);
factoryBean.setPackagesToScan("com.example.app");
return factoryBean;
}
----
======
To make sure the scanning occurs ahead of time, a `PersistenceManagedTypes` bean must be declared and used by the
factory bean definition, as shown by the following example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Bean
PersistenceManagedTypes persistenceManagedTypes(ResourceLoader resourceLoader) {
return new PersistenceManagedTypesScanner(resourceLoader)
.scan("com.example.app");
}
@Bean
LocalContainerEntityManagerFactoryBean customDBEntityManagerFactory(DataSource dataSource, PersistenceManagedTypes managedTypes) {
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(dataSource);
factoryBean.setManagedTypes(managedTypes);
return factoryBean;
}
----
======
[[aot.hints]]
== Runtime Hints
@@ -565,17 +565,6 @@ is a convenience mechanism that sets up a xref:core/beans/factory-extension.adoc
for you. If you need more control over the specific
`PropertySourcesPlaceholderConfigurer` setup, you can explicitly define it as a bean yourself.
[WARNING]
=====
Only one such element should be defined for a given application with the properties
that it needs. Several property placeholders can be configured as long as they have distinct
placeholder syntax (`${...}`).
If you need to modularize the source of properties used for the replacement, you should
not create multiple properties placeholders. Rather, each module should contribute a
`PropertySource` to the `Environment`. Alternatively, you can create your own
`PropertySourcesPlaceholderConfigurer` bean that gathers the properties to use.
=====
[[xsd-schemas-context-ac]]
=== Using `<annotation-config/>`
@@ -153,16 +153,15 @@ Letting qualifier values select against target bean names, within the type-match
candidates, does not require a `@Qualifier` annotation at the injection point.
If there is no other resolution indicator (such as a qualifier or a primary marker),
for a non-unique dependency situation, Spring matches the injection point name
(that is, the field name or parameter name) against the target bean names and chooses
the same-named candidate, if any (either by bean name or by associated alias).
This requires the `-parameters` Java compiler flag to be present. As a fallback,
version 6.0 still supports parameter names from debug symbols via `-debug` as well.
(that is, the field name or parameter name) against the target bean names and chooses the
same-named candidate, if any.
====
As an alternative for injection by name, consider the JSR-250 `@Resource` annotation
which is semantically defined to identify a specific target component by its unique name,
with the declared type being irrelevant for the matching process. `@Autowired` has rather
That said, if you intend to express annotation-driven injection by name, do not
primarily use `@Autowired`, even if it is capable of selecting by bean name among
type-matching candidates. Instead, use the JSR-250 `@Resource` annotation, which is
semantically defined to identify a specific target component by its unique name, with
the declared type being irrelevant for the matching process. `@Autowired` has rather
different semantics: After selecting candidate beans by type, the specified `String`
qualifier value is considered within those type-selected candidates only (for example,
matching an `account` qualifier against beans marked with the same qualifier label).
@@ -364,6 +364,8 @@ ignored if it cannot be autowired. This allows properties to be assigned default
that can be optionally overridden via dependency injection.
====
[[beans-autowired-annotation-constructor-resolution]]
Injected constructor and factory method arguments are a special case since the `required`
attribute in `@Autowired` has a somewhat different meaning due to Spring's constructor
@@ -22,11 +22,10 @@ additional metadata formats.
In most application scenarios, explicit user code is not required to instantiate one or
more instances of a Spring IoC container. For example, in a web application scenario, a
simple eight (or so) lines of boilerplate web descriptor XML in the `web.xml` file
of the application typically suffices (see
xref:core/beans/context-introduction.adoc#context-create[Convenient ApplicationContext Instantiation for Web Applications]).
If you use the https://spring.io/tools[Spring Tools for Eclipse] (an Eclipse-powered
development environment), you can easily create this boilerplate configuration with a
few mouse clicks or keystrokes.
of the application typically suffices (see xref:core/beans/context-introduction.adoc#context-create[Convenient ApplicationContext Instantiation for Web Applications]). If you use the
https://spring.io/tools[Spring Tools for Eclipse] (an Eclipse-powered development
environment), you can easily create this boilerplate configuration with a few mouse clicks or
keystrokes.
The following diagram shows a high-level view of how Spring works. Your application classes
are combined with configuration metadata so that, after the `ApplicationContext` is
@@ -140,11 +139,9 @@ Kotlin::
[NOTE]
====
After you learn about Spring's IoC container, you may want to know more about Spring's
`Resource` abstraction (as described in
xref:core/resources.adoc[Resources])
which provides a convenient mechanism for reading an InputStream from locations defined
in a URI syntax. In particular, `Resource` paths are used to construct applications contexts,
as described in xref:core/resources.adoc#resources-app-ctx[Application Contexts and Resource Paths].
`Resource` abstraction (as described in xref:web/webflux-webclient/client-builder.adoc#webflux-client-builder-reactor-resources[Resources]), which provides a convenient
mechanism for reading an InputStream from locations defined in a URI syntax. In particular,
`Resource` paths are used to construct applications contexts, as described in xref:core/resources.adoc#resources-app-ctx[Application Contexts and Resource Paths].
====
The following example shows the service layer objects `(services.xml)` configuration file:
@@ -211,9 +208,9 @@ XML configuration file represents a logical layer or module in your architecture
You can use the application context constructor to load bean definitions from all these
XML fragments. This constructor takes multiple `Resource` locations, as was shown in the
xref:core/beans/basics.adoc#beans-factory-instantiation[previous section]. Alternatively,
use one or more occurrences of the `<import/>` element to load bean definitions from
another file or files. The following example shows how to do so:
xref:core/beans/basics.adoc#beans-factory-instantiation[previous section]. Alternatively, use one or more
occurrences of the `<import/>` element to load bean definitions from another file or
files. The following example shows how to do so:
[source,xml,indent=0,subs="verbatim,quotes"]
----
@@ -484,14 +484,13 @@ custom event (`BlockedListEvent` in the preceding example). This means that the
You can register as many event listeners as you wish, but note that, by default, event
listeners receive events synchronously. This means that the `publishEvent()` method
blocks until all listeners have finished processing the event. One advantage of this
synchronous and single-threaded approach is that, when a listener receives an event,
it operates inside the transaction context of the publisher if a transaction context
is available. If another strategy for event publication becomes necessary, e.g.
asynchronous event processing by default, see the javadoc for Spring's
synchronous and single-threaded approach is that, when a listener receives an event, it
operates inside the transaction context of the publisher if a transaction context is
available. If another strategy for event publication becomes necessary, see the javadoc
for Spring's
{api-spring-framework}/context/event/ApplicationEventMulticaster.html[`ApplicationEventMulticaster`] interface
and {api-spring-framework}/context/event/SimpleApplicationEventMulticaster.html[`SimpleApplicationEventMulticaster`]
implementation for configuration options which can be applied to a custom
"applicationEventMulticaster" bean definition.
implementation for configuration options.
The following example shows the bean definitions used to register and configure each of
the classes above:
@@ -511,12 +510,6 @@ the classes above:
<bean id="blockedListNotifier" class="example.BlockedListNotifier">
<property name="notificationAddress" value="blockedlist@example.org"/>
</bean>
<!-- optional: a custom ApplicationEventMulticaster definition -->
<bean id="applicationEventMulticaster" class="org.springframework.context.event.SimpleApplicationEventMulticaster">
<property name="taskExecutor" ref="..."/>
<property name="errorHandler" ref="..."/>
</bean>
----
Putting it all together, when the `sendEmail()` method of the `emailService` bean is
@@ -856,23 +849,6 @@ Kotlin::
TIP: This works not only for `ApplicationEvent` but any arbitrary object that you send as
an event.
Finally, as with classic `ApplicationListener` implementations, the actual multicasting
happens via a context-wide `ApplicationEventMulticaster` at runtime. By default, this is a
`SimpleApplicationEventMulticaster` with synchronous event publication in the caller thread.
This can be replaced/customized through an "applicationEventMulticaster" bean definition,
e.g. for processing all events asynchronously and/or for handling listener exceptions:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Bean
ApplicationEventMulticaster applicationEventMulticaster() {
SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();
multicaster.setTaskExecutor(...);
multicaster.setErrorHandler(...);
return multicaster;
}
----
[[context-functionality-resources]]
@@ -934,7 +910,7 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
// create a startup step and start recording
StartupStep scanPackages = getApplicationStartup().start("spring.context.base-packages.scan");
StartupStep scanPackages = this.getApplicationStartup().start("spring.context.base-packages.scan");
// add tagging information to the current step
scanPackages.tag("packages", () -> Arrays.toString(basePackages));
// perform the actual phase we're instrumenting
@@ -948,7 +924,7 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
// create a startup step and start recording
val scanPackages = getApplicationStartup().start("spring.context.base-packages.scan")
val scanPackages = this.getApplicationStartup().start("spring.context.base-packages.scan")
// add tagging information to the current step
scanPackages.tag("packages", () -> Arrays.toString(basePackages))
// perform the actual phase we're instrumenting
@@ -309,7 +309,7 @@ how to configure such a bean:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- the factory bean, which contains a method called createClientServiceInstance() -->
<!-- the factory bean, which contains a method called createInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
<!-- inject any dependencies required by this locator bean -->
</bean>
@@ -416,8 +416,8 @@ Kotlin::
======
This approach shows that the factory bean itself can be managed and configured through
dependency injection (DI).
See xref:core/beans/dependencies/factory-properties-detailed.adoc[Dependencies and Configuration in Detail].
dependency injection (DI). See xref:core/beans/dependencies/factory-properties-detailed.adoc[Dependencies and Configuration in Detail]
.
NOTE: In Spring documentation, "factory bean" refers to a bean that is configured in the
Spring container and that creates objects through an
@@ -444,3 +444,5 @@ cases into account and returns the type of object that a `BeanFactory.getBean` c
going to return for the same bean name.
@@ -562,9 +562,8 @@ If no profile is active, the `dataSource` is created. You can see this
as a way to provide a default definition for one or more beans. If any
profile is enabled, the default profile does not apply.
The name of the default profile is `default`. You can change the name of
the default profile by using `setDefaultProfiles()` on the `Environment` or,
declaratively, by using the `spring.profiles.default` property.
You can change the name of the default profile by using `setDefaultProfiles()` on
the `Environment` or, declaratively, by using the `spring.profiles.default` property.
@@ -23,8 +23,9 @@ You can set this property only if the `BeanPostProcessor` implements the `Ordere
interface. If you write your own `BeanPostProcessor`, you should consider implementing
the `Ordered` interface, too. For further details, see the javadoc of the
{api-spring-framework}/beans/factory/config/BeanPostProcessor.html[`BeanPostProcessor`]
and {api-spring-framework}/core/Ordered.html[`Ordered`] interfaces. See also the note on
xref:core/beans/factory-extension.adoc#beans-factory-programmatically-registering-beanpostprocessors[programmatic registration of `BeanPostProcessor` instances].
and {api-spring-framework}/core/Ordered.html[`Ordered`] interfaces. See also the note
on xref:core/beans/factory-extension.adoc#beans-factory-programmatically-registering-beanpostprocessors[programmatic registration of `BeanPostProcessor` instances]
.
[NOTE]
====
@@ -279,12 +280,11 @@ and {api-spring-framework}/core/Ordered.html[`Ordered`] interfaces for more deta
====
If you want to change the actual bean instances (that is, the objects that are created
from the configuration metadata), then you instead need to use a `BeanPostProcessor`
(described earlier in
xref:core/beans/factory-extension.adoc#beans-factory-extension-bpp[Customizing Beans by Using a `BeanPostProcessor`]).
While it is technically possible to work with bean instances within a `BeanFactoryPostProcessor`
(for example, by using `BeanFactory.getBean()`), doing so causes premature bean instantiation,
violating the standard container lifecycle. This may cause negative side effects, such as
bypassing bean post processing.
(described earlier in xref:core/beans/factory-extension.adoc#beans-factory-extension-bpp[Customizing Beans by Using a `BeanPostProcessor`]). While it is technically possible
to work with bean instances within a `BeanFactoryPostProcessor` (for example, by using
`BeanFactory.getBean()`), doing so causes premature bean instantiation, violating the
standard container lifecycle. This may cause negative side effects, such as bypassing
bean post processing.
Also, `BeanFactoryPostProcessor` instances are scoped per-container. This is only relevant
if you use container hierarchies. If you define a `BeanFactoryPostProcessor` in one
@@ -331,7 +331,8 @@ with placeholder values is defined:
<property name="locations" value="classpath:com/something/jdbc.properties"/>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<bean id="dataSource" destroy-method="close"
class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
@@ -372,17 +373,6 @@ The `PropertySourcesPlaceholderConfigurer` not only looks for properties in the
file you specify. By default, if it cannot find a property in the specified properties files,
it checks against Spring `Environment` properties and regular Java `System` properties.
[WARNING]
=====
Only one such element should be defined for a given application with the properties
that it needs. Several property placeholders can be configured as long as they have distinct
placeholder syntax (`${...}`).
If you need to modularize the source of properties used for the replacement, you should
not create multiple properties placeholders. Rather, you should create your own
`PropertySourcesPlaceholderConfigurer` bean that gathers the properties to use.
=====
[TIP]
=====
You can use the `PropertySourcesPlaceholderConfigurer` to substitute class names, which
@@ -443,8 +433,8 @@ This example file can be used with a container definition that contains a bean c
Compound property names are also supported, as long as every component of the path
except the final property being overridden is already non-null (presumably initialized
by the constructors). In the following example, the `sammy` property of the `bob`
property of the `fred` property of the `tom` bean is set to the scalar value `123`:
by the constructors). In the following example, the `sammy` property of the `bob` property of the `fred` property of the `tom` bean
is set to the scalar value `123`:
[literal,subs="verbatim,quotes"]
----
@@ -42,7 +42,6 @@ startup and shutdown process, as driven by the container's own lifecycle.
The lifecycle callback interfaces are described in this section.
[[beans-factory-lifecycle-initializingbean]]
=== Initialization Callbacks
@@ -133,30 +132,6 @@ Kotlin::
However, the first of the two preceding examples does not couple the code to Spring.
[NOTE]
====
Be aware that `@PostConstruct` and initialization methods in general are executed
within the container's singleton creation lock. The bean instance is only considered
as fully initialized and ready to be published to others after returning from the
`@PostConstruct` method. Such individual initialization methods are only meant
for validating the configuration state and possibly preparing some data structures
based on the given configuration but no further activity with external bean access.
Otherwise there is a risk for an initialization deadlock.
For a scenario where expensive post-initialization activity is to be triggered,
e.g. asynchronous database preparation steps, your bean should either implement
`SmartInitializingSingleton.afterSingletonsInstantiated()` or rely on the context
refresh event: implementing `ApplicationListener<ContextRefreshedEvent>` or
declaring its annotation equivalent `@EventListener(ContextRefreshedEvent.class)`.
Those variants come after all regular singleton initialization and therefore
outside of any singleton creation lock.
Alternatively, you may implement the `(Smart)Lifecycle` interface and integrate with
the container's overall lifecycle management, including an auto-startup mechanism,
a pre-destroy stop step, and potential stop/restart callbacks (see below).
====
[[beans-factory-lifecycle-disposablebean]]
=== Destruction Callbacks
@@ -180,7 +155,7 @@ xref:core/beans/java/bean-annotation.adoc#beans-java-lifecycle-callbacks[Receivi
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="exampleDestructionBean" class="examples.ExampleBean" destroy-method="cleanup"/>
<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>
----
[tabs]
@@ -214,7 +189,7 @@ The preceding definition has almost exactly the same effect as the following def
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="exampleDestructionBean" class="examples.AnotherExampleBean"/>
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
----
[tabs]
@@ -247,45 +222,32 @@ Kotlin::
However, the first of the two preceding definitions does not couple the code to Spring.
Note that Spring also supports inference of destroy methods, detecting a public `close` or
`shutdown` method. This is the default behavior for `@Bean` methods in Java configuration
classes and automatically matches `java.lang.AutoCloseable` or `java.io.Closeable`
implementations, not coupling the destruction logic to Spring either.
TIP: For destroy method inference with XML, you may assign the `destroy-method` attribute
of a `<bean>` element a special `(inferred)` value, which instructs Spring to automatically
detect a public `close` or `shutdown` method on the bean class for a specific bean definition.
You can also set this special `(inferred)` value on the `default-destroy-method` attribute
of a `<beans>` element to apply this behavior to an entire set of bean definitions (see
xref:core/beans/factory-nature.adoc#beans-factory-lifecycle-default-init-destroy-methods[Default Initialization and Destroy Methods]).
[NOTE]
====
For extended shutdown phases, you may implement the `Lifecycle` interface and receive
an early stop signal before the destroy methods of any singleton beans are called.
You may also implement `SmartLifecycle` for a time-bound stop step where the container
will wait for all such stop processing to complete before moving on to destroy methods.
====
TIP: You can assign the `destroy-method` attribute of a `<bean>` element a special
`(inferred)` value, which instructs Spring to automatically detect a public `close` or
`shutdown` method on the specific bean class. (Any class that implements
`java.lang.AutoCloseable` or `java.io.Closeable` would therefore match.) You can also set
this special `(inferred)` value on the `default-destroy-method` attribute of a
`<beans>` element to apply this behavior to an entire set of beans (see
xref:core/beans/factory-nature.adoc#beans-factory-lifecycle-default-init-destroy-methods[Default Initialization and Destroy Methods]). Note that this is the
default behavior with Java configuration.
[[beans-factory-lifecycle-default-init-destroy-methods]]
=== Default Initialization and Destroy Methods
When you write initialization and destroy method callbacks that do not use the
Spring-specific `InitializingBean` and `DisposableBean` callback interfaces, you
typically write methods with names such as `init()`, `initialize()`, `dispose()`,
and so on. Ideally, the names of such lifecycle callback methods are standardized across
a project so that all developers use the same method names and ensure consistency.
typically write methods with names such as `init()`, `initialize()`, `dispose()`, and so
on. Ideally, the names of such lifecycle callback methods are standardized across a
project so that all developers use the same method names and ensure consistency.
You can configure the Spring container to "`look`" for named initialization and destroy
callback method names on every bean. This means that you, as an application developer,
can write your application classes and use an initialization callback called `init()`,
without having to configure an `init-method="init"` attribute with each bean definition.
The Spring IoC container calls that method when the bean is created (and in accordance
with the standard lifecycle callback contract xref:core/beans/factory-nature.adoc#beans-factory-lifecycle[described previously]).
This feature also enforces a consistent naming convention for initialization and
destroy method callbacks.
callback method names on every bean. This means that you, as an application
developer, can write your application classes and use an initialization callback called
`init()`, without having to configure an `init-method="init"` attribute with each bean
definition. The Spring IoC container calls that method when the bean is created (and in
accordance with the standard lifecycle callback contract xref:core/beans/factory-nature.adoc#beans-factory-lifecycle[described previously]
). This feature also enforces a consistent naming convention for
initialization and destroy method callbacks.
Suppose that your initialization callback methods are named `init()` and your destroy
callback methods are named `destroy()`. Your class then resembles the class in the
@@ -445,15 +407,14 @@ and closed.
[TIP]
====
Note that the regular `org.springframework.context.Lifecycle` interface is a plain
contract for explicit start and stop notifications and does not imply auto-startup
at context refresh time. For fine-grained control over auto-startup and for graceful
stopping of a specific bean (including startup and stop phases), consider implementing
the extended `org.springframework.context.SmartLifecycle` interface instead.
contract for explicit start and stop notifications and does not imply auto-startup at context
refresh time. For fine-grained control over auto-startup of a specific bean (including startup phases),
consider implementing `org.springframework.context.SmartLifecycle` instead.
Also, please note that stop notifications are not guaranteed to come before destruction.
On regular shutdown, all `Lifecycle` beans first receive a stop notification before
the general destruction callbacks are being propagated. However, on hot refresh during
a context's lifetime or on stopped refresh attempts, only destroy methods are called.
the general destruction callbacks are being propagated. However, on hot refresh during a
context's lifetime or on stopped refresh attempts, only destroy methods are called.
====
The order of startup and shutdown invocations can be important. If a "`depends-on`"
@@ -125,8 +125,8 @@ objects regardless of scope, in the case of prototypes, configured destruction
lifecycle callbacks are not called. The client code must clean up prototype-scoped
objects and release expensive resources that the prototype beans hold. To get
the Spring container to release resources held by prototype-scoped beans, try using a
custom xref:core/beans/factory-extension.adoc#beans-factory-extension-bpp[bean post-processor]
which holds a reference to beans that need to be cleaned up.
custom xref:core/beans/factory-extension.adoc#beans-factory-extension-bpp[bean post-processor], which holds a reference to
beans that need to be cleaned up.
In some respects, the Spring container's role in regard to a prototype-scoped bean is a
replacement for the Java `new` operator. All lifecycle management past that point must
@@ -324,6 +324,7 @@ Kotlin::
[[beans-factory-scopes-application]]
=== Application Scope
@@ -373,6 +374,7 @@ Kotlin::
[[beans-factory-scopes-websocket]]
=== WebSocket Scope
@@ -382,6 +384,7 @@ xref:web/websocket/stomp/scope.adoc[WebSocket scope] for more details.
[[beans-factory-scopes-other-injection]]
=== Scoped Beans as Dependencies
@@ -446,13 +449,12 @@ understand the "`why`" as well as the "`how`" behind it:
----
<1> The line that defines the proxy.
To create such a proxy, you insert a child `<aop:scoped-proxy/>` element into a
scoped bean definition (see
xref:core/beans/factory-scopes.adoc#beans-factory-scopes-other-injection-proxies[Choosing the Type of Proxy to Create]
and xref:core/appendix/xsd-schemas.adoc[XML Schema-based configuration]).
To create such a proxy, you insert a child `<aop:scoped-proxy/>` element into a scoped
bean definition (see xref:core/beans/factory-scopes.adoc#beans-factory-scopes-other-injection-proxies[Choosing the Type of Proxy to Create] and
xref:core/appendix/xsd-schemas.adoc[XML Schema-based configuration]).
Why do definitions of beans scoped at the `request`, `session` and custom-scope
levels require the `<aop:scoped-proxy/>` element in common scenarios?
levels require the `<aop:scoped-proxy/>` element?
Consider the following singleton bean definition and contrast it with
what you need to define for the aforementioned scopes (note that the following
`userPreferences` bean definition as it stands is incomplete):
@@ -511,8 +513,8 @@ the `<aop:scoped-proxy/>` element, a CGLIB-based class proxy is created.
[NOTE]
====
CGLIB proxies do not intercept private methods. Attempting to call a private method
on such a proxy will not delegate to the actual scoped target object.
CGLIB proxies intercept only public method calls! Do not call non-public methods
on such a proxy. They are not delegated to the actual scoped target object.
====
Alternatively, you can configure the Spring container to create standard JDK
@@ -180,10 +180,9 @@ Kotlin::
----
======
The resolution mechanism is pretty much identical to constructor-based dependency
injection. See
xref:core/beans/dependencies/factory-collaborators.adoc#beans-constructor-injection[the relevant section]
for more details.
injection. See xref:core/beans/dependencies/factory-collaborators.adoc#beans-constructor-injection[the relevant section] for more details.
[[beans-java-lifecycle-callbacks]]
@@ -318,9 +317,8 @@ type, making it harder to use for cross-reference calls in other `@Bean` methods
intend to refer to the provided resource here).
=====
In the case of `BeanOne` from the example above the preceding note, it would be
equally valid to call the `init()` method directly during construction, as the
following example shows:
In the case of `BeanOne` from the example above the preceding note, it would be equally valid to call the `init()`
method directly during construction, as the following example shows:
[tabs]
======
@@ -508,10 +506,10 @@ Kotlin::
[[beans-java-bean-aliasing]]
== Bean Aliasing
As discussed in xref:core/beans/definition.adoc#beans-beanname[Naming Beans], it is
sometimes desirable to give a single bean multiple names, otherwise known as bean aliasing.
The `name` attribute of the `@Bean` annotation accepts a String array for this purpose.
The following example shows how to set a number of aliases for a bean:
As discussed in xref:core/beans/definition.adoc#beans-beanname[Naming Beans], it is sometimes desirable to give a single bean
multiple names, otherwise known as bean aliasing. The `name` attribute of the `@Bean`
annotation accepts a String array for this purpose. The following example shows how to set
a number of aliases for a bean:
[tabs]
======
@@ -113,8 +113,7 @@ issue, because no compiler is involved, and you can declare
When using `@Configuration` classes, the Java compiler places constraints on
the configuration model, in that references to other beans must be valid Java syntax.
Fortunately, solving this problem is simple. As
xref:core/beans/java/bean-annotation.adoc#beans-java-dependencies[we already discussed],
Fortunately, solving this problem is simple. As xref:core/beans/java/bean-annotation.adoc#beans-java-dependencies[we already discussed],
a `@Bean` method can have an arbitrary number of parameters that describe the bean
dependencies. Consider the following more real-world scenario with several `@Configuration`
classes, each depending on beans declared in the others:
@@ -205,6 +204,7 @@ Kotlin::
----
======
There is another way to achieve the same result. Remember that `@Configuration` classes are
ultimately only another bean in the container: This means that they can take advantage of
`@Autowired` and `@Value` injection and other features the same as any other bean.
@@ -216,11 +216,6 @@ classes are processed quite early during the initialization of the context, and
to be injected this way may lead to unexpected early initialization. Whenever possible, resort to
parameter-based injection, as in the preceding example.
Avoid access to locally defined beans within a `@PostConstruct` method on the same configuration
class. This effectively leads to a circular reference since non-static `@Bean` methods semantically
require a fully initialized configuration class instance to be called on. With circular references
disallowed (e.g. in Spring Boot 2.6+), this may trigger a `BeanCurrentlyInCreationException`.
Also, be particularly careful with `BeanPostProcessor` and `BeanFactoryPostProcessor` definitions
through `@Bean`. Those should usually be declared as `static @Bean` methods, not triggering the
instantiation of their containing configuration class. Otherwise, `@Autowired` and `@Value` may not
@@ -4,7 +4,7 @@
Projection lets a collection drive the evaluation of a sub-expression, and the result is
a new collection. The syntax for projection is `.![projectionExpression]`. For example,
suppose we have a list of inventors but want the list of cities where they were born.
Effectively, we want to evaluate `placeOfBirth.city` for every entry in the inventor
Effectively, we want to evaluate 'placeOfBirth.city' for every entry in the inventor
list. The following example uses projection to do so:
[tabs]
@@ -13,18 +13,16 @@ Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
// evaluates to ["SmilJan", "Idvor"]
List placesOfBirth = parser.parseExpression("members.![placeOfBirth.city]")
.getValue(societyContext, List.class);
// returns ['Smiljan', 'Idvor' ]
List placesOfBirth = (List)parser.parseExpression("members.![placeOfBirth.city]");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
// evaluates to ["SmilJan", "Idvor"]
val placesOfBirth = parser.parseExpression("members.![placeOfBirth.city]")
.getValue(societyContext) as List<*>
// returns ['Smiljan', 'Idvor' ]
val placesOfBirth = parser.parseExpression("members.![placeOfBirth.city]") as List<*>
----
======
@@ -28,14 +28,13 @@ Kotlin::
======
Selection is supported for arrays and anything that implements `java.lang.Iterable` or
`java.util.Map`. For an array or `Iterable`, the selection expression is evaluated
against each individual element. Against a map, the selection expression is evaluated
against each map entry (objects of the Java type `Map.Entry`). Each map entry has its
`key` and `value` accessible as properties for use in the selection.
`java.util.Map`. For a list or array, the selection criteria is evaluated against each
individual element. Against a map, the selection criteria is evaluated against each map
entry (objects of the Java type `Map.Entry`). Each map entry has its `key` and `value`
accessible as properties for use in the selection.
Given a `Map` stored in a variable named `#map`, the following expression returns a new
map that consists of those elements of the original map where the entry's value is less
than 27:
The following expression returns a new map that consists of those elements of the
original map where the entry's value is less than 27:
[tabs]
======
@@ -43,21 +42,21 @@ Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
Map newMap = parser.parseExpression("#map.?[value < 27]").getValue(Map.class);
Map newMap = parser.parseExpression("map.?[value<27]").getValue();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val newMap = parser.parseExpression("#map.?[value < 27]").getValue() as Map
val newMap = parser.parseExpression("map.?[value<27]").getValue()
----
======
In addition to returning all the selected elements, you can retrieve only the first or
the last element. To obtain the first element matching the selection expression, the
syntax is `.^[selectionExpression]`. To obtain the last element matching the selection
expression, the syntax is `.$[selectionExpression]`.
the last element. To obtain the first element matching the selection, the syntax is
`.^[selectionExpression]`. To obtain the last matching selection, the syntax is
`.$[selectionExpression]`.
@@ -38,15 +38,5 @@ Kotlin::
----
======
[NOTE]
====
If your application or framework manages its own `EvaluationContext`, you may need to
manually configure a `StandardTypeLocator` with a specific `ClassLoader` to ensure that
the SpEL expression parser is able to reliably locate user types.
For example, the `StandardBeanExpressionResolver` in the `spring-context` module
configures a `StandardTypeLocator` using the bean `ClassLoader` of the corresponding
`BeanFactory`.
====
@@ -268,8 +268,6 @@ Java::
@Service
public class MyService {
private final ConversionService conversionService;
public MyService(ConversionService conversionService) {
this.conversionService = conversionService;
}
@@ -151,10 +151,10 @@ Kotlin::
----
======
The last example we show here is for typical JDBC support. You could have the `DataSource`
injected into an initialization method or a constructor, where you would create a `JdbcTemplate`
and other data access support classes (such as `SimpleJdbcCall` and others) by using this
`DataSource`. The following example autowires a `DataSource`:
The last example we show here is for typical JDBC support. You could have the
`DataSource` injected into an initialization method or a constructor, where you would create a
`JdbcTemplate` and other data access support classes (such as `SimpleJdbcCall` and others) by using
this `DataSource`. The following example autowires a `DataSource`:
[tabs]
======
@@ -9,13 +9,13 @@ to the database.
[[jdbc-batch-classic]]
== Basic Batch Operations with `JdbcTemplate`
You accomplish `JdbcTemplate` batch processing by implementing two methods of a special interface,
`BatchPreparedStatementSetter`, and passing that implementation in as the second parameter
You accomplish `JdbcTemplate` batch processing by implementing two methods of a special
interface, `BatchPreparedStatementSetter`, and passing that implementation in as the second parameter
in your `batchUpdate` method call. You can use the `getBatchSize` method to provide the size of
the current batch. You can use the `setValues` method to set the values for the parameters of
the prepared statement. This method is called the number of times that you specified in the
`getBatchSize` call. The following example updates the `t_actor` table based on entries in a list,
and the entire list is used as the batch:
the prepared statement. This method is called the number of times that you
specified in the `getBatchSize` call. The following example updates the `t_actor` table
based on entries in a list, and the entire list is used as the batch:
[tabs]
======
@@ -10,7 +10,7 @@ This section covers:
* xref:data-access/jdbc/connections.adoc#jdbc-SingleConnectionDataSource[Using `SingleConnectionDataSource`]
* xref:data-access/jdbc/connections.adoc#jdbc-DriverManagerDataSource[Using `DriverManagerDataSource`]
* xref:data-access/jdbc/connections.adoc#jdbc-TransactionAwareDataSourceProxy[Using `TransactionAwareDataSourceProxy`]
* xref:data-access/jdbc/connections.adoc#jdbc-DataSourceTransactionManager[Using `DataSourceTransactionManager` / `JdbcTransactionManager`]
* xref:data-access/jdbc/connections.adoc#jdbc-DataSourceTransactionManager[Using `DataSourceTransactionManager`]
[[jdbc-datasource]]
@@ -125,12 +125,8 @@ The following example shows C3P0 configuration:
== Using `DataSourceUtils`
The `DataSourceUtils` class is a convenient and powerful helper class that provides
`static` methods to obtain connections from JNDI and close connections if necessary.
It supports a thread-bound JDBC `Connection` with `DataSourceTransactionManager` but
also with `JtaTransactionManager` and `JpaTransactionManager`.
Note that `JdbcTemplate` implies `DataSourceUtils` connection access, using it
behind every JDBC operation, implicitly participating in an ongoing transaction.
`static` methods to obtain connections from JNDI and close connections if necessary. It
supports thread-bound connections with, for example, `DataSourceTransactionManager`.
[[jdbc-SmartDataSource]]
@@ -169,6 +165,7 @@ In contrast to `DriverManagerDataSource`, it reuses the same connection all the
avoiding excessive creation of physical connections.
[[jdbc-DriverManagerDataSource]]
== Using `DriverManagerDataSource`
@@ -204,44 +201,29 @@ javadoc for more details.
[[jdbc-DataSourceTransactionManager]]
== Using `DataSourceTransactionManager` / `JdbcTransactionManager`
== Using `DataSourceTransactionManager`
The `DataSourceTransactionManager` class is a `PlatformTransactionManager`
implementation for a single JDBC `DataSource`. It binds a JDBC `Connection`
from the specified `DataSource` to the currently executing thread, potentially
allowing for one thread-bound `Connection` per `DataSource`.
implementation for single JDBC data sources. It binds a JDBC connection from the
specified data source to the currently executing thread, potentially allowing for one
thread connection per data source.
Application code is required to retrieve the JDBC `Connection` through
`DataSourceUtils.getConnection(DataSource)` instead of Java EE's standard
Application code is required to retrieve the JDBC connection through
`DataSourceUtils.getConnection(DataSource)` instead of Jakarta EE's standard
`DataSource.getConnection`. It throws unchecked `org.springframework.dao` exceptions
instead of checked `SQLExceptions`. All framework classes (such as `JdbcTemplate`) use
this strategy implicitly. If not used with a transaction manager, the lookup strategy
behaves exactly like `DataSource.getConnection` and can therefore be used in any case.
instead of checked `SQLExceptions`. All framework classes (such as `JdbcTemplate`) use this
strategy implicitly. If not used with this transaction manager, the lookup strategy
behaves exactly like the common one. Thus, it can be used in any case.
The `DataSourceTransactionManager` class supports savepoints (`PROPAGATION_NESTED`),
custom isolation levels, and timeouts that get applied as appropriate JDBC statement
query timeouts. To support the latter, application code must either use `JdbcTemplate` or
call the `DataSourceUtils.applyTransactionTimeout(..)` method for each created statement.
The `DataSourceTransactionManager` class supports custom isolation levels and timeouts
that get applied as appropriate JDBC statement query timeouts. To support the latter,
application code must either use `JdbcTemplate` or call the
`DataSourceUtils.applyTransactionTimeout(..)` method for each created statement.
You can use `DataSourceTransactionManager` instead of `JtaTransactionManager` in the
single-resource case, as it does not require the container to support a JTA transaction
coordinator. Switching between these transaction managers is just a matter of configuration,
provided you stick to the required connection lookup pattern. Note that JTA does not support
savepoints or custom isolation levels and has a different timeout mechanism but otherwise
exposes similar behavior in terms of JDBC resources and JDBC commit/rollback management.
NOTE: As of 5.3, Spring provides an extended `JdbcTransactionManager` variant which adds
exception translation capabilities on commit/rollback (aligned with `JdbcTemplate`).
Where `DataSourceTransactionManager` will only ever throw `TransactionSystemException`
(analogous to JTA), `JdbcTransactionManager` translates database locking failures etc to
corresponding `DataAccessException` subclasses. Note that application code needs to be
prepared for such exceptions, not exclusively expecting `TransactionSystemException`.
In scenarios where that is the case, `JdbcTransactionManager` is the recommended choice.
In terms of exception behavior, `JdbcTransactionManager` is roughly equivalent to
`JpaTransactionManager` and also to `R2dbcTransactionManager`, serving as an immediate
companion/replacement for each other. `DataSourceTransactionManager` on the other hand
is equivalent to `JtaTransactionManager` and can serve as a direct replacement there.
You can use this implementation instead of `JtaTransactionManager` in the single-resource
case, as it does not require the container to support JTA. Switching between
both is just a matter of configuration, provided you stick to the required connection lookup
pattern. JTA does not support custom isolation levels.
@@ -501,8 +501,8 @@ extend from it, your sub-class inherits a `setDataSource(..)` method from the
Regardless of which of the above template initialization styles you choose to use (or
not), it is seldom necessary to create a new instance of a `JdbcTemplate` class each
time you want to run SQL. Once configured, a `JdbcTemplate` instance is thread-safe.
If your application accesses multiple databases, you may want multiple `JdbcTemplate`
instances, which requires multiple `DataSources` and, subsequently, multiple differently
If your application accesses multiple
databases, you may want multiple `JdbcTemplate` instances, which requires multiple `DataSources` and, subsequently, multiple differently
configured `JdbcTemplate` instances.
@@ -531,8 +531,11 @@ Java::
}
public int countOfActorsByFirstName(String firstName) {
String sql = "select count(*) from t_actor where first_name = :first_name";
String sql = "select count(*) from T_ACTOR where first_name = :first_name";
SqlParameterSource namedParameters = new MapSqlParameterSource("first_name", firstName);
return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);
}
----
@@ -544,7 +547,7 @@ Kotlin::
private val namedParameterJdbcTemplate = NamedParameterJdbcTemplate(dataSource)
fun countOfActorsByFirstName(firstName: String): Int {
val sql = "select count(*) from t_actor where first_name = :first_name"
val sql = "select count(*) from T_ACTOR where first_name = :first_name"
val namedParameters = MapSqlParameterSource("first_name", firstName)
return namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Int::class.java)!!
}
@@ -576,9 +579,12 @@ Java::
}
public int countOfActorsByFirstName(String firstName) {
String sql = "select count(*) from t_actor where first_name = :first_name";
String sql = "select count(*) from T_ACTOR where first_name = :first_name";
Map<String, String> namedParameters = Collections.singletonMap("first_name", firstName);
return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);
return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);
}
----
@@ -590,7 +596,7 @@ Kotlin::
private val namedParameterJdbcTemplate = NamedParameterJdbcTemplate(dataSource)
fun countOfActorsByFirstName(firstName: String): Int {
val sql = "select count(*) from t_actor where first_name = :first_name"
val sql = "select count(*) from T_ACTOR where first_name = :first_name"
val namedParameters = mapOf("first_name" to firstName)
return namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Int::class.java)!!
}
@@ -638,6 +644,7 @@ Java::
}
// setters omitted...
}
----
@@ -666,9 +673,12 @@ Java::
}
public int countOfActors(Actor exampleActor) {
// notice how the named parameters match the properties of the above 'Actor' class
String sql = "select count(*) from t_actor where first_name = :firstName and last_name = :lastName";
String sql = "select count(*) from T_ACTOR where first_name = :firstName and last_name = :lastName";
SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(exampleActor);
return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);
}
----
@@ -684,7 +694,7 @@ Kotlin::
fun countOfActors(exampleActor: Actor): Int {
// notice how the named parameters match the properties of the above 'Actor' class
val sql = "select count(*) from t_actor where first_name = :firstName and last_name = :lastName"
val sql = "select count(*) from T_ACTOR where first_name = :firstName and last_name = :lastName"
val namedParameters = BeanPropertySqlParameterSource(exampleActor)
return namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Int::class.java)!!
}
@@ -697,8 +707,8 @@ functionality that is present only in the `JdbcTemplate` class, you can use the
`getJdbcOperations()` method to access the wrapped `JdbcTemplate` through the
`JdbcOperations` interface.
See also xref:data-access/jdbc/core.adoc#jdbc-JdbcTemplate-idioms[`JdbcTemplate` Best Practices]
for guidelines on using the `NamedParameterJdbcTemplate` class in the context of an application.
See also xref:data-access/jdbc/core.adoc#jdbc-JdbcTemplate-idioms[`JdbcTemplate` Best Practices] for guidelines on using the
`NamedParameterJdbcTemplate` class in the context of an application.
[[jdbc-SQLExceptionTranslator]]
@@ -708,22 +718,12 @@ for guidelines on using the `NamedParameterJdbcTemplate` class in the context of
between ``SQLException``s and Spring's own `org.springframework.dao.DataAccessException`,
which is agnostic in regard to data access strategy. Implementations can be generic (for
example, using SQLState codes for JDBC) or proprietary (for example, using Oracle error
codes) for greater precision. This exception translation mechanism is used behind the
the common `JdbcTemplate` and `JdbcTransactionManager` entry points which do not
propagate `SQLException` but rather `DataAccessException`.
NOTE: As of 6.0, the default exception translator is `SQLExceptionSubclassTranslator`,
detecting JDBC 4 `SQLException` subclasses with a few extra checks, and with a fallback
to `SQLState` introspection through `SQLStateSQLExceptionTranslator`. This is usually
sufficient for common database access and does not require vendor-specific detection.
For backwards compatibility, consider using `SQLErrorCodeSQLExceptionTranslator` as
described below, potentially with custom error code mappings.
codes) for greater precision.
`SQLErrorCodeSQLExceptionTranslator` is the implementation of `SQLExceptionTranslator`
that is used by default when a file named `sql-error-codes.xml` is present in the root
of the classpath. This implementation uses specific vendor codes. It is more precise than
`SQLState` or `SQLException` subclass translation. The error code translations are based
on codes held in a JavaBean type class called `SQLErrorCodes`. This class is created and
that is used by default. This implementation uses specific vendor codes. It is more
precise than the `SQLState` implementation. The error code translations are based on
codes held in a JavaBean type class called `SQLErrorCodes`. This class is created and
populated by an `SQLErrorCodesFactory`, which (as the name suggests) is a factory for
creating `SQLErrorCodes` based on the contents of a configuration file named
`sql-error-codes.xml`. This file is populated with vendor codes and based on the
@@ -744,8 +744,8 @@ The `SQLErrorCodeSQLExceptionTranslator` applies matching rules in the following
translator. If this translation is not available, the next fallback translator is
the `SQLStateSQLExceptionTranslator`.
NOTE: The `SQLErrorCodesFactory` is used by default to define error codes and custom
exception translations. They are looked up in a file named `sql-error-codes.xml` from the
NOTE: The `SQLErrorCodesFactory` is used by default to define `Error` codes and custom exception
translations. They are looked up in a file named `sql-error-codes.xml` from the
classpath, and the matching `SQLErrorCodes` instance is located based on the database
name from the database metadata of the database in use.
@@ -784,12 +784,12 @@ Kotlin::
----
======
In the preceding example, the specific error code (`-12345`) is translated while
other errors are left to be translated by the default translator implementation.
To use this custom translator, you must pass it to the `JdbcTemplate` through the
method `setExceptionTranslator`, and you must use this `JdbcTemplate` for all of the
data access processing where this translator is needed. The following example shows
how you can use this custom translator:
In the preceding example, the specific error code (`-12345`) is translated, while other errors are
left to be translated by the default translator implementation. To use this custom
translator, you must pass it to the `JdbcTemplate` through the method
`setExceptionTranslator`, and you must use this `JdbcTemplate` for all of the data access
processing where this translator is needed. The following example shows how you can use this custom
translator:
[tabs]
======
@@ -800,6 +800,7 @@ Java::
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
// create a JdbcTemplate and set data source
this.jdbcTemplate = new JdbcTemplate();
this.jdbcTemplate.setDataSource(dataSource);
@@ -808,6 +809,7 @@ Java::
CustomSQLErrorCodesTranslator tr = new CustomSQLErrorCodesTranslator();
tr.setDataSource(dataSource);
this.jdbcTemplate.setExceptionTranslator(tr);
}
public void updateShippingCharge(long orderId, long pct) {
@@ -3,30 +3,30 @@
The Spring Framework's JDBC abstraction framework consists of four different packages:
* `core`: The `org.springframework.jdbc.core` package contains the `JdbcTemplate` class
and its various callback interfaces, plus a variety of related classes. A subpackage
named `org.springframework.jdbc.core.simple` contains the `SimpleJdbcInsert` and
* `core`: The `org.springframework.jdbc.core` package contains the `JdbcTemplate` class and its
various callback interfaces, plus a variety of related classes. A subpackage named
`org.springframework.jdbc.core.simple` contains the `SimpleJdbcInsert` and
`SimpleJdbcCall` classes. Another subpackage named
`org.springframework.jdbc.core.namedparam` contains the `NamedParameterJdbcTemplate`
class and the related support classes. See xref:data-access/jdbc/core.adoc[Using the JDBC Core Classes to Control Basic JDBC Processing and Error Handling], xref:data-access/jdbc/advanced.adoc[JDBC Batch Operations], and
xref:data-access/jdbc/simple.adoc[Simplifying JDBC Operations with the `SimpleJdbc` Classes].
* `datasource`: The `org.springframework.jdbc.datasource` package contains a utility class
for easy `DataSource` access and various simple `DataSource` implementations that you can
use for testing and running unmodified JDBC code outside of a Jakarta EE container. A subpackage
named `org.springframework.jdbc.datasource.embedded` provides support for creating
* `datasource`: The `org.springframework.jdbc.datasource` package contains a utility class for easy
`DataSource` access and various simple `DataSource` implementations that you can use for
testing and running unmodified JDBC code outside of a Jakarta EE container. A subpackage
named `org.springfamework.jdbc.datasource.embedded` provides support for creating
embedded databases by using Java database engines, such as HSQL, H2, and Derby. See
xref:data-access/jdbc/connections.adoc[Controlling Database Connections] and xref:data-access/jdbc/embedded-database-support.adoc[Embedded Database Support].
* `object`: The `org.springframework.jdbc.object` package contains classes that represent
RDBMS queries, updates, and stored procedures as thread-safe, reusable objects. See
* `object`: The `org.springframework.jdbc.object` package contains classes that represent RDBMS
queries, updates, and stored procedures as thread-safe, reusable objects. See
xref:data-access/jdbc/object.adoc[Modeling JDBC Operations as Java Objects]. This approach is modeled by JDO, although objects returned by queries
are naturally disconnected from the database. This higher-level of JDBC abstraction
depends on the lower-level abstraction in the `org.springframework.jdbc.core` package.
* `support`: The `org.springframework.jdbc.support` package provides `SQLException`
translation functionality and some utility classes. Exceptions thrown during JDBC processing
are translated to exceptions defined in the `org.springframework.dao` package. This means
* `support`: The `org.springframework.jdbc.support` package provides `SQLException` translation
functionality and some utility classes. Exceptions thrown during JDBC processing are
translated to exceptions defined in the `org.springframework.dao` package. This means
that code using the Spring JDBC abstraction layer does not need to implement JDBC or
RDBMS-specific error handling. All translated exceptions are unchecked, which gives you
the option of catching the exceptions from which you can recover while letting other
@@ -121,17 +121,18 @@ Kotlin::
<3> Using the method `setBlobAsBinaryStream` to pass in the contents of the BLOB.
======
[NOTE]
====
If you invoke the `setBlobAsBinaryStream`, `setClobAsAsciiStream`, or
`setClobAsCharacterStream` method on the `LobCreator` returned from
`DefaultLobHandler.getLobCreator()`, you can optionally specify a negative value
for the `contentLength` argument. If the specified content length is negative, the
`DefaultLobHandler.getLobCreator()`, you can optionally specify a negative value for the
`contentLength` argument. If the specified content length is negative, the
`DefaultLobHandler` uses the JDBC 4.0 variants of the set-stream methods without a
length parameter. Otherwise, it passes the specified length on to the driver.
See the documentation for the JDBC driver you use to verify that it supports streaming
a LOB without providing the content length.
See the documentation for the JDBC driver you use to verify that it supports streaming a
LOB without providing the content length.
====
Now it is time to read the LOB data from the database. Again, you use a `JdbcTemplate`
@@ -183,15 +184,15 @@ variable list of values. A typical example would be `select * from T_ACTOR where
JDBC standard. You cannot declare a variable number of placeholders. You need a number
of variations with the desired number of placeholders prepared, or you need to generate
the SQL string dynamically once you know how many placeholders are required. The named
parameter support provided in the `NamedParameterJdbcTemplate` takes the latter approach.
You can pass in the values as a `java.util.List` (or any `Iterable`) of simple values.
This list is used to insert the required placeholders into the actual SQL statement
and pass in the values during statement execution.
parameter support provided in the `NamedParameterJdbcTemplate` and `JdbcTemplate` takes
the latter approach. You can pass in the values as a `java.util.List` of primitive objects. This
list is used to insert the required placeholders and pass in the values during
statement execution.
NOTE: Be careful when passing in many values. The JDBC standard does not guarantee that
you can use more than 100 values for an `IN` expression list. Various databases exceed
this number, but they usually have a hard limit for how many values are allowed.
For example, Oracle's limit is 1000.
NOTE: Be careful when passing in many values. The JDBC standard does not guarantee that you
can use more than 100 values for an `in` expression list. Various databases exceed this
number, but they usually have a hard limit for how many values are allowed. For example, Oracle's
limit is 1000.
In addition to the primitive values in the value list, you can create a `java.util.List`
of object arrays. This list can support multiple expressions being defined for the `in`
@@ -88,6 +88,12 @@ You can use this option for full JPA capabilities in a Spring-based application
This includes web containers such as Tomcat, stand-alone applications, and
integration tests with sophisticated persistence requirements.
NOTE: If you want to specifically configure a Hibernate setup, an immediate alternative
is to set up a native Hibernate `LocalSessionFactoryBean` instead of a plain JPA
`LocalContainerEntityManagerFactoryBean`, letting it interact with JPA access code
as well as native Hibernate access code.
See xref:data-access/orm/jpa.adoc#orm-jpa-hibernate[Native Hibernate setup for JPA interaction] for details.
The `LocalContainerEntityManagerFactoryBean` gives full control over
`EntityManagerFactory` configuration and is appropriate for environments where
fine-grained customization is required. The `LocalContainerEntityManagerFactoryBean`
@@ -181,7 +187,6 @@ and automatic propagation of the weaver to all weaver-aware beans:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<context:load-time-weaver/>
<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
...
</bean>
@@ -268,25 +273,25 @@ The actual JPA provider bootstrapping is handed off to the specified executor an
running in parallel, to the application bootstrap thread. The exposed `EntityManagerFactory`
proxy can be injected into other application components and is even able to respond to
`EntityManagerFactoryInfo` configuration inspection. However, once the actual JPA provider
is being accessed by other components (for example, calling `createEntityManager`), those
calls block until the background bootstrapping has completed. In particular, when you use
is being accessed by other components (for example, calling `createEntityManager`), those calls
block until the background bootstrapping has completed. In particular, when you use
Spring Data JPA, make sure to set up deferred bootstrapping for its repositories as well.
[[orm-jpa-dao]]
== Implementing DAOs Based on JPA: `EntityManagerFactory` and `EntityManager`
NOTE: Although `EntityManagerFactory` instances are thread-safe, `EntityManager` instances
are not. The injected JPA `EntityManager` behaves like an `EntityManager` fetched from an
NOTE: Although `EntityManagerFactory` instances are thread-safe, `EntityManager` instances are
not. The injected JPA `EntityManager` behaves like an `EntityManager` fetched from an
application server's JNDI environment, as defined by the JPA specification. It delegates
all calls to the current transactional `EntityManager`, if any. Otherwise, it falls back
to a newly created `EntityManager` per operation, in effect making its usage thread-safe.
It is possible to write code against the plain JPA without any Spring dependencies, by
using an injected `EntityManagerFactory` or `EntityManager`. Spring can understand the
`@PersistenceUnit` and `@PersistenceContext` annotations both at the field and the method
level if a `PersistenceAnnotationBeanPostProcessor` is enabled. The following example
shows a plain JPA DAO implementation that uses the `@PersistenceUnit` annotation:
`@PersistenceUnit` and `@PersistenceContext` annotations both at the field and the method level
if a `PersistenceAnnotationBeanPostProcessor` is enabled. The following example shows a plain JPA DAO implementation
that uses the `@PersistenceUnit` annotation:
[tabs]
======
@@ -379,9 +384,9 @@ Consider the following example:
----
The main problem with such a DAO is that it always creates a new `EntityManager` through
the factory. You can avoid this by requesting a transactional `EntityManager` (also called a
"`shared EntityManager`" because it is a shared, thread-safe proxy for the actual transactional
EntityManager) to be injected instead of the factory. The following example shows how to do so:
the factory. You can avoid this by requesting a transactional `EntityManager` (also
called a "`shared EntityManager`" because it is a shared, thread-safe proxy for the actual
transactional EntityManager) to be injected instead of the factory. The following example shows how to do so:
[tabs]
======
@@ -420,24 +425,24 @@ Kotlin::
----
======
The `@PersistenceContext` annotation has an optional attribute called `type`, which defaults
to `PersistenceContextType.TRANSACTION`. You can use this default to receive a shared
The `@PersistenceContext` annotation has an optional attribute called `type`, which defaults to
`PersistenceContextType.TRANSACTION`. You can use this default to receive a shared
`EntityManager` proxy. The alternative, `PersistenceContextType.EXTENDED`, is a completely
different affair. This results in a so-called extended `EntityManager`, which is not
thread-safe and, hence, must not be used in a concurrently accessed component, such as a
Spring-managed singleton bean. Extended `EntityManager` instances are only supposed to be used
in stateful components that, for example, reside in a session, with the lifecycle of the
Spring-managed singleton bean. Extended `EntityManager` instances are only supposed to be used in
stateful components that, for example, reside in a session, with the lifecycle of the
`EntityManager` not tied to a current transaction but rather being completely up to the
application.
.Method- and field-level Injection
****
You can apply annotations that indicate dependency injections (such as `@PersistenceUnit`
and `@PersistenceContext`) on field or methods inside a class -- hence the expressions
"`method-level injection`" and "`field-level injection`". Field-level annotations are
concise and easier to use while method-level annotations allow for further processing of the
injected dependency. In both cases, the member visibility (public, protected, or private)
does not matter.
You can apply annotations that indicate dependency injections (such as `@PersistenceUnit` and
`@PersistenceContext`) on field or methods inside a class -- hence the
expressions "`method-level injection`" and "`field-level injection`". Field-level
annotations are concise and easier to use while method-level annotations allow for further
processing of the injected dependency. In both cases, the member visibility (public,
protected, or private) does not matter.
What about class-level annotations?
@@ -446,62 +451,21 @@ injection.
****
The injected `EntityManager` is Spring-managed (aware of the ongoing transaction).
Even though the new DAO implementation uses method-level injection of an `EntityManager`
instead of an `EntityManagerFactory`, no change is required in the bean definition
due to annotation usage.
Even though the new DAO implementation uses method-level
injection of an `EntityManager` instead of an `EntityManagerFactory`, no change is
required in the application context XML, due to annotation usage.
The main advantage of this DAO style is that it depends only on the Java Persistence API.
No import of any Spring class is required. Moreover, as the JPA annotations are understood,
the injections are applied automatically by the Spring container. This is appealing from
a non-invasiveness perspective and can feel more natural to JPA developers.
[[orm-jpa-dao-autowired]]
=== Implementing DAOs Based on `@Autowired` (typically with constructor-based injection)
`@PersistenceUnit` and `@PersistenceContext` can only be declared on methods and fields.
What about providing JPA resources via constructors and other `@Autowired` injection points?
`EntityManagerFactory` can easily be injected via constructors and `@Autowired` fields/methods
as long as the target is defined as a bean, e.g. via `LocalContainerEntityManagerFactoryBean`.
The injection point matches the original `EntityManagerFactory` definition by type as-is.
However, an `@PersistenceContext`-style shared `EntityManager` reference is not available for
regular dependency injection out of the box. In order to make it available for type-based
matching as required by `@Autowired`, consider defining a `SharedEntityManagerBean` as a
companion for your `EntityManagerFactory` definition:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
...
</bean>
<bean id="em" class="org.springframework.orm.jpa.support.SharedEntityManagerBean">
<property name="entityManagerFactory" ref="emf"/>
</bean>
----
Alternatively, you may define an `@Bean` method based on `SharedEntityManagerCreator`:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Bean("em")
public static EntityManager sharedEntityManager(EntityManagerFactory emf) {
return SharedEntityManagerCreator.createSharedEntityManager(emf);
}
----
In case of multiple persistence units, each `EntityManagerFactory` definition needs to be
accompanied by a corresponding `EntityManager` bean definition, ideally with qualifiers
that match with the distinct `EntityManagerFactory` definition in order to distinguish
the persistence units via `@Autowired @Qualifier("...")`.
[[orm-jpa-tx]]
== Spring-driven JPA Transactions
== Spring-driven JPA transactions
NOTE: We strongly encourage you to read xref:data-access/transaction/declarative.adoc[Declarative Transaction Management],
if you have not already done so, to get more detailed coverage of Spring's declarative transaction support.
NOTE: We strongly encourage you to read xref:data-access/transaction/declarative.adoc[Declarative Transaction Management], if you have not
already done so, to get more detailed coverage of Spring's declarative transaction support.
The recommended strategy for JPA is local transactions through JPA's native transaction
support. Spring's `JpaTransactionManager` provides many capabilities known from local
@@ -514,6 +478,11 @@ to JDBC access code that accesses the same `DataSource`, provided that the regis
Spring provides dialects for the EclipseLink and Hibernate JPA implementations.
See the xref:data-access/orm/jpa.adoc#orm-jpa-dialect[next section] for details on the `JpaDialect` mechanism.
NOTE: As an immediate alternative, Spring's native `HibernateTransactionManager` is capable
of interacting with JPA access code, adapting to several Hibernate specifics and providing
JDBC interaction. This makes particular sense in combination with `LocalSessionFactoryBean`
setup. See xref:data-access/orm/jpa.adoc#orm-jpa-hibernate[Native Hibernate Setup for JPA Interaction] for details.
[[orm-jpa-dialect]]
== Understanding `JpaDialect` and `JpaVendorAdapter`
@@ -526,7 +495,7 @@ features supported by Spring, usually in a vendor-specific manner:
* Applying specific transaction semantics (such as custom isolation level or transaction
timeout)
* Retrieving the transactional JDBC `Connection` (for exposure to JDBC-based DAOs)
* Advanced translation of `PersistenceException` to Spring's `DataAccessException`
* Advanced translation of `PersistenceExceptions` to Spring `DataAccessExceptions`
This is particularly valuable for special transaction semantics and for advanced
translation of exception. The default implementation (`DefaultJpaDialect`) does
@@ -11,13 +11,11 @@ specification effort to standardize access to SQL databases using reactive patte
The Spring Framework's R2DBC abstraction framework consists of two different packages:
* `core`: The `org.springframework.r2dbc.core` package contains the `DatabaseClient`
class plus a variety of related classes. See
xref:data-access/r2dbc.adoc#r2dbc-core[Using the R2DBC Core Classes to Control Basic R2DBC Processing and Error Handling].
class plus a variety of related classes. See xref:data-access/r2dbc.adoc#r2dbc-core[Using the R2DBC Core Classes to Control Basic R2DBC Processing and Error Handling].
* `connection`: The `org.springframework.r2dbc.connection` package contains a utility class
for easy `ConnectionFactory` access and various simple `ConnectionFactory` implementations
that you can use for testing and running unmodified R2DBC. See
xref:data-access/r2dbc.adoc#r2dbc-connections[Controlling Database Connections].
that you can use for testing and running unmodified R2DBC. See xref:data-access/r2dbc.adoc#r2dbc-connections[Controlling Database Connections].
[[r2dbc-core]]
@@ -33,7 +31,6 @@ including error handling. It includes the following topics:
* xref:data-access/r2dbc.adoc#r2dbc-DatabaseClient-filter[Statement Filters]
* xref:data-access/r2dbc.adoc#r2dbc-auto-generated-keys[Retrieving Auto-generated Keys]
[[r2dbc-DatabaseClient]]
=== Using `DatabaseClient`
@@ -46,9 +43,8 @@ SQL and extract results. The `DatabaseClient` class:
* Runs SQL queries
* Update statements and stored procedure calls
* Performs iteration over `Result` instances
* Catches R2DBC exceptions and translates them to the generic, more informative,
exception hierarchy defined in the `org.springframework.dao` package.
(See xref:data-access/dao.adoc#dao-exceptions[Consistent Exception Hierarchy].)
* Catches R2DBC exceptions and translates them to the generic, more informative, exception
hierarchy defined in the `org.springframework.dao` package. (See xref:data-access/dao.adoc#dao-exceptions[Consistent Exception Hierarchy].)
The client has a functional, fluent API using reactive types for declarative composition.
@@ -254,6 +250,7 @@ Kotlin::
----
======
[[r2dbc-DatabaseClient-mapping-null]]
.What about `null`?
****
@@ -318,10 +315,10 @@ The following example shows parameter binding for a query:
[source,java]
----
db.sql("INSERT INTO person (id, name, age) VALUES(:id, :name, :age)")
.bind("id", "joe")
.bind("name", "Joe")
.bind("age", 34);
db.sql("INSERT INTO person (id, name, age) VALUES(:id, :name, :age)")
.bind("id", "joe")
.bind("name", "Joe")
.bind("age", 34);
----
.R2DBC Native Bind Markers
@@ -330,7 +327,7 @@ R2DBC uses database-native bind markers that depend on the actual database vendo
As an example, Postgres uses indexed markers, such as `$1`, `$2`, `$n`.
Another example is SQL Server, which uses named bind markers prefixed with `@`.
This is different from JDBC which requires `?` as bind markers.
This is different from JDBC, which requires `?` as bind markers.
In JDBC, the actual drivers translate `?` bind markers to database-native
markers as part of their statement execution.
@@ -366,7 +363,7 @@ Java::
tuples.add(new Object[] {"Ann", 50});
client.sql("SELECT id, name, state FROM table WHERE (name, age) IN (:tuples)")
.bind("tuples", tuples);
.bind("tuples", tuples);
----
Kotlin::
@@ -378,7 +375,7 @@ Kotlin::
tuples.add(arrayOf("Ann", 50))
client.sql("SELECT id, name, state FROM table WHERE (name, age) IN (:tuples)")
.bind("tuples", tuples)
.bind("tuples", tuples)
----
======
@@ -393,7 +390,7 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
client.sql("SELECT id, name, state FROM table WHERE age IN (:ages)")
.bind("ages", Arrays.asList(35, 50));
.bind("ages", Arrays.asList(35, 50));
----
Kotlin::
@@ -405,7 +402,7 @@ Kotlin::
tuples.add(arrayOf("Ann", 50))
client.sql("SELECT id, name, state FROM table WHERE age IN (:ages)")
.bind("tuples", arrayOf(35, 50))
.bind("tuples", arrayOf(35, 50))
----
======
@@ -432,9 +429,9 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
.filter((s, next) -> next.execute(s.returnGeneratedValues("id")))
.bind("name", …)
.bind("state", …);
.filter((s, next) -> next.execute(s.returnGeneratedValues("id")))
.bind("name", …)
.bind("state", …);
----
Kotlin::
@@ -442,9 +439,9 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
.filter { s: Statement, next: ExecuteFunction -> next.execute(s.returnGeneratedValues("id")) }
.bind("name", …)
.bind("state", …)
.filter { s: Statement, next: ExecuteFunction -> next.execute(s.returnGeneratedValues("id")) }
.bind("name", …)
.bind("state", …)
----
======
@@ -458,10 +455,10 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
.filter(statement -> s.returnGeneratedValues("id"));
.filter(statement -> s.returnGeneratedValues("id"));
client.sql("SELECT id, name, state FROM table")
.filter(statement -> s.fetchSize(25));
.filter(statement -> s.fetchSize(25));
----
Kotlin::
@@ -469,10 +466,10 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
.filter { statement -> s.returnGeneratedValues("id") }
.filter { statement -> s.returnGeneratedValues("id") }
client.sql("SELECT id, name, state FROM table")
.filter { statement -> s.fetchSize(25) }
.filter { statement -> s.fetchSize(25) }
----
======
@@ -596,7 +593,7 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
Mono<Integer> generatedId = client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
.filter(statement -> s.returnGeneratedValues("id"))
.filter(statement -> s.returnGeneratedValues("id"))
.map(row -> row.get("id", Integer.class))
.first();
@@ -608,7 +605,7 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val generatedId = client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
.filter { statement -> s.returnGeneratedValues("id") }
.filter { statement -> s.returnGeneratedValues("id") }
.map { row -> row.get("id", Integer.class) }
.awaitOne()
@@ -675,6 +672,7 @@ Kotlin::
[[r2dbc-ConnectionFactoryUtils]]
=== Using `ConnectionFactoryUtils`
The `ConnectionFactoryUtils` class is a convenient and powerful helper class
that provides `static` methods to obtain connections from `ConnectionFactory`
and close connections (if necessary).
@@ -720,15 +718,19 @@ javadoc for more details.
=== Using `R2dbcTransactionManager`
The `R2dbcTransactionManager` class is a `ReactiveTransactionManager` implementation for
a single R2DBC `ConnectionFactory`. It binds an R2DBC `Connection` from the specified
`ConnectionFactory` to the subscriber `Context`, potentially allowing for one subscriber
`Connection` for each `ConnectionFactory`.
single R2DBC data sources. It binds an R2DBC connection from the specified connection factory
to the subscriber `Context`, potentially allowing for one subscriber connection for each
connection factory.
Application code is required to retrieve the R2DBC `Connection` through
Application code is required to retrieve the R2DBC connection through
`ConnectionFactoryUtils.getConnection(ConnectionFactory)`, instead of R2DBC's standard
`ConnectionFactory.create()`. All framework classes (such as `DatabaseClient`) use this
strategy implicitly. If not used with a transaction manager, the lookup strategy behaves
exactly like `ConnectionFactory.create()` and can therefore be used in any case.
`ConnectionFactory.create()`.
All framework classes (such as `DatabaseClient`) use this strategy implicitly.
If not used with this transaction manager, the lookup strategy behaves exactly like the common one.
Thus, it can be used in any case.
The `R2dbcTransactionManager` class supports custom isolation levels that get applied to the connection.
@@ -12,16 +12,19 @@ management that delivers the following benefits:
than complex transaction APIs, such as JTA.
* Excellent integration with Spring's data access abstractions.
The following sections describe the Spring Framework's transaction features and technologies:
The following sections describe the Spring Framework's transaction features and
technologies:
* xref:data-access/transaction/motivation.adoc[Advantages of the Spring Framework's transaction support model]
describes why you would use the Spring Framework's transaction abstraction instead of EJB
Container-Managed Transactions (CMT) or choosing to drive transactions through a proprietary API.
* xref:data-access/transaction/motivation.adoc[Advantages of the Spring Framework's transaction support model]
describes why you would use the Spring Framework's transaction abstraction
instead of EJB Container-Managed Transactions (CMT) or choosing to drive local
transactions through a proprietary API, such as Hibernate.
* xref:data-access/transaction/strategies.adoc[Understanding the Spring Framework transaction abstraction]
outlines the core classes and describes how to configure and obtain `DataSource` instances
from a variety of sources.
* xref:data-access/transaction/tx-resource-synchronization.adoc[Synchronizing resources with transactions]
describes how the application code ensures that resources are created, reused, and cleaned up properly.
outlines the core classes and describes how to configure and obtain `DataSource`
instances from a variety of sources.
* xref:data-access/transaction/tx-resource-synchronization.adoc[Synchronizing resources with transactions] describes
how the application code ensures that resources are created, reused, and cleaned up
properly.
* xref:data-access/transaction/declarative.adoc[Declarative transaction management] describes support for
declarative transaction management.
* xref:data-access/transaction/programmatic.adoc[Programmatic transaction management] covers support for
@@ -13,7 +13,39 @@ javadoc for details.
Spring's `JtaTransactionManager` is the standard choice to run on Jakarta EE application
servers and is known to work on all common servers. Advanced functionality, such as
transaction suspension, works on many servers as well (including GlassFish, JBoss and
Geronimo) without any special configuration required.
Geronimo) without any special configuration required. However, for fully supported
transaction suspension and further advanced integration, Spring includes special adapters
for WebLogic Server and WebSphere. These adapters are discussed in the following
sections.
For standard scenarios, including WebLogic Server and WebSphere, consider using the
convenient `<tx:jta-transaction-manager/>` configuration element. When configured,
this element automatically detects the underlying server and chooses the best
transaction manager available for the platform. This means that you need not explicitly
configure server-specific adapter classes (as discussed in the following sections).
Rather, they are chosen automatically, with the standard
`JtaTransactionManager` as the default fallback.
[[transaction-application-server-integration-websphere]]
== IBM WebSphere
On WebSphere 6.1.0.9 and above, the recommended Spring JTA transaction manager to use is
`WebSphereUowTransactionManager`. This special adapter uses IBM's `UOWManager` API,
which is available in WebSphere Application Server 6.1.0.9 and later. With this adapter,
Spring-driven transaction suspension (suspend and resume as initiated by
`PROPAGATION_REQUIRES_NEW`) is officially supported by IBM.
[[transaction-application-server-integration-weblogic]]
== Oracle WebLogic Server
On WebLogic Server 9.0 or above, you would typically use the
`WebLogicJtaTransactionManager` instead of the stock `JtaTransactionManager` class. This
special WebLogic-specific subclass of the normal `JtaTransactionManager` supports the
full power of Spring's transaction definitions in a WebLogic-managed transaction
environment, beyond standard JTA semantics. Features include transaction names,
per-transaction isolation levels, and proper resuming of transactions in all cases.
@@ -76,7 +76,7 @@ Kotlin::
Used at the class level as above, the annotation indicates a default for all methods of
the declaring class (as well as its subclasses). Alternatively, each method can be
annotated individually. See xref:data-access/transaction/declarative/annotations.adoc#transaction-declarative-annotations-method-visibility[method visibility] for
annotated individually. See xref:data-access/transaction/declarative/annotations.adoc#transaction-declarative-annotations-method-visibility[null] for
further details on which methods Spring considers transactional. Note that a class-level
annotation does not apply to ancestor classes up the class hierarchy; in such a scenario,
inherited methods need to be locally redeclared in order to participate in a
@@ -124,6 +124,7 @@ In XML configuration, the `<tx:annotation-driven/>` tag provides similar conveni
----
<1> The line that makes the bean instance transactional.
TIP: You can omit the `transaction-manager` attribute in the `<tx:annotation-driven/>`
tag if the bean name of the `TransactionManager` that you want to wire in has the name
`transactionManager`. If the `TransactionManager` bean that you want to dependency-inject
@@ -193,63 +194,74 @@ Kotlin::
======
Note that there are special considerations for the returned `Publisher` with regards to
Reactive Streams cancellation signals. See the
xref:data-access/transaction/programmatic.adoc#tx-prog-operator-cancel[Cancel Signals]
section under "Using the TransactionalOperator" for more details.
Reactive Streams cancellation signals. See the xref:data-access/transaction/programmatic.adoc#tx-prog-operator-cancel[Cancel Signals] section under
"Using the TransactionalOperator" for more details.
[[transaction-declarative-annotations-method-visibility]]
.Method visibility and `@Transactional` in proxy mode
.Method visibility and `@Transactional`
[NOTE]
====
The `@Transactional` annotation is typically used on methods with `public` visibility.
As of 6.0, `protected` or package-visible methods can also be made transactional for
class-based proxies by default. Note that transactional methods in interface-based
proxies must always be `public` and defined in the proxied interface. For both kinds
of proxies, only external method calls coming in through the proxy are intercepted.
When you use transactional proxies with Spring's standard configuration, you should apply
the `@Transactional` annotation only to methods with `public` visibility. If you do
annotate `protected`, `private`, or package-visible methods with the `@Transactional`
annotation, no error is raised, but the annotated method does not exhibit the configured
transactional settings. If you need to annotate non-public methods, consider the tip in
the following paragraph for class-based proxies or consider using AspectJ compile-time or
load-time weaving (described later).
If you prefer consistent treatment of method visibility across the different kinds of
proxies (which was the default up until 5.3), consider specifying `publicMethodsOnly`:
When using `@EnableTransactionManagement` in a `@Configuration` class, `protected` or
package-visible methods can also be made transactional for class-based proxies by
registering a custom `transactionAttributeSource` bean like in the following example.
Note, however, that transactional methods in interface-based proxies must always be
`public` and defined in the proxied interface.
[source,java,indent=0,subs="verbatim,quotes"]
----
/**
* Register a custom AnnotationTransactionAttributeSource with the
* publicMethodsOnly flag set to true to consistently ignore non-public methods.
* publicMethodsOnly flag set to false to enable support for
* protected and package-private @Transactional methods in
* class-based proxies.
*
* @see ProxyTransactionManagementConfiguration#transactionAttributeSource()
*/
@Bean
TransactionAttributeSource transactionAttributeSource() {
return new AnnotationTransactionAttributeSource(true);
return new AnnotationTransactionAttributeSource(false);
}
----
The _Spring TestContext Framework_ supports non-private `@Transactional` test methods
by default as well. See xref:testing/testcontext-framework/tx.adoc[Transaction Management]
in the testing chapter for examples.
The _Spring TestContext Framework_ supports non-private `@Transactional` test methods by
default. See xref:testing/testcontext-framework/tx.adoc[Transaction Management] in the testing
chapter for examples.
====
You can apply the `@Transactional` annotation to an interface definition, a method
on an interface, a class definition, or a method on a class. However, the mere presence
of the `@Transactional` annotation is not enough to activate the transactional behavior.
The `@Transactional` annotation is merely metadata that can be consumed by corresponding
runtime infrastructure which uses that metadata to configure the appropriate beans with
transactional behavior. In the preceding example, the `<tx:annotation-driven/>` element
switches on actual transaction management at runtime.
on an interface, a class definition, or a method on a class. However, the
mere presence of the `@Transactional` annotation is not enough to activate the
transactional behavior. The `@Transactional` annotation is merely metadata that can
be consumed by some runtime infrastructure that is `@Transactional`-aware and that
can use the metadata to configure the appropriate beans with transactional behavior.
In the preceding example, the `<tx:annotation-driven/>` element switches on the
transactional behavior.
TIP: The Spring team recommends that you annotate methods of concrete classes with the
`@Transactional` annotation, rather than relying on annotated methods in interfaces,
even if the latter does work for interface-based and target-class proxies as of 5.0.
Since Java annotations are not inherited from interfaces, interface-declared annotations
are still not recognized by the weaving infrastructure when using AspectJ mode, so the
aspect does not get applied. As a consequence, your transaction annotations may be
silently ignored: Your code might appear to "work" until you test a rollback scenario.
TIP: The Spring team recommends that you annotate only concrete classes (and methods of
concrete classes) with the `@Transactional` annotation, as opposed to annotating interfaces.
You certainly can place the `@Transactional` annotation on an interface (or an interface
method), but this works only as you would expect it to if you use interface-based
proxies. The fact that Java annotations are not inherited from interfaces means that,
if you use class-based proxies (`proxy-target-class="true"`) or the weaving-based
aspect (`mode="aspectj"`), the transaction settings are not recognized by the proxying
and weaving infrastructure, and the object is not wrapped in a transactional proxy.
NOTE: In proxy mode (which is the default), only external method calls coming in through
the proxy are intercepted. This means that self-invocation (in effect, a method within
the target object calling another method of the target object) does not lead to an actual
transaction at runtime even if the invoked method is marked with `@Transactional`. Also,
the proxy must be fully initialized to provide the expected behavior, so you should not
rely on this feature in your initialization code -- e.g. in a `@PostConstruct` method.
rely on this feature in your initialization code -- for example, in a `@PostConstruct`
method.
Consider using AspectJ mode (see the `mode` attribute in the following table) if you
expect self-invocations to be wrapped with transactions as well. In this case, there is
@@ -274,20 +286,20 @@ is modified) to support `@Transactional` runtime behavior on any kind of method.
framework (following proxy semantics, as discussed earlier, applying to method calls
coming in through the proxy only). The alternative mode (`aspectj`) instead weaves the
affected classes with Spring's AspectJ transaction aspect, modifying the target class
byte code to apply to any kind of method call. AspectJ weaving requires `spring-aspects.jar`
in the classpath as well as having load-time weaving (or compile-time weaving) enabled.
(See xref:core/aop/using-aspectj.adoc#aop-aj-ltw-spring[Spring configuration] for details
on how to set up load-time weaving.)
byte code to apply to any kind of method call. AspectJ weaving requires
`spring-aspects.jar` in the classpath as well as having load-time weaving (or compile-time
weaving) enabled. (See xref:core/aop/using-aspectj.adoc#aop-aj-ltw-spring[Spring configuration]
for details on how to set up load-time weaving.)
| `proxy-target-class`
| `proxyTargetClass`
| `false`
| Applies to `proxy` mode only. Controls what type of transactional proxies are created
for classes annotated with the `@Transactional` annotation. If the `proxy-target-class`
attribute is set to `true`, class-based proxies are created. If `proxy-target-class` is
`false` or if the attribute is omitted, then standard JDK interface-based proxies are
created. (See xref:core/aop/proxying.adoc[Proxying Mechanisms] for a detailed examination
of the different proxy types.)
for classes annotated with the `@Transactional` annotation. If the
`proxy-target-class` attribute is set to `true`, class-based proxies are created.
If `proxy-target-class` is `false` or if the attribute is omitted, then standard JDK
interface-based proxies are created. (See xref:core/aop/proxying.adoc[Proxying Mechanisms]
for a detailed examination of the different proxy types.)
| `order`
| `order`
@@ -363,6 +375,7 @@ Kotlin::
----
======
[[transaction-declarative-attransactional-settings]]
== `@Transactional` Settings
@@ -441,9 +454,10 @@ on rollback rule semantics, patterns, and warnings regarding possible unintentio
matches for pattern-based rollback rules.
Currently, you cannot have explicit control over the name of a transaction, where 'name'
means the transaction name that appears in a transaction monitor and in logging output.
For declarative transactions, the transaction name is always the fully-qualified class
name + `.` + the method name of the transactionally advised class. For example, if the
means the transaction name that appears in a transaction monitor, if applicable
(for example, WebLogic's transaction monitor), and in logging output. For declarative
transactions, the transaction name is always the fully-qualified class name + `.`
+ the method name of the transactionally advised class. For example, if the
`handlePayment(..)` method of the `BusinessService` class started a transaction, the
name of the transaction would be: `com.example.BusinessService.handlePayment`.
@@ -508,17 +522,17 @@ The following listing shows the bean declarations:
----
<tx:annotation-driven/>
<bean id="transactionManager1" class="org.springframework.jdbc.support.JdbcTransactionManager">
<bean id="transactionManager1" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
...
<qualifier value="order"/>
</bean>
<bean id="transactionManager2" class="org.springframework.jdbc.support.JdbcTransactionManager">
<bean id="transactionManager2" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
...
<qualifier value="account"/>
</bean>
<bean id="transactionManager3" class="org.springframework.data.r2dbc.connection.R2dbcTransactionManager">
<bean id="transactionManager3" class="org.springframework.data.r2dbc.connectionfactory.R2dbcTransactionManager">
...
<qualifier value="reactive-account"/>
</bean>
@@ -23,7 +23,7 @@ As of Spring Framework 5.2, the default configuration also provides support for
Vavr's `Try` method to trigger transaction rollbacks when it returns a 'Failure'.
This allows you to handle functional-style errors using Try and have the transaction
automatically rolled back in case of a failure. For more information on Vavr's Try,
refer to the https://docs.vavr.io/#_try[official Vavr documentation].
refer to the [official Vavr documentation](https://www.vavr.io/vavr-docs/#_try).
Here's an example of how to use Vavr's Try with a transactional method:
[tabs]
@@ -59,14 +59,6 @@ status and with an inner transaction's locks released immediately after its comp
Such an independent inner transaction can also declare its own isolation level, timeout,
and read-only settings and not inherit an outer transaction's characteristics.
NOTE: The resources attached to the outer transaction will remain bound there while
the inner transaction acquires its own resources such as a new database connection.
This may lead to exhaustion of the connection pool and potentially to a deadlock if
several threads have an active outer transaction and wait to acquire a new connection
for their inner transaction, with the pool not being able to hand out any such inner
connection anymore. Do not use `PROPAGATION_REQUIRES_NEW` unless your connection pool
is appropriately sized, exceeding the number of concurrent threads by at least 1.
[[tx-propagation-nested]]
== Understanding `PROPAGATION_NESTED`
+3 -1
View File
@@ -29,6 +29,8 @@ Brannen, Ramnivas Laddad, Arjen Poutsma, Chris Beams, Tareq Abedrabbo, Andy Clem
Syer, Oliver Gierke, Rossen Stoyanchev, Phillip Webb, Rob Winch, Brian Clozel, Stephane
Nicoll, Sebastien Deleuze, Jay Bryant, Mark Paluch
Copyright © 2002 - 2023 VMware, Inc. All Rights Reserved.
Copies of this document may be made for your own use and for distribution to others,
provided that you do not charge any fee for such copies and further provided that each
copy contains the Copyright Notice, whether distributed in print or electronically.
copy contains this Copyright Notice, whether distributed in print or electronically.
@@ -98,9 +98,9 @@ through its `key` attribute. You can use xref:core/expressions.adoc[SpEL] to pic
arguments of interest (or their nested properties), perform operations, or even
invoke arbitrary methods without having to write any code or implement any interface.
This is the recommended approach over the
xref:integration/cache/annotations.adoc#cache-annotations-cacheable-default-key[default generator],
since methods tend to be quite different in signatures as the code base grows. While the
default strategy might work for some methods, it rarely works for all methods.
xref:integration/cache/annotations.adoc#cache-annotations-cacheable-default-key[default generator], since methods tend to be
quite different in signatures as the code base grows. While the default strategy might
work for some methods, it rarely works for all methods.
The following examples use various SpEL declarations (if you are not familiar with SpEL,
do yourself a favor and read xref:core/expressions.adoc[Spring Expression Language]):
@@ -137,8 +137,9 @@ that specifies both results in an exception.
[[cache-annotations-cacheable-default-cache-resolver]]
=== Default Cache Resolution
The caching abstraction uses a simple `CacheResolver` that retrieves the caches
defined at the operation level by using the configured `CacheManager`.
The caching abstraction uses a simple `CacheResolver` that
retrieves the caches defined at the operation level by using the configured
`CacheManager`.
To provide a different default cache resolver, you need to implement the
`org.springframework.cache.interceptor.CacheResolver` interface.
@@ -159,11 +160,12 @@ For applications that work with several cache managers, you can set the
----
<1> Specifying `anotherCacheManager`.
You can also replace the `CacheResolver` entirely in a fashion similar to that of
replacing xref:integration/cache/annotations.adoc#cache-annotations-cacheable-key[key generation].
The resolution is requested for every cache operation, letting the implementation
actually resolve the caches to use based on runtime arguments. The following example
shows how to specify a `CacheResolver`:
replacing xref:integration/cache/annotations.adoc#cache-annotations-cacheable-key[key generation]. The resolution is
requested for every cache operation, letting the implementation actually resolve
the caches to use based on runtime arguments. The following example shows how to
specify a `CacheResolver`:
[source,java,indent=0,subs="verbatim,quotes"]
----
@@ -172,6 +174,7 @@ shows how to specify a `CacheResolver`:
----
<1> Specifying the `CacheResolver`.
[NOTE]
====
Since Spring 4.1, the `value` attribute of the cache annotations are no longer
@@ -226,6 +229,7 @@ argument `name` has a length shorter than 32:
----
<1> Setting a condition on `@Cacheable`.
In addition to the `condition` parameter, you can use the `unless` parameter to veto the
adding of a value to the cache. Unlike `condition`, `unless` expressions are evaluated
after the method has been invoked. To expand on the previous example, perhaps we only
@@ -238,6 +242,7 @@ want to cache paperback books, as the following example does:
----
<1> Using the `unless` attribute to block hardbacks.
The cache abstraction supports `java.util.Optional` return types. If an `Optional` value
is _present_, it will be stored in the associated cache. If an `Optional` value is not
present, `null` will be stored in the associated cache. `#result` always refers to the
@@ -339,7 +344,7 @@ confirm the exclusion.
[[cache-annotations-evict]]
== The `@CacheEvict` Annotation
== The `@CacheEvict` annotation
The cache abstraction allows not just population of a cache store but also eviction.
This process is useful for removing stale or unused data from the cache. As opposed to
@@ -397,7 +402,7 @@ The following example uses two `@CacheEvict` annotations:
[[cache-annotations-config]]
== The `@CacheConfig` Annotation
== The `@CacheConfig` annotation
So far, we have seen that caching operations offer many customization options and that
you can set these options for each operation. However, some of the customization options
@@ -424,13 +429,10 @@ Placing this annotation on the class does not turn on any caching operation.
An operation-level customization always overrides a customization set on `@CacheConfig`.
Therefore, this gives three levels of customizations for each cache operation:
* Globally configured, e.g. through `CachingConfigurer`: see next section.
* Globally configured, available for `CacheManager`, `KeyGenerator`.
* At the class level, using `@CacheConfig`.
* At the operation level.
NOTE: Provider-specific settings are typically available on the `CacheManager` bean,
e.g. on `CaffeineCacheManager`. These are effectively also global.
[[cache-annotation-enable]]
== Enabling Caching Annotations
@@ -449,13 +451,6 @@ To enable caching annotations add the annotation `@EnableCaching` to one of your
@Configuration
@EnableCaching
public class AppConfig {
@Bean
CacheManager cacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCacheSpecification(...);
return cacheManager;
}
}
----
@@ -470,11 +465,7 @@ Alternatively, for XML configuration you can use the `cache:annotation-driven` e
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/cache https://www.springframework.org/schema/cache/spring-cache.xsd">
<cache:annotation-driven/>
<bean id="cacheManager" class="org.springframework.cache.caffeine.CaffeineCacheManager">
<property name="cacheSpecification" value="..."/>
</bean>
<cache:annotation-driven/>
</beans>
----
@@ -85,7 +85,7 @@ email when someone places an order:
// Call the collaborators to persist the order...
// Create a thread-safe "copy" of the template message and customize it
// Create a thread safe "copy" of the template message and customize it
SimpleMailMessage msg = new SimpleMailMessage(this.templateMessage);
msg.setTo(order.getCustomer().getEmailAddress());
msg.setText(
@@ -56,13 +56,14 @@ locally, as the following example shows:
----
The specified `WorkManager` can also point to an environment-specific thread pool --
typically through a `SimpleTaskWorkManager` instance's `asyncTaskExecutor` property.
Consider defining a shared thread pool for all your `ResourceAdapter` instances
if you happen to use multiple adapters.
typically through a `SimpleTaskWorkManager` instance's `asyncTaskExecutor` property. Consider
defining a shared thread pool for all your `ResourceAdapter` instances if you happen to
use multiple adapters.
In some environments, you can instead obtain the entire `ResourceAdapter` object from JNDI
(by using `<jee:jndi-lookup>`). The Spring-based message listeners can then interact with
the server-hosted `ResourceAdapter`, which also use the server's built-in `WorkManager`.
In some environments (such as WebLogic 9 or above), you can instead obtain the entire `ResourceAdapter` object
from JNDI (by using `<jee:jndi-lookup>`). The Spring-based message
listeners can then interact with the server-hosted `ResourceAdapter`, which also use the
server's built-in `WorkManager`.
See the javadoc for {api-spring-framework}/jms/listener/endpoint/JmsMessageEndpointManager.html[`JmsMessageEndpointManager`],
{api-spring-framework}/jms/listener/endpoint/JmsActivationSpecConfig.html[`JmsActivationSpecConfig`],
@@ -183,21 +183,18 @@ as the following example shows:
If you configure a bean with an `MBeanExporter` that is also configured for lazy
initialization, the `MBeanExporter` does not break this contract and avoids
instantiating the bean. Instead, it registers a proxy with the `MBeanServer` and defers
obtaining the bean from the container until the first invocation on the proxy occurs.
This also affects `FactoryBean` resolution where `MBeanExporter` will regularly
introspect the produced object, effectively triggering `FactoryBean.getObject()`.
In order to avoid this, mark the corresponding bean definition as lazy-init.
instantiating the bean. Instead, it registers a proxy with the `MBeanServer` and
defers obtaining the bean from the container until the first invocation on the proxy
occurs.
[[jmx-exporting-auto]]
== Automatic Registration of MBeans
Any beans that are exported through the `MBeanExporter` and are already valid MBeans
are registered as-is with the `MBeanServer` without further intervention from Spring.
You can cause MBeans to be automatically detected by the `MBeanExporter` by setting
the `autodetect` property to `true`, as the following example shows:
Any beans that are exported through the `MBeanExporter` and are already valid MBeans are
registered as-is with the `MBeanServer` without further intervention from Spring. You can cause MBeans
to be automatically detected by the `MBeanExporter` by setting the `autodetect`
property to `true`, as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
@@ -2,8 +2,8 @@
= Observability Support
Micrometer defines an https://micrometer.io/docs/observation[Observation concept that enables both Metrics and Traces] in applications.
Metrics support offers a way to create timers, gauges, or counters for collecting statistics about the runtime behavior of your application.
Metrics can help you to track error rates, usage patterns, performance, and more.
Metrics support offers a way to create timers, gauges or counters for collecting statistics about the runtime behavior of your application.
Metrics can help you to track error rates, usage patterns, performance and more.
Traces provide a holistic view of an entire system, crossing application boundaries; you can zoom in on particular user requests and follow their entire completion across applications.
Spring Framework instruments various parts of its own codebase to publish observations if an `ObservationRegistry` is configured.
@@ -36,16 +36,16 @@ https://micrometer.io/docs/concepts#_naming_meters[to the format preferred by th
[[observability.concepts]]
== Micrometer Observation concepts
If you are not familiar with Micrometer Observation, here's a quick summary of the concepts you should know about.
If you are not familiar with Micrometer Observation, here's a quick summary of the new concepts you should know about.
* `Observation` is the actual recording of something happening in your application. This is processed by `ObservationHandler` implementations to produce metrics or traces.
* Each observation has a corresponding `ObservationContext` implementation; this type holds all the relevant information for extracting metadata for it.
In the case of an HTTP server observation, the context implementation could hold the HTTP request, the HTTP response, any exception thrown during processing, and so forth.
* Each `Observation` holds `KeyValues` metadata. In the case of an HTTP server observation, this could be the HTTP request method, the HTTP response status, and so forth.
In the case of an HTTP server observation, the context implementation could hold the HTTP request, the HTTP response, any Exception thrown during processing...
* Each `Observation` holds `KeyValues` metadata. In the case of a server HTTP observation, this could be the HTTP request method, the HTTP response status...
This metadata is contributed by `ObservationConvention` implementations which should declare the type of `ObservationContext` they support.
* `KeyValues` are said to be "low cardinality" if there is a low, bounded number of possible values for the `KeyValue` tuple (HTTP method is a good example).
Low cardinality values are contributed to metrics only.
Conversely, "high cardinality" values are unbounded (for example, HTTP request URIs) and are only contributed to traces.
High cardinality values are on the other hand unbounded (for example, HTTP request URIs) and are only contributed to Traces.
* An `ObservationDocumentation` documents all observations in a particular domain, listing the expected key names and their meaning.
@@ -63,16 +63,16 @@ Each instrumented component will provide two extension points:
=== Using custom Observation conventions
Let's take the example of the Spring MVC "http.server.requests" metrics instrumentation with the `ServerHttpObservationFilter`.
This observation uses a `ServerRequestObservationConvention` with a `ServerRequestObservationContext`; custom conventions can be configured on the Servlet filter.
This observation is using a `ServerRequestObservationConvention` with a `ServerRequestObservationContext`; custom conventions can be configured on the Servlet filter.
If you would like to customize the metadata produced with the observation, you can extend the `DefaultServerRequestObservationConvention` for your requirements:
include-code::./ExtendedServerRequestObservationConvention[]
If you want full control, you can implement the entire convention contract for the observation you're interested in:
If you want full control, you can then implement the entire convention contract for the observation you're interested in:
include-code::./CustomServerRequestObservationConvention[]
You can also achieve similar goals using a custom `ObservationFilter` adding or removing key values for an observation.
You can also achieve similar goals using a custom `ObservationFilter` - adding or removing key values for an observation.
Filters do not replace the default convention and are used as a post-processing component.
include-code::./ServerRequestObservationFilter[]
@@ -83,24 +83,20 @@ You can configure `ObservationFilter` instances on the `ObservationRegistry`.
[[observability.http-server]]
== HTTP Server instrumentation
HTTP server exchange observations are created with the name `"http.server.requests"` for Servlet and Reactive applications.
HTTP server exchanges observations are created with the name `"http.server.requests"` for Servlet and Reactive applications.
[[observability.http-server.servlet]]
=== Servlet applications
Applications need to configure the `org.springframework.web.filter.ServerHttpObservationFilter` Servlet filter in their application.
It uses the `org.springframework.http.server.observation.DefaultServerRequestObservationConvention` by default, backed by the `ServerRequestObservationContext`.
It is using the `org.springframework.http.server.observation.DefaultServerRequestObservationConvention` by default, backed by the `ServerRequestObservationContext`.
This will only record an observation as an error if the `Exception` has not been handled by the web framework and has bubbled up to the Servlet filter.
This will only record an observation as an error if the `Exception` has not been handled by the web Framework and has bubbled up to the Servlet filter.
Typically, all exceptions handled by Spring MVC's `@ExceptionHandler` and xref:web/webmvc/mvc-ann-rest-exceptions.adoc[`ProblemDetail` support] will not be recorded with the observation.
You can, at any point during request processing, set the error field on the `ObservationContext` yourself:
include-code::./UserController[]
NOTE: Because the instrumentation is done at the Servlet Filter level, the observation scope only covers the filters ordered after this one as well as the handling of the request.
Typically, Servlet container error handling is performed at a lower level and won't have any active observation or span.
For this use case, a container-specific implementation is required, such as a `org.apache.catalina.Valve` for Tomcat; this is outside of the scope of this project.
By default, the following `KeyValues` are created:
.Low cardinality Keys
@@ -108,7 +104,7 @@ By default, the following `KeyValues` are created:
|===
|Name | Description
|`exception` _(required)_|Name of the exception thrown during the exchange, or `KeyValue#NONE_VALUE`} if no exception happened.
|`method` _(required)_|Name of HTTP request method or `"none"` if not a well-known method.
|`method` _(required)_|Name of HTTP request method or `"none"` if the request was not received properly.
|`outcome` _(required)_|Outcome of the HTTP server exchange.
|`status` _(required)_|HTTP response raw status code, or `"UNKNOWN"` if no response was created.
|`uri` _(required)_|URI pattern for the matching handler if available, falling back to `REDIRECTION` for 3xx responses, `NOT_FOUND` for 404 responses, `root` for requests with no path info, and `UNKNOWN` for all other requests.
@@ -126,9 +122,9 @@ By default, the following `KeyValues` are created:
=== Reactive applications
Applications need to configure the `org.springframework.web.filter.reactive.ServerHttpObservationFilter` reactive `WebFilter` in their application.
It uses the `org.springframework.http.server.reactive.observation.DefaultServerRequestObservationConvention` by default, backed by the `ServerRequestObservationContext`.
It is using the `org.springframework.http.server.reactive.observation.DefaultServerRequestObservationConvention` by default, backed by the `ServerRequestObservationContext`.
This will only record an observation as an error if the `Exception` has not been handled by the web framework and has bubbled up to the `WebFilter`.
This will only record an observation as an error if the `Exception` has not been handled by the web Framework and has bubbled up to the `WebFilter`.
Typically, all exceptions handled by Spring WebFlux's `@ExceptionHandler` and xref:web/webflux/ann-rest-exceptions.adoc[`ProblemDetail` support] will not be recorded with the observation.
You can, at any point during request processing, set the error field on the `ObservationContext` yourself:
@@ -141,7 +137,7 @@ By default, the following `KeyValues` are created:
|===
|Name | Description
|`exception` _(required)_|Name of the exception thrown during the exchange, or `"none"` if no exception happened.
|`method` _(required)_|Name of HTTP request method or `"none"` if not a well-known method.
|`method` _(required)_|Name of HTTP request method or `"none"` if the request was not received properly.
|`outcome` _(required)_|Outcome of the HTTP server exchange.
|`status` _(required)_|HTTP response raw status code, or `"UNKNOWN"` if no response was created.
|`uri` _(required)_|URI pattern for the matching handler if available, falling back to `REDIRECTION` for 3xx responses, `NOT_FOUND` for 404 responses, `root` for requests with no path info, and `UNKNOWN` for all other requests.
@@ -157,9 +153,9 @@ By default, the following `KeyValues` are created:
[[observability.http-client]]
== HTTP Client Instrumentation
== HTTP Client instrumentation
HTTP client exchange observations are created with the name `"http.client.requests"` for blocking and reactive clients.
HTTP client exchanges observations are created with the name `"http.client.requests"` for blocking and reactive clients.
Unlike their server counterparts, the instrumentation is implemented directly in the client so the only required step is to configure an `ObservationRegistry` on the client.
[[observability.http-client.resttemplate]]
@@ -168,13 +164,13 @@ Unlike their server counterparts, the instrumentation is implemented directly in
Applications must configure an `ObservationRegistry` on `RestTemplate` instances to enable the instrumentation; without that, observations are "no-ops".
Spring Boot will auto-configure `RestTemplateBuilder` beans with the observation registry already set.
Instrumentation uses the `org.springframework.http.client.observation.ClientRequestObservationConvention` by default, backed by the `ClientRequestObservationContext`.
Instrumentation is using the `org.springframework.http.client.observation.ClientRequestObservationConvention` by default, backed by the `ClientRequestObservationContext`.
.Low cardinality Keys
[cols="a,a"]
|===
|Name | Description
|`method` _(required)_|Name of HTTP request method or `"none"` if not a well-known method.
|`method` _(required)_|Name of HTTP request method or `"none"` if the request could not be created.
|`uri` _(required)_|URI template used for HTTP request, or `"none"` if none was provided. Only the path part of the URI is considered.
|`client.name` _(required)_|Client name derived from the request URI host.
|`status` _(required)_|HTTP response raw status code, or `"IO_ERROR"` in case of `IOException`, or `"CLIENT_ERROR"` if no response was received.
@@ -197,13 +193,13 @@ Instrumentation uses the `org.springframework.http.client.observation.ClientRequ
Applications must configure an `ObservationRegistry` on the `WebClient` builder to enable the instrumentation; without that, observations are "no-ops".
Spring Boot will auto-configure `WebClient.Builder` beans with the observation registry already set.
Instrumentation uses the `org.springframework.web.reactive.function.client.ClientRequestObservationConvention` by default, backed by the `ClientRequestObservationContext`.
Instrumentation is using the `org.springframework.web.reactive.function.client.ClientRequestObservationConvention` by default, backed by the `ClientRequestObservationContext`.
.Low cardinality Keys
[cols="a,a"]
|===
|Name | Description
|`method` _(required)_|Name of HTTP request method or `"none"` if not a well-known method.
|`method` _(required)_|Name of HTTP request method or `"none"` if the request could not be created.
|`uri` _(required)_|URI template used for HTTP request, or `"none"` if none was provided. Only the path part of the URI is considered.
|`client.name` _(required)_|Client name derived from the request URI host.
|`status` _(required)_|HTTP response raw status code, or `"IO_ERROR"` in case of `IOException`, or `"CLIENT_ERROR"` if no response was received.
@@ -97,7 +97,11 @@ preparation and response extraction through callback interfaces.
The default constructor uses `java.net.HttpURLConnection` to perform requests. You can
switch to a different HTTP library with an implementation of `ClientHttpRequestFactory`.
Currently, there is also built-in support for Apache HttpComponents and OkHttp.
There is built-in support for the following:
* Apache HttpComponents
* Netty
* OkHttp
For example, to switch to Apache HttpComponents, you can use the following:
@@ -5,11 +5,16 @@ The Spring Framework provides abstractions for the asynchronous execution and sc
tasks with the `TaskExecutor` and `TaskScheduler` interfaces, respectively. Spring also
features implementations of those interfaces that support thread pools or delegation to
CommonJ within an application server environment. Ultimately, the use of these
implementations behind the common interfaces abstracts away the differences between
Java SE and Jakarta EE environments.
implementations behind the common interfaces abstracts away the differences between Java
SE 5, Java SE 6, and Jakarta EE environments.
Spring also features integration classes to support scheduling with the
https://www.quartz-scheduler.org/[Quartz Scheduler].
Spring also features integration classes to support scheduling with the `Timer`
(part of the JDK since 1.3) and the https://www.quartz-scheduler.org/[Quartz Scheduler].
You can set up both of those schedulers by using a `FactoryBean` with optional references to
`Timer` or `Trigger` instances, respectively. Furthermore, a convenience class for both
the Quartz Scheduler and the `Timer` is available that lets you invoke a method of
an existing target object (analogous to the normal `MethodInvokingFactoryBean`
operation).
@@ -57,10 +62,10 @@ The variants that Spring provides are as follows:
`ConcurrentTaskExecutor` directly. However, if the `ThreadPoolTaskExecutor` is not
flexible enough for your needs, `ConcurrentTaskExecutor` is an alternative.
* `ThreadPoolTaskExecutor`:
This implementation is most commonly used. It exposes bean properties for configuring
a `java.util.concurrent.ThreadPoolExecutor` and wraps it in a `TaskExecutor`.
If you need to adapt to a different kind of `java.util.concurrent.Executor`,
we recommend that you use a `ConcurrentTaskExecutor` instead.
This implementation is most commonly used. It exposes bean properties for
configuring a `java.util.concurrent.ThreadPoolExecutor` and wraps it in a `TaskExecutor`.
If you need to adapt to a different kind of `java.util.concurrent.Executor`, we
recommend that you use a `ConcurrentTaskExecutor` instead.
* `DefaultManagedTaskExecutor`:
This implementation uses a JNDI-obtained `ManagedExecutorService` in a JSR-236
compatible runtime environment (such as a Jakarta EE application server),
@@ -70,9 +75,9 @@ The variants that Spring provides are as follows:
[[scheduling-task-executor-usage]]
=== Using a `TaskExecutor`
Spring's `TaskExecutor` implementations are commonly used with dependency injection.
In the following example, we define a bean that uses the `ThreadPoolTaskExecutor`
to asynchronously print out a set of messages:
Spring's `TaskExecutor` implementations are used as simple JavaBeans. In the following example,
we define a bean that uses the `ThreadPoolTaskExecutor` to asynchronously print
out a set of messages:
[source,java,indent=0,subs="verbatim,quotes"]
----
@@ -222,8 +227,8 @@ fixed delay, those methods should be used directly whenever possible. The value
`PeriodicTrigger` implementation is that you can use it within components that rely on
the `Trigger` abstraction. For example, it may be convenient to allow periodic triggers,
cron-based triggers, and even custom trigger implementations to be used interchangeably.
Such a component could take advantage of dependency injection so that you can configure
such `Triggers` externally and, therefore, easily modify or extend them.
Such a component could take advantage of dependency injection so that you can configure such `Triggers`
externally and, therefore, easily modify or extend them.
[[scheduling-task-scheduler-implementations]]
@@ -233,8 +238,10 @@ As with Spring's `TaskExecutor` abstraction, the primary benefit of the `TaskSch
arrangement is that an application's scheduling needs are decoupled from the deployment
environment. This abstraction level is particularly relevant when deploying to an
application server environment where threads should not be created directly by the
application itself. For such scenarios, Spring provides a `DefaultManagedTaskScheduler`
that delegates to a JSR-236 `ManagedScheduledExecutorService` in a Jakarta EE environment.
application itself. For such scenarios, Spring provides a `TimerManagerTaskScheduler`
that delegates to a CommonJ `TimerManager` on WebLogic or WebSphere as well as a more recent
`DefaultManagedTaskScheduler` that delegates to a JSR-236 `ManagedScheduledExecutorService`
in a Jakarta EE environment. Both are typically configured with a JNDI lookup.
Whenever external thread management is not a requirement, a simpler alternative is
a local `ScheduledExecutorService` setup within the application, which can be adapted
@@ -256,8 +263,8 @@ execution.
[[scheduling-enable-annotation-support]]
=== Enable Scheduling Annotations
To enable support for `@Scheduled` and `@Async` annotations, you can add `@EnableScheduling`
and `@EnableAsync` to one of your `@Configuration` classes, as the following example shows:
To enable support for `@Scheduled` and `@Async` annotations, you can add `@EnableScheduling` and
`@EnableAsync` to one of your `@Configuration` classes, as the following example shows:
[source,java,indent=0,subs="verbatim,quotes"]
----
@@ -331,7 +338,7 @@ For example, the previous example can also be written as follows.
If you need a fixed-rate execution, you can use the `fixedRate` attribute within the
annotation. The following method is invoked every five seconds (measured between the
successive start times of each invocation):
successive start times of each invocation).
[source,java,indent=0,subs="verbatim,quotes"]
----
@@ -341,9 +348,9 @@ successive start times of each invocation):
}
----
For fixed-delay and fixed-rate tasks, you can specify an initial delay by indicating
the amount of time to wait before the first execution of the method, as the following
`fixedRate` example shows:
For fixed-delay and fixed-rate tasks, you can specify an initial delay by indicating the
amount of time to wait before the first execution of the method, as the following
`fixedRate` example shows.
[source,java,indent=0,subs="verbatim,quotes"]
----
@@ -373,12 +380,6 @@ Notice that the methods to be scheduled must have void returns and must not acce
arguments. If the method needs to interact with other objects from the application
context, those would typically have been provided through dependency injection.
`@Scheduled` can be used as a repeatable annotation. If several scheduled declarations
are found on the same method, each of them will be processed independently, with a
separate trigger firing for each of them. As a consequence, such co-located schedules
may overlap and execute multiple times in parallel or in immediate succession.
Please make sure that your specified cron expressions etc do not accidentally overlap.
[NOTE]
====
As of Spring Framework 4.3, `@Scheduled` methods are supported on beans of any scope.
@@ -412,8 +413,8 @@ to a method that returns `void`, as the following example shows:
Unlike the methods annotated with the `@Scheduled` annotation, these methods can expect
arguments, because they are invoked in the "`normal`" way by callers at runtime rather
than from a scheduled task being managed by the container. For example, the following
code is a legitimate application of the `@Async` annotation:
than from a scheduled task being managed by the container. For example, the following code is
a legitimate application of the `@Async` annotation:
[source,java,indent=0,subs="verbatim,quotes"]
----
@@ -437,15 +438,15 @@ that returns a value:
}
----
TIP: `@Async` methods may not only declare a regular `java.util.concurrent.Future` return
type but also Spring's `org.springframework.util.concurrent.ListenableFuture` or, as of
Spring 4.2, JDK 8's `java.util.concurrent.CompletableFuture`, for richer interaction with
the asynchronous task and for immediate composition with further processing steps.
TIP: `@Async` methods may not only declare a regular `java.util.concurrent.Future` return type
but also Spring's `org.springframework.util.concurrent.ListenableFuture` or, as of Spring
4.2, JDK 8's `java.util.concurrent.CompletableFuture`, for richer interaction with the
asynchronous task and for immediate composition with further processing steps.
You can not use `@Async` in conjunction with lifecycle callbacks such as `@PostConstruct`.
To asynchronously initialize Spring beans, you currently have to use a separate
initializing Spring bean that then invokes the `@Async` annotated method on the target,
as the following example shows:
You can not use `@Async` in conjunction with lifecycle callbacks such as
`@PostConstruct`. To asynchronously initialize Spring beans, you currently have to use
a separate initializing Spring bean that then invokes the `@Async` annotated method on the
target, as the following example shows:
[source,java,indent=0,subs="verbatim,quotes"]
----
@@ -499,8 +500,8 @@ used when executing a given method. The following example shows how to do so:
----
In this case, `"otherExecutor"` can be the name of any `Executor` bean in the Spring
container, or it may be the name of a qualifier associated with any `Executor` (for example,
as specified with the `<qualifier>` element or Spring's `@Qualifier` annotation).
container, or it may be the name of a qualifier associated with any `Executor` (for example, as
specified with the `<qualifier>` element or Spring's `@Qualifier` annotation).
[[scheduling-annotation-support-exception]]
@@ -668,15 +669,14 @@ invoked on that object. The following listing shows a simple example:
----
The scheduler is referenced by the outer element, and each individual
task includes the configuration of its trigger metadata. In the preceding example,
that metadata defines a periodic trigger with a fixed delay indicating the number of
task includes the configuration of its trigger metadata. In the preceding example, that
metadata defines a periodic trigger with a fixed delay indicating the number of
milliseconds to wait after each task execution has completed. Another option is
`fixed-rate`, indicating how often the method should be run regardless of how long
any previous execution takes. Additionally, for both `fixed-delay` and `fixed-rate`
tasks, you can specify an 'initial-delay' parameter, indicating the number of
milliseconds to wait before the first execution of the method. For more control,
you can instead provide a `cron` attribute to provide a
xref:integration/scheduling.adoc#scheduling-cron-expression[cron expression].
any previous execution takes. Additionally, for both `fixed-delay` and `fixed-rate` tasks, you can specify an
'initial-delay' parameter, indicating the number of milliseconds to wait
before the first execution of the method. For more control, you can instead provide a `cron` attribute
to provide a xref:integration/scheduling.adoc#scheduling-cron-expression[cron expression].
The following example shows these other options:
[source,xml,indent=0]
@@ -699,8 +699,9 @@ The following example shows these other options:
All Spring cron expressions have to conform to the same format, whether you are using them in
xref:integration/scheduling.adoc#scheduling-annotation-support-scheduled[`@Scheduled` annotations],
xref:integration/scheduling.adoc#scheduling-task-namespace-scheduled-tasks[`task:scheduled-tasks` elements],
or someplace else. A well-formed cron expression, such as `* * * * * *`, consists of six
space-separated time and date fields, each with its own range of valid values:
or someplace else.
A well-formed cron expression, such as `* * * * * *`, consists of six space-separated time and date
fields, each with its own range of valid values:
....
@@ -765,10 +766,9 @@ Here are some examples:
[[macros]]
=== Macros
Expressions such as `0 0 * * * *` are hard for humans to parse and are, therefore,
hard to fix in case of bugs. To improve readability, Spring supports the following
macros, which represent commonly used sequences. You can use these macros instead
of the six-digit value, thus: `@Scheduled(cron = "@hourly")`.
Expressions such as `0 0 * * * *` are hard for humans to parse and are, therefore, hard to fix in case of bugs.
To improve readability, Spring supports the following macros, which represent commonly used sequences.
You can use these macros instead of the six-digit value, thus: `@Scheduled(cron = "@hourly")`.
|===
|Macro | Meaning
@@ -785,8 +785,8 @@ of the six-digit value, thus: `@Scheduled(cron = "@hourly")`.
[[scheduling-quartz]]
== Using the Quartz Scheduler
Quartz uses `Trigger`, `Job`, and `JobDetail` objects to realize scheduling of all
kinds of jobs. For the basic concepts behind Quartz, see the
Quartz uses `Trigger`, `Job`, and `JobDetail` objects to realize scheduling of all kinds
of jobs. For the basic concepts behind Quartz, see the
https://www.quartz-scheduler.org/[Quartz Web site]. For convenience purposes, Spring
offers a couple of classes that simplify using Quartz within Spring-based applications.
@@ -794,9 +794,9 @@ offers a couple of classes that simplify using Quartz within Spring-based applic
[[scheduling-quartz-jobdetail]]
=== Using the `JobDetailFactoryBean`
Quartz `JobDetail` objects contain all the information needed to run a job. Spring
provides a `JobDetailFactoryBean`, which provides bean-style properties for XML
configuration purposes. Consider the following example:
Quartz `JobDetail` objects contain all the information needed to run a job. Spring provides a
`JobDetailFactoryBean`, which provides bean-style properties for XML configuration purposes.
Consider the following example:
[source,xml,indent=0,subs="verbatim,quotes"]
----
@@ -813,9 +813,9 @@ configuration purposes. Consider the following example:
The job detail configuration has all the information it needs to run the job (`ExampleJob`).
The timeout is specified in the job data map. The job data map is available through the
`JobExecutionContext` (passed to you at execution time), but the `JobDetail` also gets
its properties from the job data mapped to properties of the job instance. So, in the
following example, the `ExampleJob` contains a bean property named `timeout`, and the
`JobDetail` has it applied automatically:
its properties from the job data mapped to properties of the job instance. So, in the following example,
the `ExampleJob` contains a bean property named `timeout`, and the `JobDetail`
has it applied automatically:
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
----
@@ -908,8 +908,8 @@ NOTE: By default, jobs will run in a concurrent fashion.
[[scheduling-quartz-cron]]
=== Wiring up Jobs by Using Triggers and `SchedulerFactoryBean`
We have created job details and jobs. We have also reviewed the convenience bean that
lets you invoke a method on a specific object. Of course, we still need to schedule the
We have created job details and jobs. We have also reviewed the convenience bean that lets
you invoke a method on a specific object. Of course, we still need to schedule the
jobs themselves. This is done by using triggers and a `SchedulerFactoryBean`. Several
triggers are available within Quartz, and Spring offers two Quartz `FactoryBean`
implementations with convenient defaults: `CronTriggerFactoryBean` and
@@ -940,9 +940,9 @@ The following listing uses both a `SimpleTriggerFactoryBean` and a `CronTriggerF
</bean>
----
The preceding example sets up two triggers, one running every 50 seconds with a starting
delay of 10 seconds and one running every morning at 6 AM. To finalize everything,
we need to set up the `SchedulerFactoryBean`, as the following example shows:
The preceding example sets up two triggers, one running every 50 seconds with a starting delay of 10
seconds and one running every morning at 6 AM. To finalize everything, we need to set up the
`SchedulerFactoryBean`, as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
@@ -236,42 +236,6 @@ be matched, not only the `GET` method.
[[declaration-site-variance]]
== Declaration-site variance
Dealing with generic types in Spring applications written in Kotlin may require, for some use cases, to understand
Kotlin https://kotlinlang.org/docs/generics.html#declaration-site-variance[declaration-site variance]
which allows to define the variance when declaring a type, which is not possible in Java which supports only use-site
variance.
For example, declaring `List<Foo>` in Kotlin is conceptually equivalent to `java.util.List<? extends Foo>` because
`kotlin.collections.List` is declared as
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/[`interface List<out E> : kotlin.collections.Collection<E>`].
This needs to be taken in account by using the `out` Kotlin keyword on generic types when using Java classes,
for example when writing a `org.springframework.core.convert.converter.Converter` from a Kotlin type to a Java type.
[source,kotlin,indent=0]
----
class ListOfFooConverter : Converter<List<Foo>, CustomJavaList<out Foo>> {
// ...
}
----
When converting any kind of objects, star projection with `*` can be used instead of `out Any`.
[source,kotlin,indent=0]
----
class ListOfAnyConverter : Converter<List<*>, CustomJavaList<*>> {
// ...
}
----
NOTE: Spring Framework does not leverage yet declaration-site variance type information for injecting beans,
subscribe to https://github.com/spring-projects/spring-framework/issues/22313[spring-framework#22313] to track related
progresses.
[[testing]]
== Testing
@@ -1023,7 +1023,7 @@ Two, create a proxy that will perform the declared RSocket exchanges:
RSocketRequester requester = ... ;
RSocketServiceProxyFactory factory = RSocketServiceProxyFactory.builder(requester).build();
RadarService service = factory.createClient(RadarService.class);
RepositoryService service = factory.createClient(RadarService.class);
----
@@ -3,4 +3,13 @@
:page-section-summary-toc: 1
This section covers annotations that you can use when you test Spring applications.
It includes the following topics:
* xref:testing/annotations/integration-standard.adoc[Standard Annotation Support]
* xref:testing/annotations/integration-spring.adoc[Spring Testing Annotations]
* xref:testing/annotations/integration-junit4.adoc[Spring JUnit 4 Testing Annotations]
* xref:testing/annotations/integration-junit-jupiter.adoc[Spring JUnit Jupiter Testing Annotations]
* xref:testing/annotations/integration-meta.adoc[Meta-Annotation Support for Testing]
@@ -10,7 +10,7 @@ Resource locations are typically XML configuration files or Groovy scripts locat
classpath, while component classes are typically `@Configuration` classes. However,
resource locations can also refer to files and scripts in the file system, and component
classes can be `@Component` classes, `@Service` classes, and so on. See
xref:testing/testcontext-framework/ctx-management/javaconfig.adoc#testcontext-ctx-management-javaconfig-component-classes[Component Classes] for further details.
xref:testing/testcontext-framework/ctx-management/javaconfig.adoc#testcontext-ctx-management-javaconfig-component-classes[null] for further details.
The following example shows a `@ContextConfiguration` annotation that refers to an XML
file:
@@ -152,7 +152,7 @@ Kotlin::
@Autowired
lateinit var accountService: AccountService
lateinit var mockMvc: MockMvc
lateinit mockMvc: MockMvc
@BeforeEach
fun setup(wac: WebApplicationContext) {
@@ -1,7 +1,7 @@
[[spring-mvc-test-vs-end-to-end-integration-tests]]
= MockMvc vs End-to-End Tests
MockMvc is built on Servlet API mock implementations from the
MockMVc is built on Servlet API mock implementations from the
`spring-test` module and does not rely on a running container. Therefore, there are
some differences when compared to full end-to-end integration tests with an actual
client and a live server running.
@@ -1,15 +1,38 @@
[[spring-mvc-test-vs-streaming-response]]
= Streaming Responses
You can use `WebTestClient` to test xref:testing/webtestclient.adoc#webtestclient-stream[streaming responses]
such as Server-Sent Events. However, `MockMvcWebTestClient` doesn't support infinite
streams because there is no way to cancel the server stream from the client side.
To test infinite streams, you'll need to
xref:testing/webtestclient.adoc#webtestclient-server-config[bind to] a running server,
or when using Spring Boot,
{docs-spring-boot}/spring-boot-features.html#boot-features-testing-spring-boot-applications-testing-with-running-server[test with a running server].
The best way to test streaming responses such as Server-Sent Events is through the
<<WebTestClient>> which can be used as a test client to connect to a `MockMvc` instance
to perform tests on Spring MVC controllers without a running server. For example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
WebTestClient client = MockMvcWebTestClient.bindToController(new SseController()).build();
FluxExchangeResult<Person> exchangeResult = client.get()
.uri("/persons")
.exchange()
.expectStatus().isOk()
.expectHeader().contentType("text/event-stream")
.returnResult(Person.class);
// Use StepVerifier from Project Reactor to test the streaming response
StepVerifier.create(exchangeResult.getResponseBody())
.expectNext(new Person("N0"), new Person("N1"), new Person("N2"))
.expectNextCount(4)
.consumeNextWith(person -> assertThat(person.getName()).endsWith("7"))
.thenCancel()
.verify();
----
======
`WebTestClient` can also connect to a live server and perform full end-to-end integration
tests. This is also supported in Spring Boot where you can
{docs-spring-boot}/html/spring-boot-features.html#boot-features-testing-spring-boot-applications-testing-with-running-server[test a running server].
`MockMvcWebTestClient` does support asynchronous responses, and even streaming responses.
The limitation is that it can't influence the server to stop, and therefore the server
must finish writing the response on its own.
@@ -14,7 +14,7 @@ following features.
testing annotations -- as long as the tests are run using a JUnit Platform
`TestEngine` that is registered for the current project.
* Build-time AOT processing: each unique test `ApplicationContext` in the current project
will be xref:core/aot.adoc#aot.refresh[refreshed for AOT processing].
will be xref:core/aot.adoc#refresh[refreshed for AOT processing].
* Runtime AOT support: when executing in AOT runtime mode, a Spring integration test will
use an AOT-optimized `ApplicationContext` that participates transparently with the
xref:testing/testcontext-framework/ctx-management/caching.adoc[context cache].
@@ -35,7 +35,7 @@ the following options.
via {api-spring-framework}/context/annotation/ImportRuntimeHints.html[`@ImportRuntimeHints`].
* Annotate a test class with {api-spring-framework}/aot/hint/annotation/Reflective.html[`@Reflective`] or
{api-spring-framework}/aot/hint/annotation/RegisterReflectionForBinding.html[`@RegisterReflectionForBinding`].
* See xref:core/aot.adoc#aot.hints[Runtime Hints] for details on Spring's core runtime hints
* See xref:core/aot.adoc#hints[Runtime Hints] for details on Spring's core runtime hints
and annotation support.
[TIP]
@@ -35,8 +35,8 @@ in which the test class is defined. A path starting with a slash is treated as a
absolute classpath resource (for example: `"/org/example/test.xml"`). A path that
references a URL (for example, a path prefixed with `classpath:`, `file:`, or `http:`) is
loaded by using the specified resource protocol. Resource location wildcards (such as
`{asterisk}{asterisk}/{asterisk}.properties`) are not permitted: Each location must
evaluate to exactly one `.properties` or `.xml` resource.
`**/*.properties`) are not permitted: Each location must evaluate to exactly one
`.properties` or `.xml` resource.
The following example uses a test properties file:
@@ -75,35 +75,6 @@ To learn more from the source or to make advanced customizations, see:
[[webflux-cors-credentialed-requests]]
== Credentialed Requests
[.small]#xref:web/webmvc-cors.adoc#mvc-cors-credentialed-requests[See equivalent in the Servlet stack]#
Using CORS with credentialed requests requires enabling `allowedCredentials`. Be aware that
this option establishes a high level of trust with the configured domains and also increases
the surface of attack of the web application by exposing sensitive user-specific information
such as cookies and CSRF tokens.
Enabling credentials also impacts how the configured `"*"` CORS wildcards are processed:
* Wildcards are not authorized in `allowOrigins`, but alternatively
the `allowOriginPatterns` property may be used to match to a dynamic set of origins.
* When set on `allowedHeaders` or `allowedMethods`, the `Access-Control-Allow-Headers`
and `Access-Control-Allow-Methods` response headers are handled by copying the related
headers and method specified in the CORS preflight request.
* When set on `exposedHeaders`, `Access-Control-Expose-Headers` response header is set
either to the configured list of headers or to the wildcard character. While the CORS spec
does not allow the wildcard character when `Access-Control-Allow-Credentials` is set to
`true`, most browsers support it and the response headers are not all available during the
CORS processing, so as a consequence the wildcard character is the header value used when
specified regardless of the value of the `allowCredentials` property.
WARNING: While such wildcard configuration can be handy, it is recommended when possible to configure
a finite set of values instead to provide a higher level of security.
[[webflux-cors-controller]]
== `@CrossOrigin`
[.small]#xref:web/webmvc-cors.adoc#mvc-cors-controller[See equivalent in the Servlet stack]#
@@ -166,7 +166,4 @@ By default, any argument that is not a simple value type (as determined by
and is not resolved by any other argument resolver is treated as if it were annotated
with `@ModelAttribute`.
WARNING: When compiling to a native image with GraalVM, the implicit `@ModelAttribute`
support described above does not allow proper ahead-of-time inference of related data
binding reflection hints. As a consequence, it is recommended to explicitly annotate
method parameters with `@ModelAttribute` for use in a GraalVM native image.
@@ -189,9 +189,6 @@ lets applications use the Servlet API directly if they need to. Spring WebFlux
relies on Servlet non-blocking I/O and uses the Servlet API behind a low-level
adapter. It is not exposed for direct use.
NOTE: It is strongly advised not to map Servlet filters or directly manipulate the Servlet API in the context of a WebFlux application.
For the reasons listed above, mixing blocking I/O and non-blocking I/O in the same context will cause runtime issues.
For Undertow, Spring WebFlux uses Undertow APIs directly without the Servlet API.
@@ -200,9 +197,9 @@ For Undertow, Spring WebFlux uses Undertow APIs directly without the Servlet API
== Performance
Performance has many characteristics and meanings. Reactive and non-blocking generally
do not make applications run faster. They can in some cases for example, if using the
`WebClient` to run remote calls in parallel. However, it requires more work to do
things the non-blocking way, and that can slightly increase the required processing time.
do not make applications run faster. They can, in some cases, (for example, if using the
`WebClient` to run remote calls in parallel). On the whole, it requires more work to do
things the non-blocking way and that can slightly increase the required processing time.
The key expected benefit of reactive and non-blocking is the ability to scale with a small,
fixed number of threads and less memory. That makes applications more resilient under load,
@@ -224,10 +221,10 @@ block the current thread, (for example, for remote calls). For this reason, serv
use a large thread pool to absorb potential blocking during request handling.
In Spring WebFlux (and non-blocking servers in general), it is assumed that applications
do not block. Therefore, non-blocking servers use a small, fixed-size thread pool
do not block. Therefore, non-blocking servers use a small, fixed-size thread pool
(event loop workers) to handle requests.
TIP: "`To scale`" and "`small number of threads`" may sound contradictory, but to never block the
TIP: "`To scale`" and "`small number of threads`" may sound contradictory but to never block the
current thread (and rely on callbacks instead) means that you do not need extra threads, as
there are no blocking calls to absorb.
@@ -253,7 +250,7 @@ application code within that pipeline is never invoked concurrently.
What threads should you expect to see on a server running with Spring WebFlux?
* On a "`vanilla`" Spring WebFlux server (for example, no data access or other optional
* On a "`vanilla`" Spring WebFlux server (for example, no data access nor other optional
dependencies), you can expect one thread for the server and several others for request
processing (typically as many as the number of CPU cores). Servlet containers, however,
may start with more threads (for example, 10 on Tomcat), in support of both servlet (blocking) I/O
@@ -25,35 +25,6 @@ powerful workarounds based on IFRAME or JSONP.
[[mvc-cors-credentialed-requests]]
== Credentialed Requests
[.small]#xref:web/webflux-cors.adoc#webflux-cors-credentialed-requests[See equivalent in the Reactive stack]#
Using CORS with credentialed requests requires enabling `allowedCredentials`. Be aware that
this option establishes a high level of trust with the configured domains and also increases
the surface of attack of the web application by exposing sensitive user-specific information
such as cookies and CSRF tokens.
Enabling credentials also impacts how the configured `"*"` CORS wildcards are processed:
* Wildcards are not authorized in `allowOrigins`, but alternatively
the `allowOriginPatterns` property may be used to match to a dynamic set of origins.
* When set on `allowedHeaders` or `allowedMethods`, the `Access-Control-Allow-Headers`
and `Access-Control-Allow-Methods` response headers are handled by copying the related
headers and method specified in the CORS preflight request.
* When set on `exposedHeaders`, `Access-Control-Expose-Headers` response header is set
either to the configured list of headers or to the wildcard character. While the CORS spec
does not allow the wildcard character when `Access-Control-Allow-Credentials` is set to
`true`, most browsers support it and the response headers are not all available during the
CORS processing, so as a consequence the wildcard character is the header value used when
specified regardless of the value of the `allowCredentials` property.
WARNING: While such wildcard configuration can be handy, it is recommended when possible to configure
a finite set of values instead to provide a higher level of security.
[[mvc-cors-processing]]
== Processing
[.small]#xref:web/webflux-cors.adoc#webflux-cors-processing[See equivalent in the Reactive stack]#
@@ -69,11 +69,9 @@ written to the response and computing an MD5 hash from it. The next time a clien
it does the same, but it also compares the computed value against the `If-None-Match`
request header and, if the two are equal, returns a 304 (NOT_MODIFIED).
This strategy saves network bandwidth but not CPU, as the full response must be computed for each request.
State-changing HTTP methods and other HTTP conditional request headers such as `If-Match` and
`If-Unmodified-Since` are outside the scope of this filter. Other strategies at the controller level
can avoid the computation and have a broader support for HTTP conditional requests.
See xref:web/webmvc/mvc-caching.adoc[HTTP Caching].
This strategy saves network bandwidth but not CPU, as the full response must be computed
for each request. Other strategies at the controller level, described earlier, can avoid
the computation. See xref:web/webmvc/mvc-caching.adoc[HTTP Caching].
This filter has a `writeWeakETag` parameter that configures the filter to write weak ETags
similar to the following: `W/"02a2d595e6ed9a0b24f027f2b63b134d6"` (as defined in
@@ -52,15 +52,15 @@ The following example shows how to achieve the same configuration in XML:
</mvc:interceptors>
----
WARNING: Interceptors are not ideally suited as a security layer due to the potential for
a mismatch with annotated controller path matching. Generally, we recommend using Spring
Security, or alternatively a similar approach integrated with the Servlet filter chain,
and applied as early as possible.
NOTE: Mapped interceptors are not ideally suited as a security layer due to the potential
for a mismatch with annotated controller path matching, which can also match trailing
slashes and path extensions transparently, along with other path matching options. Many
of these options have been deprecated but the potential for a mismatch remains.
Generally, we recommend using Spring Security which includes a dedicated
https://docs.spring.io/spring-security/reference/servlet/integrations/mvc.html#mvc-requestmatcher[MvcRequestMatcher]
to align with Spring MVC path matching and also has a security firewall that blocks many
unwanted characters in URL paths.
NOTE: The XML config declares interceptors as `MappedInterceptor` beans, and those are in
turn detected by any `HandlerMapping` bean, including those from other frameworks.
By contrast, the Java config passes interceptors only to the `HandlerMapping` beans it manages.
To re-use the same interceptors across Spring MVC and other framework `HandlerMapping`
beans with the MVC Java config, either declare `MappedInterceptor` beans (and don't
manually add them in the Java config), or configure the same interceptors in both
the Java config and in other `HandlerMapping` beans.
@@ -216,7 +216,4 @@ By default, any argument that is not a simple value type (as determined by
and is not resolved by any other argument resolver is treated as if it were annotated
with `@ModelAttribute`.
WARNING: When compiling to a native image with GraalVM, the implicit `@ModelAttribute`
support described above does not allow proper ahead-of-time inference of related data
binding reflection hints. As a consequence, it is recommended to explicitly annotate
method parameters with `@ModelAttribute` for use in a GraalVM native image.
@@ -220,7 +220,7 @@ one of the following depending on whether use of parsed `PathPattern` is enabled
* {api-spring-framework}/web/util/pattern/PathPattern.html#SPECIFICITY_COMPARATOR[`PathPattern.SPECIFICITY_COMPARATOR`]
* {api-spring-framework}/util/AntPathMatcher.html#getPatternComparator-java.lang.String-[`AntPathMatcher.getPatternComparator(String path)`]
Both help to sort patterns with more specific ones on top. A pattern is more specific if
Both help to sort patterns with more specific ones on top. A pattern is less specific if
it has a lower count of URI variables (counted as 1), single wildcards (counted as 1),
and double wildcards (counted as 2). Given an equal score, the longer pattern is chosen.
Given the same score and length, the pattern with more URI variables than wildcards is
@@ -1,29 +1,34 @@
[[mvc-handlermapping-interceptor]]
= Interception
All `HandlerMapping` implementations support handler interception which is useful when
you want to apply functionality across requests. A `HandlerInterceptor` can implement the
following:
All `HandlerMapping` implementations support handler interceptors that are useful when
you want to apply specific functionality to certain requests -- for example, checking for
a principal. Interceptors must implement `HandlerInterceptor` from the
`org.springframework.web.servlet` package with three methods that should provide enough
flexibility to do all kinds of pre-processing and post-processing:
* `preHandle(..)` -- callback before the actual handler is run that returns a boolean.
If the method returns `true`, execution continues; if it returns `false`, the rest of the
execution chain is bypassed and the handler is not called.
* `postHandle(..)` -- callback after the handler is run.
* `afterCompletion(..)` -- callback after the complete request has finished.
* `preHandle(..)`: Before the actual handler is run
* `postHandle(..)`: After the handler is run
* `afterCompletion(..)`: After the complete request has finished
NOTE: For `@ResponseBody` and `ResponseEntity` controller methods, the response is written
and committed within the `HandlerAdapter`, before `postHandle` is called. That means it is
too late to change the response, such as to add an extra header. You can implement
`ResponseBodyAdvice` and declare it as an
xref:web/webmvc/mvc-controller/ann-advice.adoc[Controller Advice] bean or configure it
directly on `RequestMappingHandlerAdapter`.
The `preHandle(..)` method returns a boolean value. You can use this method to break or
continue the processing of the execution chain. When this method returns `true`, the
handler execution chain continues. When it returns false, the `DispatcherServlet`
assumes the interceptor itself has taken care of requests (and, for example, rendered an
appropriate view) and does not continue executing the other interceptors and the actual
handler in the execution chain.
See xref:web/webmvc/mvc-config/interceptors.adoc[Interceptors] in the section on MVC configuration for examples of how to
configure interceptors. You can also register them directly by using setters on individual
`HandlerMapping` implementations.
WARNING: Interceptors are not ideally suited as a security layer due to the potential for
a mismatch with annotated controller path matching. Generally, we recommend using Spring
Security, or alternatively a similar approach integrated with the Servlet filter chain,
and applied as early as possible.
`postHandle` method is less useful with `@ResponseBody` and `ResponseEntity` methods for
which the response is written and committed within the `HandlerAdapter` and before
`postHandle`. That means it is too late to make any changes to the response, such as adding
an extra header. For such scenarios, you can implement `ResponseBodyAdvice` and either
declare it as an xref:web/webmvc/mvc-controller/ann-advice.adoc[Controller Advice] bean or configure it directly on
`RequestMappingHandlerAdapter`.
@@ -229,27 +229,34 @@ Java initialization API. The following example shows how to do so:
[[websocket-server-runtime-configuration]]
== Configuring the Server
== Server Configuration
[.small]#xref:web/webflux-websocket.adoc#webflux-websocket-server-config[See equivalent in the Reactive stack]#
You can configure of the underlying WebSocket server such as input message buffer size,
idle timeout, and more.
Each underlying WebSocket engine exposes configuration properties that control
runtime characteristics, such as the size of message buffer sizes, idle timeout,
and others.
For Jakarta WebSocket servers, you can add a `ServletServerContainerFactoryBean` to your
Java configuration. For example:
For Tomcat, WildFly, and GlassFish, you can add a `ServletServerContainerFactoryBean` to your
WebSocket Java config, as the following example shows:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Bean
public ServletServerContainerFactoryBean createWebSocketContainer() {
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
container.setMaxTextMessageBufferSize(8192);
container.setMaxBinaryMessageBufferSize(8192);
return container;
}
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Bean
public ServletServerContainerFactoryBean createWebSocketContainer() {
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
container.setMaxTextMessageBufferSize(8192);
container.setMaxBinaryMessageBufferSize(8192);
return container;
}
}
----
Or to your XML configuration:
The following example shows the XML configuration equivalent of the preceding example:
[source,xml,indent=0,subs="verbatim,quotes,attributes"]
----
@@ -270,11 +277,12 @@ Or to your XML configuration:
</beans>
----
NOTE: For client Jakarta WebSocket configuration, use
ContainerProvider.getWebSocketContainer() in Java configuration, or
`WebSocketContainerFactoryBean` in XML.
NOTE: For client-side WebSocket configuration, you should use `WebSocketContainerFactoryBean`
(XML) or `ContainerProvider.getWebSocketContainer()` (Java configuration).
For Jetty, you can supply a `Consumer` callback to configure the WebSocket server:
For Jetty, you need to supply a pre-configured Jetty `WebSocketServerFactory` and plug
that into Spring's `DefaultHandshakeHandler` through your WebSocket Java config.
The following example shows how to do so:
[source,java,indent=0,subs="verbatim,quotes"]
----
@@ -290,9 +298,11 @@ For Jetty, you can supply a `Consumer` callback to configure the WebSocket serve
@Bean
public DefaultHandshakeHandler handshakeHandler() {
WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);
policy.setInputBufferSize(8192);
policy.setIdleTimeout(600000);
return new DefaultHandshakeHandler(
new JettyRequestUpgradeStrategy(new WebSocketServerFactory(policy)));
}
@@ -339,10 +349,6 @@ The following example shows the XML configuration equivalent of the preceding ex
</beans>
----
TIP: When using STOMP over WebSocket, you will also need to configure
xref:web/websocket/stomp/server-config.adoc[STOMP WebSocket transport]
properties.
[[websocket-server-allowed-origins]]
@@ -103,9 +103,9 @@ You can also use the WebSocket transport configuration shown earlier to configur
maximum allowed size for incoming STOMP messages. In theory, a WebSocket
message can be almost unlimited in size. In practice, WebSocket servers impose
limits -- for example, 8K on Tomcat and 64K on Jetty. For this reason, STOMP clients
such as https://github.com/stomp-js/stompjs[`stomp-js/stompjs`] and others split larger
STOMP messages at 16K boundaries and send them as multiple WebSocket messages,
which requires the server to buffer and re-assemble.
(such as the JavaScript https://github.com/JSteunou/webstomp-client[webstomp-client]
and others) split larger STOMP messages at 16K boundaries and send them as multiple
WebSocket messages, which requires the server to buffer and re-assemble.
Spring's STOMP-over-WebSocket support does this ,so applications can configure the
maximum size for STOMP messages irrespective of WebSocket server-specific message
@@ -3,7 +3,7 @@
STOMP over WebSocket support is available in the `spring-messaging` and
`spring-websocket` modules. Once you have those dependencies, you can expose a STOMP
endpoint over WebSocket, as the following example shows:
endpoint over WebSocket with xref:web/websocket/fallback.adoc[SockJS Fallback], as the following example shows:
[source,java,indent=0,subs="verbatim,quotes"]
----
@@ -16,7 +16,7 @@ endpoint over WebSocket, as the following example shows:
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/portfolio"); // <1>
registry.addEndpoint("/portfolio").withSockJS(); // <1>
}
@Override
@@ -49,7 +49,9 @@ The following example shows the XML configuration equivalent of the preceding ex
https://www.springframework.org/schema/websocket/spring-websocket.xsd">
<websocket:message-broker application-destination-prefix="/app">
<websocket:stomp-endpoint path="/portfolio" />
<websocket:stomp-endpoint path="/portfolio">
<websocket:sockjs/>
</websocket:stomp-endpoint>
<websocket:simple-broker prefix="/topic, /queue"/>
</websocket:message-broker>
@@ -62,27 +64,34 @@ messaging (that is, many subscribers versus one consumer). When you use an exter
check the STOMP page of the broker to understand what kind of STOMP destinations and
prefixes it supports.
To connect from a browser, for STOMP, you can use
https://github.com/stomp-js/stompjs[`stomp-js/stompjs`] which is the most
actively maintained JavaScript library.
The following example code is based on it:
To connect from a browser, for SockJS, you can use the
https://github.com/sockjs/sockjs-client[`sockjs-client`]. For STOMP, many applications have
used the https://github.com/jmesnil/stomp-websocket[jmesnil/stomp-websocket] library
(also known as stomp.js), which is feature-complete and has been used in production for
years but is no longer maintained. At present the
https://github.com/JSteunou/webstomp-client[JSteunou/webstomp-client] is the most
actively maintained and evolving successor of that library. The following example code
is based on it:
[source,javascript,indent=0,subs="verbatim,quotes"]
----
const stompClient = new StompJs.Client({
brokerURL: 'ws://domain.com/portfolio',
onConnect: () => {
// ...
}
});
var socket = new SockJS("/spring-websocket-portfolio/portfolio");
var stompClient = webstomp.over(socket);
stompClient.connect({}, function(frame) {
}
----
Alternatively, if you connect through SockJS, you can enable the
xref:web/websocket/fallback.adoc[SockJS Fallback] on server-side with
`registry.addEndpoint("/portfolio").withSockJS()` and on JavaScript side,
by following
https://stomp-js.github.io/guide/stompjs/rx-stomp/using-stomp-with-sockjs.html[those instructions].
Alternatively, if you connect through WebSocket (without SockJS), you can use the following code:
[source,javascript,indent=0,subs="verbatim,quotes"]
----
var socket = new WebSocket("/spring-websocket-portfolio/portfolio");
var stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
}
----
Note that `stompClient` in the preceding example does not need to specify `login`
and `passcode` headers. Even if it did, they would be ignored (or, rather,
@@ -1,14 +1,9 @@
[[websocket-stomp-server-config]]
= WebSocket Transport
= WebSocket Server
This section explains how to configure the underlying WebSocket server transport.
For Jakarta WebSocket servers, add a `ServletServerContainerFactoryBean` to your
configuration. For examples, see
xref:web/websocket/server.adoc#websocket-server-runtime-configuration[Configuring the Server]
under the WebSocket section.
For Jetty WebSocket servers, customize the `JettyRequestUpgradeStrategy` as follows:
To configure the underlying WebSocket server, the information in
xref:web/websocket/server.adoc#websocket-server-runtime-configuration[Server Configuration] applies. For Jetty, however you need to set
the `HandshakeHandler` and `WebSocketPolicy` through the `StompEndpointRegistry`:
[source,java,indent=0,subs="verbatim,quotes"]
----
@@ -34,20 +29,5 @@ For Jetty WebSocket servers, customize the `JettyRequestUpgradeStrategy` as foll
}
----
In addition to WebSocket server properties, there are also STOMP WebSocket transport properties
to customize as follows:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureWebSocketTransport(WebSocketTransportRegistration registry) {
registry.setMessageSizeLimit(4 * 8192);
registry.setTimeToFirstMessage(30000);
}
}
----
-10
View File
@@ -1,10 +0,0 @@
{
"dependencies": {
"antora": "3.2.0-alpha.4",
"@antora/atlas-extension": "1.0.0-alpha.2",
"@antora/collector-extension": "1.0.0-alpha.3",
"@asciidoctor/tabs": "1.0.0-beta.6",
"@springio/antora-extensions": "1.11.1",
"@springio/asciidoctor-extensions": "1.0.0-alpha.10"
}
}
+41 -39
View File
@@ -8,30 +8,30 @@ javaPlatform {
dependencies {
api(platform("com.fasterxml.jackson:jackson-bom:2.14.3"))
api(platform("io.micrometer:micrometer-bom:1.10.13"))
api(platform("io.netty:netty-bom:4.1.111.Final"))
api(platform("io.micrometer:micrometer-bom:1.10.8"))
api(platform("io.netty:netty-bom:4.1.93.Final"))
api(platform("io.netty:netty5-bom:5.0.0.Alpha5"))
api(platform("io.projectreactor:reactor-bom:2022.0.20"))
api(platform("io.projectreactor:reactor-bom:2022.0.8"))
api(platform("io.rsocket:rsocket-bom:1.1.3"))
api(platform("org.apache.groovy:groovy-bom:4.0.21"))
api(platform("org.apache.logging.log4j:log4j-bom:2.21.1"))
api(platform("org.eclipse.jetty:jetty-bom:11.0.20"))
api(platform("org.apache.groovy:groovy-bom:4.0.12"))
api(platform("org.apache.logging.log4j:log4j-bom:2.20.0"))
api(platform("org.eclipse.jetty:jetty-bom:11.0.15"))
api(platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.6.4"))
api(platform("org.jetbrains.kotlinx:kotlinx-serialization-bom:1.4.0"))
api(platform("org.junit:junit-bom:5.9.3"))
api(platform("org.mockito:mockito-bom:5.12.0"))
api(platform("org.mockito:mockito-bom:5.3.1"))
constraints {
api("com.fasterxml:aalto-xml:1.3.2")
api("com.fasterxml.woodstox:woodstox-core:6.5.1")
api("com.github.ben-manes.caffeine:caffeine:3.1.8")
api("com.github.librepdf:openpdf:1.3.43")
api("com.github.ben-manes.caffeine:caffeine:3.1.6")
api("com.github.librepdf:openpdf:1.3.30")
api("com.google.code.findbugs:findbugs:3.0.1")
api("com.google.code.findbugs:jsr305:3.0.2")
api("com.google.code.gson:gson:2.10.1")
api("com.google.protobuf:protobuf-java-util:3.23.2")
api("com.googlecode.protobuf-java-format:protobuf-java-format:1.4")
api("com.h2database:h2:2.2.224")
api("com.h2database:h2:2.1.214")
api("com.jayway.jsonpath:json-path:2.8.0")
api("com.rometools:rome:1.19.0")
api("com.squareup.okhttp3:mockwebserver:3.14.9")
@@ -43,20 +43,21 @@ dependencies {
api("com.sun.xml.bind:jaxb-xjc:3.0.2")
api("com.thoughtworks.qdox:qdox:2.0.3")
api("com.thoughtworks.xstream:xstream:1.4.20")
api("commons-io:commons-io:2.15.0")
api("commons-io:commons-io:2.11.0")
api("de.bechte.junit:junit-hierarchicalcontextrunner:4.12.2")
api("io.micrometer:context-propagation:1.0.6")
api("info.picocli:picocli:4.7.4")
api("io.micrometer:context-propagation:1.0.3")
api("io.mockk:mockk:1.13.4")
api("io.projectreactor.netty:reactor-netty5-http:2.0.0-M3")
api("io.projectreactor.tools:blockhound:1.0.8.RELEASE")
api("io.r2dbc:r2dbc-h2:1.0.0.RELEASE")
api("io.r2dbc:r2dbc-spi-test:1.0.0.RELEASE")
api("io.r2dbc:r2dbc-spi:1.0.0.RELEASE")
api("io.reactivex.rxjava3:rxjava:3.1.8")
api("io.smallrye.reactive:mutiny:1.10.0")
api("io.undertow:undertow-core:2.3.13.Final")
api("io.undertow:undertow-servlet:2.3.13.Final")
api("io.undertow:undertow-websockets-jsr:2.3.13.Final")
api("io.reactivex.rxjava3:rxjava:3.1.6")
api("io.smallrye.reactive:mutiny:1.9.0")
api("io.undertow:undertow-core:2.3.7.Final")
api("io.undertow:undertow-servlet:2.3.7.Final")
api("io.undertow:undertow-websockets-jsr:2.3.7.Final")
api("io.vavr:vavr:0.10.4")
api("jakarta.activation:jakarta.activation-api:2.0.1")
api("jakarta.annotation:jakarta.annotation-api:2.0.0")
@@ -81,7 +82,6 @@ dependencies {
api("jakarta.websocket:jakarta.websocket-api:2.1.0")
api("jakarta.websocket:jakarta.websocket-client-api:2.1.0")
api("jakarta.xml.bind:jakarta.xml.bind-api:3.0.1")
api("javax.annotation:javax.annotation-api:1.3.2")
api("javax.cache:cache-api:1.1.1")
api("javax.money:money-api:1.1")
api("jaxen:jaxen:1.2.0")
@@ -89,44 +89,46 @@ dependencies {
api("net.sf.jopt-simple:jopt-simple:5.0.4")
api("net.sourceforge.htmlunit:htmlunit:2.70.0")
api("org.apache-extras.beanshell:bsh:2.0b6")
api("org.apache.activemq:activemq-broker:5.17.6")
api("org.apache.activemq:activemq-kahadb-store:5.17.6")
api("org.apache.activemq:activemq-stomp:5.17.6")
api("org.apache.activemq:activemq-broker:5.17.4")
api("org.apache.activemq:activemq-kahadb-store:5.17.4")
api("org.apache.activemq:activemq-stomp:5.17.4")
api("org.apache.commons:commons-pool2:2.9.0")
api("org.apache.derby:derby:10.16.1.1")
api("org.apache.derby:derbyclient:10.16.1.1")
api("org.apache.derby:derbytools:10.16.1.1")
api("org.apache.httpcomponents.client5:httpclient5:5.2.3")
api("org.apache.httpcomponents.core5:httpcore5-reactive:5.2.4")
api("org.apache.poi:poi-ooxml:5.2.5")
api("org.apache.tomcat.embed:tomcat-embed-core:10.1.15")
api("org.apache.tomcat.embed:tomcat-embed-websocket:10.1.15")
api("org.apache.tomcat:tomcat-util:10.1.15")
api("org.apache.tomcat:tomcat-websocket:10.1.15")
api("org.aspectj:aspectjrt:1.9.22.1")
api("org.aspectj:aspectjtools:1.9.22.1")
api("org.aspectj:aspectjweaver:1.9.22.1")
api("org.apache.httpcomponents.client5:httpclient5:5.2.1")
api("org.apache.httpcomponents.core5:httpcore5-reactive:5.2.1")
api("org.apache.poi:poi-ooxml:5.2.3")
api("org.apache.tomcat.embed:tomcat-embed-core:10.1.9")
api("org.apache.tomcat.embed:tomcat-embed-websocket:10.1.9")
api("org.apache.tomcat:tomcat-util:10.1.8")
api("org.apache.tomcat:tomcat-websocket:10.1.8")
api("org.aspectj:aspectjrt:1.9.19")
api("org.aspectj:aspectjtools:1.9.19")
api("org.aspectj:aspectjweaver:1.9.19")
api("org.assertj:assertj-core:3.24.2")
api("org.awaitility:awaitility:4.2.1")
api("org.awaitility:awaitility:4.2.0")
api("org.bouncycastle:bcpkix-jdk18on:1.72")
api("org.codehaus.jettison:jettison:1.5.4")
api("org.dom4j:dom4j:2.1.4")
api("org.eclipse.jetty:jetty-reactive-httpclient:3.0.12")
api("org.eclipse.persistence:org.eclipse.persistence.jpa:3.0.4")
api("org.eclipse.jetty:jetty-reactive-httpclient:3.0.8")
api("org.eclipse.persistence:org.eclipse.persistence.jpa:3.0.3")
api("org.eclipse:yasson:2.0.4")
api("org.ehcache:ehcache:3.10.8")
api("org.ehcache:jcache:1.0.1")
api("org.freemarker:freemarker:2.3.32")
// Substitute for "javax.management:jmxremote_optional:1.0.1_04" which
// is not available on Maven Central
api("org.glassfish.external:opendmk_jmxremote_optional_jar:1.0-b01-ea")
api("org.glassfish:jakarta.el:4.0.2")
api("org.glassfish.tyrus:tyrus-container-servlet:2.1.3")
api("org.glassfish:jakarta.el:4.0.2")
api("org.graalvm.sdk:graal-sdk:22.3.1")
api("org.hamcrest:hamcrest:2.2")
api("org.hibernate:hibernate-core-jakarta:5.6.15.Final")
api("org.hibernate:hibernate-validator:7.0.5.Final")
api("org.hsqldb:hsqldb:2.7.2")
api("org.hsqldb:hsqldb:2.7.1")
api("org.javamoney:moneta:1.4.2")
api("org.jruby:jruby:9.4.7.0")
api("org.jruby:jruby:9.4.3.0")
api("org.junit.support:testng-engine:1.0.4")
api("org.mozilla:rhino:1.7.14")
api("org.ogce:xpp3:1.1.6")
@@ -135,10 +137,10 @@ dependencies {
api("org.seleniumhq.selenium:htmlunit-driver:2.70.0")
api("org.seleniumhq.selenium:selenium-java:3.141.59")
api("org.skyscreamer:jsonassert:1.5.1")
api("org.slf4j:slf4j-api:2.0.13")
api("org.slf4j:slf4j-api:2.0.7")
api("org.testng:testng:7.8.0")
api("org.webjars:underscorejs:1.8.3")
api("org.webjars:webjars-locator-core:0.55")
api("org.webjars:webjars-locator-core:0.52")
api("org.xmlunit:xmlunit-assertj:2.9.1")
api("org.xmlunit:xmlunit-matchers:2.9.1")
api("org.yaml:snakeyaml:1.33")
+1 -1
View File
@@ -1,4 +1,4 @@
version=6.0.22
version=6.0.10
org.gradle.caching=true
org.gradle.jvmargs=-Xmx2048m

Some files were not shown because too many files have changed in this diff Show More