Compare commits

..

1 Commits

Author SHA1 Message Date
Spring Builds c9930dd831 Release v6.0.9 2023-05-11 12:22:53 +00:00
1079 changed files with 11541 additions and 17554 deletions
+2 -1
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/
+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.8.1-librca
java=17.0.7-librca
+18 -12
View File
@@ -4,11 +4,13 @@ plugins {
// kotlinVersion is managed in gradle.properties
id 'org.jetbrains.kotlin.plugin.serialization' version "${kotlinVersion}" apply false
id 'org.jetbrains.dokka' version '1.8.10'
id 'org.asciidoctor.jvm.convert' version '3.3.2' apply false
id 'org.asciidoctor.jvm.pdf' version '3.3.2' apply false
id 'org.unbroken-dome.xjc' version '2.0.0' apply false
id 'com.github.ben-manes.versions' version '0.49.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.1' apply false
id 'me.champeau.jmh' version '0.7.0' apply false
}
ext {
@@ -26,6 +28,7 @@ configure(allprojects) { project ->
includeGroup 'io.projectreactor.netty'
}
}
maven { url "https://repo.spring.io/libs-spring-framework-build" }
if (version.contains('-')) {
maven { url "https://repo.spring.io/milestone" }
}
@@ -75,7 +78,7 @@ configure([rootProject] + javaProjects) { project ->
}
checkstyle {
toolVersion = "10.12.5"
toolVersion = "10.10.0"
configDirectory.set(rootProject.file("src/checkstyle"))
}
@@ -106,17 +109,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.39")
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://docs.oracle.com/cd/E13222_01/wls/docs90/javadocs/", // CommonJ
"https://www.ibm.com/docs/api/v1/content/SSEQTP_8.5.5/com.ibm.websphere.javadoc.doc/web/apidocs/",
"https://docs.jboss.org/jbossas/javadoc/4.0.5/connector/",
"https://docs.jboss.org/jbossas/javadoc/7.1.2.Final/",
"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/",
@@ -131,9 +134,11 @@ configure([rootProject] + javaProjects) { project ->
"https://www.reactive-streams.org/reactive-streams-1.0.3-javadoc/",
"https://javadoc.io/static/io.rsocket/rsocket-core/1.1.1/",
"https://r2dbc.io/spec/1.0.0.RELEASE/api/",
// Previously there could be a split-package issue between JSR250 and JSR305 javax.annotation packages,
// but since 6.0 JSR 250 annotations such as @Resource and @PostConstruct have been replaced by their
// JakartaEE equivalents in the jakarta.annotation package.
// The external Javadoc link for JSR 305 must come last to ensure that types from
// JSR 250 (such as @PostConstruct) are still supported. This is due to the fact
// that JSR 250 and JSR 305 both define types in javax.annotation, which results
// in a split package, and the javadoc tool does not support split packages
// across multiple external Javadoc sites.
"https://www.javadoc.io/doc/com.google.code.findbugs/jsr305/3.0.2/"
] as String[]
}
@@ -146,6 +151,7 @@ configure(rootProject) {
description = "Spring Framework"
apply plugin: "io.spring.nohttp"
apply plugin: 'org.springframework.build.api-diff'
nohttp {
source.exclude "**/test-output/**"
+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());
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
FROM ubuntu:jammy-20231004
FROM ubuntu:jammy-20230425
ADD setup.sh /setup.sh
ADD get-jdk-url.sh /get-jdk-url.sh
+1 -1
View File
@@ -3,7 +3,7 @@ set -e
case "$1" in
java17)
echo "https://github.com/bell-sw/Liberica/releases/download/17.0.9+11/bellsoft-jdk17.0.9+11-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"
+47
View File
@@ -64,6 +64,12 @@ resource_types:
<<: *docker-resource-source
repository: dpb587/github-status-resource
tag: master
- name: pull-request
type: registry-image
source:
<<: *docker-resource-source
repository: teliaoss/github-pr-resource
tag: v0.23.0
- name: slack-notification
type: registry-image
source:
@@ -105,6 +111,14 @@ resources:
username: ((artifactory-username))
password: ((artifactory-password))
build_name: ((build-name))
- name: git-pull-request
type: pull-request
icon: source-pull
source:
access_token: ((github-ci-pull-request-token))
repository: ((github-repo-name))
base_branch: ((branch))
ignore_paths: ["ci/*"]
- name: repo-status-build
type: github-status-resource
icon: eye-check-outline
@@ -245,6 +259,37 @@ jobs:
<<: *slack-fail-params
- put: repo-status-jdk20-build
params: { state: "success", commit: "git-repo" }
- name: build-pull-requests
serial: true
public: true
plan:
- get: ci-image
- get: git-repo
resource: git-pull-request
trigger: true
version: every
- do:
- put: git-pull-request
params:
path: git-repo
status: pending
- task: build-pr
image: ci-image
file: git-repo/ci/tasks/build-pr.yml
privileged: true
timeout: ((task-timeout))
params:
BRANCH: ((branch))
on_success:
put: git-pull-request
params:
path: git-repo
status: success
on_failure:
put: git-pull-request
params:
path: git-repo
status: failure
- name: stage-milestone
serial: true
plan:
@@ -401,3 +446,5 @@ groups:
jobs: ["stage-milestone", "stage-rc", "stage-release", "promote-milestone", "promote-rc", "promote-release", "create-github-release"]
- name: "ci-images"
jobs: ["build-ci-images"]
- name: "pull-requests"
jobs: [ "build-pull-requests" ]
+1 -1
View File
@@ -5,5 +5,5 @@ 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
-PmainToolchain=${MAIN_TOOLCHAIN} -PtestToolchain=${TEST_TOOLCHAIN} --no-daemon --max-workers=4 check
popd > /dev/null
+1 -1
View File
@@ -4,7 +4,7 @@ image_resource:
type: registry-image
source:
repository: springio/concourse-release-scripts
tag: '0.4.0-SNAPSHOT'
tag: '0.3.4'
username: ((docker-hub-username))
password: ((docker-hub-password))
inputs:
+6 -8
View File
@@ -11,7 +11,7 @@ apply from: "${rootDir}/gradle/publications.gradle"
antora {
version = '3.2.0-alpha.2'
playbook = 'cached-antora-playbook.yml'
playbook = layout.buildDirectory.file('cached-antora-playbook.yml').get().getAsFile()
playbookProvider {
repository = 'spring-projects/spring-framework'
branch = 'docs-build'
@@ -45,11 +45,9 @@ tasks.create("generateAntoraResources") {
dependsOn 'generateAntoraYml'
}
// Commented out for now:
// https://github.com/spring-projects/spring-framework/issues/30481
// tasks.named("check") {
// dependsOn 'antora'
// }
tasks.named("check") {
dependsOn 'antora'
}
jar {
enabled = false
@@ -104,7 +102,7 @@ task api(type: Javadoc) {
overview = "framework-docs/src/docs/api/overview.html"
splitIndex = true
links(project.ext.javadocLinks)
addBooleanOption('Xdoclint:syntax,reference', true) // only check syntax and reference with doclint
addBooleanOption('Xdoclint:syntax', true) // only check syntax with doclint
addBooleanOption('Werror', true) // fail build on Javadoc warnings
}
source moduleProjects.collect { project ->
@@ -229,4 +227,4 @@ publishing {
artifact distZip
}
}
}
}
@@ -25,7 +25,7 @@ The following table lists all currently supported Spring properties.
| `spring.beaninfo.ignore`
| Instructs Spring to use the `Introspector.IGNORE_ALL_BEANINFO` mode when calling the
JavaBeans `Introspector`. See
{api-spring-framework}++/beans/StandardBeanInfoFactory.html#IGNORE_BEANINFO_PROPERTY_NAME++[`CachedIntrospectionResults`]
{api-spring-framework}++/beans/CachedIntrospectionResults.html#IGNORE_BEANINFO_PROPERTY_NAME++[`CachedIntrospectionResults`]
for details.
| `spring.expression.compiler.mode`
@@ -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/>`
@@ -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: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].
`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>
@@ -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
@@ -449,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):
@@ -514,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
@@ -38,6 +38,7 @@ The expression language supports the following functionality:
* Class expressions
* Accessing properties, arrays, lists, and maps
* Method invocation
* Relational operators
* Assignment
* Calling constructors
* Bean references
@@ -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`.
====
@@ -2,13 +2,13 @@
= Null-safety
Although Java does not let you express null-safety with its type system, the Spring Framework
provides the following annotations in the `org.springframework.lang` package to let you
now provides the following annotations in the `org.springframework.lang` package to let you
declare nullability of APIs and fields:
* {api-spring-framework}/lang/Nullable.html[`@Nullable`]: Annotation to indicate that a
specific parameter, return value, or field can be `null`.
* {api-spring-framework}/lang/NonNull.html[`@NonNull`]: Annotation to indicate that a specific
parameter, return value, or field cannot be `null` (not needed on parameters, return values,
parameter, return value, or field cannot be `null` (not needed on parameters / return values
and fields where `@NonNullApi` and `@NonNullFields` apply, respectively).
* {api-spring-framework}/lang/NonNullApi.html[`@NonNullApi`]: Annotation at the package level
that declares non-null as the default semantics for parameters and return values.
@@ -17,10 +17,11 @@ level that declares non-null as the default semantics for fields.
The Spring Framework itself leverages these annotations, but they can also be used in any
Spring-based Java project to declare null-safe APIs and optionally null-safe fields.
Nullability declarations for generic type arguments, varargs, and array elements are not supported yet.
Nullability declarations are expected to be fine-tuned between Spring Framework releases,
including minor ones. Nullability of types used inside method bodies is outside the
scope of this feature.
Generic type arguments, varargs and array elements nullability are not supported yet but
should be in an upcoming release, see https://jira.spring.io/browse/SPR-15942[SPR-15942]
for up-to-date information. Nullability declarations are expected to be fine-tuned between
Spring Framework releases, including minor ones. Nullability of types used inside method
bodies is outside of the scope of this feature.
NOTE: Other common libraries such as Reactor and Spring Data provide null-safe APIs that
use a similar nullability arrangement, delivering a consistent overall experience for
@@ -36,8 +37,8 @@ In addition to providing an explicit declaration for Spring Framework API nullab
these annotations can be used by an IDE (such as IDEA or Eclipse) to provide useful
warnings related to null-safety in order to avoid `NullPointerException` at runtime.
They are also used to make Spring APIs null-safe in Kotlin projects, since Kotlin natively
supports https://kotlinlang.org/docs/null-safety.html[null-safety]. More details
They are also used to make Spring API null-safe in Kotlin projects, since Kotlin natively
supports https://kotlinlang.org/docs/reference/null-safety.html[null-safety]. More details
are available in the xref:languages/kotlin/null-safety.adoc[Kotlin support documentation].
@@ -47,11 +48,11 @@ are available in the xref:languages/kotlin/null-safety.adoc[Kotlin support docum
== JSR-305 meta-annotations
Spring annotations are meta-annotated with https://jcp.org/en/jsr/detail?id=305[JSR 305]
annotations (a dormant but widespread JSR). JSR-305 meta-annotations let tooling vendors
annotations (a dormant but wide-spread JSR). JSR-305 meta-annotations let tooling vendors
like IDEA or Kotlin provide null-safety support in a generic way, without having to
hard-code support for Spring annotations.
It is neither necessary nor recommended to add a JSR-305 dependency to the project classpath to
take advantage of Spring's null-safe APIs. Only projects such as Spring-based libraries that use
It is not necessary nor recommended to add a JSR-305 dependency to the project classpath to
take advantage of Spring null-safe API. Only projects such as Spring-based libraries that use
null-safety annotations in their codebase should add `com.google.code.findbugs:jsr305:3.0.2`
with `compileOnly` Gradle configuration or Maven `provided` scope to avoid compiler warnings.
with `compileOnly` Gradle configuration or Maven `provided` scope to avoid compile warnings.
@@ -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`
@@ -8,16 +8,10 @@ implementations and transaction demarcation. Most of these patterns can be direc
translated to all other supported ORM tools. The later sections in this chapter then
cover the other ORM technologies and show brief examples.
[NOTE]
====
As of Spring Framework 6.0, Spring requires Hibernate ORM 5.5+ for Spring's
NOTE: As of Spring Framework 5.3, Spring requires Hibernate ORM 5.2+ for Spring's
`HibernateJpaVendorAdapter` as well as for a native Hibernate `SessionFactory` setup.
We recommend Hibernate ORM 5.6 as the last feature branch in that Hibernate generation.
Hibernate ORM 6.x is only supported as a JPA provider (`HibernateJpaVendorAdapter`).
Plain `SessionFactory` setup with the `orm.hibernate5` package is not supported anymore.
We recommend Hibernate ORM 6.1/6.2 with JPA-style setup for new development projects.
====
It is strongly recommended to go with Hibernate ORM 5.4 for a newly started application.
For use with `HibernateJpaVendorAdapter`, Hibernate Search needs to be upgraded to 5.11.6.
[[orm-session-factory-setup]]
@@ -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>
@@ -276,8 +281,8 @@ Spring Data JPA, make sure to set up deferred bootstrapping for its repositories
[[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.
@@ -285,8 +290,8 @@ to a newly created `EntityManager` per operation, in effect making its usage thr
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:
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))
----
======
@@ -420,9 +417,9 @@ Do not pass `Collection<String>` or the like as an array parameter.
[[r2dbc-DatabaseClient-filter]]
==== Statement Filters
Sometimes you need to fine-tune options on the actual `Statement`
before it gets run. To do so, register a `Statement` filter
(`StatementFilterFunction`) with the `DatabaseClient` to intercept and
Sometimes it you need to fine-tune options on the actual `Statement`
before it gets run. Register a `Statement` filter
(`StatementFilterFunction`) through `DatabaseClient` to intercept and
modify statements in their execution, as the following example shows:
[tabs]
@@ -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,14 +439,13 @@ 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", …)
----
======
`DatabaseClient` also exposes a simplified `filter(…)` overload that accepts
a `Function<Statement, Statement>`:
`DatabaseClient` exposes also simplified `filter(…)` overload accepting `Function<Statement, Statement>`:
[tabs]
======
@@ -458,10 +454,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 +465,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 +592,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 +604,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 +671,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 +717,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>
@@ -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`
@@ -4,10 +4,10 @@
For more information about the Spring Framework's transaction support, see:
* link:++https://www.infoworld.com/article/2077963/distributed-transactions-in-spring--with-and-without-xa.html++[
Distributed transactions in Spring, with and without XA] is a JavaWorld presentation in
which Spring's David Syer guides you through seven patterns for distributed transactions
in Spring applications, three of them with XA and four without.
* https://www.infoworld.com/article/2077963/distributed-transactions-in-spring--with-and-without-xa.html[Distributed
transactions in Spring, with and without XA] is a JavaWorld presentation in which
Spring's David Syer guides you through seven patterns for distributed
transactions in Spring applications, three of them with XA and four without.
* https://www.infoq.com/minibooks/JTDS[_Java Transaction Design Strategies_] is a book
available from https://www.infoq.com/[InfoQ] that provides a well-paced introduction
to transactions in Java. It also includes side-by-side examples of how to configure
@@ -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>
----
@@ -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.
@@ -3,7 +3,7 @@
The Spring Framework provides the following choices for making calls to REST endpoints:
* xref:integration/rest-clients.adoc#rest-webclient[`WebClient`] - non-blocking, reactive client with fluent API.
* xref:integration/rest-clients.adoc#rest-webclient[`WebClient`] - non-blocking, reactive client w fluent API.
* xref:integration/rest-clients.adoc#rest-resttemplate[`RestTemplate`] - synchronous client with template method API.
* xref:integration/rest-clients.adoc#rest-http-interface[HTTP Interface] - annotated interface with generated, dynamic proxy implementation.
@@ -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:
@@ -12,11 +12,7 @@ metadata.
You can use `@DirtiesContext` as both a class-level and a method-level annotation within
the same class or class hierarchy. In such scenarios, the `ApplicationContext` is marked
as dirty before or after any such annotated method as well as before or after the current
test class, depending on the configured `methodMode` and `classMode`. When
`@DirtiesContext` is declared at both the class level and the method level, the
configured modes from both annotations will be honored. For example, if the class mode is
set to `BEFORE_EACH_TEST_METHOD` and the method mode is set to `AFTER_METHOD`, the
context will be marked as dirty both before and after the given test method.
test class, depending on the configured `methodMode` and `classMode`.
The following examples explain when the context would be dirtied for various
configuration scenarios:
@@ -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:
@@ -12,8 +12,6 @@ by default, exactly in the following order:
xref:testing/testcontext-framework/application-events.adoc[`ApplicationEvents`].
* `DependencyInjectionTestExecutionListener`: Provides dependency injection for the test
instance.
* `MicrometerObservationRegistryTestExecutionListener`: Provides support for
Micrometer's `ObservationRegistry`.
* `DirtiesContextTestExecutionListener`: Handles the `@DirtiesContext` annotation for
"`after`" modes.
* `TransactionalTestExecutionListener`: Provides transactional test execution with
@@ -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,10 +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,7 +52,7 @@ The following example shows how to achieve the same configuration in XML:
</mvc:interceptors>
----
NOTE: Interceptors are not ideally suited as a security layer due to the potential
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.
@@ -61,10 +61,6 @@ https://docs.spring.io/spring-security/reference/servlet/integrations/mvc.html#m
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.
@@ -3,18 +3,11 @@
[.small]#xref:web/webflux/config.adoc#webflux-config-message-codecs[See equivalent in the Reactive stack]#
You can set the `HttpMessageConverter` instances to use in Java configuration,
replacing the ones used by default, by overriding
{api-spring-framework}/web/servlet/config/annotation/WebMvcConfigurer.html#configureMessageConverters-java.util.List-[`configureMessageConverters()`].
You can also customize the list of configured message converters at the end by overriding
{api-spring-framework}/web/servlet/config/annotation/WebMvcConfigurer.html#extendMessageConverters-java.util.List-[`extendMessageConverters()`].
TIP: In a Spring Boot application, the `WebMvcAutoConfiguration` adds any
`HttpMessageConverter` beans it detects, in addition to default converters. Hence, in a
Boot application, prefer to use the
https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-config/message-converters.html[HttpMessageConverters]
mechanism. Or alternatively, use `extendMessageConverters` to modify message converters
at the end.
You can customize `HttpMessageConverter` in Java configuration by overriding
{api-spring-framework}/web/servlet/config/annotation/WebMvcConfigurer.html#configureMessageConverters-java.util.List-[`configureMessageConverters()`]
(to replace the default converters created by Spring MVC) or by overriding
{api-spring-framework}/web/servlet/config/annotation/WebMvcConfigurer.html#extendMessageConverters-java.util.List-[`extendMessageConverters()`]
(to customize the default converters or add additional converters to the default ones).
The following example adds XML and Jackson JSON converters with a customized
`ObjectMapper` instead of the default ones:
@@ -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
@@ -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:
endpoints, 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
@@ -32,7 +32,7 @@ client needs to connect for the WebSocket handshake.
<2> STOMP messages whose destination header begins with `/app` are routed to
`@MessageMapping` methods in `@Controller` classes.
<3> Use the built-in message broker for subscriptions and broadcasting and
route messages whose destination header begins with `/topic` or `/queue` to the broker.
route messages whose destination header begins with `/topic `or `/queue` to the broker.
The following example shows the XML configuration equivalent of the preceding example:
@@ -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);
}
}
----
+40 -38
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.101.Final"))
api(platform("io.micrometer:micrometer-bom:1.10.7"))
api(platform("io.netty:netty-bom:4.1.92.Final"))
api(platform("io.netty:netty5-bom:5.0.0.Alpha5"))
api(platform("io.projectreactor:reactor-bom:2022.0.14"))
api(platform("io.projectreactor:reactor-bom:2022.0.7"))
api(platform("io.rsocket:rsocket-bom:1.1.3"))
api(platform("org.apache.groovy:groovy-bom:4.0.16"))
api(platform("org.apache.logging.log4j:log4j-bom:2.21.1"))
api(platform("org.eclipse.jetty:jetty-bom:11.0.18"))
api(platform("org.apache.groovy:groovy-bom:4.0.11"))
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.8.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.33")
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.google.protobuf:protobuf-java-util:3.23.0")
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.3")
api("io.micrometer:context-propagation:1.0.0")
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.reactivex.rxjava3:rxjava:3.1.6")
api("io.smallrye.reactive:mutiny:1.9.0")
api("io.undertow:undertow-core:2.3.10.Final")
api("io.undertow:undertow-servlet:2.3.10.Final")
api("io.undertow:undertow-websockets-jsr:2.3.10.Final")
api("io.undertow:undertow-core:2.3.5.Final")
api("io.undertow:undertow-servlet:2.3.5.Final")
api("io.undertow:undertow-websockets-jsr:2.3.5.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.1")
api("org.apache.httpcomponents.core5:httpcore5-reactive:5.2.3")
api("org.apache.poi:poi-ooxml:5.2.4")
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.20.1")
api("org.aspectj:aspectjtools:1.9.20.1")
api("org.aspectj:aspectjweaver:1.9.20.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.8")
api("org.apache.tomcat.embed:tomcat-embed-websocket:10.1.8")
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.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.10")
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.5.0")
api("org.jruby:jruby:9.4.2.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.9")
api("org.testng:testng:7.8.0")
api("org.slf4j:slf4j-api:2.0.7")
api("org.testng:testng:7.7.1")
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.15
version=6.0.9
org.gradle.caching=true
org.gradle.jvmargs=-Xmx2048m
+2 -5
View File
@@ -72,13 +72,10 @@ javadoc {
options.header = project.name
options.use = true
options.links(project.ext.javadocLinks)
// Check for syntax during linting. 'none' doesn't seem to work in suppressing
// all linting warnings all the time (see/link references most notably).
options.addStringOption("Xdoclint:syntax", "-quiet")
options.addStringOption("Xdoclint:none", "-quiet")
// Suppress warnings due to cross-module @see and @link references.
// Note that global 'api' task does display all warnings, and
// checks for 'reference' on top of 'syntax'.
// Note that global 'api' task does display all warnings.
logging.captureStandardError LogLevel.INFO
logging.captureStandardOutput LogLevel.INFO // suppress "## warnings" message
}
Binary file not shown.
+1 -2
View File
@@ -1,7 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored
+9 -13
View File
@@ -83,8 +83,7 @@ done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -131,13 +130,10 @@ location of your Java installation."
fi
else
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
@@ -145,7 +141,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
# shellcheck disable=SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
@@ -153,7 +149,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
# shellcheck disable=SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
@@ -202,11 +198,11 @@ fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* 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.
@@ -21,15 +21,11 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.assertj.core.api.InstanceOfAssertFactories;
import org.junit.jupiter.api.Test;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.MethodExecutor;
import org.springframework.expression.TypedValue;
import static org.assertj.core.api.Assertions.assertThat;
class Spr7538Tests {
@@ -53,9 +49,8 @@ class Spr7538Tests {
ReflectiveMethodResolver resolver = new ReflectiveMethodResolver();
MethodExecutor executor = resolver.resolve(context, target, "checkCompleteness", argumentTypes);
TypedValue typedValue = executor.execute(context, target, arguments);
assertThat(typedValue.getValue()).asInstanceOf(InstanceOfAssertFactories.BOOLEAN).isTrue();
assertThat(typedValue.getTypeDescriptor().getType()).isEqualTo(Boolean.class);
Object result = executor.execute(context, target, arguments);
System.out.println("Result: " + result);
}
static class AlwaysTrueReleaseStrategy {
@@ -60,8 +60,8 @@ class ScheduledAndTransactionalAnnotationIntegrationTests {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(Config.class, JdkProxyTxConfig.class, RepoConfigA.class);
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(ctx::refresh)
.withCauseInstanceOf(IllegalStateException.class);
.isThrownBy(ctx::refresh)
.withCauseInstanceOf(IllegalStateException.class);
}
@Test
@@ -70,7 +70,7 @@ class ScheduledAndTransactionalAnnotationIntegrationTests {
ctx.register(Config.class, SubclassProxyTxConfig.class, RepoConfigA.class);
ctx.refresh();
Thread.sleep(200); // allow @Scheduled method to be called several times
Thread.sleep(100); // allow @Scheduled method to be called several times
MyRepository repository = ctx.getBean(MyRepository.class);
CallCountingTransactionManager txManager = ctx.getBean(CallCountingTransactionManager.class);
@@ -85,7 +85,7 @@ class ScheduledAndTransactionalAnnotationIntegrationTests {
ctx.register(Config.class, JdkProxyTxConfig.class, RepoConfigB.class);
ctx.refresh();
Thread.sleep(200); // allow @Scheduled method to be called several times
Thread.sleep(100); // allow @Scheduled method to be called several times
MyRepositoryWithScheduledMethod repository = ctx.getBean(MyRepositoryWithScheduledMethod.class);
CallCountingTransactionManager txManager = ctx.getBean(CallCountingTransactionManager.class);
@@ -100,7 +100,7 @@ class ScheduledAndTransactionalAnnotationIntegrationTests {
ctx.register(AspectConfig.class, MyRepositoryWithScheduledMethodImpl.class);
ctx.refresh();
Thread.sleep(200); // allow @Scheduled method to be called several times
Thread.sleep(100); // allow @Scheduled method to be called several times
MyRepositoryWithScheduledMethod repository = ctx.getBean(MyRepositoryWithScheduledMethod.class);
assertThat(AopUtils.isCglibProxy(repository)).isTrue();
+1 -2
View File
@@ -47,8 +47,7 @@ rootProject.children.each {project ->
settings.gradle.projectsLoaded {
gradleEnterprise {
buildScan {
File buildDir = settings.gradle.rootProject
.getLayout().getBuildDirectory().getAsFile().get()
File buildDir = settings.gradle.rootProject.getBuildDir()
buildDir.mkdirs()
new File(buildDir, "build-scan-uri.txt").text = "(build scan not generated)"
buildScanPublished { scan ->
+3 -4
View File
@@ -3,12 +3,11 @@ description = "Spring AOP"
dependencies {
api(project(":spring-beans"))
api(project(":spring-core"))
optional("org.apache.commons:commons-pool2")
optional("org.aspectj:aspectjweaver")
testFixturesImplementation(testFixtures(project(":spring-beans")))
testFixturesImplementation(testFixtures(project(":spring-core")))
testFixturesImplementation("com.google.code.findbugs:jsr305")
optional("org.apache.commons:commons-pool2")
testImplementation(project(":spring-core-test"))
testImplementation(testFixtures(project(":spring-beans")))
testImplementation(testFixtures(project(":spring-core")))
testFixturesImplementation(testFixtures(project(":spring-beans")))
testFixturesImplementation(testFixtures(project(":spring-core")))
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2017 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.
@@ -62,11 +62,8 @@ public interface Advisor {
* Typical Advisor implementations always return {@code true}.
* Use singleton/prototype bean definitions or appropriate programmatic
* proxy creation to ensure that Advisors have the correct lifecycle model.
* <p>As of 6.0.10, the default implementation returns {@code true}.
* @return whether this advice is associated with a particular target instance
*/
default boolean isPerInstance() {
return true;
}
boolean isPerInstance();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* 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.
@@ -17,23 +17,18 @@
package org.springframework.aop;
/**
* Filter that restricts matching of a pointcut or introduction to a given set
* of target classes.
* Filter that restricts matching of a pointcut or introduction to
* a given set of target classes.
*
* <p>Can be used as part of a {@link Pointcut} or for the entire targeting of
* an {@link IntroductionAdvisor}.
* <p>Can be used as part of a {@link Pointcut} or for the entire
* targeting of an {@link IntroductionAdvisor}.
*
* <p><strong>WARNING</strong>: Concrete implementations of this interface must
* provide proper implementations of {@link Object#equals(Object)},
* {@link Object#hashCode()}, and {@link Object#toString()} in order to allow the
* filter to be used in caching scenarios &mdash; for example, in proxies generated
* by CGLIB. As of Spring Framework 6.0.13, the {@code toString()} implementation
* must generate a unique string representation that aligns with the logic used
* to implement {@code equals()}. See concrete implementations of this interface
* within the framework for examples.
* <p>Concrete implementations of this interface typically should provide proper
* implementations of {@link Object#equals(Object)} and {@link Object#hashCode()}
* in order to allow the filter to be used in caching scenarios &mdash; for
* example, in proxies generated by CGLIB.
*
* @author Rod Johnson
* @author Sam Brannen
* @see Pointcut
* @see MethodMatcher
*/
@@ -49,7 +44,7 @@ public interface ClassFilter {
/**
* Canonical instance of a {@code ClassFilter} that matches all classes.
* Canonical instance of a ClassFilter that matches all classes.
*/
ClassFilter TRUE = TrueClassFilter.INSTANCE;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* 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.
@@ -21,37 +21,31 @@ import java.lang.reflect.Method;
/**
* Part of a {@link Pointcut}: Checks whether the target method is eligible for advice.
*
* <p>A {@code MethodMatcher} may be evaluated <b>statically</b> or at <b>runtime</b>
* (dynamically). Static matching involves a method and (possibly) method attributes.
* Dynamic matching also makes arguments for a particular call available, and any
* effects of running previous advice applying to the joinpoint.
* <p>A MethodMatcher may be evaluated <b>statically</b> or at <b>runtime</b> (dynamically).
* Static matching involves method and (possibly) method attributes. Dynamic matching
* also makes arguments for a particular call available, and any effects of running
* previous advice applying to the joinpoint.
*
* <p>If an implementation returns {@code false} from its {@link #isRuntime()}
* method, evaluation can be performed statically, and the result will be the same
* for all invocations of this method, whatever their arguments. This means that
* if the {@link #isRuntime()} method returns {@code false}, the 3-arg
* {@link #matches(Method, Class, Object[])} method will never be invoked.
* {@link #matches(java.lang.reflect.Method, Class, Object[])} method will never be invoked.
*
* <p>If an implementation returns {@code true} from its 2-arg
* {@link #matches(Method, Class)} method and its {@link #isRuntime()} method
* returns {@code true}, the 3-arg {@link #matches(Method, Class, Object[])}
* method will be invoked <i>immediately before each potential execution of the
* related advice</i> to decide whether the advice should run. All previous advice,
* such as earlier interceptors in an interceptor chain, will have run, so any
* state changes they have produced in parameters or {@code ThreadLocal} state will
* be available at the time of evaluation.
* {@link #matches(java.lang.reflect.Method, Class)} method and its {@link #isRuntime()} method
* returns {@code true}, the 3-arg {@link #matches(java.lang.reflect.Method, Class, Object[])}
* method will be invoked <i>immediately before each potential execution of the related advice</i>,
* to decide whether the advice should run. All previous advice, such as earlier interceptors
* in an interceptor chain, will have run, so any state changes they have produced in
* parameters or ThreadLocal state will be available at the time of evaluation.
*
* <p><strong>WARNING</strong>: Concrete implementations of this interface must
* provide proper implementations of {@link Object#equals(Object)},
* {@link Object#hashCode()}, and {@link Object#toString()} in order to allow the
* matcher to be used in caching scenarios &mdash; for example, in proxies generated
* by CGLIB. As of Spring Framework 6.0.13, the {@code toString()} implementation
* must generate a unique string representation that aligns with the logic used
* to implement {@code equals()}. See concrete implementations of this interface
* within the framework for examples.
* <p>Concrete implementations of this interface typically should provide proper
* implementations of {@link Object#equals(Object)} and {@link Object#hashCode()}
* in order to allow the matcher to be used in caching scenarios &mdash; for
* example, in proxies generated by CGLIB.
*
* @author Rod Johnson
* @author Sam Brannen
* @since 11.11.2003
* @see Pointcut
* @see ClassFilter
@@ -59,10 +53,11 @@ import java.lang.reflect.Method;
public interface MethodMatcher {
/**
* Perform static checking to determine whether the given method matches.
* <p>If this method returns {@code false} or if {@link #isRuntime()}
* returns {@code false}, no runtime check (i.e. no
* {@link #matches(Method, Class, Object[])} call) will be made.
* Perform static checking whether the given method matches.
* <p>If this returns {@code false} or if the {@link #isRuntime()}
* method returns {@code false}, no runtime check (i.e. no
* {@link #matches(java.lang.reflect.Method, Class, Object[])} call)
* will be made.
* @param method the candidate method
* @param targetClass the target class
* @return whether this method matches statically
@@ -70,35 +65,36 @@ public interface MethodMatcher {
boolean matches(Method method, Class<?> targetClass);
/**
* Is this {@code MethodMatcher} dynamic, that is, must a final check be made
* via the {@link #matches(Method, Class, Object[])} method at runtime even
* if {@link #matches(Method, Class)} returns {@code true}?
* Is this MethodMatcher dynamic, that is, must a final call be made on the
* {@link #matches(java.lang.reflect.Method, Class, Object[])} method at
* runtime even if the 2-arg matches method returns {@code true}?
* <p>Can be invoked when an AOP proxy is created, and need not be invoked
* again before each method invocation.
* @return whether a runtime match via {@link #matches(Method, Class, Object[])}
* again before each method invocation,
* @return whether a runtime match via the 3-arg
* {@link #matches(java.lang.reflect.Method, Class, Object[])} method
* is required if static matching passed
*/
boolean isRuntime();
/**
* Check whether there is a runtime (dynamic) match for this method, which
* must have matched statically.
* <p>This method is invoked only if {@link #matches(Method, Class)} returns
* {@code true} for the given method and target class, and if
* {@link #isRuntime()} returns {@code true}.
* <p>Invoked immediately before potential running of the advice, after any
* Check whether there a runtime (dynamic) match for this method,
* which must have matched statically.
* <p>This method is invoked only if the 2-arg matches method returns
* {@code true} for the given method and target class, and if the
* {@link #isRuntime()} method returns {@code true}. Invoked
* immediately before potential running of the advice, after any
* advice earlier in the advice chain has run.
* @param method the candidate method
* @param targetClass the target class
* @param args arguments to the method
* @return whether there's a runtime match
* @see #matches(Method, Class)
* @see MethodMatcher#matches(Method, Class)
*/
boolean matches(Method method, Class<?> targetClass, Object... args);
/**
* Canonical instance of a {@code MethodMatcher} that matches all methods.
* Canonical instance that matches all methods.
*/
MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
@@ -714,8 +714,13 @@ public abstract class AbstractAspectJAdvice implements Advice, AspectJPrecedence
@Override
public boolean equals(@Nullable Object other) {
return (this == other || (other instanceof AdviceExcludingMethodMatcher that &&
this.adviceMethod.equals(that.adviceMethod)));
if (this == other) {
return true;
}
if (!(other instanceof AdviceExcludingMethodMatcher otherMm)) {
return false;
}
return this.adviceMethod.equals(otherMm.adviceMethod);
}
@Override
@@ -302,7 +302,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
}
private void bindParameterName(int index, @Nullable String name) {
private void bindParameterName(int index, String name) {
this.parameterNameBindings[index] = name;
this.numberOfRemainingUnboundArguments--;
}
@@ -243,8 +243,8 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
/**
* If a pointcut expression has been specified in XML, the user cannot
* write "and" as "&&" (though {@code &amp;&amp;} will work).
* <p>We also allow "and" between two pointcut sub-expressions.
* write {@code and} as "&&" (though &amp;&amp; will work).
* We also allow {@code and} between two pointcut sub-expressions.
* <p>This method converts back to {@code &&} for the AspectJ pointcut parser.
*/
private String replaceBooleanOperators(String pcExpr) {
@@ -516,16 +516,21 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
@Override
public boolean equals(@Nullable Object other) {
return (this == other || (other instanceof AspectJExpressionPointcut that &&
ObjectUtils.nullSafeEquals(getExpression(), that.getExpression()) &&
ObjectUtils.nullSafeEquals(this.pointcutDeclarationScope, that.pointcutDeclarationScope) &&
ObjectUtils.nullSafeEquals(this.pointcutParameterNames, that.pointcutParameterNames) &&
ObjectUtils.nullSafeEquals(this.pointcutParameterTypes, that.pointcutParameterTypes)));
if (this == other) {
return true;
}
if (!(other instanceof AspectJExpressionPointcut otherPc)) {
return false;
}
return ObjectUtils.nullSafeEquals(this.getExpression(), otherPc.getExpression()) &&
ObjectUtils.nullSafeEquals(this.pointcutDeclarationScope, otherPc.pointcutDeclarationScope) &&
ObjectUtils.nullSafeEquals(this.pointcutParameterNames, otherPc.pointcutParameterNames) &&
ObjectUtils.nullSafeEquals(this.pointcutParameterTypes, otherPc.pointcutParameterTypes);
}
@Override
public int hashCode() {
int hashCode = ObjectUtils.nullSafeHashCode(getExpression());
int hashCode = ObjectUtils.nullSafeHashCode(this.getExpression());
hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.pointcutDeclarationScope);
hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.pointcutParameterNames);
hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.pointcutParameterTypes);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2021 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.
@@ -25,7 +25,7 @@ import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* AspectJ {@link PointcutAdvisor} that adapts an {@link AbstractAspectJAdvice}
* AspectJPointcutAdvisor that adapts an {@link AbstractAspectJAdvice}
* to the {@link org.springframework.aop.PointcutAdvisor} interface.
*
* @author Adrian Colyer
@@ -67,6 +67,11 @@ public class AspectJPointcutAdvisor implements PointcutAdvisor, Ordered {
}
}
@Override
public boolean isPerInstance() {
return true;
}
@Override
public Advice getAdvice() {
return this.advice;
@@ -89,8 +94,13 @@ public class AspectJPointcutAdvisor implements PointcutAdvisor, Ordered {
@Override
public boolean equals(@Nullable Object other) {
return (this == other || (other instanceof AspectJPointcutAdvisor that &&
this.advice.equals(that.advice)));
if (this == other) {
return true;
}
if (!(other instanceof AspectJPointcutAdvisor otherAdvisor)) {
return false;
}
return this.advice.equals(otherAdvisor.advice);
}
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2018 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.
@@ -91,6 +91,11 @@ public class DeclareParentsAdvisor implements IntroductionAdvisor {
// Do nothing
}
@Override
public boolean isPerInstance() {
return true;
}
@Override
public Advice getAdvice() {
return this.advice;
@@ -117,8 +117,8 @@ public class TypePatternClassFilter implements ClassFilter {
}
@Override
public boolean equals(@Nullable Object other) {
return (this == other || (other instanceof TypePatternClassFilter that &&
public boolean equals(@Nullable Object obj) {
return (this == obj || (obj instanceof TypePatternClassFilter that &&
ObjectUtils.nullSafeEquals(this.typePattern, that.typePattern)));
}
@@ -1,90 +0,0 @@
/*
* Copyright 2002-2023 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.aop.aspectj.annotation;
import java.util.List;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AbstractAspectJAdvice;
import org.springframework.aot.generate.GenerationContext;
import org.springframework.aot.hint.ExecutableMode;
import org.springframework.aot.hint.ReflectionHints;
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution;
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor;
import org.springframework.beans.factory.aot.BeanFactoryInitializationCode;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
/**
* {@link BeanFactoryInitializationAotProcessor} implementation responsible for registering
* hints for AOP advices.
*
* @author Sebastien Deleuze
* @author Stephane Nicoll
* @since 6.0.11
*/
class AspectJBeanFactoryInitializationAotProcessor implements BeanFactoryInitializationAotProcessor {
private static final boolean aspectJPresent = ClassUtils.isPresent("org.aspectj.lang.annotation.Pointcut",
AspectJBeanFactoryInitializationAotProcessor.class.getClassLoader());
@Override
@Nullable
public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) {
if (aspectJPresent) {
return AspectDelegate.processAheadOfTime(beanFactory);
}
return null;
}
/**
* Inner class to avoid a hard dependency on AspectJ at runtime.
*/
private static class AspectDelegate {
@Nullable
private static AspectContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) {
BeanFactoryAspectJAdvisorsBuilder builder = new BeanFactoryAspectJAdvisorsBuilder(beanFactory);
List<Advisor> advisors = builder.buildAspectJAdvisors();
return (advisors.isEmpty() ? null : new AspectContribution(advisors));
}
}
private static class AspectContribution implements BeanFactoryInitializationAotContribution {
private final List<Advisor> advisors;
public AspectContribution(List<Advisor> advisors) {
this.advisors = advisors;
}
@Override
public void applyTo(GenerationContext generationContext, BeanFactoryInitializationCode beanFactoryInitializationCode) {
ReflectionHints reflectionHints = generationContext.getRuntimeHints().reflection();
for (Advisor advisor : this.advisors) {
if (advisor.getAdvice() instanceof AbstractAspectJAdvice aspectJAdvice) {
reflectionHints.registerMethod(aspectJAdvice.getAspectJAdviceMethod(), ExecutableMode.INVOKE);
}
}
}
}
}
@@ -32,7 +32,6 @@ import org.springframework.aop.aspectj.annotation.AbstractAspectJAdvisorFactory.
import org.springframework.aop.support.DynamicMethodMatcherPointcut;
import org.springframework.aop.support.Pointcuts;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
/**
* Internal implementation of AspectJPointcutAdvisor.
@@ -41,7 +40,6 @@ import org.springframework.util.ObjectUtils;
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Sam Brannen
* @since 2.0
*/
@SuppressWarnings("serial")
@@ -299,27 +297,6 @@ final class InstantiationModelAwarePointcutAdvisorImpl
private boolean isAspectMaterialized() {
return (this.aspectInstanceFactory == null || this.aspectInstanceFactory.isMaterialized());
}
@Override
public boolean equals(@Nullable Object other) {
// For equivalence, we only need to compare the preInstantiationPointcut fields since
// they include the declaredPointcut fields. In addition, we should not compare the
// aspectInstanceFactory fields since LazySingletonAspectInstanceFactoryDecorator does
// not implement equals().
return (this == other || (other instanceof PerTargetInstantiationModelPointcut that &&
ObjectUtils.nullSafeEquals(this.preInstantiationPointcut, that.preInstantiationPointcut)));
}
@Override
public int hashCode() {
return ObjectUtils.nullSafeHashCode(this.declaredPointcut.getExpression());
}
@Override
public String toString() {
return PerTargetInstantiationModelPointcut.class.getName() + ": " + this.declaredPointcut.getExpression();
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2022 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.
@@ -22,7 +22,6 @@ import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -33,8 +32,6 @@ import org.springframework.aop.Advisor;
import org.springframework.aop.DynamicIntroductionAdvice;
import org.springframework.aop.IntroductionAdvisor;
import org.springframework.aop.IntroductionInfo;
import org.springframework.aop.Pointcut;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.aop.TargetSource;
import org.springframework.aop.support.DefaultIntroductionAdvisor;
import org.springframework.aop.support.DefaultPointcutAdvisor;
@@ -44,12 +41,10 @@ import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
/**
* Base class for AOP proxy configuration managers.
*
* <p>These are not themselves AOP proxies, but subclasses of this class are
* These are not themselves AOP proxies, but subclasses of this class are
* normally factories from which AOP proxy instances are obtained directly.
*
* <p>This class frees subclasses of the housekeeping of Advices
@@ -57,12 +52,10 @@ import org.springframework.util.ObjectUtils;
* methods, which are provided by subclasses.
*
* <p>This class is serializable; subclasses need not be.
*
* <p>This class is used to hold snapshots of proxies.
* This class is used to hold snapshots of proxies.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Sam Brannen
* @see org.springframework.aop.framework.AopProxy
*/
public class AdvisedSupport extends ProxyConfig implements Advised {
@@ -79,13 +72,15 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
/** Package-protected to allow direct access for efficiency. */
@SuppressWarnings("serial")
TargetSource targetSource = EMPTY_TARGET_SOURCE;
/** Whether the Advisors are already filtered for the specific target class. */
private boolean preFiltered = false;
/** The AdvisorChainFactory to use. */
private AdvisorChainFactory advisorChainFactory;
@SuppressWarnings("serial")
AdvisorChainFactory advisorChainFactory = new DefaultAdvisorChainFactory();
/** Cache with Method as key and advisor chain List as value. */
private transient Map<MethodCacheKey, List<Object>> methodCache;
@@ -94,27 +89,26 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
* Interfaces to be implemented by the proxy. Held in List to keep the order
* of registration, to create JDK proxy with specified order of interfaces.
*/
@SuppressWarnings("serial")
private List<Class<?>> interfaces = new ArrayList<>();
/**
* List of Advisors. If an Advice is added, it will be wrapped
* in an Advisor before being added to this List.
*/
@SuppressWarnings("serial")
private List<Advisor> advisors = new ArrayList<>();
private List<Advisor> advisorKey = this.advisors;
/**
* No-arg constructor for use as a JavaBean.
*/
public AdvisedSupport() {
this.advisorChainFactory = DefaultAdvisorChainFactory.INSTANCE;
this.methodCache = new ConcurrentHashMap<>(32);
}
/**
* Create an {@code AdvisedSupport} instance with the given parameters.
* Create a AdvisedSupport instance with the given parameters.
* @param interfaces the proxied interfaces
*/
public AdvisedSupport(Class<?>... interfaces) {
@@ -122,19 +116,10 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
setInterfaces(interfaces);
}
/**
* Internal constructor for {@link #getConfigurationOnlyCopy()}.
* @since 6.0.10
*/
private AdvisedSupport(AdvisorChainFactory advisorChainFactory, Map<MethodCacheKey, List<Object>> methodCache) {
this.advisorChainFactory = advisorChainFactory;
this.methodCache = methodCache;
}
/**
* Set the given object as target.
* <p>Will create a SingletonTargetSource for the object.
* Will create a SingletonTargetSource for the object.
* @see #setTargetSource
* @see org.springframework.aop.target.SingletonTargetSource
*/
@@ -509,9 +494,9 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
}
/**
* Copy the AOP configuration from the given {@link AdvisedSupport} object,
* but allow substitution of a fresh {@link TargetSource} and a given interceptor chain.
* @param other the {@code AdvisedSupport} object to take proxy configuration from
* Copy the AOP configuration from the given AdvisedSupport object,
* but allow substitution of a fresh TargetSource and a given interceptor chain.
* @param other the AdvisedSupport object to take proxy configuration from
* @param targetSource the new TargetSource
* @param advisors the Advisors for the chain
*/
@@ -531,31 +516,19 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
}
/**
* Build a configuration-only copy of this {@link AdvisedSupport},
* replacing the {@link TargetSource}.
* Build a configuration-only copy of this AdvisedSupport,
* replacing the TargetSource.
*/
AdvisedSupport getConfigurationOnlyCopy() {
AdvisedSupport copy = new AdvisedSupport(this.advisorChainFactory, this.methodCache);
AdvisedSupport copy = new AdvisedSupport();
copy.copyFrom(this);
copy.targetSource = EmptyTargetSource.forClass(getTargetClass(), getTargetSource().isStatic());
copy.advisorChainFactory = this.advisorChainFactory;
copy.interfaces = new ArrayList<>(this.interfaces);
copy.advisors = new ArrayList<>(this.advisors);
copy.advisorKey = new ArrayList<>(this.advisors.size());
for (Advisor advisor : this.advisors) {
copy.advisorKey.add(new AdvisorKeyEntry(advisor));
}
return copy;
}
void reduceToAdvisorKey() {
this.advisors = this.advisorKey;
this.methodCache = Collections.emptyMap();
}
Object getAdvisorKey() {
return this.advisorKey;
}
//---------------------------------------------------------------------
// Serialization support
@@ -607,7 +580,8 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
@Override
public boolean equals(@Nullable Object other) {
return (this == other || (other instanceof MethodCacheKey that && this.method == that.method));
return (this == other || (other instanceof MethodCacheKey methodCacheKey &&
this.method == methodCacheKey.method));
}
@Override
@@ -630,56 +604,4 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
}
}
/**
* Stub for an {@link Advisor} instance that is just needed for key purposes,
* allowing for efficient equals and hashCode comparisons against the
* advice class and the pointcut.
* @since 6.0.10
* @see #getConfigurationOnlyCopy()
* @see #getAdvisorKey()
*/
private static class AdvisorKeyEntry implements Advisor {
private final Class<?> adviceType;
@Nullable
private final String classFilterKey;
@Nullable
private final String methodMatcherKey;
public AdvisorKeyEntry(Advisor advisor) {
this.adviceType = advisor.getAdvice().getClass();
if (advisor instanceof PointcutAdvisor pointcutAdvisor) {
Pointcut pointcut = pointcutAdvisor.getPointcut();
this.classFilterKey = pointcut.getClassFilter().toString();
this.methodMatcherKey = pointcut.getMethodMatcher().toString();
}
else {
this.classFilterKey = null;
this.methodMatcherKey = null;
}
}
@Override
public Advice getAdvice() {
throw new UnsupportedOperationException();
}
@Override
public boolean equals(Object other) {
return (this == other || (other instanceof AdvisorKeyEntry that &&
this.adviceType == that.adviceType &&
ObjectUtils.nullSafeEquals(this.classFilterKey, that.classFilterKey) &&
ObjectUtils.nullSafeEquals(this.methodMatcherKey, that.methodMatcherKey)));
}
@Override
public int hashCode() {
return this.adviceType.hashCode();
}
}
}
@@ -26,11 +26,14 @@ import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.Advisor;
import org.springframework.aop.AopInvocationException;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.aop.RawTargetAccess;
import org.springframework.aop.TargetSource;
import org.springframework.aop.support.AopUtils;
@@ -202,21 +205,12 @@ class CglibAopProxy implements AopProxy, Serializable {
types[x] = callbacks[x].getClass();
}
// fixedInterceptorMap only populated at this point, after getCallbacks call above
ProxyCallbackFilter filter = new ProxyCallbackFilter(
this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset);
enhancer.setCallbackFilter(filter);
enhancer.setCallbackFilter(new ProxyCallbackFilter(
this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
enhancer.setCallbackTypes(types);
// Generate the proxy class and create a proxy instance.
// ProxyCallbackFilter has method introspection capability with Advisor access.
try {
return (classOnly ? createProxyClass(enhancer) : createProxyClassAndInstance(enhancer, callbacks));
}
finally {
// Reduce ProxyCallbackFilter to key-only state for its class cache role
// in the CGLIB$CALLBACK_FILTER field, not leaking any Advisor state...
filter.advised.reduceToAdvisorKey();
}
return (classOnly ? createProxyClass(enhancer) : createProxyClassAndInstance(enhancer, callbacks));
}
catch (CodeGenerationException | IllegalArgumentException ex) {
throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
@@ -300,9 +294,9 @@ class CglibAopProxy implements AopProxy, Serializable {
private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
// Parameters used for optimization choices...
boolean isStatic = this.advised.getTargetSource().isStatic();
boolean isFrozen = this.advised.isFrozen();
boolean exposeProxy = this.advised.isExposeProxy();
boolean isFrozen = this.advised.isFrozen();
boolean isStatic = this.advised.getTargetSource().isStatic();
// Choose an "aop" interceptor (used for AOP calls).
Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
@@ -370,8 +364,8 @@ class CglibAopProxy implements AopProxy, Serializable {
@Override
public boolean equals(@Nullable Object other) {
return (this == other || (other instanceof CglibAopProxy that &&
AopProxyUtils.equalsInProxy(this.advised, that.advised)));
return (this == other || (other instanceof CglibAopProxy cglibAopProxy &&
AopProxyUtils.equalsInProxy(this.advised, cglibAopProxy.advised)));
}
@Override
@@ -597,10 +591,15 @@ class CglibAopProxy implements AopProxy, Serializable {
}
if (other instanceof Factory factory) {
Callback callback = factory.getCallback(INVOKE_EQUALS);
return (callback instanceof EqualsInterceptor that &&
AopProxyUtils.equalsInProxy(this.advised, that.advised));
if (!(callback instanceof EqualsInterceptor equalsInterceptor)) {
return false;
}
AdvisedSupport otherAdvised = equalsInterceptor.advised;
return AopProxyUtils.equalsInProxy(this.advised, otherAdvised);
}
else {
return false;
}
return false;
}
}
@@ -777,7 +776,7 @@ class CglibAopProxy implements AopProxy, Serializable {
*/
private static class ProxyCallbackFilter implements CallbackFilter {
final AdvisedSupport advised;
private final AdvisedSupport advised;
private final Map<Method, Integer> fixedInterceptorMap;
@@ -858,9 +857,9 @@ class CglibAopProxy implements AopProxy, Serializable {
// Proxy is not yet available, but that shouldn't matter.
List<?> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
boolean haveAdvice = !chain.isEmpty();
boolean exposeProxy = this.advised.isExposeProxy();
boolean isStatic = this.advised.getTargetSource().isStatic();
boolean isFrozen = this.advised.isFrozen();
boolean exposeProxy = this.advised.isExposeProxy();
if (haveAdvice || !isFrozen) {
// If exposing the proxy, then AOP_PROXY must be used.
if (exposeProxy) {
@@ -915,19 +914,70 @@ class CglibAopProxy implements AopProxy, Serializable {
@Override
public boolean equals(@Nullable Object other) {
return (this == other || (other instanceof ProxyCallbackFilter that &&
this.advised.getAdvisorKey().equals(that.advised.getAdvisorKey()) &&
AopProxyUtils.equalsProxiedInterfaces(this.advised, that.advised) &&
ObjectUtils.nullSafeEquals(this.advised.getTargetClass(), that.advised.getTargetClass()) &&
this.advised.getTargetSource().isStatic() == that.advised.getTargetSource().isStatic() &&
this.advised.isFrozen() == that.advised.isFrozen() &&
this.advised.isExposeProxy() == that.advised.isExposeProxy() &&
this.advised.isOpaque() == that.advised.isOpaque()));
if (this == other) {
return true;
}
if (!(other instanceof ProxyCallbackFilter otherCallbackFilter)) {
return false;
}
AdvisedSupport otherAdvised = otherCallbackFilter.advised;
if (this.advised.isFrozen() != otherAdvised.isFrozen()) {
return false;
}
if (this.advised.isExposeProxy() != otherAdvised.isExposeProxy()) {
return false;
}
if (this.advised.getTargetSource().isStatic() != otherAdvised.getTargetSource().isStatic()) {
return false;
}
if (!AopProxyUtils.equalsProxiedInterfaces(this.advised, otherAdvised)) {
return false;
}
// Advice instance identity is unimportant to the proxy class:
// All that matters is type and ordering.
if (this.advised.getAdvisorCount() != otherAdvised.getAdvisorCount()) {
return false;
}
Advisor[] thisAdvisors = this.advised.getAdvisors();
Advisor[] thatAdvisors = otherAdvised.getAdvisors();
for (int i = 0; i < thisAdvisors.length; i++) {
Advisor thisAdvisor = thisAdvisors[i];
Advisor thatAdvisor = thatAdvisors[i];
if (!equalsAdviceClasses(thisAdvisor, thatAdvisor)) {
return false;
}
if (!equalsPointcuts(thisAdvisor, thatAdvisor)) {
return false;
}
}
return true;
}
private static boolean equalsAdviceClasses(Advisor a, Advisor b) {
return (a.getAdvice().getClass() == b.getAdvice().getClass());
}
private static boolean equalsPointcuts(Advisor a, Advisor b) {
// If only one of the advisor (but not both) is PointcutAdvisor, then it is a mismatch.
// Takes care of the situations where an IntroductionAdvisor is used (see SPR-3959).
return (!(a instanceof PointcutAdvisor pointcutAdvisor1) ||
(b instanceof PointcutAdvisor pointcutAdvisor2 &&
ObjectUtils.nullSafeEquals(pointcutAdvisor1.getPointcut(), pointcutAdvisor2.getPointcut())));
}
@Override
public int hashCode() {
return this.advised.getAdvisorKey().hashCode();
int hashCode = 0;
Advisor[] advisors = this.advised.getAdvisors();
for (Advisor advisor : advisors) {
Advice advice = advisor.getAdvice();
hashCode = 13 * hashCode + advice.getClass().hashCode();
}
hashCode = 13 * hashCode + (this.advised.isFrozen() ? 1 : 0);
hashCode = 13 * hashCode + (this.advised.isExposeProxy() ? 1 : 0);
hashCode = 13 * hashCode + (this.advised.isOptimize() ? 1 : 0);
hashCode = 13 * hashCode + (this.advised.isOpaque() ? 1 : 0);
return hashCode;
}
}

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