mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
259 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d045d40702 | |||
| afd67a0deb | |||
| dcaddddbc5 | |||
| ce3be72e7f | |||
| 3e3f046dc9 | |||
| 5847454aa4 | |||
| 569df6eecc | |||
| 979118c1eb | |||
| f87a87e29d | |||
| 0619e19140 | |||
| ac521a366a | |||
| 9a4df5a97d | |||
| f54e1ef880 | |||
| a3efcabe0c | |||
| dd3dc2d0d0 | |||
| 2578e3894e | |||
| 5335778d8d | |||
| 616e7779e0 | |||
| 84c7f07364 | |||
| ac429a4ef7 | |||
| 12d4dc1bae | |||
| 4da2499613 | |||
| 18896aceca | |||
| d7824c7831 | |||
| f91b2249b3 | |||
| 33b329e13a | |||
| b23316302d | |||
| dc843adb45 | |||
| 9fcafd2650 | |||
| ccbb4bdd27 | |||
| 5b79a57f14 | |||
| 189d4e3e4c | |||
| 79a1fcb099 | |||
| 026be04b75 | |||
| 45ae00fda3 | |||
| 3e2f58cdd2 | |||
| 72926c29f9 | |||
| f5c1e2ffa1 | |||
| 67df0756cd | |||
| 17bc3facaa | |||
| 1de9426df0 | |||
| 27fdf56ed5 | |||
| 55392dfbd7 | |||
| ca9439657e | |||
| 5819d76d98 | |||
| 431ae03447 | |||
| a7ec6b4a2a | |||
| e5ff54955f | |||
| 9c0b28ffdc | |||
| 08e7f5a292 | |||
| 5f458e2578 | |||
| 7584dfc79a | |||
| dc2c33e0ab | |||
| dbf360997a | |||
| b719a87061 | |||
| 97e557c886 | |||
| 89c170b095 | |||
| 482482f62b | |||
| a2a676241c | |||
| 28602ad012 | |||
| 8e9dc3590d | |||
| b8a7aa9395 | |||
| cb63164593 | |||
| 9f061f1eec | |||
| 1d4bf58e8d | |||
| ce85fdc5c7 | |||
| 7851994a17 | |||
| 84bc1dfa89 | |||
| 159a3e71f2 | |||
| adbba712d6 | |||
| e050c37158 | |||
| eff1a1a664 | |||
| 8838e0d825 | |||
| 0d21f0b1d4 | |||
| 41aeb07585 | |||
| 70aaec2a8d | |||
| 3029b7b9be | |||
| 708f600afe | |||
| f7418704de | |||
| 7440afe571 | |||
| bc583ea74f | |||
| dcda127b60 | |||
| ac6385025b | |||
| e170c16b02 | |||
| 4beb05ddb3 | |||
| 06fd6ecf1f | |||
| b8827d8e11 | |||
| 23a60aeb27 | |||
| 58fbf60d2d | |||
| ebca9f726f | |||
| 5fd73f0bae | |||
| 16937c7ce5 | |||
| c0c9ba5c2c | |||
| 4d6249811e | |||
| 3a08d643ed | |||
| a74b86e812 | |||
| 7c9dca3d2e | |||
| 64219783f6 | |||
| 846a54aef4 | |||
| 9e735d4f0f | |||
| 0ddb394fd0 | |||
| f5a39308a0 | |||
| e4ceb80678 | |||
| 64b89429cb | |||
| 8e843c242a | |||
| 635f010de3 | |||
| aac1ed6ee5 | |||
| d5b0782700 | |||
| ebe717ff6c | |||
| 89c7c6e9dd | |||
| 9c6fd3ed06 | |||
| e564a0de46 | |||
| 17f1eadb6f | |||
| 09991f2492 | |||
| 15931532cd | |||
| aa9f445422 | |||
| b578ff1296 | |||
| cf9fc69d6b | |||
| 04321d0577 | |||
| 67c141f15b | |||
| ad4e0d9ad7 | |||
| c13dfc5144 | |||
| b7d53ac9a4 | |||
| f9884e08af | |||
| 9819cb3eb5 | |||
| 9b0929c392 | |||
| af21ebe6c6 | |||
| 0b446a94c5 | |||
| 5dfa61eb0b | |||
| b267547fb4 | |||
| e0f60dc09d | |||
| 8783075e40 | |||
| f2cdced501 | |||
| 33a0e65135 | |||
| e3fb0a39a2 | |||
| 57fb072215 | |||
| 75046bbea0 | |||
| 8e2eeb5364 | |||
| 556863d401 | |||
| 6a6a35a0b9 | |||
| 7da6e93597 | |||
| 963274a915 | |||
| d72df5ace4 | |||
| 767f59a3ae | |||
| da088e58c3 | |||
| 5502e61519 | |||
| ab9bea1d93 | |||
| e6397c8a38 | |||
| 1e47f31210 | |||
| 57b6f7e494 | |||
| d37ef61b30 | |||
| 3f148c2aaa | |||
| fa1ef1dc85 | |||
| 58872c79ee | |||
| 24f18275dd | |||
| 8f94c4e933 | |||
| 20c79e1481 | |||
| fb0aa5abb3 | |||
| 86a40716bd | |||
| 7f48d1b55a | |||
| fd84b0997a | |||
| 0e8838db90 | |||
| 94fc24b6fb | |||
| ab0d1c395d | |||
| 047f660572 | |||
| 22323072fd | |||
| a34b1af49e | |||
| 1ace42f245 | |||
| 0502d18e3d | |||
| c4c786596f | |||
| c3d123fef7 | |||
| e4b25ab480 | |||
| 1680502313 | |||
| e725e3a51b | |||
| 2fe75c1a24 | |||
| 9ebd1e8d64 | |||
| 64de6de725 | |||
| c3e8ff9ce7 | |||
| 5448e81021 | |||
| f7e07322ab | |||
| 37217a63e1 | |||
| ad5c636aff | |||
| ebdc82b86b | |||
| a12c9a0430 | |||
| 0d14c3bc1a | |||
| 8be542016b | |||
| 3a585f8c29 | |||
| c0e38a2f1e | |||
| 5de1460f88 | |||
| 312db36849 | |||
| 46875c91ce | |||
| 86305c45a9 | |||
| 1342837686 | |||
| 012b8f18c6 | |||
| 5ea310452c | |||
| a4956dfe26 | |||
| 0415975dd1 | |||
| afb8a0d1b1 | |||
| 48abd493fe | |||
| 3d571e8ab5 | |||
| c7cfdfc26c | |||
| 4d27aee7cf | |||
| 63f0beb209 | |||
| 8dbdfb0449 | |||
| d5fb5d029b | |||
| 3738a45658 | |||
| fd3e99c7ee | |||
| 4396801933 | |||
| a516ed8739 | |||
| 88cac6a4a3 | |||
| 11df955d88 | |||
| 77832a6da9 | |||
| ce1f6cf0bf | |||
| c702cd418e | |||
| 4bcc24372a | |||
| ae7cff35dc | |||
| 8282be682c | |||
| b81a291c85 | |||
| 3348e74ab8 | |||
| e2832ea596 | |||
| 74f58198fd | |||
| 42b16591ec | |||
| 62cf2f0a4f | |||
| 32e1a6452a | |||
| 55295faff0 | |||
| 254c3725e2 | |||
| d5ff232246 | |||
| a644245e0e | |||
| 697292ba0c | |||
| 5366ac84e6 | |||
| b71db12c43 | |||
| ab571e3562 | |||
| 5e42a6eb0a | |||
| 0bfddbc9cb | |||
| 511dab1ade | |||
| d4623a393b | |||
| c2c3be4ee3 | |||
| 270f8526d1 | |||
| 4171e35546 | |||
| bea505d32e | |||
| 5c28b56823 | |||
| d2b54e2611 | |||
| f8a58f2bc4 | |||
| f6e1fb3150 | |||
| 14c7b9bd08 | |||
| 11f3edc352 | |||
| fbd4463f20 | |||
| a3a94950f6 | |||
| b51a14ff92 | |||
| 929d5567b8 | |||
| beaa7ba0e3 | |||
| 1a839f5103 | |||
| 8df4f927fd | |||
| 5965917d16 | |||
| 57cfb94f1f | |||
| b5b80d0a07 | |||
| e5cfbae3fd | |||
| 5d2ca11315 | |||
| 858394b720 |
@@ -30,5 +30,7 @@ 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,3 +1,3 @@
|
||||
# Enable auto-env through the sdkman_auto_env config
|
||||
# Add key=value pairs of SDKs to use below
|
||||
java=17.0.5-librca
|
||||
java=17.0.6-librca
|
||||
|
||||
+7
-7
@@ -1,5 +1,5 @@
|
||||
plugins {
|
||||
id 'io.spring.nohttp' version '0.0.10'
|
||||
id 'io.spring.nohttp' version '0.0.11'
|
||||
id 'io.freefair.aspectj' version '6.5.0.3' apply false
|
||||
// kotlinVersion is managed in gradle.properties
|
||||
id 'org.jetbrains.kotlin.plugin.serialization' version "${kotlinVersion}" apply false
|
||||
@@ -78,7 +78,7 @@ configure([rootProject] + javaProjects) { project ->
|
||||
}
|
||||
|
||||
checkstyle {
|
||||
toolVersion = "10.5.0"
|
||||
toolVersion = "10.7.0"
|
||||
configDirectory.set(rootProject.file("src/checkstyle"))
|
||||
}
|
||||
|
||||
@@ -121,18 +121,18 @@ configure([rootProject] + javaProjects) { project ->
|
||||
"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://fasterxml.github.io/jackson-core/javadoc/2.10/",
|
||||
"https://fasterxml.github.io/jackson-databind/javadoc/2.10/",
|
||||
"https://fasterxml.github.io/jackson-dataformat-xml/javadoc/2.10/",
|
||||
"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/",
|
||||
"https://www.javadoc.io/doc/com.fasterxml.jackson.dataformat/jackson-dataformat-xml/2.14.1/",
|
||||
"https://hc.apache.org/httpcomponents-client-5.2.x/current/httpclient5/apidocs/",
|
||||
"https://projectreactor.io/docs/test/release/api/",
|
||||
"https://junit.org/junit4/javadoc/4.13.2/",
|
||||
// TODO Uncomment link to JUnit 5 docs once we have sorted out
|
||||
// the following warning in the build.
|
||||
//
|
||||
// warning: The code being documented uses packages in the unnamed module, but the packages defined in https://junit.org/junit5/docs/5.9.1/api/ are in named modules.
|
||||
// warning: The code being documented uses packages in the unnamed module, but the packages defined in https://junit.org/junit5/docs/5.9.2/api/ are in named modules.
|
||||
//
|
||||
// "https://junit.org/junit5/docs/5.9.1/api/",
|
||||
// "https://junit.org/junit5/docs/5.9.2/api/",
|
||||
"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/",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* 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.
|
||||
@@ -40,7 +40,6 @@ class TestConventions {
|
||||
}
|
||||
|
||||
private void configureTestConventions(Project project) {
|
||||
project.getPlugins().apply(TestRetryPlugin.class);
|
||||
project.getTasks().withType(Test.class,
|
||||
(test) -> project.getPlugins().withType(TestRetryPlugin.class, (testRetryPlugin) -> {
|
||||
TestRetryTaskExtension testRetry = test.getExtensions().getByType(TestRetryTaskExtension.class);
|
||||
|
||||
@@ -17,4 +17,4 @@ changelog:
|
||||
- "type: dependency-upgrade"
|
||||
contributors:
|
||||
exclude:
|
||||
names: ["bclozel", "jhoeller", "poutsma", "rstoyanchev", "sbrannen", "sdeleuze", "snicoll"]
|
||||
names: ["bclozel", "jhoeller", "poutsma", "rstoyanchev", "sbrannen", "sdeleuze", "snicoll", "simonbasle"]
|
||||
|
||||
@@ -6,7 +6,6 @@ RUN ./setup.sh
|
||||
|
||||
ENV JAVA_HOME /opt/openjdk/java17
|
||||
ENV JDK17 /opt/openjdk/java17
|
||||
ENV JDK18 /opt/openjdk/java18
|
||||
ENV JDK19 /opt/openjdk/java19
|
||||
|
||||
ENV PATH $JAVA_HOME/bin:$PATH
|
||||
|
||||
@@ -3,13 +3,10 @@ set -e
|
||||
|
||||
case "$1" in
|
||||
java17)
|
||||
echo "https://github.com/bell-sw/Liberica/releases/download/17.0.5+8/bellsoft-jdk17.0.5+8-linux-amd64.tar.gz"
|
||||
;;
|
||||
java18)
|
||||
echo "https://github.com/bell-sw/Liberica/releases/download/18.0.2.1%2B1/bellsoft-jdk18.0.2.1+1-linux-amd64.tar.gz"
|
||||
echo "https://github.com/bell-sw/Liberica/releases/download/17.0.6+10/bellsoft-jdk17.0.6+10-linux-amd64.tar.gz"
|
||||
;;
|
||||
java19)
|
||||
echo "https://github.com/bell-sw/Liberica/releases/download/19.0.1%2B11/bellsoft-jdk19.0.1+11-linux-amd64.tar.gz"
|
||||
echo "https://github.com/bell-sw/Liberica/releases/download/19.0.2+9/bellsoft-jdk19.0.2+9-linux-amd64.tar.gz"
|
||||
;;
|
||||
*)
|
||||
echo $"Unknown java version"
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ curl https://raw.githubusercontent.com/spring-io/concourse-java-scripts/v0.0.4/c
|
||||
|
||||
mkdir -p /opt/openjdk
|
||||
pushd /opt/openjdk > /dev/null
|
||||
for jdk in java17 java18 java19
|
||||
for jdk in java17 java19
|
||||
do
|
||||
JDK_URL=$( /get-jdk-url.sh $jdk )
|
||||
mkdir $jdk
|
||||
|
||||
@@ -8,7 +8,4 @@ milestone: "6.0.x"
|
||||
build-name: "spring-framework"
|
||||
pipeline-name: "spring-framework"
|
||||
concourse-url: "https://ci.spring.io"
|
||||
registry-mirror-host: docker.repo.spring.io
|
||||
registry-mirror-username: ((artifactory-username))
|
||||
registry-mirror-password: ((artifactory-password))
|
||||
task-timeout: 1h00m
|
||||
|
||||
+14
-44
@@ -23,11 +23,6 @@ anchors:
|
||||
docker-resource-source: &docker-resource-source
|
||||
username: ((docker-hub-username))
|
||||
password: ((docker-hub-password))
|
||||
tag: ((milestone))
|
||||
registry-mirror-vars: ®istry-mirror-vars
|
||||
registry-mirror-host: ((registry-mirror-host))
|
||||
registry-mirror-username: ((registry-mirror-username))
|
||||
registry-mirror-password: ((registry-mirror-password))
|
||||
slack-fail-params: &slack-fail-params
|
||||
text: >
|
||||
:concourse-failed: <https://ci.spring.io/teams/${BUILD_TEAM_NAME}/pipelines/${BUILD_PIPELINE_NAME}/jobs/${BUILD_JOB_NAME}/builds/${BUILD_NAME}|${BUILD_PIPELINE_NAME} ${BUILD_JOB_NAME} failed!>
|
||||
@@ -48,31 +43,37 @@ resource_types:
|
||||
- name: registry-image
|
||||
type: registry-image
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: concourse/registry-image-resource
|
||||
tag: 1.5.0
|
||||
- name: artifactory-resource
|
||||
type: registry-image
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: springio/artifactory-resource
|
||||
tag: 0.0.17
|
||||
tag: 0.0.18
|
||||
- name: github-release
|
||||
type: registry-image
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: concourse/github-release-resource
|
||||
tag: 1.5.5
|
||||
- name: github-status-resource
|
||||
type: registry-image
|
||||
source:
|
||||
<<: *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:
|
||||
<<: *docker-resource-source
|
||||
repository: cfcommunity/slack-notification-resource
|
||||
tag: latest
|
||||
resources:
|
||||
@@ -94,6 +95,7 @@ resources:
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: ((docker-hub-organization))/spring-framework-ci
|
||||
tag: ((milestone))
|
||||
- name: every-morning
|
||||
type: time
|
||||
icon: alarm
|
||||
@@ -125,14 +127,6 @@ resources:
|
||||
access_token: ((github-ci-status-token))
|
||||
branch: ((branch))
|
||||
context: build
|
||||
- name: repo-status-jdk18-build
|
||||
type: github-status-resource
|
||||
icon: eye-check-outline
|
||||
source:
|
||||
repository: ((github-repo-name))
|
||||
access_token: ((github-ci-status-token))
|
||||
branch: ((branch))
|
||||
context: jdk18-build
|
||||
- name: repo-status-jdk19-build
|
||||
type: github-status-resource
|
||||
icon: eye-check-outline
|
||||
@@ -176,7 +170,7 @@ jobs:
|
||||
image: ci-image
|
||||
vars:
|
||||
ci-image-name: ci-image
|
||||
<<: *registry-mirror-vars
|
||||
<<: *docker-resource-source
|
||||
- put: ci-image
|
||||
params:
|
||||
image: ci-image/image.tar
|
||||
@@ -237,34 +231,6 @@ jobs:
|
||||
"zip.type": "schema"
|
||||
get_params:
|
||||
threads: 8
|
||||
- name: jdk18-build
|
||||
serial: true
|
||||
public: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
- get: every-morning
|
||||
trigger: true
|
||||
- put: repo-status-jdk18-build
|
||||
params: { state: "pending", commit: "git-repo" }
|
||||
- do:
|
||||
- task: check-project
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/check-project.yml
|
||||
privileged: true
|
||||
timeout: ((task-timeout))
|
||||
params:
|
||||
TEST_TOOLCHAIN: 18
|
||||
<<: *build-project-task-params
|
||||
on_failure:
|
||||
do:
|
||||
- put: repo-status-jdk18-build
|
||||
params: { state: "failure", commit: "git-repo" }
|
||||
- put: slack-alert
|
||||
params:
|
||||
<<: *slack-fail-params
|
||||
- put: repo-status-jdk18-build
|
||||
params: { state: "success", commit: "git-repo" }
|
||||
- name: jdk19-build
|
||||
serial: true
|
||||
public: true
|
||||
@@ -365,6 +331,7 @@ jobs:
|
||||
params:
|
||||
RELEASE_TYPE: M
|
||||
<<: *github-task-params
|
||||
<<: *docker-resource-source
|
||||
- put: github-pre-release
|
||||
params:
|
||||
<<: *changelog-task-params
|
||||
@@ -403,6 +370,7 @@ jobs:
|
||||
file: git-repo/ci/tasks/promote-version.yml
|
||||
params:
|
||||
RELEASE_TYPE: RC
|
||||
<<: *docker-resource-source
|
||||
<<: *artifactory-task-params
|
||||
- task: generate-changelog
|
||||
file: git-repo/ci/tasks/generate-changelog.yml
|
||||
@@ -447,6 +415,7 @@ jobs:
|
||||
file: git-repo/ci/tasks/promote-version.yml
|
||||
params:
|
||||
RELEASE_TYPE: RELEASE
|
||||
<<: *docker-resource-source
|
||||
<<: *artifactory-task-params
|
||||
<<: *sonatype-task-params
|
||||
- name: create-github-release
|
||||
@@ -464,6 +433,7 @@ jobs:
|
||||
file: git-repo/ci/tasks/generate-changelog.yml
|
||||
params:
|
||||
RELEASE_TYPE: RELEASE
|
||||
<<: *docker-resource-source
|
||||
<<: *github-task-params
|
||||
- put: github-release
|
||||
params:
|
||||
@@ -471,7 +441,7 @@ jobs:
|
||||
|
||||
groups:
|
||||
- name: "builds"
|
||||
jobs: ["build", "jdk18-build", "jdk19-build"]
|
||||
jobs: ["build", "jdk19-build"]
|
||||
- name: "releases"
|
||||
jobs: ["stage-milestone", "stage-rc", "stage-release", "promote-milestone", "promote-rc", "promote-release", "create-github-release"]
|
||||
- name: "ci-images"
|
||||
|
||||
@@ -4,6 +4,6 @@ set -e
|
||||
source $(dirname $0)/common.sh
|
||||
|
||||
pushd git-repo > /dev/null
|
||||
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false -Porg.gradle.java.installations.fromEnv=JDK17,JDK18 \
|
||||
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false -Porg.gradle.java.installations.fromEnv=JDK17,JDK19 \
|
||||
-PmainToolchain=${MAIN_TOOLCHAIN} -PtestToolchain=${TEST_TOOLCHAIN} --no-daemon --max-workers=4 check
|
||||
popd > /dev/null
|
||||
|
||||
@@ -5,10 +5,8 @@ image_resource:
|
||||
source:
|
||||
repository: concourse/oci-build-task
|
||||
tag: 0.10.0
|
||||
registry_mirror:
|
||||
host: ((registry-mirror-host))
|
||||
username: ((registry-mirror-username))
|
||||
password: ((registry-mirror-password))
|
||||
username: ((docker-hub-username))
|
||||
password: ((docker-hub-password))
|
||||
inputs:
|
||||
- name: ci-images-git-repo
|
||||
outputs:
|
||||
|
||||
@@ -5,6 +5,8 @@ image_resource:
|
||||
source:
|
||||
repository: springio/github-changelog-generator
|
||||
tag: '0.0.8'
|
||||
username: ((docker-hub-username))
|
||||
password: ((docker-hub-password))
|
||||
inputs:
|
||||
- name: git-repo
|
||||
- name: artifactory-repo
|
||||
|
||||
@@ -5,6 +5,8 @@ image_resource:
|
||||
source:
|
||||
repository: springio/concourse-release-scripts
|
||||
tag: '0.3.4'
|
||||
username: ((docker-hub-username))
|
||||
password: ((docker-hub-password))
|
||||
inputs:
|
||||
- name: git-repo
|
||||
- name: artifactory-repo
|
||||
|
||||
@@ -28,13 +28,7 @@ javadoc {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
asciidoctorExtensions "io.spring.asciidoctor.backends:spring-asciidoctor-backends:0.0.3"
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
url "https://repo.spring.io/release"
|
||||
}
|
||||
asciidoctorExtensions "io.spring.asciidoctor.backends:spring-asciidoctor-backends:0.0.4"
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
:chomp: default headers packages
|
||||
:fold: all
|
||||
:docs-site: https://docs.spring.io
|
||||
// Spring Framework
|
||||
:docs-spring-framework: {docs-site}/spring-framework/docs/{spring-version}
|
||||
|
||||
@@ -23,7 +23,7 @@ Spring's pointcut model enables pointcut reuse independent of advice types. You
|
||||
target different advice with the same pointcut.
|
||||
|
||||
The `org.springframework.aop.Pointcut` interface is the central interface, used to
|
||||
target advices to particular classes and methods. The complete interface follows:
|
||||
target advice to particular classes and methods. The complete interface follows:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@@ -843,7 +843,7 @@ created by the implementation of the `getObject()` method in the `ProxyFactoryBe
|
||||
method creates an AOP proxy that wraps a target object.
|
||||
|
||||
One of the most important benefits of using a `ProxyFactoryBean` or another IoC-aware
|
||||
class to create AOP proxies is that advices and pointcuts can also be
|
||||
class to create AOP proxies is that advice and pointcuts can also be
|
||||
managed by IoC. This is a powerful feature, enabling certain approaches that are hard to
|
||||
achieve with other AOP frameworks. For example, an advice may itself reference
|
||||
application objects (besides the target, which should be available in any AOP
|
||||
@@ -901,7 +901,7 @@ to be applied. You can find an example of using this feature in <<aop-global-adv
|
||||
* singleton: Whether or not the factory should return a single object, no matter how
|
||||
often the `getObject()` method is called. Several `FactoryBean` implementations offer
|
||||
such a method. The default value is `true`. If you want to use stateful advice - for
|
||||
example, for stateful mixins - use prototype advices along with a singleton value of
|
||||
example, for stateful mixins - use prototype advice along with a singleton value of
|
||||
`false`.
|
||||
|
||||
|
||||
@@ -965,7 +965,7 @@ Consider a simple example of `ProxyFactoryBean` in action. This example involves
|
||||
the example.
|
||||
* An `Advisor` and an `Interceptor` used to provide advice.
|
||||
* An AOP proxy bean definition to specify the target object (the `personTarget` bean),
|
||||
the interfaces to proxy, and the advices to apply.
|
||||
the interfaces to proxy, and the advice to apply.
|
||||
|
||||
The following listing shows the example:
|
||||
|
||||
@@ -1100,12 +1100,15 @@ subclass is used to implement the Decorator pattern, weaving in the advice.
|
||||
CGLIB proxying should generally be transparent to users. However, there are some issues
|
||||
to consider:
|
||||
|
||||
* `Final` methods cannot be advised, as they cannot be overridden.
|
||||
* There is no need to add CGLIB to your classpath. As of Spring 3.2, CGLIB is repackaged
|
||||
and included in the spring-core JAR. In other words, CGLIB-based AOP works "`out of
|
||||
the box`", as do JDK dynamic proxies.
|
||||
* `final` classes cannot be proxied, because they cannot be extended.
|
||||
* `final` methods cannot be advised, because they cannot be overridden.
|
||||
* `private` methods cannot be advised, because they cannot be overridden.
|
||||
|
||||
There is little performance difference between CGLIB proxying and dynamic proxies.
|
||||
NOTE: There is no need to add CGLIB to your classpath. CGLIB is repackaged and included
|
||||
in the `spring-core` JAR. In other words, CGLIB-based AOP works "out of the box", as do
|
||||
JDK dynamic proxies.
|
||||
|
||||
There is little performance difference between CGLIB proxies and dynamic proxies.
|
||||
Performance should not be a decisive consideration in this case.
|
||||
|
||||
|
||||
@@ -1239,7 +1242,7 @@ The first step is to construct an object of type
|
||||
object, as in the preceding example, or specify the interfaces to be proxied in an alternate
|
||||
constructor.
|
||||
|
||||
You can add advices (with interceptors as a specialized kind of advice), advisors, or both
|
||||
You can add advice (with interceptors as a specialized kind of advice), advisors, or both
|
||||
and manipulate them for the life of the `ProxyFactory`. If you add an
|
||||
`IntroductionInterceptionAroundAdvisor`, you can cause the proxy to implement additional
|
||||
interfaces.
|
||||
@@ -1468,7 +1471,7 @@ Using this mechanism involves:
|
||||
|
||||
* Specifying a `DefaultAdvisorAutoProxyCreator` bean definition.
|
||||
* Specifying any number of advisors in the same or related contexts. Note that these
|
||||
must be advisors, not interceptors or other advices. This is necessary,
|
||||
must be advisors, not interceptors or other advice. This is necessary,
|
||||
because there must be a pointcut to evaluate, to check the eligibility of each advice
|
||||
to candidate bean definitions.
|
||||
|
||||
@@ -1731,7 +1734,7 @@ of target source, as the following example shows:
|
||||
|
||||
NOTE: `ThreadLocal` instances come with serious issues (potentially resulting in memory leaks) when
|
||||
incorrectly using them in multi-threaded and multi-classloader environments. You
|
||||
should always consider wrapping a threadlocal in some other class and never directly use
|
||||
should always consider wrapping a `ThreadLocal` in some other class and never directly use
|
||||
the `ThreadLocal` itself (except in the wrapper class). Also, you should
|
||||
always remember to correctly set and unset (where the latter simply involves a call to
|
||||
`ThreadLocal.set(null)`) the resource local to the thread. Unsetting should be done in
|
||||
|
||||
@@ -2680,7 +2680,7 @@ following:
|
||||
|
||||
|
||||
|
||||
[[aop-schema-instatiation-models]]
|
||||
[[aop-schema-instantiation-models]]
|
||||
=== Aspect Instantiation Models
|
||||
|
||||
The only supported instantiation model for schema-defined aspects is the singleton
|
||||
@@ -2920,8 +2920,7 @@ AspectJ. You also need to use AspectJ if you wish to advise join points other th
|
||||
simple method executions (for example, field get or set join points and so on).
|
||||
|
||||
When you use AspectJ, you have the choice of the AspectJ language syntax (also known as
|
||||
the "`code style`") or the @AspectJ annotation style. Clearly, if you do not use Java
|
||||
5+, the choice has been made for you: Use the code style. If aspects play a large
|
||||
the "`code style`") or the @AspectJ annotation style. If aspects play a large
|
||||
role in your design, and you are able to use the https://www.eclipse.org/ajdt/[AspectJ
|
||||
Development Tools (AJDT)] plugin for Eclipse, the AspectJ language syntax is the
|
||||
preferred option. It is cleaner and simpler because the language was purposefully
|
||||
@@ -4150,7 +4149,7 @@ The following example specifies a `ReflectiveLoadTimeWeaver`:
|
||||
}
|
||||
----
|
||||
|
||||
If you use XML-based configuration, you can specify the fully qualified classname
|
||||
If you use XML-based configuration, you can specify the fully qualified class name
|
||||
as the value of the `weaver-class` attribute on the `<context:load-time-weaver/>`
|
||||
element. Again, the following example specifies a `ReflectiveLoadTimeWeaver`:
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ Applying such optimizations early implies the following restrictions:
|
||||
* The beans defined in your application cannot change at runtime, meaning:
|
||||
** `@Profile`, in particular profile-specific configuration needs to be chosen at build time.
|
||||
** Environment properties that impact the presence of a bean (`@Conditional`) are only considered at build time.
|
||||
* Bean definitions with instance suppliers (lambdas or method references) can't be transformed Ahead of Time (see https://github.com/spring-projects/spring-framework/issues/29555[spring-framework#29555] related issue)
|
||||
* The return type of methods annotated with `@Bean` should be the most specific one in order to allow proper hint inference (typically the concrete class, not an interface).
|
||||
|
||||
When these restrictions are in place, it becomes possible to perform ahead-of-time processing at build time and generate additional assets.
|
||||
A Spring AOT processed application typically generates:
|
||||
@@ -72,7 +74,7 @@ These are:
|
||||
* `SmartInstantiationAwareBeanPostProcessor` implementations determine a more precise bean type if necessary.
|
||||
This makes sure to create any proxy that will be required at runtime.
|
||||
|
||||
One this part completes, the `BeanFactory` contains the bean definitions that are necessary for the application to run. It does not trigger bean instantiation but allows the AOT engine to inspect the beans that will be created at runtime.
|
||||
Once this part completes, the `BeanFactory` contains the bean definitions that are necessary for the application to run. It does not trigger bean instantiation but allows the AOT engine to inspect the beans that will be created at runtime.
|
||||
|
||||
[[core.aot.bean-factory-initialization-contributions]]
|
||||
== Bean Factory Initialization AOT Contributions
|
||||
|
||||
@@ -1069,7 +1069,7 @@ The following listing shows the `Component` class:
|
||||
private String name;
|
||||
private List<Component> components = new ArrayList<Component> ();
|
||||
|
||||
// mmm, there is no setter method for the 'components'
|
||||
// there is no setter method for the 'components'
|
||||
public void addComponent(Component component) {
|
||||
this.components.add(component);
|
||||
}
|
||||
@@ -1099,7 +1099,7 @@ The following listing shows the `Component` class:
|
||||
var name: String? = null
|
||||
private val components = ArrayList<Component>()
|
||||
|
||||
// mmm, there is no setter method for the 'components'
|
||||
// there is no setter method for the 'components'
|
||||
fun addComponent(component: Component) {
|
||||
this.components.add(component)
|
||||
}
|
||||
|
||||
@@ -524,15 +524,13 @@ container that hosts the bean. A bean usually has only one identifier. However,
|
||||
requires more than one, the extra ones can be considered aliases.
|
||||
|
||||
In XML-based configuration metadata, you use the `id` attribute, the `name` attribute, or
|
||||
both to specify the bean identifiers. The `id` attribute lets you specify
|
||||
exactly one id. Conventionally, these names are alphanumeric ('myBean',
|
||||
'someService', etc.), but they can contain special characters as well. If you want to
|
||||
introduce other aliases for the bean, you can also specify them in the `name`
|
||||
attribute, separated by a comma (`,`), semicolon (`;`), or white space. As a
|
||||
historical note, in versions prior to Spring 3.1, the `id` attribute was
|
||||
defined as an `xsd:ID` type, which constrained possible characters. As of 3.1,
|
||||
it is defined as an `xsd:string` type. Note that bean `id` uniqueness is still
|
||||
enforced by the container, though no longer by XML parsers.
|
||||
both to specify bean identifiers. The `id` attribute lets you specify exactly one `id`.
|
||||
Conventionally, these names are alphanumeric ('myBean', 'someService', etc.), but they
|
||||
can contain special characters as well. If you want to introduce other aliases for the
|
||||
bean, you can also specify them in the `name` attribute, separated by a comma (`,`),
|
||||
semicolon (`;`), or white space. Although the `id` attribute is defined as an
|
||||
`xsd:string` type, bean `id` uniqueness is enforced by the container, though not by XML
|
||||
parsers.
|
||||
|
||||
You are not required to supply a `name` or an `id` for a bean. If you do not supply a
|
||||
`name` or `id` explicitly, the container generates a unique name for that bean. However,
|
||||
@@ -605,7 +603,7 @@ creating a namespace), yet they refer to the same bean.
|
||||
|
||||
.Java-configuration
|
||||
****
|
||||
If you use Javaconfiguration, the `@Bean` annotation can be used to provide aliases.
|
||||
If you use Java Configuration, the `@Bean` annotation can be used to provide aliases.
|
||||
See <<beans-java-bean-annotation>> for details.
|
||||
****
|
||||
|
||||
@@ -2704,8 +2702,8 @@ The following table describes the supported scopes:
|
||||
the context of a web-aware Spring `ApplicationContext`.
|
||||
|===
|
||||
|
||||
NOTE: As of Spring 3.0, a thread scope is available but is not registered by default. For
|
||||
more information, see the documentation for
|
||||
NOTE: A thread scope is available but is not registered by default. For more information,
|
||||
see the documentation for
|
||||
{api-spring-framework}/context/support/SimpleThreadScope.html[`SimpleThreadScope`].
|
||||
For instructions on how to register this or any other custom scope, see
|
||||
<<beans-factory-scopes-custom-using>>.
|
||||
@@ -3981,7 +3979,7 @@ dependency type. The following table summarizes the most important `Aware` inter
|
||||
| <<aop-aj-ltw>>
|
||||
|
||||
| `MessageSourceAware`
|
||||
| Configured strategy for resolving messages (with support for parametrization and
|
||||
| Configured strategy for resolving messages (with support for parameterization and
|
||||
internationalization).
|
||||
| <<context-introduction>>
|
||||
|
||||
@@ -4590,26 +4588,24 @@ configuration becomes decentralized and harder to control.
|
||||
|
||||
No matter the choice, Spring can accommodate both styles and even mix them together.
|
||||
It is worth pointing out that through its <<beans-java, JavaConfig>> option, Spring lets
|
||||
annotations be used in a non-invasive way, without touching the target components
|
||||
source code and that, in terms of tooling, all configuration styles are supported by the
|
||||
https://spring.io/tools[Spring Tools for Eclipse].
|
||||
annotations be used in a non-invasive way, without touching the target components'
|
||||
source code and that, in terms of tooling, all configuration styles are supported by
|
||||
https://spring.io/tools[Spring Tools] for Eclipse, Visual Studio Code, and Theia.
|
||||
****
|
||||
|
||||
An alternative to XML setup is provided by annotation-based configuration, which relies
|
||||
on the bytecode metadata for wiring up components instead of angle-bracket declarations.
|
||||
Instead of using XML to describe a bean wiring, the developer moves the configuration
|
||||
into the component class itself by using annotations on the relevant class, method, or
|
||||
field declaration. As mentioned in <<beans-factory-extension-bpp-examples-aabpp>>, using
|
||||
a `BeanPostProcessor` in conjunction with annotations is a common means of extending the
|
||||
Spring IoC container. For example, Spring 2.5 introduced an annotation-based approach to
|
||||
drive Spring's dependency injection. Essentially, the <<beans-autowired-annotation,
|
||||
`@Autowired`>> annotation provides the same capabilities as described in
|
||||
<<beans-factory-autowire>> but with more fine-grained control and wider applicability.
|
||||
Spring 2.5 also added support for JSR-250 annotations, such as `@PostConstruct` and
|
||||
`@PreDestroy`. Spring 3.0 added support for JSR-330 (Dependency Injection for Java)
|
||||
annotations contained in the `jakarta.inject` package such as `@Inject` and `@Named`.
|
||||
Details about those annotations can be found in the <<beans-standard-annotations,
|
||||
relevant section>>.
|
||||
on bytecode metadata for wiring up components instead of XML declarations. Instead of
|
||||
using XML to describe a bean wiring, the developer moves the configuration into the
|
||||
component class itself by using annotations on the relevant class, method, or field
|
||||
declaration. As mentioned in <<beans-factory-extension-bpp-examples-aabpp>>, using a
|
||||
`BeanPostProcessor` in conjunction with annotations is a common means of extending the
|
||||
Spring IoC container. For example, the <<beans-autowired-annotation, `@Autowired`>>
|
||||
annotation provides the same capabilities as described in <<beans-factory-autowire>> but
|
||||
with more fine-grained control and wider applicability. In addition, Spring provides
|
||||
support for JSR-250 annotations, such as `@PostConstruct` and `@PreDestroy`, as well as
|
||||
support for JSR-330 (Dependency Injection for Java) annotations contained in the
|
||||
`jakarta.inject` package such as `@Inject` and `@Named`. Details about those annotations
|
||||
can be found in the <<beans-standard-annotations, relevant section>>.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
@@ -5020,7 +5016,7 @@ through Java 8's `java.util.Optional`, as the following example shows:
|
||||
|
||||
As of Spring Framework 5.0, you can also use a `@Nullable` annotation (of any kind
|
||||
in any package -- for example, `javax.annotation.Nullable` from JSR-305) or just leverage
|
||||
Kotlin builtin null-safety support:
|
||||
Kotlin built-in null-safety support:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
@@ -6237,7 +6233,7 @@ simply to be added to the application's classpath like any other library.
|
||||
Most examples in this chapter use XML to specify the configuration metadata that produces
|
||||
each `BeanDefinition` within the Spring container. The previous section
|
||||
(<<beans-annotation-config>>) demonstrates how to provide a lot of the configuration
|
||||
metadata through source-level annotations. Even in those examples, however, the "`base`"
|
||||
metadata through source-level annotations. Even in those examples, however, the "base"
|
||||
bean definitions are explicitly defined in the XML file, while the annotations drive only
|
||||
the dependency injection. This section describes an option for implicitly detecting the
|
||||
candidate components by scanning the classpath. Candidate components are classes that
|
||||
@@ -6249,10 +6245,9 @@ the container.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
Starting with Spring 3.0, many features provided by the Spring JavaConfig project are
|
||||
part of the core Spring Framework. This allows you to define beans using Java rather
|
||||
than using the traditional XML files. Take a look at the `@Configuration`, `@Bean`,
|
||||
`@Import`, and `@DependsOn` annotations for examples of how to use these new features.
|
||||
You can define beans using Java rather than using XML files. Take a look at the
|
||||
`@Configuration`, `@Bean`, `@Import`, and `@DependsOn` annotations for examples of how to
|
||||
use these features.
|
||||
====
|
||||
|
||||
|
||||
@@ -6324,7 +6319,7 @@ the `@RestController` annotation from Spring MVC is composed of `@Controller` an
|
||||
In addition, composed annotations can optionally redeclare attributes from
|
||||
meta-annotations to allow customization. This can be particularly useful when you
|
||||
want to only expose a subset of the meta-annotation's attributes. For example, Spring's
|
||||
`@SessionScope` annotation hardcodes the scope name to `session` but still allows
|
||||
`@SessionScope` annotation hard codes the scope name to `session` but still allows
|
||||
customization of the `proxyMode`. The following listing shows the definition of the
|
||||
`SessionScope` annotation:
|
||||
|
||||
@@ -7172,15 +7167,15 @@ arrangement (as though no index were present at all) by setting `spring.index.ig
|
||||
[[beans-standard-annotations]]
|
||||
== Using JSR 330 Standard Annotations
|
||||
|
||||
Starting with Spring 3.0, Spring offers support for JSR-330 standard annotations
|
||||
(Dependency Injection). Those annotations are scanned in the same way as the Spring
|
||||
annotations. To use them, you need to have the relevant jars in your classpath.
|
||||
Spring offers support for JSR-330 standard annotations (Dependency Injection). Those
|
||||
annotations are scanned in the same way as the Spring annotations. To use them, you need
|
||||
to have the relevant jars in your classpath.
|
||||
|
||||
[NOTE]
|
||||
=====
|
||||
If you use Maven, the `jakarta.inject` artifact is available in the standard Maven
|
||||
repository (
|
||||
https://repo1.maven.org/maven2/jakarta/inject/jakarta.inject-api/2.0.0/[https://repo1.maven.org/maven2/jakarta/inject/jakarta.inject-api/2.0.0/]).
|
||||
https://repo.maven.apache.org/maven2/jakarta/inject/jakarta.inject-api/2.0.0/[https://repo.maven.apache.org/maven2/jakarta/inject/jakarta.inject-api/2.0.0/]).
|
||||
You can add the following dependency to your file pom.xml:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
@@ -7562,7 +7557,7 @@ The simplest possible `@Configuration` class reads as follows:
|
||||
public class AppConfig {
|
||||
|
||||
@Bean
|
||||
public MyService myService() {
|
||||
public MyServiceImpl myService() {
|
||||
return new MyServiceImpl();
|
||||
}
|
||||
}
|
||||
@@ -7574,7 +7569,7 @@ The simplest possible `@Configuration` class reads as follows:
|
||||
class AppConfig {
|
||||
|
||||
@Bean
|
||||
fun myService(): MyService {
|
||||
fun myService(): MyServiceImpl {
|
||||
return MyServiceImpl()
|
||||
}
|
||||
}
|
||||
@@ -8611,22 +8606,24 @@ about singletons here.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
As of Spring 3.2, it is no longer necessary to add CGLIB to your classpath because CGLIB
|
||||
classes have been repackaged under `org.springframework.cglib` and included directly
|
||||
within the spring-core JAR.
|
||||
It is not necessary to add CGLIB to your classpath because CGLIB classes are repackaged
|
||||
under the `org.springframework.cglib` package and included directly within the
|
||||
`spring-core` JAR.
|
||||
====
|
||||
|
||||
[TIP]
|
||||
====
|
||||
There are a few restrictions due to the fact that CGLIB dynamically adds features at
|
||||
startup-time. In particular, configuration classes must not be final. However, as
|
||||
of 4.3, any constructors are allowed on configuration classes, including the use of
|
||||
`@Autowired` or a single non-default constructor declaration for default injection.
|
||||
startup-time. In particular, configuration classes must not be final. However, any
|
||||
constructors are allowed on configuration classes, including the use of `@Autowired` or a
|
||||
single non-default constructor declaration for default injection.
|
||||
|
||||
If you prefer to avoid any CGLIB-imposed limitations, consider declaring your `@Bean`
|
||||
methods on non-`@Configuration` classes (for example, on plain `@Component` classes instead).
|
||||
Cross-method calls between `@Bean` methods are not then intercepted, so you have
|
||||
to exclusively rely on dependency injection at the constructor or method level there.
|
||||
methods on non-`@Configuration` classes (for example, on plain `@Component` classes
|
||||
instead) or by annotating your configuration class with
|
||||
`@Configuration(proxyBeanMethods = false)`. Cross-method calls between `@Bean` methods
|
||||
are then not intercepted, so you have to exclusively rely on dependency injection at the
|
||||
constructor or method level there.
|
||||
====
|
||||
|
||||
|
||||
|
||||
@@ -744,25 +744,42 @@ topics:
|
||||
[[expressions-ref-literal]]
|
||||
=== Literal Expressions
|
||||
|
||||
The types of literal expressions supported are strings, numeric values (int, real, hex),
|
||||
boolean, and null. Strings are delimited by single quotation marks. To put a single quotation mark itself
|
||||
in a string, use two single quotation mark characters.
|
||||
SpEL supports the following types of literal expressions.
|
||||
|
||||
The following listing shows simple usage of literals. Typically, they are not used
|
||||
in isolation like this but, rather, as part of a more complex expression -- for example,
|
||||
using a literal on one side of a logical comparison operator.
|
||||
- strings
|
||||
- numeric values: integer (`int` or `long`), hexadecimal (`int` or `long`), real (`float`
|
||||
or `double`)
|
||||
- boolean values: `true` or `false`
|
||||
- null
|
||||
|
||||
Strings can delimited by single quotation marks (`'`) or double quotation marks (`"`). To
|
||||
include a single quotation mark within a string literal enclosed in single quotation
|
||||
marks, use two adjacent single quotation mark characters. Similarly, to include a double
|
||||
quotation mark within a string literal enclosed in double quotation marks, use two
|
||||
adjacent double quotation mark characters.
|
||||
|
||||
Numbers support the use of the negative sign, exponential notation, and decimal points.
|
||||
By default, real numbers are parsed by using `Double.parseDouble()`.
|
||||
|
||||
The following listing shows simple usage of literals. Typically, they are not used in
|
||||
isolation like this but, rather, as part of a more complex expression -- for example,
|
||||
using a literal on one side of a logical comparison operator or as an argument to a
|
||||
method.
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
ExpressionParser parser = new SpelExpressionParser();
|
||||
|
||||
// evals to "Hello World"
|
||||
// evaluates to "Hello World"
|
||||
String helloWorld = (String) parser.parseExpression("'Hello World'").getValue();
|
||||
|
||||
// evaluates to "Tony's Pizza"
|
||||
String pizzaParlor = (String) parser.parseExpression("'Tony''s Pizza'").getValue();
|
||||
|
||||
double avogadrosNumber = (Double) parser.parseExpression("6.0221415E+23").getValue();
|
||||
|
||||
// evals to 2147483647
|
||||
// evaluates to 2147483647
|
||||
int maxValue = (Integer) parser.parseExpression("0x7FFFFFFF").getValue();
|
||||
|
||||
boolean trueValue = (Boolean) parser.parseExpression("true").getValue();
|
||||
@@ -774,12 +791,15 @@ using a literal on one side of a logical comparison operator.
|
||||
----
|
||||
val parser = SpelExpressionParser()
|
||||
|
||||
// evals to "Hello World"
|
||||
// evaluates to "Hello World"
|
||||
val helloWorld = parser.parseExpression("'Hello World'").value as String
|
||||
|
||||
// evaluates to "Tony's Pizza"
|
||||
val pizzaParlor = parser.parseExpression("'Tony''s Pizza'").value as String
|
||||
|
||||
val avogadrosNumber = parser.parseExpression("6.0221415E+23").value as Double
|
||||
|
||||
// evals to 2147483647
|
||||
// evaluates to 2147483647
|
||||
val maxValue = parser.parseExpression("0x7FFFFFFF").value as Int
|
||||
|
||||
val trueValue = parser.parseExpression("true").value as Boolean
|
||||
@@ -787,9 +807,6 @@ using a literal on one side of a logical comparison operator.
|
||||
val nullValue = parser.parseExpression("null").value
|
||||
----
|
||||
|
||||
Numbers support the use of the negative sign, exponential notation, and decimal points.
|
||||
By default, real numbers are parsed by using `Double.parseDouble()`.
|
||||
|
||||
|
||||
|
||||
[[expressions-properties-arrays]]
|
||||
@@ -804,7 +821,7 @@ Pupin's city of birth, we use the following expressions:
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
// evals to 1856
|
||||
// evaluates to 1856
|
||||
int year = (Integer) parser.parseExpression("birthdate.year + 1900").getValue(context);
|
||||
|
||||
String city = (String) parser.parseExpression("placeOfBirth.city").getValue(context);
|
||||
@@ -812,7 +829,7 @@ Pupin's city of birth, we use the following expressions:
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
// evals to 1856
|
||||
// evaluates to 1856
|
||||
val year = parser.parseExpression("birthdate.year + 1900").getValue(context) as Int
|
||||
|
||||
val city = parser.parseExpression("placeOfBirth.city").getValue(context) as String
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
= Validation, Data Binding, and Type Conversion
|
||||
|
||||
There are pros and cons for considering validation as business logic, and Spring offers
|
||||
a design for validation (and data binding) that does not exclude either one of them.
|
||||
a design for validation and data binding that does not exclude either one of them.
|
||||
Specifically, validation should not be tied to the web tier and should be easy to localize,
|
||||
and it should be possible to plug in any available validator. Considering these concerns,
|
||||
Spring provides a `Validator` contract that is both basic and eminently usable
|
||||
@@ -15,18 +15,18 @@ provides the aptly named `DataBinder` to do exactly that. The `Validator` and th
|
||||
limited to the web layer.
|
||||
|
||||
The `BeanWrapper` is a fundamental concept in the Spring Framework and is used in a lot
|
||||
of places. However, you probably do not need to use the `BeanWrapper`
|
||||
directly. Because this is reference documentation, however, we felt that some explanation
|
||||
might be in order. We explain the `BeanWrapper` in this chapter, since, if you are
|
||||
going to use it at all, you are most likely do so when trying to bind data to objects.
|
||||
of places. However, you probably do not need to use the `BeanWrapper` directly. Because
|
||||
this is reference documentation, however, we feel that some explanation might be in
|
||||
order. We explain the `BeanWrapper` in this chapter, since, if you are going to use it at
|
||||
all, you are most likely do so when trying to bind data to objects.
|
||||
|
||||
Spring's `DataBinder` and the lower-level `BeanWrapper` both use `PropertyEditorSupport`
|
||||
implementations to parse and format property values. The `PropertyEditor` and
|
||||
`PropertyEditorSupport` types are part of the JavaBeans specification and are also
|
||||
explained in this chapter. Spring 3 introduced a `core.convert` package that provides a
|
||||
general type conversion facility, as well as a higher-level "`format`" package for
|
||||
formatting UI field values. You can use these packages as simpler alternatives to
|
||||
`PropertyEditorSupport` implementations. They are also discussed in this chapter.
|
||||
explained in this chapter. Spring's `core.convert` package provides a general type
|
||||
conversion facility, as well as a higher-level `format` package for formatting UI field
|
||||
values. You can use these packages as simpler alternatives to `PropertyEditorSupport`
|
||||
implementations. They are also discussed in this chapter.
|
||||
|
||||
Spring supports Java Bean Validation through setup infrastructure and an adaptor to
|
||||
Spring's own `Validator` contract. Applications can enable Bean Validation once globally,
|
||||
@@ -861,12 +861,12 @@ as needed.
|
||||
[[core-convert]]
|
||||
== Spring Type Conversion
|
||||
|
||||
Spring 3 introduced a `core.convert` package that provides a general type conversion
|
||||
system. The system defines an SPI to implement type conversion logic and an API
|
||||
to perform type conversions at runtime. Within a Spring container, you can use this system
|
||||
as an alternative to `PropertyEditor` implementations to convert externalized bean property value
|
||||
strings to the required property types. You can also use the public API anywhere in your
|
||||
application where type conversion is needed.
|
||||
The `core.convert` package provides a general type conversion system. The system defines
|
||||
an SPI to implement type conversion logic and an API to perform type conversions at
|
||||
runtime. Within a Spring container, you can use this system as an alternative to
|
||||
`PropertyEditor` implementations to convert externalized bean property value strings to
|
||||
the required property types. You can also use the public API anywhere in your application
|
||||
where type conversion is needed.
|
||||
|
||||
|
||||
|
||||
@@ -1205,8 +1205,9 @@ web or desktop application. In such environments, you typically convert from `St
|
||||
to support the client postback process, as well as back to `String` to support the
|
||||
view rendering process. In addition, you often need to localize `String` values. The more
|
||||
general `core.convert` `Converter` SPI does not address such formatting requirements
|
||||
directly. To directly address them, Spring 3 introduced a convenient `Formatter` SPI that
|
||||
provides a simple and robust alternative to `PropertyEditor` implementations for client environments.
|
||||
directly. To directly address them, Spring provides a convenient `Formatter` SPI that
|
||||
provides a simple and robust alternative to `PropertyEditor` implementations for client
|
||||
environments.
|
||||
|
||||
In general, you can use the `Converter` SPI when you need to implement general-purpose type
|
||||
conversion logic -- for example, for converting between a `java.util.Date` and a `Long`.
|
||||
@@ -1956,9 +1957,9 @@ javadoc for more information on these options.
|
||||
[[validation-binder]]
|
||||
=== Configuring a `DataBinder`
|
||||
|
||||
Since Spring 3, you can configure a `DataBinder` instance with a `Validator`. Once
|
||||
configured, you can invoke the `Validator` by calling `binder.validate()`. Any validation
|
||||
`Errors` are automatically added to the binder's `BindingResult`.
|
||||
You can configure a `DataBinder` instance with a `Validator`. Once configured, you can
|
||||
invoke the `Validator` by calling `binder.validate()`. Any validation `Errors` are
|
||||
automatically added to the binder's `BindingResult`.
|
||||
|
||||
The following example shows how to use a `DataBinder` programmatically to invoke validation
|
||||
logic after binding to a target object:
|
||||
|
||||
@@ -129,7 +129,7 @@ Typically, you need an application server's JTA capability only if your applicat
|
||||
to handle transactions across multiple resources, which is not a requirement for many
|
||||
applications. Many high-end applications use a single, highly scalable database (such as
|
||||
Oracle RAC) instead. Stand-alone transaction managers (such as
|
||||
https://www.atomikos.com/[Atomikos Transactions] and https://jotm.ow2.org/[JOTM])
|
||||
https://www.atomikos.com/[Atomikos Transactions])
|
||||
are other options. Of course, you may need other application server capabilities, such as
|
||||
Java Message Service (JMS) and Jakarta EE Connector Architecture (JCA).
|
||||
|
||||
@@ -1090,8 +1090,8 @@ application-specific `Exception` type by supplying an _exception pattern_ via th
|
||||
----
|
||||
<tx:advice id="txAdvice" transaction-manager="txManager">
|
||||
<tx:attributes>
|
||||
<tx:method name="get*" read-only="true" rollback-for="NoProductInStockException"/>
|
||||
<tx:method name="*"/>
|
||||
<tx:method name="get*" read-only="true" rollback-for="NoProductInStockException"/>
|
||||
<tx:method name="*"/>
|
||||
</tx:attributes>
|
||||
</tx:advice>
|
||||
----
|
||||
@@ -1105,8 +1105,8 @@ unhandled `InstrumentNotFoundException`:
|
||||
----
|
||||
<tx:advice id="txAdvice">
|
||||
<tx:attributes>
|
||||
<tx:method name="updateStock" no-rollback-for="InstrumentNotFoundException"/>
|
||||
<tx:method name="*"/>
|
||||
<tx:method name="updateStock" no-rollback-for="InstrumentNotFoundException"/>
|
||||
<tx:method name="*"/>
|
||||
</tx:attributes>
|
||||
</tx:advice>
|
||||
----
|
||||
@@ -1121,7 +1121,7 @@ attendant transaction:
|
||||
----
|
||||
<tx:advice id="txAdvice">
|
||||
<tx:attributes>
|
||||
<tx:method name="*" rollback-for="Throwable" no-rollback-for="InstrumentNotFoundException"/>
|
||||
<tx:method name="*" rollback-for="Throwable" no-rollback-for="InstrumentNotFoundException"/>
|
||||
</tx:attributes>
|
||||
</tx:advice>
|
||||
----
|
||||
@@ -4188,7 +4188,7 @@ To configure a `DriverManagerDataSource`:
|
||||
|
||||
. Obtain a connection with `DriverManagerDataSource` as you typically obtain a JDBC
|
||||
connection.
|
||||
. Specify the fully qualified classname of the JDBC driver so that the `DriverManager`
|
||||
. Specify the fully qualified class name of the JDBC driver so that the `DriverManager`
|
||||
can load the driver class.
|
||||
. Provide a URL that varies between JDBC drivers. (See the documentation for your driver
|
||||
for the correct value.)
|
||||
@@ -4346,7 +4346,7 @@ javadoc for more details.
|
||||
==== Using `DataSourceTransactionManager`
|
||||
|
||||
The `DataSourceTransactionManager` class is a `PlatformTransactionManager`
|
||||
implementation for single JDBC datasources. It binds a JDBC connection from the
|
||||
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.
|
||||
|
||||
@@ -6825,7 +6825,7 @@ The following query uses a bind variable:
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
val first = client.sql("SELECT id, name FROM person WHERE WHERE first_name = :fn")
|
||||
val first = client.sql("SELECT id, name FROM person WHERE first_name = :fn")
|
||||
.bind("fn", "Joe")
|
||||
.fetch().awaitSingle()
|
||||
----
|
||||
@@ -6850,7 +6850,7 @@ Without specifying further mapping details, queries return tabular results
|
||||
as `Map` whose keys are case-insensitive column names that map to their column value.
|
||||
|
||||
You can take control over result mapping by supplying a `Function<Row, T>` that gets
|
||||
called for each `Row` so it can can return arbitrary values (singular values,
|
||||
called for each `Row` so it can return arbitrary values (singular values,
|
||||
collections and maps, and objects).
|
||||
|
||||
The following example extracts the `name` column and emits its value:
|
||||
@@ -6966,7 +6966,7 @@ Consider the following query:
|
||||
SELECT id, name, state FROM table WHERE (name, age) IN (('John', 35), ('Ann', 50))
|
||||
----
|
||||
|
||||
The preceding query can be parametrized and run as follows:
|
||||
The preceding query can be parameterized and run as follows:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
@@ -7212,7 +7212,7 @@ responsibility of the administrator who sets up the `ConnectionFactory`. You
|
||||
most likely fill both roles as you develop and test code, but you do not
|
||||
necessarily have to know how the production data source is configured.
|
||||
|
||||
When you use Spring's R2DBC layer, you can can configure your own with a
|
||||
When you use Spring's R2DBC layer, you can configure your own with a
|
||||
connection pool implementation provided by a third party. A popular
|
||||
implementation is R2DBC Pool (`r2dbc-pool`). Implementations in the Spring
|
||||
distribution are meant only for testing purposes and do not provide pooling.
|
||||
@@ -7286,7 +7286,7 @@ javadoc for more details.
|
||||
==== Using `R2dbcTransactionManager`
|
||||
|
||||
The `R2dbcTransactionManager` class is a `ReactiveTransactionManager` implementation for
|
||||
single R2DBC datasources. It binds an R2DBC connection from the specified connection factory
|
||||
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.
|
||||
|
||||
@@ -8636,7 +8636,7 @@ given `javax.xml.transform.Result`. The result is a tagging interface that basic
|
||||
represents an XML output abstraction. Concrete implementations wrap various XML
|
||||
representations, as the following table indicates:
|
||||
|
||||
[[oxm-marshller-tbl]]
|
||||
[[oxm-marshaller-tbl]]
|
||||
|===
|
||||
| Result implementation| Wraps XML representation
|
||||
|
||||
@@ -8679,7 +8679,7 @@ This interface also has one method, which reads from the given
|
||||
with `Result`, `Source` is a tagging interface that has three concrete implementations. Each
|
||||
wraps a different XML representation, as the following table indicates:
|
||||
|
||||
[[oxm-unmarshller-tbl]]
|
||||
[[oxm-unmarshaller-tbl]]
|
||||
|===
|
||||
| Source implementation| Wraps XML representation
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ Brannen, Ramnivas Laddad, Arjen Poutsma, Chris Beams, Tareq Abedrabbo, Andy Clem
|
||||
Syer, Oliver Gierke, Rossen Stoyanchev, Phillip Webb, Rob Winch, Brian Clozel, Stephane
|
||||
Nicoll, Sebastien Deleuze, Jay Bryant, Mark Paluch
|
||||
|
||||
Copyright © 2002 - 2022 VMware, Inc. All Rights Reserved.
|
||||
Copyright © 2002 - 2023 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Copies of this document may be made for your own use and for distribution to others,
|
||||
provided that you do not charge any fee for such copies and further provided that each
|
||||
|
||||
@@ -140,9 +140,9 @@ Since caches are essentially key-value stores, each invocation of a cached metho
|
||||
needs to be translated into a suitable key for cache access. The caching abstraction
|
||||
uses a simple `KeyGenerator` based on the following algorithm:
|
||||
|
||||
* If no params are given, return `SimpleKey.EMPTY`.
|
||||
* If only one param is given, return that instance.
|
||||
* If more than one param is given, return a `SimpleKey` that contains all parameters.
|
||||
* If no parameters are given, return `SimpleKey.EMPTY`.
|
||||
* If only one parameter is given, return that instance.
|
||||
* If more than one parameter is given, return a `SimpleKey` that contains all parameters.
|
||||
|
||||
This approach works well for most use-cases, as long as parameters have natural keys
|
||||
and implement valid `hashCode()` and `equals()` methods. If that is not the case,
|
||||
|
||||
@@ -5,14 +5,15 @@ This section describes how to send email with the Spring Framework.
|
||||
|
||||
.Library dependencies
|
||||
****
|
||||
The following JAR needs to be on the classpath of your application in order to use
|
||||
the Spring Framework's email library:
|
||||
The following JAR needs to be on the classpath of your application in order to use the
|
||||
Spring Framework's email support:
|
||||
|
||||
* The https://eclipse-ee4j.github.io/mail/[JavaMail / Jakarta Mail 1.6] library
|
||||
* The https://jakartaee.github.io/mail-api/[Jakarta Mail] library
|
||||
|
||||
This library is freely available on the web -- for example, in Maven Central as
|
||||
`com.sun.mail:jakarta.mail`. Please make sure to use the latest 1.6.x version
|
||||
rather than Jakarta Mail 2.0 (which comes with a different package namespace).
|
||||
`com.sun.mail:jakarta.mail`. Please make sure to use the latest 2.x version (which uses
|
||||
the `jakarta.mail` package namespace) rather than Jakarta Mail 1.6.x (which uses the
|
||||
`javax.mail` package namespace).
|
||||
****
|
||||
|
||||
The Spring Framework provides a helpful utility library for sending email that shields
|
||||
|
||||
@@ -67,6 +67,12 @@ HTTP server exchanges observations are created with the name `"http.server.reque
|
||||
Applications need to configure the `org.springframework.web.filter.ServerHttpObservationFilter` Servlet filter in their application.
|
||||
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.
|
||||
Typically, all exceptions handled by Spring MVC's `@ExceptionHandler` and <<web.adoc#mvc-ann-rest-exceptions,`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[]
|
||||
|
||||
By default, the following `KeyValues` are created:
|
||||
|
||||
.Low cardinality Keys
|
||||
@@ -94,6 +100,12 @@ By default, the following `KeyValues` are created:
|
||||
Applications need to configure the `org.springframework.web.filter.reactive.ServerHttpObservationFilter` reactive `WebFilter` in their application.
|
||||
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`.
|
||||
Typically, all exceptions handled by Spring WebFlux's `@ExceptionHandler` and <<web.adoc#webflux-ann-rest-exceptions,`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[]
|
||||
|
||||
By default, the following `KeyValues` are created:
|
||||
|
||||
.Low cardinality Keys
|
||||
@@ -125,24 +137,27 @@ Unlike their server counterparts, the instrumentation is implemented directly in
|
||||
[[integration.observability.http-client.resttemplate]]
|
||||
=== RestTemplate
|
||||
|
||||
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 is using the `org.springframework.http.client.observation.ClientRequestObservationConvention` by default, backed by the `ClientRequestObservationContext`.
|
||||
|
||||
.Low cardinality Keys
|
||||
[cols="a,a"]
|
||||
|===
|
||||
|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 the request could not be created.
|
||||
|`outcome` _(required)_|Outcome of the HTTP client exchange.
|
||||
|`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.
|
||||
|`uri` _(required)_|URI template used for HTTP request, or `"none"` if none was provided.
|
||||
|`outcome` _(required)_|Outcome of the HTTP client exchange.
|
||||
|`exception` _(required)_|Name of the exception thrown during the exchange, or `"none"` if no exception happened.
|
||||
|===
|
||||
|
||||
.High cardinality Keys
|
||||
[cols="a,a"]
|
||||
|===
|
||||
|Name | Description
|
||||
|`client.name` _(required)_|Client name derived from the request URI host.
|
||||
|`http.url` _(required)_|HTTP request URI.
|
||||
|===
|
||||
|
||||
@@ -151,24 +166,27 @@ Instrumentation is using the `org.springframework.http.client.observation.Client
|
||||
[[integration.observability.http-client.webclient]]
|
||||
=== WebClient
|
||||
|
||||
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 is using the `org.springframework.web.reactive.function.client.ClientRequestObservationConvention` by default, backed by the `ClientRequestObservationContext`.
|
||||
|
||||
.Low cardinality Keys
|
||||
[cols="a,a"]
|
||||
|===
|
||||
|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 the request could not be created.
|
||||
|`outcome` _(required)_|Outcome of the HTTP client exchange.
|
||||
|`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.
|
||||
|`uri` _(required)_|URI template used for HTTP request, or `"none"` if none was provided.
|
||||
|`outcome` _(required)_|Outcome of the HTTP client exchange.
|
||||
|`exception` _(required)_|Name of the exception thrown during the exchange, or `"none"` if no exception happened.
|
||||
|===
|
||||
|
||||
.High cardinality Keys
|
||||
[cols="a,a"]
|
||||
|===
|
||||
|Name | Description
|
||||
|`client.name` _(required)_|Client name derived from the request URI host.
|
||||
|`http.url` _(required)_|HTTP request URI.
|
||||
|===
|
||||
|
||||
|
||||
@@ -117,6 +117,9 @@ TIP: Note that the `java.net` implementation for HTTP requests can raise an exce
|
||||
accessing the status of a response that represents an error (such as 401). If this is an
|
||||
issue, switch to another HTTP client library.
|
||||
|
||||
NOTE: `RestTemplate` can be instrumented for observability, in order to produce metrics and traces.
|
||||
See the <<integration.adoc#integration.observability.http-client.resttemplate,RestTemplate Observability support>> section.
|
||||
|
||||
[[rest-resttemplate-uri]]
|
||||
==== URIs
|
||||
|
||||
@@ -215,7 +218,7 @@ converters to use explicitly.
|
||||
|
||||
[[rest-message-conversion]]
|
||||
==== Message Conversion
|
||||
[.small]#<<web-reactive.adoc#webflux-codecs, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-codecs, See equivalent in the Reactive stack>>#
|
||||
|
||||
The `spring-web` module contains the `HttpMessageConverter` contract for reading and
|
||||
writing the body of HTTP requests and responses through `InputStream` and `OutputStream`.
|
||||
@@ -417,7 +420,7 @@ method parameters:
|
||||
| Dynamically set the HTTP method for the request, overriding the annotation's `method` attribute
|
||||
|
||||
| `@RequestHeader`
|
||||
| Add a request header or mutliple headers. The argument may be a `Map<String, ?>` or
|
||||
| Add a request header or multiple headers. The argument may be a `Map<String, ?>` or
|
||||
`MultiValueMap<String, ?>` with multiple headers, a `Collection<?>` of values, or an
|
||||
individual value. Type conversion is supported for non-String values.
|
||||
|
||||
@@ -432,7 +435,7 @@ method parameters:
|
||||
through the configured `ReactiveAdapterRegistry`.
|
||||
|
||||
| `@RequestParam`
|
||||
| Add a request parameter or mutliple parameters. The argument may be a `Map<String, ?>`
|
||||
| Add a request parameter or multiple parameters. The argument may be a `Map<String, ?>`
|
||||
or `MultiValueMap<String, ?>` with multiple parameters, a `Collection<?>` of values, or
|
||||
an individual value. Type conversion is supported for non-String values.
|
||||
|
||||
@@ -446,7 +449,7 @@ method parameters:
|
||||
a Spring `Part`, or Reactive Streams `Publisher` of any of the above.
|
||||
|
||||
| `@CookieValue`
|
||||
| Add a cookie or mutliple cookies. The argument may be a `Map<String, ?>` or
|
||||
| Add a cookie or multiple cookies. The argument may be a `Map<String, ?>` or
|
||||
`MultiValueMap<String, ?>` with multiple cookies, a `Collection<?>` of values, or an
|
||||
individual value. Type conversion is supported for non-String values.
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ implementations behind the common interfaces abstracts away the differences betw
|
||||
SE 5, Java SE 6, and Jakarta EE environments.
|
||||
|
||||
Spring also features integration classes to support scheduling with the `Timer`
|
||||
(part of the JDK since 1.3) and the Quartz Scheduler ( https://www.quartz-scheduler.org/[]).
|
||||
(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
|
||||
@@ -136,14 +136,16 @@ To configure the rules that the `TaskExecutor` uses, we expose simple bean prope
|
||||
[[scheduling-task-scheduler]]
|
||||
== The Spring `TaskScheduler` Abstraction
|
||||
|
||||
In addition to the `TaskExecutor` abstraction, Spring 3.0 introduced a `TaskScheduler`
|
||||
with a variety of methods for scheduling tasks to run at some point in the future.
|
||||
The following listing shows the `TaskScheduler` interface definition:
|
||||
In addition to the `TaskExecutor` abstraction, Spring has a `TaskScheduler` SPI with a
|
||||
variety of methods for scheduling tasks to run at some point in the future. The following
|
||||
listing shows the `TaskScheduler` interface definition:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
public interface TaskScheduler {
|
||||
|
||||
Clock getClock();
|
||||
|
||||
ScheduledFuture schedule(Runnable task, Trigger trigger);
|
||||
|
||||
ScheduledFuture schedule(Runnable task, Instant startTime);
|
||||
@@ -168,18 +170,17 @@ much more flexible.
|
||||
[[scheduling-trigger-interface]]
|
||||
=== `Trigger` Interface
|
||||
|
||||
The `Trigger` interface is essentially inspired by JSR-236 which, as of Spring 3.0,
|
||||
was not yet officially implemented. The basic idea of the `Trigger` is that execution
|
||||
times may be determined based on past execution outcomes or even arbitrary conditions.
|
||||
If these determinations do take into account the outcome of the preceding execution,
|
||||
that information is available within a `TriggerContext`. The `Trigger` interface itself
|
||||
is quite simple, as the following listing shows:
|
||||
The `Trigger` interface is essentially inspired by JSR-236. The basic idea of the
|
||||
`Trigger` is that execution times may be determined based on past execution outcomes or
|
||||
even arbitrary conditions. If these determinations take into account the outcome of the
|
||||
preceding execution, that information is available within a `TriggerContext`. The
|
||||
`Trigger` interface itself is quite simple, as the following listing shows:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
public interface Trigger {
|
||||
|
||||
Date nextExecutionTime(TriggerContext triggerContext);
|
||||
Instant nextExecution(TriggerContext triggerContext);
|
||||
}
|
||||
----
|
||||
|
||||
@@ -192,11 +193,13 @@ default). The following listing shows the available methods for `Trigger` implem
|
||||
----
|
||||
public interface TriggerContext {
|
||||
|
||||
Date lastScheduledExecutionTime();
|
||||
Clock getClock();
|
||||
|
||||
Date lastActualExecutionTime();
|
||||
Instant lastScheduledExecution();
|
||||
|
||||
Date lastCompletionTime();
|
||||
Instant lastActualExecution();
|
||||
|
||||
Instant lastCompletion();
|
||||
}
|
||||
----
|
||||
|
||||
@@ -208,7 +211,7 @@ Spring provides two implementations of the `Trigger` interface. The most interes
|
||||
is the `CronTrigger`. It enables the scheduling of tasks based on
|
||||
<<scheduling-cron-expression,cron expressions>>.
|
||||
For example, the following task is scheduled to run 15 minutes past each hour but only
|
||||
during the 9-to-5 "`business hours`" on weekdays:
|
||||
during the 9-to-5 "business hours" on weekdays:
|
||||
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim"]
|
||||
@@ -782,9 +785,9 @@ You can use these macros instead of the six-digit value, thus: `@Scheduled(cron
|
||||
== 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
|
||||
https://www.quartz-scheduler.org/[]. For convenience purposes, Spring offers a couple of
|
||||
classes that simplify using Quartz within Spring-based applications.
|
||||
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.
|
||||
|
||||
|
||||
[[scheduling-quartz-jobdetail]]
|
||||
@@ -882,12 +885,13 @@ that merely invoke a method. You need only create the actual business object and
|
||||
wire up the detail object.
|
||||
|
||||
By default, Quartz Jobs are stateless, resulting in the possibility of jobs interfering
|
||||
with each other. If you specify two triggers for the same `JobDetail`, it is
|
||||
possible that, before the first job has finished, the second one starts. If
|
||||
`JobDetail` classes implement the `Stateful` interface, this does not happen. The second
|
||||
job does not start before the first one has finished. To make jobs resulting from the
|
||||
`MethodInvokingJobDetailFactoryBean` be non-concurrent, set the `concurrent` flag to
|
||||
`false`, as the following example shows:
|
||||
with each other. If you specify two triggers for the same `JobDetail`, it is possible
|
||||
that the second one starts before the first job has finished. If `JobDetail` classes
|
||||
implement the `Stateful` interface, this does not happen: the second job does not start
|
||||
before the first one has finished.
|
||||
|
||||
To make jobs resulting from the `MethodInvokingJobDetailFactoryBean` be non-concurrent,
|
||||
set the `concurrent` flag to `false`, as the following example shows:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
:noheader:
|
||||
:toc:
|
||||
:toclevels: 4
|
||||
:tabsize: 4
|
||||
include::attributes.adoc[]
|
||||
= Spring Framework Documentation
|
||||
Rod Johnson; Juergen Hoeller; Keith Donald; Colin Sampaleanu; Rob Harrop; Thomas Risberg; Alef Arendsen; Darren Davison; Dmitriy Kopylenko; Mark Pollack; Thierry Templier; Erwin Vervaet; Portia Tung; Ben Hale; Adrian Colyer; John Lewis; Costin Leau; Mark Fisher; Sam Brannen; Ramnivas Laddad; Arjen Poutsma; Chris Beams; Tareq Abedrabbo; Andy Clement; Dave Syer; Oliver Gierke; Rossen Stoyanchev; Phillip Webb; Rob Winch; Brian Clozel; Stephane Nicoll; Sebastien Deleuze; Jay Bryant; Mark Paluch
|
||||
@@ -9,7 +11,7 @@ NOTE: This documentation is also available in {docs-spring-framework}/reference/
|
||||
[[legal]]
|
||||
== Legal
|
||||
|
||||
Copyright © 2002 - 2022 VMware, Inc. All Rights Reserved.
|
||||
Copyright © 2002 - 2023 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically.
|
||||
|
||||
|
||||
@@ -117,6 +117,55 @@ logic but without running a server. The following example shows how to do so:
|
||||
// Test code that uses the above RestTemplate ...
|
||||
----
|
||||
|
||||
In some cases it may be necessary to perform an actual call to a remote service instead
|
||||
of mocking the response. The following example shows how to do that through
|
||||
`ExecutingResponseCreator`:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
|
||||
// Create ExecutingResponseCreator with the original request factory
|
||||
ExecutingResponseCreator withActualResponse = new ExecutingResponseCreator(restTemplate.getRequestFactory());
|
||||
|
||||
MockRestServiceServer mockServer = MockRestServiceServer.bindTo(restTemplate).build();
|
||||
mockServer.expect(requestTo("/profile")).andRespond(withSuccess());
|
||||
mockServer.expect(requestTo("/quoteOfTheDay")).andRespond(withActualResponse);
|
||||
|
||||
// Test code that uses the above RestTemplate ...
|
||||
|
||||
mockServer.verify();
|
||||
----
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
val restTemplate = RestTemplate()
|
||||
|
||||
// Create ExecutingResponseCreator with the original request factory
|
||||
val withActualResponse = new ExecutingResponseCreator(restTemplate.getRequestFactory())
|
||||
|
||||
val mockServer = MockRestServiceServer.bindTo(restTemplate).build()
|
||||
mockServer.expect(requestTo("/profile")).andRespond(withSuccess())
|
||||
mockServer.expect(requestTo("/quoteOfTheDay")).andRespond(withActualResponse)
|
||||
|
||||
// Test code that uses the above RestTemplate ...
|
||||
|
||||
mockServer.verify()
|
||||
----
|
||||
|
||||
In the preceding example, we create the `ExecutingResponseCreator` using the
|
||||
`ClientHttpRequestFactory` from the `RestTemplate` _before_ `MockRestServiceServer` replaces
|
||||
it with a different one that mocks responses.
|
||||
Then we define expectations with two kinds of responses:
|
||||
|
||||
* a stub `200` response for the `/profile` endpoint (no actual request will be executed)
|
||||
* a response obtained through a call to the `/quoteOfTheDay` endpoint
|
||||
|
||||
In the second case, the request is executed through the `ClientHttpRequestFactory` that was
|
||||
captured earlier. This generates a response that could e.g. come from an actual remote server,
|
||||
depending on how the `RestTemplate` was originally configured.
|
||||
|
||||
[[spring-mvc-test-client-static-imports]]
|
||||
== Static Imports
|
||||
|
||||
|
||||
@@ -445,6 +445,17 @@ all failures will be tracked and reported.
|
||||
content().contentType("application/json;charset=UTF-8"));
|
||||
----
|
||||
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
import org.springframework.test.web.servlet.get
|
||||
|
||||
mockMvc.get("/accounts/1").andExpectAll {
|
||||
status { isOk() }
|
||||
content { contentType(APPLICATION_JSON) }
|
||||
}
|
||||
----
|
||||
|
||||
`MockMvcResultMatchers.*` provides a number of expectations, some of which are further
|
||||
nested with more detailed expectations.
|
||||
|
||||
@@ -961,7 +972,7 @@ leads to a number of additional challenges:
|
||||
* Testing can become slow, since each test would need to ensure that the database is in
|
||||
the correct state.
|
||||
* Since our database needs to be in a specific state, we cannot run tests in parallel.
|
||||
* Performing assertions on such items as auto-generated ids, timestamps, and others can
|
||||
* Performing assertions on such items as auto-generated IDs, timestamps, and others can
|
||||
be difficult.
|
||||
|
||||
These challenges do not mean that we should abandon end-to-end integration testing
|
||||
|
||||
@@ -2022,12 +2022,13 @@ properties.
|
||||
class ExampleIntegrationTests {
|
||||
|
||||
@Container
|
||||
static RedisContainer redis = new RedisContainer();
|
||||
static GenericContainer redis =
|
||||
new GenericContainer("redis:5.0.3-alpine").withExposedPorts(6379);
|
||||
|
||||
@DynamicPropertySource
|
||||
static void redisProperties(DynamicPropertyRegistry registry) {
|
||||
registry.add("redis.host", redis::getHost);
|
||||
registry.add("redis.port", redis::getMappedPort);
|
||||
registry.add("redis.port", redis::getFirstMappedPort);
|
||||
}
|
||||
|
||||
// tests ...
|
||||
@@ -2045,13 +2046,14 @@ properties.
|
||||
|
||||
@Container
|
||||
@JvmStatic
|
||||
val redis: RedisContainer = RedisContainer()
|
||||
val redis: GenericContainer =
|
||||
GenericContainer("redis:5.0.3-alpine").withExposedPorts(6379)
|
||||
|
||||
@DynamicPropertySource
|
||||
@JvmStatic
|
||||
fun redisProperties(registry: DynamicPropertyRegistry) {
|
||||
registry.add("redis.host", redis::getHost)
|
||||
registry.add("redis.port", redis::getMappedPort)
|
||||
registry.add("redis.port", redis::getFirstMappedPort)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[[webflux-cors]]
|
||||
= CORS
|
||||
[.small]#<<web.adoc#mvc-cors, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-cors, See equivalent in the Servlet stack>>#
|
||||
|
||||
Spring WebFlux lets you handle CORS (Cross-Origin Resource Sharing). This section
|
||||
describes how to do so.
|
||||
@@ -10,7 +10,7 @@ describes how to do so.
|
||||
|
||||
[[webflux-cors-intro]]
|
||||
== Introduction
|
||||
[.small]#<<web.adoc#mvc-cors-intro, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-cors-intro, See equivalent in the Servlet stack>>#
|
||||
|
||||
For security reasons, browsers prohibit AJAX calls to resources outside the current origin.
|
||||
For example, you could have your bank account in one tab and evil.com in another. Scripts
|
||||
@@ -27,7 +27,7 @@ powerful workarounds based on IFRAME or JSONP.
|
||||
|
||||
[[webflux-cors-processing]]
|
||||
== Processing
|
||||
[.small]#<<web.adoc#mvc-cors-processing, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-cors-processing, See equivalent in the Servlet stack>>#
|
||||
|
||||
The CORS specification distinguishes between preflight, simple, and actual requests.
|
||||
To learn how CORS works, you can read
|
||||
@@ -77,7 +77,7 @@ To learn more from the source or to make advanced customizations, see:
|
||||
|
||||
[[webflux-cors-controller]]
|
||||
== `@CrossOrigin`
|
||||
[.small]#<<web.adoc#mvc-cors-controller, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-cors-controller, See equivalent in the Servlet stack>>#
|
||||
|
||||
The {api-spring-framework}/web/bind/annotation/CrossOrigin.html[`@CrossOrigin`]
|
||||
annotation enables cross-origin requests on annotated controller methods, as the
|
||||
@@ -237,7 +237,7 @@ as the following example shows:
|
||||
|
||||
[[webflux-cors-global]]
|
||||
== Global Configuration
|
||||
[.small]#<<web.adoc#mvc-cors-global, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-cors-global, See equivalent in the Servlet stack>>#
|
||||
|
||||
In addition to fine-grained, controller method-level configuration, you probably want to
|
||||
define some global CORS configuration, too. You can set URL-based `CorsConfiguration`
|
||||
@@ -308,7 +308,7 @@ as the following example shows:
|
||||
|
||||
[[webflux-cors-webfilter]]
|
||||
== CORS `WebFilter`
|
||||
[.small]#<<web.adoc#mvc-cors-filter, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-cors-filter, See equivalent in the Servlet stack>>#
|
||||
|
||||
You can apply CORS support through the built-in
|
||||
{api-spring-framework}/web/cors/reactive/CorsWebFilter.html[`CorsWebFilter`], which is a
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[[webflux-fn]]
|
||||
= Functional Endpoints
|
||||
[.small]#<<web.adoc#webmvc-fn, Web MVC>>#
|
||||
[.small]#<<web.adoc#webmvc-fn, See equivalent in the Servlet stack>>#
|
||||
|
||||
Spring WebFlux includes WebFlux.fn, a lightweight functional programming model in which functions
|
||||
are used to route and handle requests and contracts are designed for immutability.
|
||||
@@ -12,7 +12,7 @@ the same <<web-reactive.adoc#webflux-reactive-spring-web>> foundation.
|
||||
|
||||
[[webflux-fn-overview]]
|
||||
== Overview
|
||||
[.small]#<<web.adoc#webmvc-fn-overview, Web MVC>>#
|
||||
[.small]#<<web.adoc#webmvc-fn-overview, See equivalent in the Servlet stack>>#
|
||||
|
||||
In WebFlux.fn, an HTTP request is handled with a `HandlerFunction`: a function that takes
|
||||
`ServerRequest` and returns a delayed `ServerResponse` (i.e. `Mono<ServerResponse>`).
|
||||
@@ -113,7 +113,7 @@ Most applications can run through the WebFlux Java configuration, see <<webflux-
|
||||
|
||||
[[webflux-fn-handler-functions]]
|
||||
== HandlerFunction
|
||||
[.small]#<<web.adoc#webmvc-fn-handler-functions, Web MVC>>#
|
||||
[.small]#<<web.adoc#webmvc-fn-handler-functions, See equivalent in the Servlet stack>>#
|
||||
|
||||
`ServerRequest` and `ServerResponse` are immutable interfaces that offer JDK 8-friendly
|
||||
access to the HTTP request and response.
|
||||
@@ -486,7 +486,7 @@ See <<core.adoc#validation-beanvalidation, Spring Validation>>.
|
||||
|
||||
[[webflux-fn-router-functions]]
|
||||
== `RouterFunction`
|
||||
[.small]#<<web.adoc#webmvc-fn-router-functions, Web MVC>>#
|
||||
[.small]#<<web.adoc#webmvc-fn-router-functions, See equivalent in the Servlet stack>>#
|
||||
|
||||
Router functions are used to route the requests to the corresponding `HandlerFunction`.
|
||||
Typically, you do not write router functions yourself, but rather use a method on the
|
||||
@@ -687,7 +687,7 @@ We can further improve by using the `nest` method together with `accept`:
|
||||
|
||||
[[webflux-fn-running]]
|
||||
== Running a Server
|
||||
[.small]#<<web.adoc#webmvc-fn-running, Web MVC>>#
|
||||
[.small]#<<web.adoc#webmvc-fn-running, See equivalent in the Servlet stack>>#
|
||||
|
||||
How do you run a router function in an HTTP server? A simple option is to convert a router
|
||||
function to an `HttpHandler` by using one of the following:
|
||||
@@ -793,7 +793,7 @@ The following example shows a WebFlux Java configuration (see
|
||||
|
||||
[[webflux-fn-handler-filter-function]]
|
||||
== Filtering Handler Functions
|
||||
[.small]#<<web.adoc#webmvc-fn-handler-filter-function, Web MVC>>#
|
||||
[.small]#<<web.adoc#webmvc-fn-handler-filter-function, See equivalent in the Servlet stack>>#
|
||||
|
||||
You can filter handler functions by using the `before`, `after`, or `filter` methods on the routing
|
||||
function builder.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[[webflux-view]]
|
||||
= View Technologies
|
||||
[.small]#<<web.adoc#mvc-view, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-view, See equivalent in the Servlet stack>>#
|
||||
|
||||
The use of view technologies in Spring WebFlux is pluggable. Whether you decide to
|
||||
use Thymeleaf, FreeMarker, or some other view technology is primarily a matter of a
|
||||
@@ -12,7 +12,7 @@ WebFlux. We assume you are already familiar with <<webflux-viewresolution>>.
|
||||
|
||||
[[webflux-view-thymeleaf]]
|
||||
== Thymeleaf
|
||||
[.small]#<<web.adoc#mvc-view-thymeleaf, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-view-thymeleaf, See equivalent in the Servlet stack>>#
|
||||
|
||||
Thymeleaf is a modern server-side Java template engine that emphasizes natural HTML
|
||||
templates that can be previewed in a browser by double-clicking, which is very
|
||||
@@ -33,7 +33,7 @@ https://web.archive.org/web/20210623051330/http%3A//forum.thymeleaf.org/Thymelea
|
||||
|
||||
[[webflux-view-freemarker]]
|
||||
== FreeMarker
|
||||
[.small]#<<web.adoc#mvc-view-freemarker, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-view-freemarker, See equivalent in the Servlet stack>>#
|
||||
|
||||
https://freemarker.apache.org/[Apache FreeMarker] is a template engine for generating any
|
||||
kind of text output from HTML to email and others. The Spring Framework has built-in
|
||||
@@ -43,7 +43,7 @@ integration for using Spring WebFlux with FreeMarker templates.
|
||||
|
||||
[[webflux-view-freemarker-contextconfig]]
|
||||
=== View Configuration
|
||||
[.small]#<<web.adoc#mvc-view-freemarker-contextconfig, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-view-freemarker-contextconfig, See equivalent in the Servlet stack>>#
|
||||
|
||||
The following example shows how to configure FreeMarker as a view technology:
|
||||
|
||||
@@ -98,7 +98,7 @@ returns the view name, `welcome`, the resolver looks for the
|
||||
|
||||
[[webflux-views-freemarker]]
|
||||
=== FreeMarker Configuration
|
||||
[.small]#<<web.adoc#mvc-views-freemarker, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-views-freemarker, See equivalent in the Servlet stack>>#
|
||||
|
||||
You can pass FreeMarker 'Settings' and 'SharedVariables' directly to the FreeMarker
|
||||
`Configuration` object (which is managed by Spring) by setting the appropriate bean
|
||||
@@ -151,7 +151,7 @@ the `Configuration` object.
|
||||
|
||||
[[webflux-view-freemarker-forms]]
|
||||
=== Form Handling
|
||||
[.small]#<<web.adoc#mvc-view-freemarker-forms, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-view-freemarker-forms, See equivalent in the Servlet stack>>#
|
||||
|
||||
Spring provides a tag library for use in JSPs that contains, among others, a
|
||||
`<spring:bind/>` element. This element primarily lets forms display values from
|
||||
@@ -162,7 +162,7 @@ with additional convenience macros for generating form input elements themselves
|
||||
|
||||
[[webflux-view-bind-macros]]
|
||||
==== The Bind Macros
|
||||
[.small]#<<web.adoc#mvc-view-bind-macros, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-view-bind-macros, See equivalent in the Servlet stack>>#
|
||||
|
||||
A standard set of macros are maintained within the `spring-webflux.jar` file for
|
||||
FreeMarker, so they are always available to a suitably configured application.
|
||||
@@ -193,7 +193,7 @@ sections of the Spring MVC documentation.
|
||||
|
||||
[[webflux-view-script]]
|
||||
== Script Views
|
||||
[.small]#<<web.adoc#mvc-view-script, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-view-script, See equivalent in the Servlet stack>>#
|
||||
|
||||
The Spring Framework has a built-in integration for using Spring WebFlux with any
|
||||
templating library that can run on top of the
|
||||
@@ -219,7 +219,7 @@ TIP: The basic rule for integrating any other script engine is that it must impl
|
||||
|
||||
[[webflux-view-script-dependencies]]
|
||||
=== Requirements
|
||||
[.small]#<<web.adoc#mvc-view-script-dependencies, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-view-script-dependencies, See equivalent in the Servlet stack>>#
|
||||
|
||||
You need to have the script engine on your classpath, the details of which vary by script engine:
|
||||
|
||||
@@ -239,7 +239,7 @@ through https://www.webjars.org/[WebJars].
|
||||
|
||||
[[webflux-view-script-integrate]]
|
||||
=== Script Templates
|
||||
[.small]#<<web.adoc#mvc-view-script-integrate, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-view-script-integrate, See equivalent in the Servlet stack>>#
|
||||
|
||||
You can declare a `ScriptTemplateConfigurer` bean to specify the script engine to use,
|
||||
the script files to load, what function to call to render templates, and so on.
|
||||
@@ -389,7 +389,7 @@ for more configuration examples.
|
||||
|
||||
[[webflux-view-httpmessagewriter]]
|
||||
== JSON and XML
|
||||
[.small]#<<web.adoc#mvc-view-jackson, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-view-jackson, See equivalent in the Servlet stack>>#
|
||||
|
||||
For <<webflux-multiple-representations>> purposes, it is useful to be able to alternate
|
||||
between rendering a model with an HTML template or as other formats (such as JSON or XML),
|
||||
|
||||
@@ -38,6 +38,8 @@ You can also use `WebClient.builder()` with further options:
|
||||
* `filter`: Client filter for every request.
|
||||
* `exchangeStrategies`: HTTP message reader/writer customizations.
|
||||
* `clientConnector`: HTTP client library settings.
|
||||
* `observationRegistry`: the registry to use for enabling <<integration.adoc#integration.observability.http-client.webclient, Observability support>>.
|
||||
* `observationConvention`: <<integration.adoc#integration.observability.config,an optional, custom convention to extract metadata>> for recorded observations.
|
||||
|
||||
For example:
|
||||
|
||||
@@ -1196,7 +1198,7 @@ response individually, and instead wait for the combined result:
|
||||
|
||||
The above is merely one example. There are lots of other patterns and operators for putting
|
||||
together a reactive pipeline that makes many remote calls, potentially some nested,
|
||||
inter-dependent, without ever blocking until the end.
|
||||
interdependent, without ever blocking until the end.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[[webflux-websocket]]
|
||||
= WebSockets
|
||||
[.small]#<<web.adoc#websocket, Same as in the Servlet stack>>#
|
||||
[.small]#<<web.adoc#websocket, See equivalent in the Servlet stack>>#
|
||||
|
||||
This part of the reference documentation covers support for reactive-stack WebSocket
|
||||
messaging.
|
||||
@@ -12,7 +12,7 @@ include::websocket-intro.adoc[leveloffset=+1]
|
||||
|
||||
[[webflux-websocket-server]]
|
||||
== WebSocket API
|
||||
[.small]#<<web.adoc#websocket-server, Same as in the Servlet stack>>#
|
||||
[.small]#<<web.adoc#websocket-server, See equivalent in the Servlet stack>>#
|
||||
|
||||
The Spring Framework provides a WebSocket API that you can use to write client- and
|
||||
server-side applications that handle WebSocket messages.
|
||||
@@ -21,7 +21,7 @@ server-side applications that handle WebSocket messages.
|
||||
|
||||
[[webflux-websocket-server-handler]]
|
||||
=== Server
|
||||
[.small]#<<web.adoc#websocket-server-handler, Same as in the Servlet stack>>#
|
||||
[.small]#<<web.adoc#websocket-server-handler, See equivalent in the Servlet stack>>#
|
||||
|
||||
To create a WebSocket server, you can first create a `WebSocketHandler`.
|
||||
The following example shows how to do so:
|
||||
@@ -339,7 +339,7 @@ subsequently use `DataBufferUtils.release(dataBuffer)` when the buffers are cons
|
||||
|
||||
[[webflux-websocket-server-handshake]]
|
||||
=== Handshake
|
||||
[.small]#<<web.adoc#websocket-server-handshake, Same as in the Servlet stack>>#
|
||||
[.small]#<<web.adoc#websocket-server-handshake, See equivalent in the Servlet stack>>#
|
||||
|
||||
`WebSocketHandlerAdapter` delegates to a `WebSocketService`. By default, that is an instance
|
||||
of `HandshakeWebSocketService`, which performs basic checks on the WebSocket request and
|
||||
@@ -354,7 +354,7 @@ into the attributes of the `WebSocketSession`.
|
||||
|
||||
[[webflux-websocket-server-config]]
|
||||
=== Server Configuration
|
||||
[.small]#<<web.adoc#websocket-server-runtime-configuration, Same as in the Servlet stack>>#
|
||||
[.small]#<<web.adoc#websocket-server-runtime-configuration, See equivalent in the Servlet stack>>#
|
||||
|
||||
The `RequestUpgradeStrategy` for each server exposes configuration specific to the
|
||||
underlying WebSocket server engine. When using the WebFlux Java config you can customize
|
||||
@@ -408,7 +408,7 @@ only Tomcat and Jetty expose such options.
|
||||
|
||||
[[webflux-websocket-server-cors]]
|
||||
=== CORS
|
||||
[.small]#<<web.adoc#websocket-server-allowed-origins, Same as in the Servlet stack>>#
|
||||
[.small]#<<web.adoc#websocket-server-allowed-origins, See equivalent in the Servlet stack>>#
|
||||
|
||||
The easiest way to configure CORS and restrict access to a WebSocket endpoint is to
|
||||
have your `WebSocketHandler` implement `CorsConfigurationSource` and return a
|
||||
|
||||
@@ -248,20 +248,22 @@ current thread (and rely on callbacks instead) means that you do not need extra
|
||||
there are no blocking calls to absorb.
|
||||
|
||||
|
||||
==== Invoking a Blocking API
|
||||
|
||||
.Invoking a Blocking API
|
||||
What if you do need to use a blocking library? Both Reactor and RxJava provide the
|
||||
`publishOn` operator to continue processing on a different thread. That means there is an
|
||||
easy escape hatch. Keep in mind, however, that blocking APIs are not a good fit for
|
||||
this concurrency model.
|
||||
|
||||
.Mutable State
|
||||
==== Mutable State
|
||||
|
||||
In Reactor and RxJava, you declare logic through operators. At runtime, a reactive
|
||||
pipeline is formed where data is processed sequentially, in distinct stages. A key benefit
|
||||
of this is that it frees applications from having to protect mutable state because
|
||||
application code within that pipeline is never invoked concurrently.
|
||||
|
||||
.Threading Model
|
||||
==== Threading Model
|
||||
|
||||
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 nor other optional
|
||||
@@ -285,7 +287,8 @@ specific thread pool `Scheduler` strategy.
|
||||
* Data access libraries and other third party dependencies can also create and use threads
|
||||
of their own.
|
||||
|
||||
.Configuring
|
||||
==== Configuring
|
||||
|
||||
The Spring Framework does not provide support for starting and stopping
|
||||
<<webflux-server-choice, servers>>. To configure the threading model for a server,
|
||||
you need to use server-specific configuration APIs, or, if you use Spring Boot,
|
||||
@@ -594,7 +597,7 @@ The `DefaultServerWebExchange` uses the configured `HttpMessageReader` to parse
|
||||
|
||||
[[webflux-multipart]]
|
||||
==== Multipart Data
|
||||
[.small]#<<web.adoc#mvc-multipart, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-multipart, See equivalent in the Servlet stack>>#
|
||||
|
||||
`ServerWebExchange` exposes the following method for accessing multipart data:
|
||||
|
||||
@@ -610,8 +613,8 @@ The `DefaultServerWebExchange` uses the configured `HttpMessageReader` to parse
|
||||
----
|
||||
|
||||
The `DefaultServerWebExchange` uses the configured
|
||||
`HttpMessageReader<MultiValueMap<String, Part>>` to parse `multipart/form-data` content
|
||||
into a `MultiValueMap`.
|
||||
`HttpMessageReader<MultiValueMap<String, Part>>` to parse `multipart/form-data`,
|
||||
`multipart/mixed`, and `multipart/related` content into a `MultiValueMap`.
|
||||
By default, this is the `DefaultPartHttpMessageReader`, which does not have any third-party
|
||||
dependencies.
|
||||
Alternatively, the `SynchronossPartHttpMessageReader` can be used, which is based on the
|
||||
@@ -628,7 +631,7 @@ collecting to a `MultiValueMap`.
|
||||
|
||||
[[webflux-forwarded-headers]]
|
||||
==== Forwarded Headers
|
||||
[.small]#<<web.adoc#filters-forwarded-headers, Web MVC>>#
|
||||
[.small]#<<web.adoc#filters-forwarded-headers, See equivalent in the Servlet stack>>#
|
||||
|
||||
As a request goes through proxies (such as load balancers), the host, port, and
|
||||
scheme may change. That makes it a challenge, from a client perspective, to create links that point to the correct
|
||||
@@ -659,7 +662,7 @@ filters, and `ForwardedHeaderTransformer` is used instead.
|
||||
|
||||
[[webflux-filters]]
|
||||
=== Filters
|
||||
[.small]#<<web.adoc#filters, Web MVC>>#
|
||||
[.small]#<<web.adoc#filters, See equivalent in the Servlet stack>>#
|
||||
|
||||
In the <<webflux-web-handler-api>>, you can use a `WebFilter` to apply interception-style
|
||||
logic before and after the rest of the processing chain of filters and the target
|
||||
@@ -670,7 +673,7 @@ the bean declaration or by implementing `Ordered`.
|
||||
|
||||
[[webflux-filters-cors]]
|
||||
==== CORS
|
||||
[.small]#<<web.adoc#filters-cors, Web MVC>>#
|
||||
[.small]#<<web.adoc#filters-cors, See equivalent in the Servlet stack>>#
|
||||
|
||||
Spring WebFlux provides fine-grained support for CORS configuration through annotations on
|
||||
controllers. However, when you use it with Spring Security, we advise relying on the built-in
|
||||
@@ -681,7 +684,7 @@ See the section on <<webflux-cors>> and the <<webflux-cors-webfilter>> for more
|
||||
|
||||
[[webflux-exception-handler]]
|
||||
=== Exceptions
|
||||
[.small]#<<web.adoc#mvc-ann-customer-servlet-container-error-page, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-ann-customer-servlet-container-error-page, See equivalent in the Servlet stack>>#
|
||||
|
||||
In the <<webflux-web-handler-api>>, you can use a `WebExceptionHandler` to handle
|
||||
exceptions from the chain of `WebFilter` instances and the target `WebHandler`. When using the
|
||||
@@ -712,7 +715,7 @@ The following table describes the available `WebExceptionHandler` implementation
|
||||
|
||||
[[webflux-codecs]]
|
||||
=== Codecs
|
||||
[.small]#<<integration.adoc#rest-message-conversion, Web MVC>>#
|
||||
[.small]#<<integration.adoc#rest-message-conversion, See equivalent in the Servlet stack>>#
|
||||
|
||||
The `spring-web` and `spring-core` modules provide support for serializing and
|
||||
deserializing byte content to and from higher level objects through non-blocking I/O with
|
||||
@@ -802,12 +805,12 @@ consistently for access to the cached form data versus reading from the raw requ
|
||||
==== Multipart
|
||||
|
||||
`MultipartHttpMessageReader` and `MultipartHttpMessageWriter` support decoding and
|
||||
encoding "multipart/form-data" content. In turn `MultipartHttpMessageReader` delegates to
|
||||
another `HttpMessageReader` for the actual parsing to a `Flux<Part>` and then simply
|
||||
collects the parts into a `MultiValueMap`.
|
||||
encoding "multipart/form-data", "multipart/mixed", and "multipart/related" content.
|
||||
In turn `MultipartHttpMessageReader` delegates to another `HttpMessageReader`
|
||||
for the actual parsing to a `Flux<Part>` and then simply collects the parts into a `MultiValueMap`.
|
||||
By default, the `DefaultPartHttpMessageReader` is used, but this can be changed through the
|
||||
`ServerCodecConfigurer`.
|
||||
For more information about the `DefaultPartHttpMessageReader`, refer to to the
|
||||
For more information about the `DefaultPartHttpMessageReader`, refer to the
|
||||
{api-spring-framework}/http/codec/multipart/DefaultPartHttpMessageReader.html[javadoc of `DefaultPartHttpMessageReader`].
|
||||
|
||||
On the server side where multipart form content may need to be accessed from multiple
|
||||
@@ -852,7 +855,7 @@ To configure all three in WebFlux, you'll need to supply a pre-configured instan
|
||||
|
||||
[[webflux-codecs-streaming]]
|
||||
==== Streaming
|
||||
[.small]#<<web.adoc#mvc-ann-async-http-streaming, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-ann-async-http-streaming, See equivalent in the Servlet stack>>#
|
||||
|
||||
When streaming to the HTTP response (for example, `text/event-stream`,
|
||||
`application/x-ndjson`), it is important to send data periodically, in order to
|
||||
@@ -880,7 +883,7 @@ especially the section on <<core#databuffers-using, Using DataBuffer>>.
|
||||
|
||||
[[webflux-logging]]
|
||||
=== Logging
|
||||
[.small]#<<web.adoc#mvc-logging, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-logging, See equivalent in the Servlet stack>>#
|
||||
|
||||
`DEBUG` level logging in Spring WebFlux is designed to be compact, minimal, and
|
||||
human-friendly. It focuses on high value bits of information that are useful over and
|
||||
@@ -912,7 +915,7 @@ while a fully formatted prefix based on that ID is available from
|
||||
|
||||
[[webflux-logging-sensitive-data]]
|
||||
==== Sensitive Data
|
||||
[.small]#<<web.adoc#mvc-logging-sensitive-data, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-logging-sensitive-data, See equivalent in the Servlet stack>>#
|
||||
|
||||
`DEBUG` and `TRACE` logging can log sensitive information. This is why form parameters and
|
||||
headers are masked by default and you must explicitly enable their logging in full.
|
||||
@@ -1014,7 +1017,7 @@ The following example shows how to do so for client-side requests:
|
||||
|
||||
[[webflux-dispatcher-handler]]
|
||||
== `DispatcherHandler`
|
||||
[.small]#<<web.adoc#mvc-servlet, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-servlet, See equivalent in the Servlet stack>>#
|
||||
|
||||
Spring WebFlux, similarly to Spring MVC, is designed around the front controller pattern,
|
||||
where a central `WebHandler`, the `DispatcherHandler`, provides a shared algorithm for
|
||||
@@ -1057,7 +1060,7 @@ The resulting `HttpHandler` is ready for use with a <<webflux-httphandler, serve
|
||||
|
||||
[[webflux-special-bean-types]]
|
||||
=== Special Bean Types
|
||||
[.small]#<<web.adoc#mvc-servlet-special-bean-types, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-servlet-special-bean-types, See equivalent in the Servlet stack>>#
|
||||
|
||||
The `DispatcherHandler` delegates to special beans to process requests and render the
|
||||
appropriate responses. By "`special beans,`" we mean Spring-managed `Object` instances that
|
||||
@@ -1099,7 +1102,7 @@ there are also some other beans detected at a lower level (see
|
||||
|
||||
[[webflux-framework-config]]
|
||||
=== WebFlux Config
|
||||
[.small]#<<web.adoc#mvc-servlet-config, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-servlet-config, See equivalent in the Servlet stack>>#
|
||||
|
||||
Applications can declare the infrastructure beans (listed under
|
||||
<<webflux-web-handler-api-special-beans, Web Handler API>> and
|
||||
@@ -1114,7 +1117,7 @@ many extra convenient options.
|
||||
|
||||
[[webflux-dispatcher-handler-sequence]]
|
||||
=== Processing
|
||||
[.small]#<<web.adoc#mvc-servlet-sequence, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-servlet-sequence, See equivalent in the Servlet stack>>#
|
||||
|
||||
`DispatcherHandler` processes requests as follows:
|
||||
|
||||
@@ -1165,7 +1168,7 @@ as a `HandlerResult`, along with some additional context, and passed to the firs
|
||||
|
||||
[[webflux-dispatcher-exceptions]]
|
||||
=== Exceptions
|
||||
[.small]#<<web.adoc#mvc-exceptionhandlers, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-exceptionhandlers, See equivalent in the Servlet stack>>#
|
||||
|
||||
`HandlerAdapter` implementations can handle internally exceptions from invoking a request
|
||||
handler, such as a controller method. However, an exception may be deferred if the request
|
||||
@@ -1175,7 +1178,7 @@ A `HandlerAdapter` may expose its exception handling mechanism as a
|
||||
`DispatchExceptionHandler` set on the `HandlerResult` it returns. When that's set,
|
||||
`DispatcherHandler` will also apply it to the handling of the result.
|
||||
|
||||
A `HandlerAdapter` may also choose to implement `DispatchExceptionHandler`. Inn that case
|
||||
A `HandlerAdapter` may also choose to implement `DispatchExceptionHandler`. In that case
|
||||
`DispatcherHandler` will apply it to exceptions that arise before a handler is mapped,
|
||||
e.g. during handler mapping, or earlier, e.g. in a `WebFilter`.
|
||||
|
||||
@@ -1186,7 +1189,7 @@ See also <<webflux-ann-controller-exceptions>> in the "`Annotated Controller`" s
|
||||
|
||||
[[webflux-viewresolution]]
|
||||
=== View Resolution
|
||||
[.small]#<<web.adoc#mvc-viewresolver, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-viewresolver, See equivalent in the Servlet stack>>#
|
||||
|
||||
View resolution enables rendering to a browser with an HTML template and a model without
|
||||
tying you to a specific view technology. In Spring WebFlux, view resolution is
|
||||
@@ -1197,7 +1200,7 @@ instance. The `View` is then used to render the response.
|
||||
|
||||
[[webflux-viewresolution-handling]]
|
||||
==== Handling
|
||||
[.small]#<<web.adoc#mvc-viewresolver-handling, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-viewresolver-handling, See equivalent in the Servlet stack>>#
|
||||
|
||||
The `HandlerResult` passed into `ViewResolutionResultHandler` contains the return value
|
||||
from the handler and the model that contains attributes added during request
|
||||
@@ -1233,7 +1236,7 @@ See <<webflux-view>> for more on the view technologies integrated with Spring We
|
||||
|
||||
[[webflux-redirecting-redirect-prefix]]
|
||||
==== Redirecting
|
||||
[.small]#<<web.adoc#mvc-redirecting-redirect-prefix, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-redirecting-redirect-prefix, See equivalent in the Servlet stack>>#
|
||||
|
||||
The special `redirect:` prefix in a view name lets you perform a redirect. The
|
||||
`UrlBasedViewResolver` (and sub-classes) recognize this as an instruction that a
|
||||
@@ -1248,7 +1251,7 @@ operate in terms of logical view names. A view name such as
|
||||
|
||||
[[webflux-multiple-representations]]
|
||||
==== Content Negotiation
|
||||
[.small]#<<web.adoc#mvc-multiple-representations, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-multiple-representations, See equivalent in the Servlet stack>>#
|
||||
|
||||
`ViewResolutionResultHandler` supports content negotiation. It compares the request
|
||||
media types with the media types supported by each selected `View`. The first `View`
|
||||
@@ -1265,7 +1268,7 @@ always selected and used if they match the requested media type.
|
||||
|
||||
[[webflux-controller]]
|
||||
== Annotated Controllers
|
||||
[.small]#<<web.adoc#mvc-controller, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-controller, See equivalent in the Servlet stack>>#
|
||||
|
||||
Spring WebFlux provides an annotation-based programming model, where `@Controller` and
|
||||
`@RestController` components use annotations to express request mappings, request input,
|
||||
@@ -1303,7 +1306,7 @@ In the preceding example, the method returns a `String` to be written to the res
|
||||
|
||||
[[webflux-ann-controller]]
|
||||
=== `@Controller`
|
||||
[.small]#<<web.adoc#mvc-ann-controller, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-ann-controller, See equivalent in the Servlet stack>>#
|
||||
|
||||
You can define controller beans by using a standard Spring bean definition.
|
||||
The `@Controller` stereotype allows for auto-detection and is aligned with Spring general support
|
||||
@@ -1347,7 +1350,7 @@ directly to the response body versus view resolution and rendering with an HTML
|
||||
|
||||
[[webflux-ann-requestmapping-proxying]]
|
||||
==== AOP Proxies
|
||||
[.small]#<<web.adoc#mvc-ann-requestmapping-proxying, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-ann-requestmapping-proxying, See equivalent in the Servlet stack>>#
|
||||
|
||||
In some cases, you may need to decorate a controller with an AOP proxy at runtime.
|
||||
One example is if you choose to have `@Transactional` annotations directly on the
|
||||
@@ -1370,7 +1373,7 @@ Please, enable class based proxying, or otherwise the interface must also have a
|
||||
|
||||
[[webflux-ann-requestmapping]]
|
||||
=== Request Mapping
|
||||
[.small]#<<web.adoc#mvc-ann-requestmapping, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-ann-requestmapping, See equivalent in the Servlet stack>>#
|
||||
|
||||
The `@RequestMapping` annotation is used to map requests to controllers methods. It has
|
||||
various attributes to match by URL, HTTP method, request parameters, headers, and media
|
||||
@@ -1434,7 +1437,7 @@ The following example uses type and method level mappings:
|
||||
|
||||
[[webflux-ann-requestmapping-uri-templates]]
|
||||
==== URI Patterns
|
||||
[.small]#<<web.adoc#mvc-ann-requestmapping-uri-templates, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-ann-requestmapping-uri-templates, See equivalent in the Servlet stack>>#
|
||||
|
||||
You can map requests by using glob patterns and wildcards:
|
||||
|
||||
@@ -1583,7 +1586,7 @@ explicit, and less vulnerable to URL path based exploits.
|
||||
|
||||
[[webflux-ann-requestmapping-pattern-comparison]]
|
||||
==== Pattern Comparison
|
||||
[.small]#<<web.adoc#mvc-ann-requestmapping-pattern-comparison, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-ann-requestmapping-pattern-comparison, See equivalent in the Servlet stack>>#
|
||||
|
||||
When multiple patterns match a URL, they must be compared to find the best match. This is done
|
||||
with `PathPattern.SPECIFICITY_COMPARATOR`, which looks for patterns that are more specific.
|
||||
@@ -1598,7 +1601,7 @@ sorted last instead. If two patterns are both catch-all, the longer is chosen.
|
||||
|
||||
[[webflux-ann-requestmapping-consumes]]
|
||||
==== Consumable Media Types
|
||||
[.small]#<<web.adoc#mvc-ann-requestmapping-consumes, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-ann-requestmapping-consumes, See equivalent in the Servlet stack>>#
|
||||
|
||||
You can narrow the request mapping based on the `Content-Type` of the request,
|
||||
as the following example shows:
|
||||
@@ -1633,7 +1636,7 @@ TIP: `MediaType` provides constants for commonly used media types -- for example
|
||||
|
||||
[[webflux-ann-requestmapping-produces]]
|
||||
==== Producible Media Types
|
||||
[.small]#<<web.adoc#mvc-ann-requestmapping-produces, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-ann-requestmapping-produces, See equivalent in the Servlet stack>>#
|
||||
|
||||
You can narrow the request mapping based on the `Accept` request header and the list of
|
||||
content types that a controller method produces, as the following example shows:
|
||||
@@ -1670,7 +1673,7 @@ TIP: `MediaType` provides constants for commonly used media types -- e.g.
|
||||
|
||||
[[webflux-ann-requestmapping-params-and-headers]]
|
||||
==== Parameters and Headers
|
||||
[.small]#<<web.adoc#mvc-ann-requestmapping-params-and-headers, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-ann-requestmapping-params-and-headers, See equivalent in the Servlet stack>>#
|
||||
|
||||
You can narrow request mappings based on query parameter conditions. You can test for the
|
||||
presence of a query parameter (`myParam`), for its absence (`!myParam`), or for a
|
||||
@@ -1722,7 +1725,7 @@ You can also use the same with request header conditions, as the following examp
|
||||
|
||||
[[webflux-ann-requestmapping-head-options]]
|
||||
==== HTTP HEAD, OPTIONS
|
||||
[.small]#<<web.adoc#mvc-ann-requestmapping-head-options, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-ann-requestmapping-head-options, See equivalent in the Servlet stack>>#
|
||||
|
||||
`@GetMapping` and `@RequestMapping(method=HttpMethod.GET)` support HTTP HEAD
|
||||
transparently for request mapping purposes. Controller methods need not change.
|
||||
@@ -1743,7 +1746,7 @@ is not necessary in the common case.
|
||||
|
||||
[[webflux-ann-requestmapping-composed]]
|
||||
==== Custom Annotations
|
||||
[.small]#<<web.adoc#mvc-ann-requestmapping-composed, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-ann-requestmapping-composed, See equivalent in the Servlet stack>>#
|
||||
|
||||
Spring WebFlux supports the use of <<core.adoc#beans-meta-annotations, composed annotations>>
|
||||
for request mapping. Those are annotations that are themselves meta-annotated with
|
||||
@@ -1764,7 +1767,7 @@ you can check the custom attribute and return your own `RequestCondition`.
|
||||
|
||||
[[webflux-ann-requestmapping-registration]]
|
||||
==== Explicit Registrations
|
||||
[.small]#<<web.adoc#mvc-ann-requestmapping-registration, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-ann-requestmapping-registration, See equivalent in the Servlet stack>>#
|
||||
|
||||
You can programmatically register Handler methods, which can be used for dynamic
|
||||
registrations or for advanced cases, such as different instances of the same handler
|
||||
@@ -1821,7 +1824,7 @@ under different URLs. The following example shows how to do so:
|
||||
|
||||
[[webflux-ann-methods]]
|
||||
=== Handler Methods
|
||||
[.small]#<<web.adoc#mvc-ann-methods, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-ann-methods, See equivalent in the Servlet stack>>#
|
||||
|
||||
`@RequestMapping` handler methods have a flexible signature and can choose from a range of
|
||||
supported controller method arguments and return values.
|
||||
@@ -1829,7 +1832,7 @@ supported controller method arguments and return values.
|
||||
|
||||
[[webflux-ann-arguments]]
|
||||
==== Method Arguments
|
||||
[.small]#<<web.adoc#mvc-ann-arguments, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-ann-arguments, See equivalent in the Servlet stack>>#
|
||||
|
||||
The following table shows the supported controller method arguments.
|
||||
|
||||
@@ -1949,7 +1952,7 @@ and others) and is equivalent to `required=false`.
|
||||
|
||||
[[webflux-ann-return-types]]
|
||||
==== Return Values
|
||||
[.small]#<<web.adoc#mvc-ann-return-types, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-ann-return-types, See equivalent in the Servlet stack>>#
|
||||
|
||||
The following table shows the supported controller method return values. Note that reactive
|
||||
types from libraries such as Reactor, RxJava, <<webflux-reactive-libraries, or other>> are
|
||||
@@ -2030,7 +2033,7 @@ generally supported for all return values.
|
||||
|
||||
[[webflux-ann-typeconversion]]
|
||||
==== Type Conversion
|
||||
[.small]#<<web.adoc#mvc-ann-typeconversion, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-ann-typeconversion, See equivalent in the Servlet stack>>#
|
||||
|
||||
Some annotated controller method arguments that represent String-based request input (for example,
|
||||
`@RequestParam`, `@RequestHeader`, `@PathVariable`, `@MatrixVariable`, and `@CookieValue`)
|
||||
@@ -2050,7 +2053,7 @@ argument as `@Nullable`.
|
||||
|
||||
[[webflux-ann-matrix-variables]]
|
||||
==== Matrix Variables
|
||||
[.small]#<<web.adoc#mvc-ann-matrix-variables, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-ann-matrix-variables, See equivalent in the Servlet stack>>#
|
||||
|
||||
https://tools.ietf.org/html/rfc3986#section-3.3[RFC 3986] discusses name-value pairs in
|
||||
path segments. In Spring WebFlux, we refer to those as "`matrix variables`" based on an
|
||||
@@ -2185,7 +2188,7 @@ To get all matrix variables, use a `MultiValueMap`, as the following example sho
|
||||
|
||||
[[webflux-ann-requestparam]]
|
||||
==== `@RequestParam`
|
||||
[.small]#<<web.adoc#mvc-ann-requestparam, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-ann-requestparam, See equivalent in the Servlet stack>>#
|
||||
|
||||
You can use the `@RequestParam` annotation to bind query parameters to a method argument in a
|
||||
controller. The following code snippet shows the usage:
|
||||
@@ -2260,7 +2263,7 @@ with `@RequestParam`.
|
||||
|
||||
[[webflux-ann-requestheader]]
|
||||
==== `@RequestHeader`
|
||||
[.small]#<<web.adoc#mvc-ann-requestheader, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-ann-requestheader, See equivalent in the Servlet stack>>#
|
||||
|
||||
You can use the `@RequestHeader` annotation to bind a request header to a method argument in a
|
||||
controller.
|
||||
@@ -2321,7 +2324,7 @@ example, a method parameter annotated with `@RequestHeader("Accept")` may be of
|
||||
|
||||
[[webflux-ann-cookievalue]]
|
||||
==== `@CookieValue`
|
||||
[.small]#<<web.adoc#mvc-ann-cookievalue, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-ann-cookievalue, See equivalent in the Servlet stack>>#
|
||||
|
||||
You can use the `@CookieValue` annotation to bind the value of an HTTP cookie to a method argument
|
||||
in a controller.
|
||||
@@ -2362,7 +2365,7 @@ Type conversion is applied automatically if the target method parameter type is
|
||||
|
||||
[[webflux-ann-modelattrib-method-args]]
|
||||
==== `@ModelAttribute`
|
||||
[.small]#<<web.adoc#mvc-ann-modelattrib-method-args, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-ann-modelattrib-method-args, See equivalent in the Servlet stack>>#
|
||||
|
||||
You can use the `@ModelAttribute` annotation on a method argument to access an attribute from the
|
||||
model or have it instantiated if not present. The model attribute is also overlaid with
|
||||
@@ -2509,7 +2512,7 @@ with `@ModelAttribute`.
|
||||
|
||||
[[webflux-ann-sessionattributes]]
|
||||
==== `@SessionAttributes`
|
||||
[.small]#<<web.adoc#mvc-ann-sessionattributes, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-ann-sessionattributes, See equivalent in the Servlet stack>>#
|
||||
|
||||
`@SessionAttributes` is used to store model attributes in the `WebSession` between
|
||||
requests. It is a type-level annotation that declares session attributes used by a
|
||||
@@ -2594,7 +2597,7 @@ as the following example shows:
|
||||
|
||||
[[webflux-ann-sessionattribute]]
|
||||
==== `@SessionAttribute`
|
||||
[.small]#<<web.adoc#mvc-ann-sessionattribute, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-ann-sessionattribute, See equivalent in the Servlet stack>>#
|
||||
|
||||
If you need access to pre-existing session attributes that are managed globally
|
||||
(that is, outside the controller -- for example, by a filter) and may or may not be present,
|
||||
@@ -2630,7 +2633,7 @@ workflow, consider using `SessionAttributes`, as described in
|
||||
|
||||
[[webflux-ann-requestattrib]]
|
||||
==== `@RequestAttribute`
|
||||
[.small]#<<web.adoc#mvc-ann-requestattrib, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-ann-requestattrib, See equivalent in the Servlet stack>>#
|
||||
|
||||
Similarly to `@SessionAttribute`, you can use the `@RequestAttribute` annotation to
|
||||
access pre-existing request attributes created earlier (for example, by a `WebFilter`),
|
||||
@@ -2659,7 +2662,7 @@ as the following example shows:
|
||||
|
||||
[[webflux-multipart-forms]]
|
||||
==== Multipart Content
|
||||
[.small]#<<web.adoc#mvc-multipart-forms, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-multipart-forms, See equivalent in the Servlet stack>>#
|
||||
|
||||
As explained in <<webflux-multipart>>, `ServerWebExchange` provides access to multipart
|
||||
content. The best way to handle a file upload form (for example, from a browser) in a controller
|
||||
@@ -2936,7 +2939,7 @@ See <<webflux-client-body-multipart>>.
|
||||
|
||||
[[webflux-ann-requestbody]]
|
||||
==== `@RequestBody`
|
||||
[.small]#<<web.adoc#mvc-ann-requestbody, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-ann-requestbody, See equivalent in the Servlet stack>>#
|
||||
|
||||
You can use the `@RequestBody` annotation to have the request body read and deserialized into an
|
||||
`Object` through an <<webflux-codecs,HttpMessageReader>>.
|
||||
@@ -3010,7 +3013,7 @@ related operators:
|
||||
|
||||
[[webflux-ann-httpentity]]
|
||||
==== `HttpEntity`
|
||||
[.small]#<<web.adoc#mvc-ann-httpentity, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-ann-httpentity, See equivalent in the Servlet stack>>#
|
||||
|
||||
`HttpEntity` is more or less identical to using <<webflux-ann-requestbody>> but is based on a
|
||||
container object that exposes request headers and the body. The following example uses an
|
||||
@@ -3036,7 +3039,7 @@ container object that exposes request headers and the body. The following exampl
|
||||
|
||||
[[webflux-ann-responsebody]]
|
||||
==== `@ResponseBody`
|
||||
[.small]#<<web.adoc#mvc-ann-responsebody, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-ann-responsebody, See equivalent in the Servlet stack>>#
|
||||
|
||||
You can use the `@ResponseBody` annotation on a method to have the return serialized
|
||||
to the response body through an <<webflux-codecs, HttpMessageWriter>>. The following
|
||||
@@ -3079,7 +3082,7 @@ configure or customize message writing.
|
||||
|
||||
[[webflux-ann-responseentity]]
|
||||
==== `ResponseEntity`
|
||||
[.small]#<<web.adoc#mvc-ann-responseentity, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-ann-responseentity, See equivalent in the Servlet stack>>#
|
||||
|
||||
`ResponseEntity` is like <<webflux-ann-responsebody>> but with status and headers. For example:
|
||||
|
||||
@@ -3126,7 +3129,7 @@ Spring offers support for the Jackson JSON library.
|
||||
|
||||
[[webflux-ann-jsonview]]
|
||||
===== JSON Views
|
||||
[.small]#<<web.adoc#mvc-ann-jackson, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-ann-jackson, See equivalent in the Servlet stack>>#
|
||||
|
||||
Spring WebFlux provides built-in support for
|
||||
https://www.baeldung.com/jackson-json-view-annotation[Jackson's Serialization Views],
|
||||
@@ -3204,7 +3207,7 @@ controller method. Use a composite interface if you need to activate multiple vi
|
||||
|
||||
[[webflux-ann-modelattrib-methods]]
|
||||
=== `Model`
|
||||
[.small]#<<web.adoc#mvc-ann-modelattrib-methods, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-ann-modelattrib-methods, See equivalent in the Servlet stack>>#
|
||||
|
||||
You can use the `@ModelAttribute` annotation:
|
||||
|
||||
@@ -3343,7 +3346,7 @@ as the following example shows:
|
||||
|
||||
[[webflux-ann-initbinder]]
|
||||
=== `DataBinder`
|
||||
[.small]#<<web.adoc#mvc-ann-initbinder, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-ann-initbinder, See equivalent in the Servlet stack>>#
|
||||
|
||||
`@Controller` or `@ControllerAdvice` classes can have `@InitBinder` methods, to
|
||||
initialize instances of `WebDataBinder`. Those, in turn, are used to:
|
||||
@@ -3442,14 +3445,14 @@ controller-specific `Formatter` instances, as the following example shows:
|
||||
|
||||
[[webflux-ann-initbinder-model-design]]
|
||||
==== Model Design
|
||||
[.small]#<<web.adoc#mvc-ann-initbinder-model-design, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-ann-initbinder-model-design, See equivalent in the Servlet stack>>#
|
||||
|
||||
include::web-data-binding-model-design.adoc[]
|
||||
|
||||
|
||||
[[webflux-ann-controller-exceptions]]
|
||||
=== Exceptions
|
||||
[.small]#<<web.adoc#mvc-ann-exceptionhandler, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-ann-exceptionhandler, See equivalent in the Servlet stack>>#
|
||||
|
||||
`@Controller` and <<webflux-ann-controller-advice, @ControllerAdvice>> classes can have
|
||||
`@ExceptionHandler` methods to handle exceptions from controller methods. The following
|
||||
@@ -3511,7 +3514,7 @@ for more detail.
|
||||
|
||||
[[webflux-ann-exceptionhandler-args]]
|
||||
==== Method Arguments
|
||||
[.small]#<<web.adoc#mvc-ann-exceptionhandler-args, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-ann-exceptionhandler-args, See equivalent in the Servlet stack>>#
|
||||
|
||||
`@ExceptionHandler` methods support the same <<webflux-ann-arguments,method arguments>>
|
||||
as `@RequestMapping` methods, except the request body might have been consumed already.
|
||||
@@ -3520,7 +3523,7 @@ as `@RequestMapping` methods, except the request body might have been consumed a
|
||||
|
||||
[[webflux-ann-exceptionhandler-return-values]]
|
||||
==== Return Values
|
||||
[.small]#<<web.adoc#mvc-ann-exceptionhandler-return-values, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-ann-exceptionhandler-return-values, See equivalent in the Servlet stack>>#
|
||||
|
||||
`@ExceptionHandler` methods support the same <<webflux-ann-return-types,return values>>
|
||||
as `@RequestMapping` methods.
|
||||
@@ -3529,7 +3532,7 @@ as `@RequestMapping` methods.
|
||||
|
||||
[[webflux-ann-controller-advice]]
|
||||
=== Controller Advice
|
||||
[.small]#<<web.adoc#mvc-ann-controller-advice, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-ann-controller-advice, See equivalent in the Servlet stack>>#
|
||||
|
||||
Typically, the `@ExceptionHandler`, `@InitBinder`, and `@ModelAttribute` methods apply
|
||||
within the `@Controller` class (or class hierarchy) in which they are declared. If you
|
||||
@@ -3597,7 +3600,7 @@ include::webflux-functional.adoc[leveloffset=+1]
|
||||
|
||||
[[webflux-uri-building]]
|
||||
== URI Links
|
||||
[.small]#<<web.adoc#mvc-uri-building, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-uri-building, See equivalent in the Servlet stack>>#
|
||||
|
||||
This section describes various options available in the Spring Framework to prepare URIs.
|
||||
|
||||
@@ -3608,7 +3611,7 @@ include::webflux-cors.adoc[leveloffset=+1]
|
||||
|
||||
[[webflux-ann-rest-exceptions]]
|
||||
== Error Responses
|
||||
[.small]#<<webmvc.adoc#mvc-ann-rest-exceptions, Web MVC>>#
|
||||
[.small]#<<webmvc.adoc#mvc-ann-rest-exceptions, See equivalent in the Servlet stack>>#
|
||||
|
||||
A common requirement for REST services is to include details in the body of error
|
||||
responses. The Spring Framework supports the "Problem Details for HTTP APIs"
|
||||
@@ -3632,7 +3635,7 @@ and any `ErrorResponseException`, and renders an error response with a body.
|
||||
|
||||
[[webflux-ann-rest-exceptions-render]]
|
||||
=== Render
|
||||
[.small]#<<webmvc.adoc#mvc-ann-rest-exceptions-render, Web MVC>>#
|
||||
[.small]#<<webmvc.adoc#mvc-ann-rest-exceptions-render, See equivalent in the Servlet stack>>#
|
||||
|
||||
You can return `ProblemDetail` or `ErrorResponse` from any `@ExceptionHandler` or from
|
||||
any `@RequestMapping` method to render an RFC 7807 response. This is processed as follows:
|
||||
@@ -3655,7 +3658,7 @@ use a protected method to map any exception to a `ProblemDetail`.
|
||||
|
||||
[[webflux-ann-rest-exceptions-non-standard]]
|
||||
=== Non-Standard Fields
|
||||
[.small]#<<webmvc.adoc#mvc-ann-rest-exceptions-non-standard, Web MVC>>#
|
||||
[.small]#<<webmvc.adoc#mvc-ann-rest-exceptions-non-standard, See equivalent in the Servlet stack>>#
|
||||
|
||||
You can extend an RFC 7807 response with non-standard fields in one of two ways.
|
||||
|
||||
@@ -3675,7 +3678,7 @@ from an existing `ProblemDetail`. This could be done centrally, e.g. from an
|
||||
|
||||
[[webflux-ann-rest-exceptions-i18n]]
|
||||
=== Internationalization
|
||||
[.small]#<<webmvc.adoc#mvc-ann-rest-exceptions-i18n, Web MVC>>#
|
||||
[.small]#<<webmvc.adoc#mvc-ann-rest-exceptions-i18n, See equivalent in the Servlet stack>>#
|
||||
|
||||
It is a common requirement to internationalize error response details, and good practice
|
||||
to customize the problem details for Spring WebFlux exceptions. This is supported as follows:
|
||||
@@ -3746,7 +3749,7 @@ qualified exception class name.
|
||||
|
||||
[[webflux-ann-rest-exceptions-client]]
|
||||
=== Client Handling
|
||||
[.small]#<<webmvc.adoc#mvc-ann-rest-exceptions-client, Web MVC>>#
|
||||
[.small]#<<webmvc.adoc#mvc-ann-rest-exceptions-client, See equivalent in the Servlet stack>>#
|
||||
|
||||
A client application can catch `WebClientResponseException`, when using the `WebClient`,
|
||||
or `RestClientResponseException` when using the `RestTemplate`, and use their
|
||||
@@ -3758,7 +3761,7 @@ or `RestClientResponseException` when using the `RestTemplate`, and use their
|
||||
|
||||
[[webflux-web-security]]
|
||||
== Web Security
|
||||
[.small]#<<web.adoc#mvc-web-security, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-web-security, See equivalent in the Servlet stack>>#
|
||||
|
||||
The https://spring.io/projects/spring-security[Spring Security] project provides support
|
||||
for protecting web applications from malicious exploits. See the Spring Security
|
||||
@@ -3774,7 +3777,7 @@ reference documentation, including:
|
||||
|
||||
[[webflux-caching]]
|
||||
== HTTP Caching
|
||||
[.small]#<<web.adoc#mvc-caching, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-caching, See equivalent in the Servlet stack>>#
|
||||
|
||||
HTTP caching can significantly improve the performance of a web application. HTTP caching
|
||||
revolves around the `Cache-Control` response header and subsequent conditional request
|
||||
@@ -3790,7 +3793,7 @@ This section describes the HTTP caching related options available in Spring WebF
|
||||
|
||||
[[webflux-caching-cachecontrol]]
|
||||
=== `CacheControl`
|
||||
[.small]#<<web.adoc#mvc-caching-cachecontrol, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-caching-cachecontrol, See equivalent in the Servlet stack>>#
|
||||
|
||||
{api-spring-framework}/http/CacheControl.html[`CacheControl`] provides support for
|
||||
configuring settings related to the `Cache-Control` header and is accepted as an argument
|
||||
@@ -3838,7 +3841,7 @@ use case-oriented approach that focuses on the common scenarios, as the followin
|
||||
|
||||
[[webflux-caching-etag-lastmodified]]
|
||||
=== Controllers
|
||||
[.small]#<<web.adoc#mvc-caching-etag-lastmodified, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-caching-etag-lastmodified, See equivalent in the Servlet stack>>#
|
||||
|
||||
Controllers can add explicit support for HTTP caching. We recommend doing so, since the
|
||||
`lastModified` or `ETag` value for a resource needs to be calculated before it can be compared
|
||||
@@ -3939,7 +3942,7 @@ to 412 (PRECONDITION_FAILED) to prevent concurrent modification.
|
||||
|
||||
[[webflux-caching-static-resources]]
|
||||
=== Static Resources
|
||||
[.small]#<<web.adoc#mvc-caching-static-resources, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-caching-static-resources, See equivalent in the Servlet stack>>#
|
||||
|
||||
You should serve static resources with a `Cache-Control` and conditional response headers
|
||||
for optimal performance. See the section on configuring <<webflux-config-static-resources>>.
|
||||
@@ -3950,7 +3953,7 @@ include::webflux-view.adoc[leveloffset=+1]
|
||||
|
||||
[[webflux-config]]
|
||||
== WebFlux Config
|
||||
[.small]#<<web.adoc#mvc-config, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-config, See equivalent in the Servlet stack>>#
|
||||
|
||||
The WebFlux Java configuration declares the components that are required to process
|
||||
requests with annotated controllers or functional endpoints, and it offers an API to
|
||||
@@ -3967,7 +3970,7 @@ gain full control over the configuration through the
|
||||
|
||||
[[webflux-config-enable]]
|
||||
=== Enabling WebFlux Config
|
||||
[.small]#<<web.adoc#mvc-config-enable, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-config-enable, See equivalent in the Servlet stack>>#
|
||||
|
||||
You can use the `@EnableWebFlux` annotation in your Java config, as the following example shows:
|
||||
|
||||
@@ -3996,7 +3999,7 @@ available on the classpath -- for JSON, XML, and others.
|
||||
|
||||
[[webflux-config-customize]]
|
||||
=== WebFlux config API
|
||||
[.small]#<<web.adoc#mvc-config-customize, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-config-customize, See equivalent in the Servlet stack>>#
|
||||
|
||||
In your Java configuration, you can implement the `WebFluxConfigurer` interface,
|
||||
as the following example shows:
|
||||
@@ -4027,7 +4030,7 @@ class WebConfig : WebFluxConfigurer {
|
||||
|
||||
[[webflux-config-conversion]]
|
||||
=== Conversion, formatting
|
||||
[.small]#<<web.adoc#mvc-config-conversion, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-config-conversion, See equivalent in the Servlet stack>>#
|
||||
|
||||
By default, formatters for various number and date types are installed, along with support
|
||||
for customization via `@NumberFormat` and `@DateTimeFormat` on fields.
|
||||
@@ -4104,7 +4107,7 @@ use `FormatterRegistrar` implementations.
|
||||
|
||||
[[webflux-config-validation]]
|
||||
=== Validation
|
||||
[.small]#<<web.adoc#mvc-config-validation, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-config-validation, See equivalent in the Servlet stack>>#
|
||||
|
||||
By default, if <<core.adoc#validation-beanvalidation-overview, Bean Validation>> is present
|
||||
on the classpath (for example, the Hibernate Validator), the `LocalValidatorFactoryBean`
|
||||
@@ -4179,7 +4182,7 @@ mark it with `@Primary` in order to avoid conflict with the one declared in the
|
||||
|
||||
[[webflux-config-content-negotiation]]
|
||||
=== Content Type Resolvers
|
||||
[.small]#<<web.adoc#mvc-config-content-negotiation, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-config-content-negotiation, See equivalent in the Servlet stack>>#
|
||||
|
||||
You can configure how Spring WebFlux determines the requested media types for
|
||||
`@Controller` instances from the request. By default, only the `Accept` header is checked,
|
||||
@@ -4217,7 +4220,7 @@ The following example shows how to customize the requested content type resoluti
|
||||
|
||||
[[webflux-config-message-codecs]]
|
||||
=== HTTP message codecs
|
||||
[.small]#<<web.adoc#mvc-config-message-converters, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-config-message-converters, See equivalent in the Servlet stack>>#
|
||||
|
||||
The following example shows how to customize how the request and response body are read and written:
|
||||
|
||||
@@ -4268,7 +4271,7 @@ It also automatically registers the following well-known modules if they are det
|
||||
|
||||
[[webflux-config-view-resolvers]]
|
||||
=== View Resolvers
|
||||
[.small]#<<web.adoc#mvc-config-view-resolvers, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-config-view-resolvers, See equivalent in the Servlet stack>>#
|
||||
|
||||
The following example shows how to configure view resolution:
|
||||
|
||||
@@ -4425,7 +4428,7 @@ See <<webflux-view>> for more on the view technologies that are integrated with
|
||||
|
||||
[[webflux-config-static-resources]]
|
||||
=== Static Resources
|
||||
[.small]#<<web.adoc#mvc-config-static-resources, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-config-static-resources, See equivalent in the Servlet stack>>#
|
||||
|
||||
This option provides a convenient way to serve static resources from a list of
|
||||
{api-spring-framework}/core/io/Resource.html[`Resource`]-based locations.
|
||||
@@ -4549,7 +4552,7 @@ for fine-grained control, e.g. last-modified behavior and optimized resource res
|
||||
|
||||
[[webflux-config-path-matching]]
|
||||
=== Path Matching
|
||||
[.small]#<<web.adoc#mvc-config-path-matching, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-config-path-matching, See equivalent in the Servlet stack>>#
|
||||
|
||||
You can customize options related to path matching. For details on the individual options, see the
|
||||
{api-spring-framework}/web/reactive/config/PathMatchConfigurer.html[`PathMatchConfigurer`] javadoc.
|
||||
@@ -4650,7 +4653,7 @@ For example:
|
||||
|
||||
[[webflux-config-advanced-java]]
|
||||
=== Advanced Configuration Mode
|
||||
[.small]#<<web.adoc#mvc-config-advanced-java, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-config-advanced-java, See equivalent in the Servlet stack>>#
|
||||
|
||||
`@EnableWebFlux` imports `DelegatingWebFluxConfiguration` that:
|
||||
|
||||
@@ -4690,7 +4693,7 @@ the classpath.
|
||||
|
||||
[[webflux-http2]]
|
||||
== HTTP/2
|
||||
[.small]#<<web.adoc#mvc-http2, Web MVC>>#
|
||||
[.small]#<<web.adoc#mvc-http2, See equivalent in the Servlet stack>>#
|
||||
|
||||
HTTP/2 is supported with Reactor Netty, Tomcat, Jetty, and Undertow. However, there are
|
||||
considerations related to server configuration. For more details, see the
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[[mvc-cors]]
|
||||
= CORS
|
||||
[.small]#<<web-reactive.adoc#webflux-cors, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-cors, See equivalent in the Reactive stack>>#
|
||||
|
||||
Spring MVC lets you handle CORS (Cross-Origin Resource Sharing). This section
|
||||
describes how to do so.
|
||||
@@ -10,7 +10,7 @@ describes how to do so.
|
||||
|
||||
[[mvc-cors-intro]]
|
||||
== Introduction
|
||||
[.small]#<<web-reactive.adoc#webflux-cors-intro, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-cors-intro, See equivalent in the Reactive stack>>#
|
||||
|
||||
For security reasons, browsers prohibit AJAX calls to resources outside the current origin.
|
||||
For example, you could have your bank account in one tab and evil.com in another. Scripts
|
||||
@@ -27,7 +27,7 @@ powerful workarounds based on IFRAME or JSONP.
|
||||
|
||||
[[mvc-cors-processing]]
|
||||
== Processing
|
||||
[.small]#<<web-reactive.adoc#webflux-cors-processing, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-cors-processing, See equivalent in the Reactive stack>>#
|
||||
|
||||
The CORS specification distinguishes between preflight, simple, and actual requests.
|
||||
To learn how CORS works, you can read
|
||||
@@ -77,7 +77,7 @@ To learn more from the source or make advanced customizations, check the code be
|
||||
|
||||
[[mvc-cors-controller]]
|
||||
== `@CrossOrigin`
|
||||
[.small]#<<web-reactive.adoc#webflux-cors-controller, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-cors-controller, See equivalent in the Reactive stack>>#
|
||||
|
||||
The {api-spring-framework}/web/bind/annotation/CrossOrigin.html[`@CrossOrigin`]
|
||||
annotation enables cross-origin requests on annotated controller methods,
|
||||
@@ -226,7 +226,7 @@ as the following example shows:
|
||||
|
||||
[[mvc-cors-global]]
|
||||
== Global Configuration
|
||||
[.small]#<<web-reactive.adoc#webflux-cors-global, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-cors-global, See equivalent in the Reactive stack>>#
|
||||
|
||||
In addition to fine-grained, controller method level configuration, you probably want to
|
||||
define some global CORS configuration, too. You can set URL-based `CorsConfiguration`
|
||||
@@ -252,7 +252,7 @@ the `allowOriginPatterns` property may be used to match to a dynamic set of orig
|
||||
|
||||
[[mvc-cors-global-java]]
|
||||
=== Java Configuration
|
||||
[.small]#<<web-reactive.adoc#webflux-cors-global, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-cors-global, See equivalent in the Reactive stack>>#
|
||||
|
||||
To enable CORS in the MVC Java config, you can use the `CorsRegistry` callback,
|
||||
as the following example shows:
|
||||
@@ -329,7 +329,7 @@ as the following example shows:
|
||||
|
||||
[[mvc-cors-filter]]
|
||||
== CORS Filter
|
||||
[.small]#<<webflux-cors.adoc#webflux-cors-webfilter, WebFlux>>#
|
||||
[.small]#<<webflux-cors.adoc#webflux-cors-webfilter, See equivalent in the Reactive stack>>#
|
||||
|
||||
You can apply CORS support through the built-in
|
||||
{api-spring-framework}/web/filter/CorsFilter.html[`CorsFilter`].
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[[webmvc-fn]]
|
||||
= Functional Endpoints
|
||||
[.small]#<<web-reactive.adoc#webflux-fn, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-fn, See equivalent in the Reactive stack>>#
|
||||
|
||||
Spring Web MVC includes WebMvc.fn, a lightweight functional programming model in which functions
|
||||
are used to route and handle requests and contracts are designed for immutability.
|
||||
@@ -12,7 +12,7 @@ the same <<web#mvc-servlet>>.
|
||||
|
||||
[[webmvc-fn-overview]]
|
||||
== Overview
|
||||
[.small]#<<web-reactive.adoc#webflux-fn-overview, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-fn-overview, See equivalent in the Reactive stack>>#
|
||||
|
||||
In WebMvc.fn, an HTTP request is handled with a `HandlerFunction`: a function that takes
|
||||
`ServerRequest` and returns a `ServerResponse`.
|
||||
@@ -111,7 +111,7 @@ If you register the `RouterFunction` as a bean, for instance by exposing it in a
|
||||
|
||||
[[webmvc-fn-handler-functions]]
|
||||
== HandlerFunction
|
||||
[.small]#<<web-reactive.adoc#webflux-fn-handler-functions, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-fn-handler-functions, See equivalent in the Reactive stack>>#
|
||||
|
||||
`ServerRequest` and `ServerResponse` are immutable interfaces that offer JDK 8-friendly
|
||||
access to the HTTP request and response, including headers, body, method, and status code.
|
||||
@@ -238,22 +238,22 @@ allows you to send Strings, or other objects as JSON. For example:
|
||||
.Java
|
||||
----
|
||||
public RouterFunction<ServerResponse> sse() {
|
||||
return route(GET("/sse"), request -> ServerResponse.sse(sseBuilder -> {
|
||||
// Save the sseBuilder object somewhere..
|
||||
}));
|
||||
return route(GET("/sse"), request -> ServerResponse.sse(sseBuilder -> {
|
||||
// Save the sseBuilder object somewhere..
|
||||
}));
|
||||
}
|
||||
|
||||
// In some other thread, sending a String
|
||||
sseBuilder.send("Hello world");
|
||||
|
||||
// Or an object, which will be transformed into JSON
|
||||
Person person = ...
|
||||
Person person = ...
|
||||
sseBuilder.send(person);
|
||||
|
||||
// Customize the event by using the other methods
|
||||
sseBuilder.id("42")
|
||||
.event("sse event")
|
||||
.data(person);
|
||||
// Customize the event by using the other methods
|
||||
sseBuilder.id("42")
|
||||
.event("sse event")
|
||||
.data(person);
|
||||
|
||||
// and done at some point
|
||||
sseBuilder.complete();
|
||||
@@ -261,23 +261,23 @@ allows you to send Strings, or other objects as JSON. For example:
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
fun sse(): RouterFunction<ServerResponse> = router {
|
||||
GET("/sse") { request -> ServerResponse.sse { sseBuilder ->
|
||||
// Save the sseBuilder object somewhere..
|
||||
}
|
||||
}
|
||||
fun sse(): RouterFunction<ServerResponse> = router {
|
||||
GET("/sse") { request -> ServerResponse.sse { sseBuilder ->
|
||||
// Save the sseBuilder object somewhere..
|
||||
}
|
||||
}
|
||||
|
||||
// In some other thread, sending a String
|
||||
sseBuilder.send("Hello world")
|
||||
|
||||
// Or an object, which will be transformed into JSON
|
||||
val person = ...
|
||||
val person = ...
|
||||
sseBuilder.send(person)
|
||||
|
||||
// Customize the event by using the other methods
|
||||
sseBuilder.id("42")
|
||||
.event("sse event")
|
||||
.data(person)
|
||||
// Customize the event by using the other methods
|
||||
sseBuilder.id("42")
|
||||
.event("sse event")
|
||||
.data(person)
|
||||
|
||||
// and done at some point
|
||||
sseBuilder.complete()
|
||||
@@ -463,7 +463,7 @@ See <<core.adoc#validation-beanvalidation, Spring Validation>>.
|
||||
|
||||
[[webmvc-fn-router-functions]]
|
||||
== `RouterFunction`
|
||||
[.small]#<<web-reactive.adoc#webflux-fn-router-functions, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-fn-router-functions, See equivalent in the Reactive stack>>#
|
||||
|
||||
Router functions are used to route the requests to the corresponding `HandlerFunction`.
|
||||
Typically, you do not write router functions yourself, but rather use a method on the
|
||||
@@ -672,7 +672,7 @@ We can further improve by using the `nest` method together with `accept`:
|
||||
|
||||
[[webmvc-fn-running]]
|
||||
== Running a Server
|
||||
[.small]#<<web-reactive.adoc#webflux-fn-running, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-fn-running, See equivalent in the Reactive stack>>#
|
||||
|
||||
You typically run router functions in a <<web.adoc#mvc-servlet, `DispatcherHandler`>>-based setup through the
|
||||
<<web.adoc#mvc-config>>, which uses Spring configuration to declare the
|
||||
@@ -765,7 +765,7 @@ The following example shows a WebFlux Java configuration:
|
||||
|
||||
[[webmvc-fn-handler-filter-function]]
|
||||
== Filtering Handler Functions
|
||||
[.small]#<<web-reactive.adoc#webflux-fn-handler-filter-function, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-fn-handler-filter-function, See equivalent in the Reactive stack>>#
|
||||
|
||||
You can filter handler functions by using the `before`, `after`, or `filter` methods on the routing
|
||||
function builder.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[[webmvc.test]]
|
||||
= Testing
|
||||
[.small]#<<web-reactive.adoc#webflux-test, Same in Spring WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-test, See equivalent in the Reactive stack>>#
|
||||
|
||||
This section summarizes the options available in `spring-test` for Spring MVC applications.
|
||||
|
||||
@@ -25,4 +25,4 @@ See <<testing.adoc#spring-mvc-test-client, Client REST Tests>> for more details.
|
||||
* `WebTestClient`: Built for testing WebFlux applications, but it can also be used for
|
||||
end-to-end integration testing, to any server, over an HTTP connection. It is a
|
||||
non-blocking, reactive client and is well suited for testing asynchronous and streaming
|
||||
scenarios.
|
||||
scenarios. See <<testing.adoc#webtestclient, `WebTestClient`>> for more details.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[[mvc-view]]
|
||||
= View Technologies
|
||||
[.small]#<<web-reactive.adoc#webflux-view, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-view, See equivalent in the Reactive stack>>#
|
||||
|
||||
The use of view technologies in Spring MVC is pluggable. Whether you decide to use
|
||||
Thymeleaf, Groovy Markup Templates, JSPs, or other technologies is primarily a matter of
|
||||
@@ -14,7 +14,7 @@ the templates are editable by external sources, since this can have security imp
|
||||
|
||||
[[mvc-view-thymeleaf]]
|
||||
== Thymeleaf
|
||||
[.small]#<<web-reactive.adoc#webflux-view-thymeleaf, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-view-thymeleaf, See equivalent in the Reactive stack>>#
|
||||
|
||||
Thymeleaf is a modern server-side Java template engine that emphasizes natural HTML
|
||||
templates that can be previewed in a browser by double-clicking, which is very helpful
|
||||
@@ -34,7 +34,7 @@ See https://www.thymeleaf.org/documentation.html[Thymeleaf+Spring] for more deta
|
||||
|
||||
[[mvc-view-freemarker]]
|
||||
== FreeMarker
|
||||
[.small]#<<web-reactive.adoc#webflux-view-freemarker, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-view-freemarker, See equivalent in the Reactive stack>>#
|
||||
|
||||
https://freemarker.apache.org/[Apache FreeMarker] is a template engine for generating any
|
||||
kind of text output from HTML to email and others. The Spring Framework has built-in
|
||||
@@ -44,7 +44,7 @@ integration for using Spring MVC with FreeMarker templates.
|
||||
|
||||
[[mvc-view-freemarker-contextconfig]]
|
||||
=== View Configuration
|
||||
[.small]#<<web-reactive.adoc#webflux-view-freemarker-contextconfig, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-view-freemarker-contextconfig, See equivalent in the Reactive stack>>#
|
||||
|
||||
The following example shows how to configure FreeMarker as a view technology:
|
||||
|
||||
@@ -125,7 +125,7 @@ returns a view name of `welcome`, the resolver looks for the
|
||||
|
||||
[[mvc-views-freemarker]]
|
||||
=== FreeMarker Configuration
|
||||
[.small]#<<web-reactive.adoc#webflux-views-freemarker, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-views-freemarker, See equivalent in the Reactive stack>>#
|
||||
|
||||
You can pass FreeMarker 'Settings' and 'SharedVariables' directly to the FreeMarker
|
||||
`Configuration` object (which is managed by Spring) by setting the appropriate bean
|
||||
@@ -164,7 +164,7 @@ with additional convenience macros for generating form input elements themselves
|
||||
|
||||
[[mvc-view-bind-macros]]
|
||||
==== The Bind Macros
|
||||
[.small]#<<web-reactive.adoc#webflux-view-bind-macros, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-view-bind-macros, See equivalent in the Reactive stack>>#
|
||||
|
||||
A standard set of macros are maintained within the `spring-webmvc.jar` file for
|
||||
FreeMarker, so they are always available to a suitably configured application.
|
||||
@@ -288,7 +288,7 @@ as the value for the `fieldType` parameter.
|
||||
|
||||
The parameters to any of the above macros have consistent meanings:
|
||||
|
||||
* `path`: The name of the field to bind to (ie "command.name")
|
||||
* `path`: The name of the field to bind to (for example, "command.name")
|
||||
* `options`: A `Map` of all the available values that can be selected from in the input
|
||||
field. The keys to the map represent the values that are POSTed back from the form
|
||||
and bound to the command object. Map objects stored against the keys are the labels
|
||||
@@ -576,7 +576,7 @@ syntax. The following example shows a sample template for an HTML page:
|
||||
|
||||
[[mvc-view-script]]
|
||||
== Script Views
|
||||
[.small]#<<web-reactive.adoc#webflux-view-script, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-view-script, See equivalent in the Reactive stack>>#
|
||||
|
||||
The Spring Framework has a built-in integration for using Spring MVC with any
|
||||
templating library that can run on top of the
|
||||
@@ -602,7 +602,7 @@ TIP: The basic rule for integrating any other script engine is that it must impl
|
||||
|
||||
[[mvc-view-script-dependencies]]
|
||||
=== Requirements
|
||||
[.small]#<<web-reactive.adoc#webflux-view-script-dependencies, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-view-script-dependencies, See equivalent in the Reactive stack>>#
|
||||
|
||||
You need to have the script engine on your classpath, the details of which vary by script engine:
|
||||
|
||||
@@ -622,7 +622,7 @@ through https://www.webjars.org/[WebJars].
|
||||
|
||||
[[mvc-view-script-integrate]]
|
||||
=== Script Templates
|
||||
[.small]#<<web-reactive.adoc#webflux-view-script, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-view-script, See equivalent in the Reactive stack>>#
|
||||
|
||||
You can declare a `ScriptTemplateConfigurer` bean to specify the script engine to use,
|
||||
the script files to load, what function to call to render templates, and so on.
|
||||
@@ -1649,138 +1649,6 @@ is the default type.
|
||||
|
||||
|
||||
|
||||
[[mvc-view-tiles]]
|
||||
== Tiles
|
||||
|
||||
You can integrate Tiles - just as any other view technology - in web
|
||||
applications that use Spring. This section describes, in a broad way, how to do so.
|
||||
|
||||
NOTE: This section focuses on Spring's support for Tiles version 3 in the
|
||||
`org.springframework.web.servlet.view.tiles3` package.
|
||||
|
||||
|
||||
|
||||
[[mvc-view-tiles-dependencies]]
|
||||
=== Dependencies
|
||||
|
||||
To be able to use Tiles, you have to add a dependency on Tiles version 3.0.1 or higher
|
||||
and https://tiles.apache.org/framework/dependency-management.html[its transitive dependencies]
|
||||
to your project.
|
||||
|
||||
|
||||
|
||||
[[mvc-view-tiles-integrate]]
|
||||
=== Configuration
|
||||
|
||||
To be able to use Tiles, you have to configure it by using files that contain definitions
|
||||
(for basic information on definitions and other Tiles concepts, see
|
||||
https://tiles.apache.org[]). In Spring, this is done by using the `TilesConfigurer`.
|
||||
The following example `ApplicationContext` configuration shows how to do so:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
|
||||
<property name="definitions">
|
||||
<list>
|
||||
<value>/WEB-INF/defs/general.xml</value>
|
||||
<value>/WEB-INF/defs/widgets.xml</value>
|
||||
<value>/WEB-INF/defs/administrator.xml</value>
|
||||
<value>/WEB-INF/defs/customer.xml</value>
|
||||
<value>/WEB-INF/defs/templates.xml</value>
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
----
|
||||
|
||||
The preceding example defines five files that contain definitions. The files are all
|
||||
located in the `WEB-INF/defs` directory. At initialization of the `WebApplicationContext`,
|
||||
the files are loaded, and the definitions factory are initialized. After that has
|
||||
been done, the Tiles included in the definition files can be used as views within your
|
||||
Spring web application. To be able to use the views, you have to have a `ViewResolver`
|
||||
as with any other view technology in Spring: typically a convenient `TilesViewResolver`.
|
||||
|
||||
You can specify locale-specific Tiles definitions by adding an underscore and then
|
||||
the locale, as the following example shows:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
|
||||
<property name="definitions">
|
||||
<list>
|
||||
<value>/WEB-INF/defs/tiles.xml</value>
|
||||
<value>/WEB-INF/defs/tiles_fr_FR.xml</value>
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
----
|
||||
|
||||
With the preceding configuration, `tiles_fr_FR.xml` is used for requests with the `fr_FR` locale,
|
||||
and `tiles.xml` is used by default.
|
||||
|
||||
NOTE: Since underscores are used to indicate locales, we recommended not using
|
||||
them otherwise in the file names for Tiles definitions.
|
||||
|
||||
|
||||
|
||||
[[mvc-view-tiles-url]]
|
||||
==== `UrlBasedViewResolver`
|
||||
|
||||
The `UrlBasedViewResolver` instantiates the given `viewClass` for each view it has to
|
||||
resolve. The following bean defines a `UrlBasedViewResolver`:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
<bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
|
||||
<property name="viewClass" value="org.springframework.web.servlet.view.tiles3.TilesView"/>
|
||||
</bean>
|
||||
----
|
||||
|
||||
|
||||
[[mvc-view-tiles-preparer]]
|
||||
==== `SimpleSpringPreparerFactory` and `SpringBeanPreparerFactory`
|
||||
|
||||
As an advanced feature, Spring also supports two special Tiles `PreparerFactory`
|
||||
implementations. See the Tiles documentation for details on how to use
|
||||
`ViewPreparer` references in your Tiles definition files.
|
||||
|
||||
You can specify `SimpleSpringPreparerFactory` to autowire `ViewPreparer` instances based on
|
||||
specified preparer classes, applying Spring's container callbacks as well as applying
|
||||
configured Spring BeanPostProcessors. If Spring's context-wide annotation configuration has
|
||||
been activated, annotations in `ViewPreparer` classes are automatically detected and
|
||||
applied. Note that this expects preparer classes in the Tiles definition files, as
|
||||
the default `PreparerFactory` does.
|
||||
|
||||
You can specify `SpringBeanPreparerFactory` to operate on specified preparer names (instead
|
||||
of classes), obtaining the corresponding Spring bean from the DispatcherServlet's
|
||||
application context. The full bean creation process is in the control of the Spring
|
||||
application context in this case, allowing for the use of explicit dependency injection
|
||||
configuration, scoped beans, and so on. Note that you need to define one Spring bean definition
|
||||
for each preparer name (as used in your Tiles definitions). The following example shows
|
||||
how to define a `SpringBeanPreparerFactory` property on a `TilesConfigurer` bean:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
|
||||
<property name="definitions">
|
||||
<list>
|
||||
<value>/WEB-INF/defs/general.xml</value>
|
||||
<value>/WEB-INF/defs/widgets.xml</value>
|
||||
<value>/WEB-INF/defs/administrator.xml</value>
|
||||
<value>/WEB-INF/defs/customer.xml</value>
|
||||
<value>/WEB-INF/defs/templates.xml</value>
|
||||
</list>
|
||||
</property>
|
||||
|
||||
<!-- resolving preparer names as Spring bean definition names -->
|
||||
<property name="preparerFactoryClass"
|
||||
value="org.springframework.web.servlet.view.tiles3.SpringBeanPreparerFactory"/>
|
||||
|
||||
</bean>
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
||||
[[mvc-view-feeds]]
|
||||
== RSS and Atom
|
||||
|
||||
@@ -1965,7 +1833,7 @@ an external definition (by name) or as a `View` instance from the handler method
|
||||
|
||||
[[mvc-view-jackson]]
|
||||
== Jackson
|
||||
[.small]#<<web-reactive.adoc#webflux-view-httpmessagewriter, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-view-httpmessagewriter, See equivalent in the Reactive stack>>#
|
||||
|
||||
Spring offers support for the Jackson JSON library.
|
||||
|
||||
@@ -1973,7 +1841,7 @@ Spring offers support for the Jackson JSON library.
|
||||
|
||||
[[mvc-view-json-mapping]]
|
||||
=== Jackson-based JSON MVC Views
|
||||
[.small]#<<web-reactive.adoc#webflux-view-httpmessagewriter, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-view-httpmessagewriter, See equivalent in the Reactive stack>>#
|
||||
|
||||
The `MappingJackson2JsonView` uses the Jackson library's `ObjectMapper` to render the response
|
||||
content as JSON. By default, the entire contents of the model map (with the exception of
|
||||
@@ -1992,7 +1860,7 @@ serializers and deserializers for specific types.
|
||||
|
||||
[[mvc-view-xml-mapping]]
|
||||
=== Jackson-based XML Views
|
||||
[.small]#<<web-reactive.adoc#webflux-view-httpmessagewriter, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-view-httpmessagewriter, See equivalent in the Reactive stack>>#
|
||||
|
||||
`MappingJackson2XmlView` uses the
|
||||
https://github.com/FasterXML/jackson-dataformat-xml[Jackson XML extension's] `XmlMapper`
|
||||
|
||||
@@ -23,7 +23,7 @@ https://github.com/spring-projects/spring-framework/wiki/Spring-Framework-Versio
|
||||
|
||||
[[mvc-servlet]]
|
||||
== DispatcherServlet
|
||||
[.small]#<<web-reactive.adoc#webflux-dispatcher-handler, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-dispatcher-handler, See equivalent in the Reactive stack>>#
|
||||
|
||||
Spring MVC, as many other web frameworks, is designed around the front controller
|
||||
pattern where a central `Servlet`, the `DispatcherServlet`, provides a shared algorithm
|
||||
@@ -240,7 +240,7 @@ TIP: If an application context hierarchy is not required, applications may confi
|
||||
|
||||
[[mvc-servlet-special-bean-types]]
|
||||
=== Special Bean Types
|
||||
[.small]#<<web-reactive.adoc#webflux-special-bean-types, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-special-bean-types, See equivalent in the Reactive stack>>#
|
||||
|
||||
The `DispatcherServlet` delegates to special beans to process requests and render the
|
||||
appropriate responses. By "`special beans`" we mean Spring-managed `Object` instances that
|
||||
@@ -300,7 +300,7 @@ The following table lists the special beans detected by the `DispatcherServlet`:
|
||||
|
||||
[[mvc-servlet-config]]
|
||||
=== Web MVC Config
|
||||
[.small]#<<web-reactive.adoc#webflux-framework-config, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-framework-config, See equivalent in the Reactive stack>>#
|
||||
|
||||
Applications can declare the infrastructure beans listed in <<mvc-servlet-special-bean-types>>
|
||||
that are required to process requests. The `DispatcherServlet` checks the
|
||||
@@ -503,7 +503,7 @@ override the `createDispatcherServlet` method.
|
||||
|
||||
[[mvc-servlet-sequence]]
|
||||
=== Processing
|
||||
[.small]#<<web-reactive.adoc#webflux-dispatcher-handler-sequence, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-dispatcher-handler-sequence, See equivalent in the Reactive stack>>#
|
||||
|
||||
The `DispatcherServlet` processes requests as follows:
|
||||
|
||||
@@ -662,7 +662,7 @@ declare it as an <<mvc-ann-controller-advice>> bean or configure it directly on
|
||||
|
||||
[[mvc-exceptionhandlers]]
|
||||
=== Exceptions
|
||||
[.small]#<<web-reactive.adoc#webflux-dispatcher-exceptions, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-dispatcher-exceptions, See equivalent in the Reactive stack>>#
|
||||
|
||||
If an exception occurs during request mapping or is thrown from a request handler (such as
|
||||
a `@Controller`), the `DispatcherServlet` delegates to a chain of `HandlerExceptionResolver`
|
||||
@@ -773,7 +773,7 @@ however, use both a `WebApplicationInitializer` and a minimal `web.xml`.
|
||||
|
||||
[[mvc-viewresolver]]
|
||||
=== View Resolution
|
||||
[.small]#<<web-reactive.adoc#webflux-viewresolution, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-viewresolution, See equivalent in the Reactive stack>>#
|
||||
|
||||
Spring MVC defines the `ViewResolver` and `View` interfaces that let you render
|
||||
models in a browser without tying you to a specific view technology. `ViewResolver`
|
||||
@@ -802,8 +802,8 @@ The following table provides more details on the `ViewResolver` hierarchy:
|
||||
|
||||
| `InternalResourceViewResolver`
|
||||
| Convenient subclass of `UrlBasedViewResolver` that supports `InternalResourceView` (in
|
||||
effect, Servlets and JSPs) and subclasses such as `JstlView` and `TilesView`. You can
|
||||
specify the view class for all views generated by this resolver by using `setViewClass(..)`.
|
||||
effect, Servlets and JSPs) and subclasses such as `JstlView`. You can specify the view
|
||||
class for all views generated by this resolver by using `setViewClass(..)`.
|
||||
See the {api-spring-framework}/web/reactive/result/view/UrlBasedViewResolver.html[`UrlBasedViewResolver`]
|
||||
javadoc for details.
|
||||
|
||||
@@ -825,7 +825,7 @@ The following table provides more details on the `ViewResolver` hierarchy:
|
||||
|
||||
[[mvc-viewresolver-handling]]
|
||||
==== Handling
|
||||
[.small]#<<web-reactive.adoc#webflux-viewresolution-handling, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-viewresolution-handling, See equivalent in the Reactive stack>>#
|
||||
|
||||
You can chain view resolvers by declaring more than one resolver bean and, if necessary, by
|
||||
setting the `order` property to specify ordering. Remember, the higher the order property,
|
||||
@@ -846,7 +846,7 @@ rendering without controller logic.
|
||||
|
||||
[[mvc-redirecting-redirect-prefix]]
|
||||
==== Redirecting
|
||||
[.small]#<<web-reactive.adoc#webflux-redirecting-redirect-prefix, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-redirecting-redirect-prefix, See equivalent in the Reactive stack>>#
|
||||
|
||||
The special `redirect:` prefix in a view name lets you perform a redirect. The
|
||||
`UrlBasedViewResolver` (and its subclasses) recognize this as an instruction that a
|
||||
@@ -876,7 +876,7 @@ Servlet/JSP engine. Note that you may also chain multiple view resolvers, instea
|
||||
|
||||
[[mvc-multiple-representations]]
|
||||
==== Content Negotiation
|
||||
[.small]#<<web-reactive.adoc#webflux-multiple-representations, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-multiple-representations, See equivalent in the Reactive stack>>#
|
||||
|
||||
{api-spring-framework}/web/servlet/view/ContentNegotiatingViewResolver.html[`ContentNegotiatingViewResolver`]
|
||||
does not resolve views itself but rather delegates
|
||||
@@ -978,7 +978,7 @@ The following table describes the properties `CookieLocaleResolver`:
|
||||
| Property | Default | Description
|
||||
|
||||
| `cookieName`
|
||||
| classname + LOCALE
|
||||
| class name + LOCALE
|
||||
| The name of the cookie
|
||||
|
||||
| `cookieMaxAge`
|
||||
@@ -1138,7 +1138,7 @@ request with a simple request parameter.
|
||||
|
||||
[[mvc-multipart]]
|
||||
=== Multipart Resolver
|
||||
[.small]#<<web-reactive.adoc#webflux-multipart, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-multipart, See equivalent in the Reactive stack>>#
|
||||
|
||||
`MultipartResolver` from the `org.springframework.web.multipart` package is a strategy
|
||||
for parsing multipart requests including file uploads. There is a container-based
|
||||
@@ -1214,7 +1214,7 @@ javadoc for details and configuration options.
|
||||
|
||||
[[mvc-logging]]
|
||||
=== Logging
|
||||
[.small]#<<web-reactive.adoc#webflux-logging, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-logging, See equivalent in the Reactive stack>>#
|
||||
|
||||
DEBUG-level logging in Spring MVC is designed to be compact, minimal, and
|
||||
human-friendly. It focuses on high-value bits of information that are useful over and
|
||||
@@ -1230,7 +1230,7 @@ not meet the stated goals, please let us know.
|
||||
|
||||
[[mvc-logging-sensitive-data]]
|
||||
==== Sensitive Data
|
||||
[.small]#<<web-reactive.adoc#webflux-logging-sensitive-data, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-logging-sensitive-data, See equivalent in the Reactive stack>>#
|
||||
|
||||
DEBUG and TRACE logging may log sensitive information. This is why request parameters and
|
||||
headers are masked by default and their logging in full must be enabled explicitly
|
||||
@@ -1294,7 +1294,7 @@ public class MyInitializer
|
||||
|
||||
[[filters]]
|
||||
== Filters
|
||||
[.small]#<<web-reactive.adoc#webflux-filters, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-filters, See equivalent in the Reactive stack>>#
|
||||
|
||||
The `spring-web` module provides some useful filters:
|
||||
|
||||
@@ -1321,7 +1321,7 @@ available through the `ServletRequest.getParameter{asterisk}()` family of method
|
||||
|
||||
[[filters-forwarded-headers]]
|
||||
=== Forwarded Headers
|
||||
[.small]#<<web-reactive.adoc#webflux-forwarded-headers, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-forwarded-headers, See equivalent in the Reactive stack>>#
|
||||
|
||||
As a request goes through proxies (such as load balancers) the host, port, and
|
||||
scheme may change, and that makes it a challenge to create links that point to the correct
|
||||
@@ -1382,7 +1382,7 @@ the filter via `web.xml` or in Spring Boot via a `FilterRegistrationBean` be sur
|
||||
|
||||
[[filters-cors]]
|
||||
=== CORS
|
||||
[.small]#<<web-reactive.adoc#webflux-filters-cors, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-filters-cors, See equivalent in the Reactive stack>>#
|
||||
|
||||
Spring MVC provides fine-grained support for CORS configuration through annotations on
|
||||
controllers. However, when used with Spring Security, we advise relying on the built-in
|
||||
@@ -1395,7 +1395,7 @@ See the sections on <<mvc-cors>> and the <<mvc-cors-filter>> for more details.
|
||||
|
||||
[[mvc-controller]]
|
||||
== Annotated Controllers
|
||||
[.small]#<<web-reactive.adoc#webflux-controller, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-controller, See equivalent in the Reactive stack>>#
|
||||
|
||||
Spring MVC provides an annotation-based programming model where `@Controller` and
|
||||
`@RestController` components use annotations to express request mappings, request input,
|
||||
@@ -1442,7 +1442,7 @@ programming model described in this section.
|
||||
|
||||
[[mvc-ann-controller]]
|
||||
=== Declaration
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-controller, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-controller, See equivalent in the Reactive stack>>#
|
||||
|
||||
You can define controller beans by using a standard Spring bean definition in the
|
||||
Servlet's `WebApplicationContext`. The `@Controller` stereotype allows for auto-detection,
|
||||
@@ -1504,7 +1504,7 @@ directly to the response body versus view resolution and rendering with an HTML
|
||||
|
||||
[[mvc-ann-requestmapping-proxying]]
|
||||
==== AOP Proxies
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-requestmapping-proxying, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-requestmapping-proxying, See equivalent in the Reactive stack>>#
|
||||
|
||||
In some cases, you may need to decorate a controller with an AOP proxy at runtime.
|
||||
One example is if you choose to have `@Transactional` annotations directly on the
|
||||
@@ -1526,7 +1526,7 @@ Please, enable class based proxying, or otherwise the interface must also have a
|
||||
|
||||
[[mvc-ann-requestmapping]]
|
||||
=== Request Mapping
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-requestmapping, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-requestmapping, See equivalent in the Reactive stack>>#
|
||||
|
||||
You can use the `@RequestMapping` annotation to map requests to controllers methods. It has
|
||||
various attributes to match by URL, HTTP method, request parameters, headers, and media
|
||||
@@ -1591,7 +1591,7 @@ The following example has type and method level mappings:
|
||||
|
||||
[[mvc-ann-requestmapping-uri-templates]]
|
||||
==== URI patterns
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-requestmapping-uri-templates, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-requestmapping-uri-templates, See equivalent in the Reactive stack>>#
|
||||
|
||||
`@RequestMapping` methods can be mapped using URL patterns. There are two alternatives:
|
||||
|
||||
@@ -1713,7 +1713,7 @@ some external configuration.
|
||||
|
||||
[[mvc-ann-requestmapping-pattern-comparison]]
|
||||
==== Pattern Comparison
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-requestmapping-pattern-comparison, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-requestmapping-pattern-comparison, See equivalent in the Reactive stack>>#
|
||||
|
||||
When multiple patterns match a URL, the best match must be selected. This is done with
|
||||
one of the following depending on whether use of parsed `PathPattern` is enabled for use or not:
|
||||
@@ -1794,7 +1794,7 @@ recommendations related to RFD.
|
||||
|
||||
[[mvc-ann-requestmapping-consumes]]
|
||||
==== Consumable Media Types
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-requestmapping-consumes, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-requestmapping-consumes, See equivalent in the Reactive stack>>#
|
||||
|
||||
You can narrow the request mapping based on the `Content-Type` of the request,
|
||||
as the following example shows:
|
||||
@@ -1832,7 +1832,7 @@ TIP: `MediaType` provides constants for commonly used media types, such as
|
||||
|
||||
[[mvc-ann-requestmapping-produces]]
|
||||
==== Producible Media Types
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-requestmapping-produces, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-requestmapping-produces, See equivalent in the Reactive stack>>#
|
||||
|
||||
You can narrow the request mapping based on the `Accept` request header and the list of
|
||||
content types that a controller method produces, as the following example shows:
|
||||
@@ -1872,7 +1872,7 @@ TIP: `MediaType` provides constants for commonly used media types, such as
|
||||
|
||||
[[mvc-ann-requestmapping-params-and-headers]]
|
||||
==== Parameters, headers
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-requestmapping-params-and-headers, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-requestmapping-params-and-headers, See equivalent in the Reactive stack>>#
|
||||
|
||||
You can narrow request mappings based on request parameter conditions. You can test for the
|
||||
presence of a request parameter (`myParam`), for the absence of one (`!myParam`), or for a
|
||||
@@ -1927,7 +1927,7 @@ instead.
|
||||
|
||||
[[mvc-ann-requestmapping-head-options]]
|
||||
==== HTTP HEAD, OPTIONS
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-requestmapping-head-options, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-requestmapping-head-options, See equivalent in the Reactive stack>>#
|
||||
|
||||
`@GetMapping` (and `@RequestMapping(method=HttpMethod.GET)`) support HTTP HEAD
|
||||
transparently for request mapping. Controller methods do not need to change.
|
||||
@@ -1953,7 +1953,7 @@ is not necessary in the common case.
|
||||
|
||||
[[mvc-ann-requestmapping-composed]]
|
||||
==== Custom Annotations
|
||||
[.small]#<<web-reactive.adoc#mvc-ann-requestmapping-head-options, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#mvc-ann-requestmapping-head-options, See equivalent in the Reactive stack>>#
|
||||
|
||||
Spring MVC supports the use of <<core.adoc#beans-meta-annotations, composed annotations>>
|
||||
for request mapping. Those are annotations that are themselves meta-annotated with
|
||||
@@ -1974,7 +1974,7 @@ you can check the custom attribute and return your own `RequestCondition`.
|
||||
|
||||
[[mvc-ann-requestmapping-registration]]
|
||||
==== Explicit Registrations
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-requestmapping-registration, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-requestmapping-registration, See equivalent in the Reactive stack>>#
|
||||
|
||||
You can programmatically register handler methods, which you can use for dynamic
|
||||
registrations or for advanced cases, such as different instances of the same handler
|
||||
@@ -2027,7 +2027,7 @@ under different URLs. The following example registers a handler method:
|
||||
|
||||
[[mvc-ann-methods]]
|
||||
=== Handler Methods
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-methods, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-methods, See equivalent in the Reactive stack>>#
|
||||
|
||||
`@RequestMapping` handler methods have a flexible signature and can choose from a range of
|
||||
supported controller method arguments and return values.
|
||||
@@ -2035,7 +2035,7 @@ supported controller method arguments and return values.
|
||||
|
||||
[[mvc-ann-arguments]]
|
||||
==== Method Arguments
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-arguments, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-arguments, See equivalent in the Reactive stack>>#
|
||||
|
||||
The next table describes the supported controller method arguments. Reactive types are not supported
|
||||
for any arguments.
|
||||
@@ -2176,7 +2176,7 @@ and others) and is equivalent to `required=false`.
|
||||
|
||||
[[mvc-ann-return-types]]
|
||||
==== Return Values
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-return-types, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-return-types, See equivalent in the Reactive stack>>#
|
||||
|
||||
The next table describes the supported controller method return values. Reactive types are
|
||||
supported for all return values.
|
||||
@@ -2280,7 +2280,7 @@ supported for all return values.
|
||||
|
||||
[[mvc-ann-typeconversion]]
|
||||
==== Type Conversion
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-typeconversion, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-typeconversion, See equivalent in the Reactive stack>>#
|
||||
|
||||
Some annotated controller method arguments that represent `String`-based request input (such as
|
||||
`@RequestParam`, `@RequestHeader`, `@PathVariable`, `@MatrixVariable`, and `@CookieValue`)
|
||||
@@ -2313,7 +2313,7 @@ an empty original value, so the corresponding `Missing...Exception` variants wil
|
||||
|
||||
[[mvc-ann-matrix-variables]]
|
||||
==== Matrix Variables
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-matrix-variables, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-matrix-variables, See equivalent in the Reactive stack>>#
|
||||
|
||||
https://tools.ietf.org/html/rfc3986#section-3.3[RFC 3986] discusses name-value pairs in
|
||||
path segments. In Spring MVC, we refer to those as "`matrix variables`" based on an
|
||||
@@ -2453,7 +2453,7 @@ you need to set a `UrlPathHelper` with `removeSemicolonContent=false` through
|
||||
|
||||
[[mvc-ann-requestparam]]
|
||||
==== `@RequestParam`
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-requestparam, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-requestparam, See equivalent in the Reactive stack>>#
|
||||
|
||||
You can use the `@RequestParam` annotation to bind Servlet request parameters (that is,
|
||||
query parameters or form data) to a method argument in a controller.
|
||||
@@ -2529,7 +2529,7 @@ with `@RequestParam`.
|
||||
|
||||
[[mvc-ann-requestheader]]
|
||||
==== `@RequestHeader`
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-requestheader, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-requestheader, See equivalent in the Reactive stack>>#
|
||||
|
||||
You can use the `@RequestHeader` annotation to bind a request header to a method argument in a
|
||||
controller.
|
||||
@@ -2590,7 +2590,7 @@ example, a method parameter annotated with `@RequestHeader("Accept")` can be of
|
||||
|
||||
[[mvc-ann-cookievalue]]
|
||||
==== `@CookieValue`
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-cookievalue, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-cookievalue, See equivalent in the Reactive stack>>#
|
||||
|
||||
You can use the `@CookieValue` annotation to bind the value of an HTTP cookie to a method argument
|
||||
in a controller.
|
||||
@@ -2630,7 +2630,7 @@ See <<mvc-ann-typeconversion>>.
|
||||
|
||||
[[mvc-ann-modelattrib-method-args]]
|
||||
==== `@ModelAttribute`
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-modelattrib-method-args, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-modelattrib-method-args, See equivalent in the Reactive stack>>#
|
||||
|
||||
You can use the `@ModelAttribute` annotation on a method argument to access an attribute from
|
||||
the model or have it be instantiated if not present. The model attribute is also overlain with
|
||||
@@ -2823,7 +2823,7 @@ with `@ModelAttribute`.
|
||||
|
||||
[[mvc-ann-sessionattributes]]
|
||||
==== `@SessionAttributes`
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-sessionattributes, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-sessionattributes, See equivalent in the Reactive stack>>#
|
||||
|
||||
`@SessionAttributes` is used to store model attributes in the HTTP Servlet session between
|
||||
requests. It is a type-level annotation that declares the session attributes used by a
|
||||
@@ -2907,7 +2907,7 @@ class EditPetForm {
|
||||
|
||||
[[mvc-ann-sessionattribute]]
|
||||
==== `@SessionAttribute`
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-sessionattribute, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-sessionattribute, See equivalent in the Reactive stack>>#
|
||||
|
||||
If you need access to pre-existing session attributes that are managed globally
|
||||
(that is, outside the controller -- for example, by a filter) and may or may not be present,
|
||||
@@ -2945,7 +2945,7 @@ workflow, consider using `@SessionAttributes` as described in
|
||||
|
||||
[[mvc-ann-requestattrib]]
|
||||
==== `@RequestAttribute`
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-requestattrib, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-requestattrib, See equivalent in the Reactive stack>>#
|
||||
|
||||
Similar to `@SessionAttribute`, you can use the `@RequestAttribute` annotations to
|
||||
access pre-existing request attributes created earlier (for example, by a Servlet `Filter`
|
||||
@@ -3072,7 +3072,7 @@ Therefore, we recommend that you use flash attributes mainly for redirect scenar
|
||||
|
||||
[[mvc-multipart-forms]]
|
||||
==== Multipart
|
||||
[.small]#<<web-reactive.adoc#webflux-multipart-forms, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-multipart-forms, See equivalent in the Reactive stack>>#
|
||||
|
||||
After a `MultipartResolver` has been <<mvc-multipart,enabled>>, the content of POST
|
||||
requests with `multipart/form-data` is parsed and accessible as regular request
|
||||
@@ -3257,7 +3257,7 @@ as the following example shows:
|
||||
|
||||
[[mvc-ann-requestbody]]
|
||||
==== `@RequestBody`
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-requestbody, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-requestbody, See equivalent in the Reactive stack>>#
|
||||
|
||||
You can use the `@RequestBody` annotation to have the request body read and deserialized into an
|
||||
`Object` through an <<integration.adoc#rest-message-conversion, `HttpMessageConverter`>>.
|
||||
@@ -3311,7 +3311,7 @@ as the following example shows:
|
||||
|
||||
[[mvc-ann-httpentity]]
|
||||
==== HttpEntity
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-httpentity, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-httpentity, See equivalent in the Reactive stack>>#
|
||||
|
||||
`HttpEntity` is more or less identical to using <<mvc-ann-requestbody>> but is based on a
|
||||
container object that exposes request headers and body. The following listing shows an example:
|
||||
@@ -3337,7 +3337,7 @@ container object that exposes request headers and body. The following listing sh
|
||||
|
||||
[[mvc-ann-responsebody]]
|
||||
==== `@ResponseBody`
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-responsebody, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-responsebody, See equivalent in the Reactive stack>>#
|
||||
|
||||
You can use the `@ResponseBody` annotation on a method to have the return serialized
|
||||
to the response body through an
|
||||
@@ -3379,7 +3379,7 @@ See <<mvc-ann-jackson>> for details.
|
||||
|
||||
[[mvc-ann-responseentity]]
|
||||
==== ResponseEntity
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-responseentity, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-responseentity, See equivalent in the Reactive stack>>#
|
||||
|
||||
`ResponseEntity` is like <<mvc-ann-responsebody>> but with status and headers. For example:
|
||||
|
||||
@@ -3423,7 +3423,7 @@ Spring offers support for the Jackson JSON library.
|
||||
|
||||
[[mvc-ann-jsonview]]
|
||||
===== JSON Views
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-jsonview, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-jsonview, See equivalent in the Reactive stack>>#
|
||||
|
||||
Spring MVC provides built-in support for
|
||||
https://www.baeldung.com/jackson-json-view-annotation[Jackson's Serialization Views],
|
||||
@@ -3563,7 +3563,7 @@ to the model, as the following example shows:
|
||||
|
||||
[[mvc-ann-modelattrib-methods]]
|
||||
=== Model
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-modelattrib-methods, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-modelattrib-methods, See equivalent in the Reactive stack>>#
|
||||
|
||||
You can use the `@ModelAttribute` annotation:
|
||||
|
||||
@@ -3661,7 +3661,7 @@ unless the return value is a `String` that would otherwise be interpreted as a v
|
||||
|
||||
[[mvc-ann-initbinder]]
|
||||
=== `DataBinder`
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-initbinder, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-initbinder, See equivalent in the Reactive stack>>#
|
||||
|
||||
`@Controller` or `@ControllerAdvice` classes can have `@InitBinder` methods that
|
||||
initialize instances of `WebDataBinder`, and those, in turn, can:
|
||||
@@ -3755,14 +3755,14 @@ controller-specific `Formatter` implementations, as the following example shows:
|
||||
|
||||
[[mvc-ann-initbinder-model-design]]
|
||||
==== Model Design
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-initbinder-model-design, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-initbinder-model-design, See equivalent in the Reactive stack>>#
|
||||
|
||||
include::web-data-binding-model-design.adoc[]
|
||||
|
||||
|
||||
[[mvc-ann-exceptionhandler]]
|
||||
=== Exceptions
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-controller-exceptions, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-controller-exceptions, See equivalent in the Reactive stack>>#
|
||||
|
||||
`@Controller` and <<mvc-ann-controller-advice, @ControllerAdvice>> classes can have
|
||||
`@ExceptionHandler` methods to handle exceptions from controller methods, as the following example shows:
|
||||
@@ -3888,7 +3888,7 @@ level, <<mvc-exceptionhandlers, HandlerExceptionResolver>> mechanism.
|
||||
|
||||
[[mvc-ann-exceptionhandler-args]]
|
||||
==== Method Arguments
|
||||
[.small]#<<webflux.adoc#webflux-ann-exceptionhandler-args, WebFlux>>#
|
||||
[.small]#<<webflux.adoc#webflux-ann-exceptionhandler-args, See equivalent in the Reactive stack>>#
|
||||
|
||||
`@ExceptionHandler` methods support the following arguments:
|
||||
|
||||
@@ -3953,7 +3953,7 @@ level, <<mvc-exceptionhandlers, HandlerExceptionResolver>> mechanism.
|
||||
|
||||
[[mvc-ann-exceptionhandler-return-values]]
|
||||
==== Return Values
|
||||
[.small]#<<webflux.adoc#webflux-ann-exceptionhandler-return-values, WebFlux>>#
|
||||
[.small]#<<webflux.adoc#webflux-ann-exceptionhandler-return-values, See equivalent in the Reactive stack>>#
|
||||
|
||||
`@ExceptionHandler` methods support the following return values:
|
||||
|
||||
@@ -4023,7 +4023,7 @@ see <<mvc-ann-rest-exceptions>>
|
||||
|
||||
[[mvc-ann-controller-advice]]
|
||||
=== Controller Advice
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-controller-advice, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-controller-advice, See equivalent in the Reactive stack>>#
|
||||
|
||||
`@ExceptionHandler`, `@InitBinder`, and `@ModelAttribute` methods apply only to the
|
||||
`@Controller` class, or class hierarchy, in which they are declared. If, instead, they
|
||||
@@ -4087,7 +4087,7 @@ include::webmvc-functional.adoc[leveloffset=+1]
|
||||
|
||||
[[mvc-uri-building]]
|
||||
== URI Links
|
||||
[.small]#<<web-reactive.adoc#webflux-uri-building, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-uri-building, See equivalent in the Reactive stack>>#
|
||||
|
||||
This section describes various options available in the Spring Framework to work with URI's.
|
||||
|
||||
@@ -4366,7 +4366,6 @@ capital letters of the class and the method name (for example, the `getThing` me
|
||||
|
||||
[[mvc-ann-async]]
|
||||
== Asynchronous Requests
|
||||
[.small]#<<mvc-ann-async-vs-webflux, Compared to WebFlux>>#
|
||||
|
||||
Spring MVC has an extensive integration with Servlet asynchronous request
|
||||
<<mvc-ann-async-processing,processing>>:
|
||||
@@ -4379,11 +4378,10 @@ return value.
|
||||
* Controllers can use reactive clients and return
|
||||
<<mvc-ann-async-reactive-types, reactive types>> for response handling.
|
||||
|
||||
|
||||
For an overview of how this differs from Spring WebFlux, see the <<mvc-ann-async-vs-webflux>> section below.
|
||||
|
||||
[[mvc-ann-async-deferredresult]]
|
||||
=== `DeferredResult`
|
||||
[.small]#<<mvc-ann-async-vs-webflux, Compared to WebFlux>>#
|
||||
|
||||
Once the asynchronous request processing feature is <<mvc-ann-async-configuration, enabled>>
|
||||
in the Servlet container, controller methods can wrap any supported controller method
|
||||
@@ -4425,7 +4423,6 @@ example, in response to an external event (JMS message), a scheduled task, or ot
|
||||
|
||||
[[mvc-ann-async-callable]]
|
||||
=== `Callable`
|
||||
[.small]#<<mvc-ann-async-vs-webflux, Compared to WebFlux>>#
|
||||
|
||||
A controller can wrap any supported return value with `java.util.concurrent.Callable`,
|
||||
as the following example shows:
|
||||
@@ -4455,7 +4452,6 @@ The return value can then be obtained by running the given task through the
|
||||
|
||||
[[mvc-ann-async-processing]]
|
||||
=== Processing
|
||||
[.small]#<<mvc-ann-async-vs-webflux, Compared to WebFlux>>#
|
||||
|
||||
Here is a very concise overview of Servlet asynchronous request processing:
|
||||
|
||||
@@ -4533,7 +4529,7 @@ methods for timeout and completion callbacks.
|
||||
|
||||
|
||||
[[mvc-ann-async-vs-webflux]]
|
||||
==== Compared to WebFlux
|
||||
==== Async Spring MVC compared to WebFlux
|
||||
|
||||
The Servlet API was originally built for making a single pass through the Filter-Servlet
|
||||
chain. Asynchronous request processing lets applications exit the Filter-Servlet chain
|
||||
@@ -4560,11 +4556,13 @@ types in controller method arguments (for example, `@RequestBody`, `@RequestPart
|
||||
nor does it have any explicit support for asynchronous and reactive types as model attributes.
|
||||
Spring WebFlux does support all that.
|
||||
|
||||
Finally, from a configuration perspective the asynchronous request processing feature must be
|
||||
<<mvc-ann-async-configuration, enabled at the Servlet container level>>.
|
||||
|
||||
|
||||
[[mvc-ann-async-http-streaming]]
|
||||
=== HTTP Streaming
|
||||
[.small]#<<web-reactive.adoc#webflux-codecs-streaming, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-codecs-streaming, See equivalent in the Reactive stack>>#
|
||||
|
||||
You can use `DeferredResult` and `Callable` for a single asynchronous return value.
|
||||
What if you want to produce multiple asynchronous values and have those written to the
|
||||
@@ -4717,7 +4715,7 @@ customize the status and headers of the response.
|
||||
|
||||
[[mvc-ann-async-reactive-types]]
|
||||
=== Reactive Types
|
||||
[.small]#<<web-reactive.adoc#webflux-codecs-streaming, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-codecs-streaming, See equivalent in the Reactive stack>>#
|
||||
|
||||
Spring MVC supports use of reactive client libraries in a controller (also read
|
||||
<<web-reactive.adoc#webflux-reactive-libraries, Reactive Libraries>> in the WebFlux section).
|
||||
@@ -4779,7 +4777,7 @@ directly. For example:
|
||||
ContextSnapshot snapshot = ContextSnapshot.captureAll();
|
||||
|
||||
// On a different thread: restore ThreadLocal values
|
||||
try (ContextSnapshot.Scope scoped = snapshot.setThreadLocals()) {
|
||||
try (ContextSnapshot.Scope scope = snapshot.setThreadLocals()) {
|
||||
// ...
|
||||
}
|
||||
----
|
||||
@@ -4792,7 +4790,7 @@ Propagation library.
|
||||
|
||||
[[mvc-ann-async-disconnects]]
|
||||
=== Disconnects
|
||||
[.small]#<<web-reactive.adoc#webflux-codecs-streaming, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-codecs-streaming, See equivalent in the Reactive stack>>#
|
||||
|
||||
The Servlet API does not provide any notification when a remote client goes away.
|
||||
Therefore, while streaming to the response, whether through <<mvc-ann-async-sse, SseEmitter>>
|
||||
@@ -4809,7 +4807,6 @@ that have a built-in heartbeat mechanism.
|
||||
|
||||
[[mvc-ann-async-configuration]]
|
||||
=== Configuration
|
||||
[.small]#<<mvc-ann-async-vs-webflux, Compared to WebFlux>>#
|
||||
|
||||
The asynchronous request processing feature must be enabled at the Servlet container level.
|
||||
The MVC configuration also exposes several options for asynchronous requests.
|
||||
@@ -4859,7 +4856,7 @@ include::webmvc-cors.adoc[leveloffset=+1]
|
||||
|
||||
[[mvc-ann-rest-exceptions]]
|
||||
== Error Responses
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-rest-exceptions, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-rest-exceptions, See equivalent in the Reactive stack>>#
|
||||
|
||||
A common requirement for REST services is to include details in the body of error
|
||||
responses. The Spring Framework supports the "Problem Details for HTTP APIs"
|
||||
@@ -4883,7 +4880,7 @@ and any `ErrorResponseException`, and renders an error response with a body.
|
||||
|
||||
[[mvc-ann-rest-exceptions-render]]
|
||||
=== Render
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-rest-exceptions-render, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-rest-exceptions-render, See equivalent in the Reactive stack>>#
|
||||
|
||||
You can return `ProblemDetail` or `ErrorResponse` from any `@ExceptionHandler` or from
|
||||
any `@RequestMapping` method to render an RFC 7807 response. This is processed as follows:
|
||||
@@ -4906,7 +4903,7 @@ use a protected method to map any exception to a `ProblemDetail`.
|
||||
|
||||
[[mvc-ann-rest-exceptions-non-standard]]
|
||||
=== Non-Standard Fields
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-rest-exceptions-non-standard, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-rest-exceptions-non-standard, See equivalent in the Reactive stack>>#
|
||||
|
||||
You can extend an RFC 7807 response with non-standard fields in one of two ways.
|
||||
|
||||
@@ -4926,7 +4923,7 @@ from an existing `ProblemDetail`. This could be done centrally, e.g. from an
|
||||
|
||||
[[mvc-ann-rest-exceptions-i18n]]
|
||||
=== Internationalization
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-rest-exceptions-i18n, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-rest-exceptions-i18n, See equivalent in the Reactive stack>>#
|
||||
|
||||
It is a common requirement to internationalize error response details, and good practice
|
||||
to customize the problem details for Spring MVC exceptions. This is supported as follows:
|
||||
@@ -5036,7 +5033,7 @@ qualified exception class name.
|
||||
|
||||
[[mvc-ann-rest-exceptions-client]]
|
||||
=== Client Handling
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-rest-exceptions-client, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-rest-exceptions-client, See equivalent in the Reactive stack>>#
|
||||
|
||||
A client application can catch `WebClientResponseException`, when using the `WebClient`,
|
||||
or `RestClientResponseException` when using the `RestTemplate`, and use their
|
||||
@@ -5047,7 +5044,7 @@ or `RestClientResponseException` when using the `RestTemplate`, and use their
|
||||
|
||||
[[mvc-web-security]]
|
||||
== Web Security
|
||||
[.small]#<<web-reactive.adoc#webflux-web-security, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-web-security, See equivalent in the Reactive stack>>#
|
||||
|
||||
The https://spring.io/projects/spring-security[Spring Security] project provides support
|
||||
for protecting web applications from malicious exploits. See the Spring Security
|
||||
@@ -5065,7 +5062,7 @@ https://hdiv.org/[HDIV] is another web security framework that integrates with S
|
||||
|
||||
[[mvc-caching]]
|
||||
== HTTP Caching
|
||||
[.small]#<<web-reactive.adoc#webflux-caching, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-caching, See equivalent in the Reactive stack>>#
|
||||
|
||||
HTTP caching can significantly improve the performance of a web application. HTTP caching
|
||||
revolves around the `Cache-Control` response header and, subsequently, conditional request
|
||||
@@ -5081,7 +5078,7 @@ This section describes the HTTP caching-related options that are available in Sp
|
||||
|
||||
[[mvc-caching-cachecontrol]]
|
||||
=== `CacheControl`
|
||||
[.small]#<<web-reactive.adoc#webflux-caching-cachecontrol, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-caching-cachecontrol, See equivalent in the Reactive stack>>#
|
||||
|
||||
{api-spring-framework}/http/CacheControl.html[`CacheControl`] provides support for
|
||||
configuring settings related to the `Cache-Control` header and is accepted as an argument
|
||||
@@ -5137,7 +5134,7 @@ works as follows:
|
||||
|
||||
[[mvc-caching-etag-lastmodified]]
|
||||
=== Controllers
|
||||
[.small]#<<web-reactive.adoc#webflux-caching-etag-lastmodified, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-caching-etag-lastmodified, See equivalent in the Reactive stack>>#
|
||||
|
||||
Controllers can add explicit support for HTTP caching. We recommended doing so, since the
|
||||
`lastModified` or `ETag` value for a resource needs to be calculated before it can be compared
|
||||
@@ -5238,7 +5235,7 @@ to 412 (PRECONDITION_FAILED), to prevent concurrent modification.
|
||||
|
||||
[[mvc-caching-static-resources]]
|
||||
=== Static Resources
|
||||
[.small]#<<web-reactive.adoc#webflux-caching-static-resources, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-caching-static-resources, See equivalent in the Reactive stack>>#
|
||||
|
||||
You should serve static resources with a `Cache-Control` and conditional response headers
|
||||
for optimal performance. See the section on configuring <<mvc-config-static-resources>>.
|
||||
@@ -5258,7 +5255,7 @@ include::webmvc-view.adoc[leveloffset=+1]
|
||||
|
||||
[[mvc-config]]
|
||||
== MVC Config
|
||||
[.small]#<<web-reactive.adoc#webflux-config, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-config, See equivalent in the Reactive stack>>#
|
||||
|
||||
The MVC Java configuration and the MVC XML namespace provide default configuration
|
||||
suitable for most applications and a configuration API to customize it.
|
||||
@@ -5274,7 +5271,7 @@ and <<mvc-servlet-config>>.
|
||||
|
||||
[[mvc-config-enable]]
|
||||
=== Enable MVC Configuration
|
||||
[.small]#<<web-reactive.adoc#webflux-config-enable, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-config-enable, See equivalent in the Reactive stack>>#
|
||||
|
||||
In Java configuration, you can use the `@EnableWebMvc` annotation to enable MVC
|
||||
configuration, as the following example shows:
|
||||
@@ -5323,7 +5320,7 @@ available on the classpath (for example, payload converters for JSON, XML, and o
|
||||
|
||||
[[mvc-config-customize]]
|
||||
=== MVC Config API
|
||||
[.small]#<<web-reactive.adoc#webflux-config-customize, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-config-customize, See equivalent in the Reactive stack>>#
|
||||
|
||||
In Java configuration, you can implement the `WebMvcConfigurer` interface, as the
|
||||
following example shows:
|
||||
@@ -5359,7 +5356,7 @@ sub-elements are available.
|
||||
|
||||
[[mvc-config-conversion]]
|
||||
=== Type Conversion
|
||||
[.small]#<<web-reactive.adoc#webflux-config-conversion, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-config-conversion, See equivalent in the Reactive stack>>#
|
||||
|
||||
By default, formatters for various number and date types are installed, along with support
|
||||
for customization via `@NumberFormat` and `@DateTimeFormat` on fields.
|
||||
@@ -5474,7 +5471,7 @@ FormatterRegistrar implementations.
|
||||
|
||||
[[mvc-config-validation]]
|
||||
=== Validation
|
||||
[.small]#<<web-reactive.adoc#webflux-config-validation, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-config-validation, See equivalent in the Reactive stack>>#
|
||||
|
||||
By default, if <<core.adoc#validation-beanvalidation-overview, Bean Validation>> is present
|
||||
on the classpath (for example, Hibernate Validator), the `LocalValidatorFactoryBean` is
|
||||
@@ -5624,7 +5621,7 @@ unwanted characters in URL paths.
|
||||
|
||||
[[mvc-config-content-negotiation]]
|
||||
=== Content Types
|
||||
[.small]#<<web-reactive.adoc#webflux-config-content-negotiation, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-config-content-negotiation, See equivalent in the Reactive stack>>#
|
||||
|
||||
You can configure how Spring MVC determines the requested media types from the request
|
||||
(for example, `Accept` header, URL path extension, query parameter, and others).
|
||||
@@ -5688,7 +5685,7 @@ The following example shows how to achieve the same configuration in XML:
|
||||
|
||||
[[mvc-config-message-converters]]
|
||||
=== Message Converters
|
||||
[.small]#<<web-reactive.adoc#webflux-config-message-codecs, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-config-message-codecs, See equivalent in the Reactive stack>>#
|
||||
|
||||
You can customize `HttpMessageConverter` in Java configuration by overriding
|
||||
{api-spring-framework}/web/servlet/config/annotation/WebMvcConfigurer.html#configureMessageConverters-java.util.List-[`configureMessageConverters()`]
|
||||
@@ -5841,7 +5838,7 @@ splitting URL handling across an annotated controller and a view controller.
|
||||
|
||||
[[mvc-config-view-resolvers]]
|
||||
=== View Resolvers
|
||||
[.small]#<<web-reactive.adoc#webflux-config-view-resolvers, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-config-view-resolvers, See equivalent in the Reactive stack>>#
|
||||
|
||||
The MVC configuration simplifies the registration of view resolvers.
|
||||
|
||||
@@ -5891,7 +5888,7 @@ The following example shows how to achieve the same configuration in XML:
|
||||
</mvc:view-resolvers>
|
||||
----
|
||||
|
||||
Note, however, that FreeMarker, Tiles, Groovy Markup, and script templates also require
|
||||
Note, however, that FreeMarker, Groovy Markup, and script templates also require
|
||||
configuration of the underlying view technology.
|
||||
|
||||
The MVC namespace provides dedicated elements. The following example works with FreeMarker:
|
||||
@@ -5959,7 +5956,7 @@ as the following example shows:
|
||||
|
||||
[[mvc-config-static-resources]]
|
||||
=== Static Resources
|
||||
[.small]#<<web-reactive.adoc#webflux-config-static-resources, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-config-static-resources, See equivalent in the Reactive stack>>#
|
||||
|
||||
This option provides a convenient way to serve static resources from a list of
|
||||
{api-spring-framework}/core/io/Resource.html[`Resource`]-based locations.
|
||||
@@ -6199,7 +6196,7 @@ The following example shows how to achieve the same configuration in XML:
|
||||
|
||||
[[mvc-config-path-matching]]
|
||||
=== Path Matching
|
||||
[.small]#<<web-reactive.adoc#webflux-config-path-matching, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-config-path-matching, See equivalent in the Reactive stack>>#
|
||||
|
||||
You can customize options related to path matching and treatment of the URL.
|
||||
For details on the individual options, see the
|
||||
@@ -6259,7 +6256,7 @@ The following example shows how to customize path matching in XML configuration:
|
||||
|
||||
[[mvc-config-advanced-java]]
|
||||
=== Advanced Java Config
|
||||
[.small]#<<web-reactive.adoc#webflux-config-advanced-java, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-config-advanced-java, See equivalent in the Reactive stack>>#
|
||||
|
||||
`@EnableWebMvc` imports `DelegatingWebMvcConfiguration`, which:
|
||||
|
||||
@@ -6334,7 +6331,7 @@ by letting it be detected through a `<component-scan/>` declaration.
|
||||
|
||||
[[mvc-http2]]
|
||||
== HTTP/2
|
||||
[.small]#<<web-reactive.adoc#webflux-http2, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-http2, See equivalent in the Reactive stack>>#
|
||||
|
||||
Servlet 4 containers are required to support HTTP/2, and Spring Framework 5 is compatible
|
||||
with Servlet API 4. From a programming model perspective, there is nothing specific that
|
||||
|
||||
@@ -84,7 +84,7 @@ In the absence of that, they need to come up with their own conventions.
|
||||
== When to Use WebSockets
|
||||
|
||||
WebSockets can make a web page be dynamic and interactive. However, in many cases,
|
||||
a combination of Ajax and HTTP streaming or long polling can provide a simple and
|
||||
a combination of AJAX and HTTP streaming or long polling can provide a simple and
|
||||
effective solution.
|
||||
|
||||
For example, news, mail, and social feeds need to update dynamically, but it may be
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[[websocket]]
|
||||
= WebSockets
|
||||
[.small]#<<web-reactive.adoc#webflux-websocket, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-websocket, See equivalent in the Reactive stack>>#
|
||||
|
||||
This part of the reference documentation covers support for Servlet stack, WebSocket
|
||||
messaging that includes raw WebSocket interactions, WebSocket emulation through SockJS, and
|
||||
@@ -13,7 +13,7 @@ include::websocket-intro.adoc[leveloffset=+1]
|
||||
|
||||
[[websocket-server]]
|
||||
== WebSocket API
|
||||
[.small]#<<web-reactive.adoc#webflux-websocket-server, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-websocket-server, See equivalent in the Reactive stack>>#
|
||||
|
||||
The Spring Framework provides a WebSocket API that you can use to write client- and
|
||||
server-side applications that handle WebSocket messages.
|
||||
@@ -22,7 +22,7 @@ server-side applications that handle WebSocket messages.
|
||||
|
||||
[[websocket-server-handler]]
|
||||
=== `WebSocketHandler`
|
||||
[.small]#<<web-reactive.adoc#webflux-websocket-server-handler, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-websocket-server-handler, See equivalent in the Reactive stack>>#
|
||||
|
||||
Creating a WebSocket server is as simple as implementing `WebSocketHandler` or, more
|
||||
likely, extending either `TextWebSocketHandler` or `BinaryWebSocketHandler`. The following
|
||||
@@ -108,7 +108,7 @@ sending. One option is to wrap the `WebSocketSession` with
|
||||
|
||||
[[websocket-server-handshake]]
|
||||
=== WebSocket Handshake
|
||||
[.small]#<<web-reactive.adoc#webflux-websocket-server-handshake, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-websocket-server-handshake, See equivalent in the Reactive stack>>#
|
||||
|
||||
The easiest way to customize the initial HTTP WebSocket handshake request is through
|
||||
a `HandshakeInterceptor`, which exposes methods for "`before`" and "`after`" the handshake.
|
||||
@@ -242,7 +242,7 @@ Java initialization API. The following example shows how to do so:
|
||||
|
||||
[[websocket-server-runtime-configuration]]
|
||||
=== Server Configuration
|
||||
[.small]#<<web-reactive.adoc#webflux-websocket-server-config, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-websocket-server-config, See equivalent in the Reactive stack>>#
|
||||
|
||||
Each underlying WebSocket engine exposes configuration properties that control
|
||||
runtime characteristics, such as the size of message buffer sizes, idle timeout,
|
||||
@@ -365,7 +365,7 @@ The following example shows the XML configuration equivalent of the preceding ex
|
||||
|
||||
[[websocket-server-allowed-origins]]
|
||||
=== Allowed Origins
|
||||
[.small]#<<web-reactive.adoc#webflux-websocket-server-cors, WebFlux>>#
|
||||
[.small]#<<web-reactive.adoc#webflux-websocket-server-cors, See equivalent in the Reactive stack>>#
|
||||
|
||||
As of Spring Framework 4.1.5, the default behavior for WebSocket and SockJS is to accept
|
||||
only same-origin requests. It is also possible to allow all or a specified list of origins.
|
||||
|
||||
+3
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* 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.
|
||||
@@ -30,6 +30,8 @@ public class AotProcessingSample {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
context.register(MyApplication.class);
|
||||
context.refreshForAotProcessing(hints);
|
||||
// ...
|
||||
context.close();
|
||||
// end::aotcontext[]
|
||||
}
|
||||
|
||||
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.docs.integration.observability.httpserver.reactive;
|
||||
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.filter.reactive.ServerHttpObservationFilter;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
@Controller
|
||||
public class UserController {
|
||||
|
||||
@ExceptionHandler(MissingUserException.class)
|
||||
ResponseEntity<Void> handleMissingUser(ServerWebExchange exchange, MissingUserException exception) {
|
||||
// We want to record this exception with the observation
|
||||
ServerHttpObservationFilter.findObservationContext(exchange)
|
||||
.ifPresent(context -> context.setError(exception));
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
// @fold:on
|
||||
@SuppressWarnings("serial")
|
||||
static class MissingUserException extends RuntimeException {
|
||||
}
|
||||
// @fold:off
|
||||
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.docs.integration.observability.httpserver.servlet;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.filter.ServerHttpObservationFilter;
|
||||
|
||||
@Controller
|
||||
public class UserController {
|
||||
|
||||
@ExceptionHandler(MissingUserException.class)
|
||||
ResponseEntity<Void> handleMissingUser(HttpServletRequest request, MissingUserException exception) {
|
||||
// We want to record this exception with the observation
|
||||
ServerHttpObservationFilter.findObservationContext(request)
|
||||
.ifPresent(context -> context.setError(exception));
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
// @fold:on
|
||||
@SuppressWarnings("serial")
|
||||
static class MissingUserException extends RuntimeException {
|
||||
}
|
||||
// @fold:off
|
||||
|
||||
}
|
||||
@@ -7,29 +7,29 @@ javaPlatform {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(platform("com.fasterxml.jackson:jackson-bom:2.14.1"))
|
||||
api(platform("io.micrometer:micrometer-bom:1.10.2"))
|
||||
api(platform("io.netty:netty-bom:4.1.85.Final"))
|
||||
api(platform("com.fasterxml.jackson:jackson-bom:2.14.2"))
|
||||
api(platform("io.micrometer:micrometer-bom:1.10.4"))
|
||||
api(platform("io.netty:netty-bom:4.1.89.Final"))
|
||||
api(platform("io.netty:netty5-bom:5.0.0.Alpha5"))
|
||||
api(platform("io.projectreactor:reactor-bom:2022.0.1"))
|
||||
api(platform("io.projectreactor:reactor-bom:2022.0.3"))
|
||||
api(platform("io.rsocket:rsocket-bom:1.1.3"))
|
||||
api(platform("org.apache.groovy:groovy-bom:4.0.6"))
|
||||
api(platform("org.apache.groovy:groovy-bom:4.0.8"))
|
||||
api(platform("org.apache.logging.log4j:log4j-bom:2.19.0"))
|
||||
api(platform("org.eclipse.jetty:jetty-bom:11.0.12"))
|
||||
api(platform("org.eclipse.jetty:jetty-bom:11.0.13"))
|
||||
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.1"))
|
||||
api(platform("org.mockito:mockito-bom:4.9.0"))
|
||||
api(platform("org.junit:junit-bom:5.9.2"))
|
||||
api(platform("org.mockito:mockito-bom:5.1.1"))
|
||||
|
||||
constraints {
|
||||
api("com.fasterxml:aalto-xml:1.3.1")
|
||||
api("com.fasterxml.woodstox:woodstox-core:6.4.0")
|
||||
api("com.fasterxml.woodstox:woodstox-core:6.5.0")
|
||||
api("com.github.ben-manes.caffeine:caffeine:3.1.2")
|
||||
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")
|
||||
api("com.google.protobuf:protobuf-java-util:3.21.11")
|
||||
api("com.google.protobuf:protobuf-java-util:3.21.12")
|
||||
api("com.googlecode.protobuf-java-format:protobuf-java-format:1.4")
|
||||
api("com.h2database:h2:2.1.214")
|
||||
api("com.jayway.jsonpath:json-path:2.7.0")
|
||||
@@ -55,9 +55,9 @@ dependencies {
|
||||
api("io.r2dbc:r2dbc-spi:1.0.0.RELEASE")
|
||||
api("io.reactivex.rxjava3:rxjava:3.1.5")
|
||||
api("io.smallrye.reactive:mutiny:1.8.0")
|
||||
api("io.undertow:undertow-core:2.3.0.Final")
|
||||
api("io.undertow:undertow-servlet:2.3.0.Final")
|
||||
api("io.undertow:undertow-websockets-jsr:2.3.0.Final")
|
||||
api("io.undertow:undertow-core:2.3.3.Final")
|
||||
api("io.undertow:undertow-servlet:2.3.3.Final")
|
||||
api("io.undertow:undertow-websockets-jsr:2.3.3.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")
|
||||
@@ -87,7 +87,7 @@ dependencies {
|
||||
api("jaxen:jaxen:1.2.0")
|
||||
api("junit:junit:4.13.2")
|
||||
api("net.sf.jopt-simple:jopt-simple:5.0.4")
|
||||
api("net.sourceforge.htmlunit:htmlunit:2.67.0")
|
||||
api("net.sourceforge.htmlunit:htmlunit:2.70.0")
|
||||
api("org.apache-extras.beanshell:bsh:2.0b6")
|
||||
api("org.apache.activemq:activemq-broker:5.16.2")
|
||||
api("org.apache.activemq:activemq-kahadb-store:5.16.2")
|
||||
@@ -96,17 +96,17 @@ dependencies {
|
||||
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")
|
||||
api("org.apache.httpcomponents.core5:httpcore5-reactive:5.2")
|
||||
api("org.apache.httpcomponents.client5:httpclient5:5.2.1")
|
||||
api("org.apache.httpcomponents.core5:httpcore5-reactive:5.2.1")
|
||||
api("org.apache.poi:poi-ooxml:5.2.3")
|
||||
api("org.apache.tomcat.embed:tomcat-embed-core:10.1.1")
|
||||
api("org.apache.tomcat.embed:tomcat-embed-websocket:10.1.1")
|
||||
api("org.apache.tomcat:tomcat-util:10.1.1")
|
||||
api("org.apache.tomcat:tomcat-websocket:10.1.1")
|
||||
api("org.apache.tomcat.embed:tomcat-embed-core:10.1.5")
|
||||
api("org.apache.tomcat.embed:tomcat-embed-websocket:10.1.5")
|
||||
api("org.apache.tomcat:tomcat-util:10.1.5")
|
||||
api("org.apache.tomcat:tomcat-websocket:10.1.5")
|
||||
api("org.aspectj:aspectjrt:1.9.9.1")
|
||||
api("org.aspectj:aspectjtools:1.9.9.1")
|
||||
api("org.aspectj:aspectjweaver:1.9.9.1")
|
||||
api("org.assertj:assertj-core:3.23.1")
|
||||
api("org.assertj:assertj-core:3.24.2")
|
||||
api("org.awaitility:awaitility:3.1.6")
|
||||
api("org.bouncycastle:bcpkix-jdk18on:1.71")
|
||||
api("org.codehaus.jettison:jettison:1.3.8")
|
||||
@@ -116,7 +116,7 @@ dependencies {
|
||||
api("org.eclipse:yasson:2.0.4")
|
||||
api("org.ehcache:ehcache:3.4.0")
|
||||
api("org.ehcache:jcache:1.0.1")
|
||||
api("org.freemarker:freemarker:2.3.31")
|
||||
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")
|
||||
@@ -124,7 +124,7 @@ dependencies {
|
||||
api("org.glassfish:jakarta.el:4.0.2")
|
||||
api("org.graalvm.sdk:graal-sdk:22.3.0")
|
||||
api("org.hamcrest:hamcrest:2.2")
|
||||
api("org.hibernate:hibernate-core-jakarta:5.6.14.Final")
|
||||
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.1")
|
||||
api("org.javamoney:moneta:1.4.2")
|
||||
@@ -134,15 +134,15 @@ dependencies {
|
||||
api("org.ogce:xpp3:1.1.6")
|
||||
api("org.python:jython-standalone:2.7.1")
|
||||
api("org.quartz-scheduler:quartz:2.3.2")
|
||||
api("org.seleniumhq.selenium:htmlunit-driver:2.66.0")
|
||||
api("org.seleniumhq.selenium:htmlunit-driver:2.70.0")
|
||||
api("org.seleniumhq.selenium:selenium-java:3.141.59")
|
||||
api("org.skyscreamer:jsonassert:1.5.0")
|
||||
api("org.slf4j:slf4j-api:2.0.5")
|
||||
api("org.testng:testng:7.6.1")
|
||||
api("org.slf4j:slf4j-api:2.0.6")
|
||||
api("org.testng:testng:7.7.1")
|
||||
api("org.webjars:underscorejs:1.8.3")
|
||||
api("org.webjars:webjars-locator-core:0.52")
|
||||
api("org.xmlunit:xmlunit-assertj:2.9.0")
|
||||
api("org.xmlunit:xmlunit-matchers:2.9.0")
|
||||
api("org.xmlunit:xmlunit-assertj:2.9.1")
|
||||
api("org.xmlunit:xmlunit-matchers:2.9.1")
|
||||
api("org.yaml:snakeyaml:1.33")
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
version=6.0.3-SNAPSHOT
|
||||
version=6.0.5
|
||||
|
||||
org.gradle.caching=true
|
||||
org.gradle.jvmargs=-Xmx2048m
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
* One can choose the toolchain to use for compiling the MAIN sources and/or compiling
|
||||
* and running the TEST sources. These options apply to Java, Kotlin and Groovy sources
|
||||
* when available.
|
||||
* {@code "./gradlew check -PmainToolchain=17 -PtestToolchain=18"} will use:
|
||||
* {@code "./gradlew check -PmainToolchain=17 -PtestToolchain=19"} will use:
|
||||
* <ul>
|
||||
* <li>a JDK17 toolchain for compiling the main SourceSet
|
||||
* <li>a JDK18 toolchain for compiling and running the test SourceSet
|
||||
* <li>a JDK19 toolchain for compiling and running the test SourceSet
|
||||
* </ul>
|
||||
*
|
||||
* By default, the build will fall back to using the current JDK and 17 language level for all sourceSets.
|
||||
@@ -23,9 +23,9 @@
|
||||
* {@code
|
||||
* $ echo JDK17
|
||||
* /opt/openjdk/java17
|
||||
* $ echo JDK18
|
||||
* $ echo JDK19
|
||||
* /opt/openjdk/java18
|
||||
* $ ./gradlew -Porg.gradle.java.installations.fromEnv=JDK17,JDK18 check
|
||||
* $ ./gradlew -Porg.gradle.java.installations.fromEnv=JDK17,JDK19 check
|
||||
* }
|
||||
*
|
||||
* @author Brian Clozel
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* 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.
|
||||
@@ -151,7 +151,7 @@ class ScheduledAndTransactionalAnnotationIntegrationTests {
|
||||
|
||||
@Bean
|
||||
PersistenceExceptionTranslator peTranslator() {
|
||||
return mock(PersistenceExceptionTranslator.class);
|
||||
return mock();
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
+2
-2
@@ -7,8 +7,8 @@ pluginManagement {
|
||||
}
|
||||
|
||||
plugins {
|
||||
id "com.gradle.enterprise" version "3.11.1"
|
||||
id "io.spring.ge.conventions" version "0.0.11"
|
||||
id "com.gradle.enterprise" version "3.12.3"
|
||||
id "io.spring.ge.conventions" version "0.0.13"
|
||||
}
|
||||
|
||||
include "spring-aop"
|
||||
|
||||
+13
-15
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* 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.
|
||||
@@ -268,18 +268,16 @@ public abstract class AbstractAspectJAdvice implements Advice, AspectJPrecedence
|
||||
this.argumentNames[i] + "' that is not a valid Java identifier");
|
||||
}
|
||||
}
|
||||
if (this.argumentNames != null) {
|
||||
if (this.aspectJAdviceMethod.getParameterCount() == this.argumentNames.length + 1) {
|
||||
// May need to add implicit join point arg name...
|
||||
Class<?> firstArgType = this.aspectJAdviceMethod.getParameterTypes()[0];
|
||||
if (firstArgType == JoinPoint.class ||
|
||||
firstArgType == ProceedingJoinPoint.class ||
|
||||
firstArgType == JoinPoint.StaticPart.class) {
|
||||
String[] oldNames = this.argumentNames;
|
||||
this.argumentNames = new String[oldNames.length + 1];
|
||||
this.argumentNames[0] = "THIS_JOIN_POINT";
|
||||
System.arraycopy(oldNames, 0, this.argumentNames, 1, oldNames.length);
|
||||
}
|
||||
if (this.aspectJAdviceMethod.getParameterCount() == this.argumentNames.length + 1) {
|
||||
// May need to add implicit join point arg name...
|
||||
Class<?> firstArgType = this.aspectJAdviceMethod.getParameterTypes()[0];
|
||||
if (firstArgType == JoinPoint.class ||
|
||||
firstArgType == ProceedingJoinPoint.class ||
|
||||
firstArgType == JoinPoint.StaticPart.class) {
|
||||
String[] oldNames = this.argumentNames;
|
||||
this.argumentNames = new String[oldNames.length + 1];
|
||||
this.argumentNames[0] = "THIS_JOIN_POINT";
|
||||
System.arraycopy(oldNames, 0, this.argumentNames, 1, oldNames.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -655,10 +653,10 @@ public abstract class AbstractAspectJAdvice implements Advice, AspectJPrecedence
|
||||
@Nullable
|
||||
protected JoinPointMatch getJoinPointMatch() {
|
||||
MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation();
|
||||
if (!(mi instanceof ProxyMethodInvocation)) {
|
||||
if (!(mi instanceof ProxyMethodInvocation pmi)) {
|
||||
throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
|
||||
}
|
||||
return getJoinPointMatch((ProxyMethodInvocation) mi);
|
||||
return getJoinPointMatch(pmi);
|
||||
}
|
||||
|
||||
// Note: We can't use JoinPointMatch.getClass().getName() as the key, since
|
||||
|
||||
+10
-23
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* 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.
|
||||
@@ -244,31 +244,18 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
|
||||
int algorithmicStep = STEP_JOIN_POINT_BINDING;
|
||||
while ((this.numberOfRemainingUnboundArguments > 0) && algorithmicStep < STEP_FINISHED) {
|
||||
switch (algorithmicStep++) {
|
||||
case STEP_JOIN_POINT_BINDING:
|
||||
case STEP_JOIN_POINT_BINDING -> {
|
||||
if (!maybeBindThisJoinPoint()) {
|
||||
maybeBindThisJoinPointStaticPart();
|
||||
}
|
||||
break;
|
||||
case STEP_THROWING_BINDING:
|
||||
maybeBindThrowingVariable();
|
||||
break;
|
||||
case STEP_ANNOTATION_BINDING:
|
||||
maybeBindAnnotationsFromPointcutExpression();
|
||||
break;
|
||||
case STEP_RETURNING_BINDING:
|
||||
maybeBindReturningVariable();
|
||||
break;
|
||||
case STEP_PRIMITIVE_ARGS_BINDING:
|
||||
maybeBindPrimitiveArgsFromPointcutExpression();
|
||||
break;
|
||||
case STEP_THIS_TARGET_ARGS_BINDING:
|
||||
maybeBindThisOrTargetOrArgsFromPointcutExpression();
|
||||
break;
|
||||
case STEP_REFERENCE_PCUT_BINDING:
|
||||
maybeBindReferencePointcutParameter();
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unknown algorithmic step: " + (algorithmicStep - 1));
|
||||
}
|
||||
case STEP_THROWING_BINDING -> maybeBindThrowingVariable();
|
||||
case STEP_ANNOTATION_BINDING -> maybeBindAnnotationsFromPointcutExpression();
|
||||
case STEP_RETURNING_BINDING -> maybeBindReturningVariable();
|
||||
case STEP_PRIMITIVE_ARGS_BINDING -> maybeBindPrimitiveArgsFromPointcutExpression();
|
||||
case STEP_THIS_TARGET_ARGS_BINDING -> maybeBindThisOrTargetOrArgsFromPointcutExpression();
|
||||
case STEP_REFERENCE_PCUT_BINDING -> maybeBindReferencePointcutParameter();
|
||||
default -> throw new IllegalStateException("Unknown algorithmic step: " + (algorithmicStep - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -61,12 +61,12 @@ public abstract class AspectJAopUtils {
|
||||
*/
|
||||
@Nullable
|
||||
public static AspectJPrecedenceInformation getAspectJPrecedenceInformationFor(Advisor anAdvisor) {
|
||||
if (anAdvisor instanceof AspectJPrecedenceInformation) {
|
||||
return (AspectJPrecedenceInformation) anAdvisor;
|
||||
if (anAdvisor instanceof AspectJPrecedenceInformation ajpi) {
|
||||
return ajpi;
|
||||
}
|
||||
Advice advice = anAdvisor.getAdvice();
|
||||
if (advice instanceof AspectJPrecedenceInformation) {
|
||||
return (AspectJPrecedenceInformation) advice;
|
||||
if (advice instanceof AspectJPrecedenceInformation ajpi) {
|
||||
return ajpi;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
+9
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* 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.
|
||||
@@ -200,8 +200,8 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
*/
|
||||
@Nullable
|
||||
private ClassLoader determinePointcutClassLoader() {
|
||||
if (this.beanFactory instanceof ConfigurableBeanFactory) {
|
||||
return ((ConfigurableBeanFactory) this.beanFactory).getBeanClassLoader();
|
||||
if (this.beanFactory instanceof ConfigurableBeanFactory cbf) {
|
||||
return cbf.getBeanClassLoader();
|
||||
}
|
||||
if (this.pointcutDeclarationScope != null) {
|
||||
return this.pointcutDeclarationScope.getClassLoader();
|
||||
@@ -335,10 +335,10 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
try {
|
||||
MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation();
|
||||
targetObject = mi.getThis();
|
||||
if (!(mi instanceof ProxyMethodInvocation)) {
|
||||
if (!(mi instanceof ProxyMethodInvocation _pmi)) {
|
||||
throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
|
||||
}
|
||||
pmi = (ProxyMethodInvocation) mi;
|
||||
pmi = _pmi;
|
||||
thisObject = pmi.getProxy();
|
||||
}
|
||||
catch (IllegalStateException ex) {
|
||||
@@ -404,8 +404,8 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
}
|
||||
|
||||
private RuntimeTestWalker getRuntimeTestWalker(ShadowMatch shadowMatch) {
|
||||
if (shadowMatch instanceof DefensiveShadowMatch) {
|
||||
return new RuntimeTestWalker(((DefensiveShadowMatch) shadowMatch).primary);
|
||||
if (shadowMatch instanceof DefensiveShadowMatch defensiveShadowMatch) {
|
||||
return new RuntimeTestWalker(defensiveShadowMatch.primary);
|
||||
}
|
||||
return new RuntimeTestWalker(shadowMatch);
|
||||
}
|
||||
@@ -422,7 +422,8 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
|
||||
private ShadowMatch getTargetShadowMatch(Method method, Class<?> targetClass) {
|
||||
Method targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);
|
||||
if (targetMethod.getDeclaringClass().isInterface()) {
|
||||
if (targetMethod.getDeclaringClass().isInterface() && targetMethod.getDeclaringClass() != targetClass &&
|
||||
obtainPointcutExpression().getPointcutExpression().contains("." + targetMethod.getName() + "(")) {
|
||||
// Try to build the most specific interface possible for inherited methods to be
|
||||
// considered for sub-interface matches as well, in particular for proxy classes.
|
||||
// Note: AspectJ is only going to take Method.getDeclaringClass() into account.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -71,8 +71,8 @@ public abstract class AspectJProxyUtils {
|
||||
private static boolean isAspectJAdvice(Advisor advisor) {
|
||||
return (advisor instanceof InstantiationModelAwarePointcutAdvisor ||
|
||||
advisor.getAdvice() instanceof AbstractAspectJAdvice ||
|
||||
(advisor instanceof PointcutAdvisor &&
|
||||
((PointcutAdvisor) advisor).getPointcut() instanceof AspectJExpressionPointcut));
|
||||
(advisor instanceof PointcutAdvisor pointcutAdvisor &&
|
||||
pointcutAdvisor.getPointcut() instanceof AspectJExpressionPointcut));
|
||||
}
|
||||
|
||||
static boolean isVariableName(@Nullable String name) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 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.
|
||||
@@ -202,8 +202,8 @@ class RuntimeTestWalker {
|
||||
}
|
||||
Class<?> typeClass = null;
|
||||
ResolvedType type = (ResolvedType) i.getType();
|
||||
if (type instanceof ReferenceType) {
|
||||
ReferenceTypeDelegate delegate = ((ReferenceType) type).getDelegate();
|
||||
if (type instanceof ReferenceType referenceType) {
|
||||
ReferenceTypeDelegate delegate = referenceType.getDelegate();
|
||||
if (delegate instanceof ReflectionBasedReferenceTypeDelegate) {
|
||||
try {
|
||||
ReflectionUtils.makeAccessible(myClassField);
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2015 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.
|
||||
@@ -69,8 +69,8 @@ public class SingletonAspectInstanceFactory implements AspectInstanceFactory, Se
|
||||
*/
|
||||
@Override
|
||||
public int getOrder() {
|
||||
if (this.aspectInstance instanceof Ordered) {
|
||||
return ((Ordered) this.aspectInstance).getOrder();
|
||||
if (this.aspectInstance instanceof Ordered ordered) {
|
||||
return ordered.getOrder();
|
||||
}
|
||||
return getOrderForAspectClass(this.aspectInstance.getClass());
|
||||
}
|
||||
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -117,9 +117,9 @@ public class TypePatternClassFilter implements ClassFilter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return (this == other || (other instanceof TypePatternClassFilter &&
|
||||
ObjectUtils.nullSafeEquals(this.typePattern, ((TypePatternClassFilter) other).typePattern)));
|
||||
public boolean equals(Object obj) {
|
||||
return (this == obj || (obj instanceof TypePatternClassFilter that &&
|
||||
ObjectUtils.nullSafeEquals(this.typePattern, that.typePattern)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+2
-10
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* 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.
|
||||
@@ -20,7 +20,6 @@ import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Map;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
@@ -52,6 +51,7 @@ import org.springframework.lang.Nullable;
|
||||
* @author Rod Johnson
|
||||
* @author Adrian Colyer
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @since 2.0
|
||||
*/
|
||||
public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFactory {
|
||||
@@ -101,14 +101,6 @@ public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFac
|
||||
|
||||
@Override
|
||||
public void validate(Class<?> aspectClass) throws AopConfigException {
|
||||
// If the parent has the annotation and isn't abstract it's an error
|
||||
Class<?> superclass = aspectClass.getSuperclass();
|
||||
if (superclass.getAnnotation(Aspect.class) != null &&
|
||||
!Modifier.isAbstract(superclass.getModifiers())) {
|
||||
throw new AopConfigException("[" + aspectClass.getName() + "] cannot extend concrete aspect [" +
|
||||
superclass.getName() + "]");
|
||||
}
|
||||
|
||||
AjType<?> ajType = AjTypeSystem.getAjType(aspectClass);
|
||||
if (!ajType.isAspect()) {
|
||||
throw new NotAnAtAspectException(aspectClass);
|
||||
|
||||
+5
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 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.
|
||||
@@ -93,9 +93,8 @@ public class BeanFactoryAspectInstanceFactory implements MetadataAwareAspectInst
|
||||
@Override
|
||||
@Nullable
|
||||
public ClassLoader getAspectClassLoader() {
|
||||
return (this.beanFactory instanceof ConfigurableBeanFactory ?
|
||||
((ConfigurableBeanFactory) this.beanFactory).getBeanClassLoader() :
|
||||
ClassUtils.getDefaultClassLoader());
|
||||
return (this.beanFactory instanceof ConfigurableBeanFactory cbf ?
|
||||
cbf.getBeanClassLoader() : ClassUtils.getDefaultClassLoader());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -110,11 +109,11 @@ public class BeanFactoryAspectInstanceFactory implements MetadataAwareAspectInst
|
||||
// Rely on singleton semantics provided by the factory -> no local lock.
|
||||
return null;
|
||||
}
|
||||
else if (this.beanFactory instanceof ConfigurableBeanFactory) {
|
||||
else if (this.beanFactory instanceof ConfigurableBeanFactory cbf) {
|
||||
// No singleton guarantees from the factory -> let's lock locally but
|
||||
// reuse the factory's singleton lock, just in case a lazy dependency
|
||||
// of our advice bean happens to trigger the singleton lock implicitly...
|
||||
return ((ConfigurableBeanFactory) this.beanFactory).getSingletonMutex();
|
||||
return cbf.getSingletonMutex();
|
||||
}
|
||||
else {
|
||||
return this;
|
||||
|
||||
+2
-2
@@ -274,8 +274,8 @@ final class InstantiationModelAwarePointcutAdvisorImpl
|
||||
|
||||
this.declaredPointcut = declaredPointcut;
|
||||
this.preInstantiationPointcut = preInstantiationPointcut;
|
||||
if (aspectInstanceFactory instanceof LazySingletonAspectInstanceFactoryDecorator) {
|
||||
this.aspectInstanceFactory = (LazySingletonAspectInstanceFactoryDecorator) aspectInstanceFactory;
|
||||
if (aspectInstanceFactory instanceof LazySingletonAspectInstanceFactoryDecorator lazyFactory) {
|
||||
this.aspectInstanceFactory = lazyFactory;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -93,8 +93,8 @@ public abstract class AbstractInterceptorDrivenBeanDefinitionDecorator implement
|
||||
// copy autowire settings from original bean definition.
|
||||
proxyDefinition.setAutowireCandidate(targetDefinition.isAutowireCandidate());
|
||||
proxyDefinition.setPrimary(targetDefinition.isPrimary());
|
||||
if (targetDefinition instanceof AbstractBeanDefinition) {
|
||||
proxyDefinition.copyQualifiersFrom((AbstractBeanDefinition) targetDefinition);
|
||||
if (targetDefinition instanceof AbstractBeanDefinition abd) {
|
||||
proxyDefinition.copyQualifiersFrom(abd);
|
||||
}
|
||||
// wrap it in a BeanDefinitionHolder with bean name
|
||||
result = new BeanDefinitionHolder(proxyDefinition, existingBeanName);
|
||||
|
||||
+13
-17
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* 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.
|
||||
@@ -108,14 +108,10 @@ class ConfigBeanDefinitionParser implements BeanDefinitionParser {
|
||||
List<Element> childElts = DomUtils.getChildElements(element);
|
||||
for (Element elt: childElts) {
|
||||
String localName = parserContext.getDelegate().getLocalName(elt);
|
||||
if (POINTCUT.equals(localName)) {
|
||||
parsePointcut(elt, parserContext);
|
||||
}
|
||||
else if (ADVISOR.equals(localName)) {
|
||||
parseAdvisor(elt, parserContext);
|
||||
}
|
||||
else if (ASPECT.equals(localName)) {
|
||||
parseAspect(elt, parserContext);
|
||||
switch (localName) {
|
||||
case POINTCUT -> parsePointcut(elt, parserContext);
|
||||
case ADVISOR -> parseAdvisor(elt, parserContext);
|
||||
case ASPECT -> parseAspect(elt, parserContext);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,13 +149,13 @@ class ConfigBeanDefinitionParser implements BeanDefinitionParser {
|
||||
}
|
||||
|
||||
Object pointcut = parsePointcutProperty(advisorElement, parserContext);
|
||||
if (pointcut instanceof BeanDefinition) {
|
||||
if (pointcut instanceof BeanDefinition beanDefinition) {
|
||||
advisorDef.getPropertyValues().add(POINTCUT, pointcut);
|
||||
parserContext.registerComponent(
|
||||
new AdvisorComponentDefinition(advisorBeanName, advisorDef, (BeanDefinition) pointcut));
|
||||
new AdvisorComponentDefinition(advisorBeanName, advisorDef, beanDefinition));
|
||||
}
|
||||
else if (pointcut instanceof String) {
|
||||
advisorDef.getPropertyValues().add(POINTCUT, new RuntimeBeanReference((String) pointcut));
|
||||
else if (pointcut instanceof String beanName) {
|
||||
advisorDef.getPropertyValues().add(POINTCUT, new RuntimeBeanReference(beanName));
|
||||
parserContext.registerComponent(
|
||||
new AdvisorComponentDefinition(advisorBeanName, advisorDef));
|
||||
}
|
||||
@@ -389,12 +385,12 @@ class ConfigBeanDefinitionParser implements BeanDefinitionParser {
|
||||
cav.addIndexedArgumentValue(METHOD_INDEX, methodDef);
|
||||
|
||||
Object pointcut = parsePointcutProperty(adviceElement, parserContext);
|
||||
if (pointcut instanceof BeanDefinition) {
|
||||
if (pointcut instanceof BeanDefinition beanDefinition) {
|
||||
cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcut);
|
||||
beanDefinitions.add((BeanDefinition) pointcut);
|
||||
beanDefinitions.add(beanDefinition);
|
||||
}
|
||||
else if (pointcut instanceof String) {
|
||||
RuntimeBeanReference pointcutRef = new RuntimeBeanReference((String) pointcut);
|
||||
else if (pointcut instanceof String beanName) {
|
||||
RuntimeBeanReference pointcutRef = new RuntimeBeanReference(beanName);
|
||||
cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcutRef);
|
||||
beanReferences.add(pointcutRef);
|
||||
}
|
||||
|
||||
+2
-2
@@ -71,8 +71,8 @@ public class SimpleBeanFactoryAwareAspectInstanceFactory implements AspectInstan
|
||||
@Override
|
||||
@Nullable
|
||||
public ClassLoader getAspectClassLoader() {
|
||||
if (this.beanFactory instanceof ConfigurableBeanFactory) {
|
||||
return ((ConfigurableBeanFactory) this.beanFactory).getBeanClassLoader();
|
||||
if (this.beanFactory instanceof ConfigurableBeanFactory cbf) {
|
||||
return cbf.getBeanClassLoader();
|
||||
}
|
||||
else {
|
||||
return ClassUtils.getDefaultClassLoader();
|
||||
|
||||
+14
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* 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.
|
||||
@@ -74,8 +74,9 @@ public abstract class AbstractAdvisingBeanPostProcessor extends ProxyProcessorSu
|
||||
|
||||
// Use original ClassLoader if bean class not locally loaded in overriding class loader
|
||||
ClassLoader classLoader = getProxyClassLoader();
|
||||
if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) {
|
||||
classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
|
||||
if (classLoader instanceof SmartClassLoader smartClassLoader &&
|
||||
classLoader != beanClass.getClassLoader()) {
|
||||
classLoader = smartClassLoader.getOriginalClassLoader();
|
||||
}
|
||||
return proxyFactory.getProxyClass(classLoader);
|
||||
}
|
||||
@@ -92,10 +93,16 @@ public abstract class AbstractAdvisingBeanPostProcessor extends ProxyProcessorSu
|
||||
|
||||
if (bean instanceof Advised advised) {
|
||||
if (!advised.isFrozen() && isEligible(AopUtils.getTargetClass(bean))) {
|
||||
// Add our local Advisor to the existing proxy's Advisor chain...
|
||||
// Add our local Advisor to the existing proxy's Advisor chain.
|
||||
if (this.beforeExistingAdvisors) {
|
||||
advised.addAdvisor(0, this.advisor);
|
||||
}
|
||||
else if (advised.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE &&
|
||||
advised.getAdvisorCount() > 0) {
|
||||
// No target, leave last Advisor in place and add new Advisor right before.
|
||||
advised.addAdvisor(advised.getAdvisorCount() - 1, this.advisor);
|
||||
return bean;
|
||||
}
|
||||
else {
|
||||
advised.addAdvisor(this.advisor);
|
||||
}
|
||||
@@ -113,8 +120,9 @@ public abstract class AbstractAdvisingBeanPostProcessor extends ProxyProcessorSu
|
||||
|
||||
// Use original ClassLoader if bean class not locally loaded in overriding class loader
|
||||
ClassLoader classLoader = getProxyClassLoader();
|
||||
if (classLoader instanceof SmartClassLoader && classLoader != bean.getClass().getClassLoader()) {
|
||||
classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
|
||||
if (classLoader instanceof SmartClassLoader smartClassLoader &&
|
||||
classLoader != bean.getClass().getClassLoader()) {
|
||||
classLoader = smartClassLoader.getOriginalClassLoader();
|
||||
}
|
||||
return proxyFactory.getProxy(classLoader);
|
||||
}
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 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.
|
||||
@@ -194,8 +194,8 @@ public abstract class AbstractSingletonProxyFactoryBean extends ProxyConfig
|
||||
* @return a TargetSource for this object
|
||||
*/
|
||||
protected TargetSource createTargetSource(Object target) {
|
||||
if (target instanceof TargetSource) {
|
||||
return (TargetSource) target;
|
||||
if (target instanceof TargetSource targetSource) {
|
||||
return targetSource;
|
||||
}
|
||||
else {
|
||||
return new SingletonTargetSource(target);
|
||||
@@ -229,8 +229,8 @@ public abstract class AbstractSingletonProxyFactoryBean extends ProxyConfig
|
||||
if (this.proxyInterfaces != null && this.proxyInterfaces.length == 1) {
|
||||
return this.proxyInterfaces[0];
|
||||
}
|
||||
if (this.target instanceof TargetSource) {
|
||||
return ((TargetSource) this.target).getTargetClass();
|
||||
if (this.target instanceof TargetSource targetSource) {
|
||||
return targetSource.getTargetClass();
|
||||
}
|
||||
if (this.target != null) {
|
||||
return this.target.getClass();
|
||||
|
||||
@@ -258,8 +258,8 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
|
||||
@Override
|
||||
public void addAdvisor(int pos, Advisor advisor) throws AopConfigException {
|
||||
if (advisor instanceof IntroductionAdvisor) {
|
||||
validateIntroductionAdvisor((IntroductionAdvisor) advisor);
|
||||
if (advisor instanceof IntroductionAdvisor introductionAdvisor) {
|
||||
validateIntroductionAdvisor(introductionAdvisor);
|
||||
}
|
||||
addAdvisorInternal(pos, advisor);
|
||||
}
|
||||
@@ -287,9 +287,9 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
}
|
||||
|
||||
Advisor advisor = this.advisors.remove(index);
|
||||
if (advisor instanceof IntroductionAdvisor ia) {
|
||||
if (advisor instanceof IntroductionAdvisor introductionAdvisor) {
|
||||
// We need to remove introduction interfaces.
|
||||
for (Class<?> ifc : ia.getInterfaces()) {
|
||||
for (Class<?> ifc : introductionAdvisor.getInterfaces()) {
|
||||
removeInterface(ifc);
|
||||
}
|
||||
}
|
||||
@@ -387,10 +387,10 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
@Override
|
||||
public void addAdvice(int pos, Advice advice) throws AopConfigException {
|
||||
Assert.notNull(advice, "Advice must not be null");
|
||||
if (advice instanceof IntroductionInfo) {
|
||||
if (advice instanceof IntroductionInfo introductionInfo) {
|
||||
// We don't need an IntroductionAdvisor for this kind of introduction:
|
||||
// It's fully self-describing.
|
||||
addAdvisor(pos, new DefaultIntroductionAdvisor(advice, (IntroductionInfo) advice));
|
||||
addAdvisor(pos, new DefaultIntroductionAdvisor(advice, introductionInfo));
|
||||
}
|
||||
else if (advice instanceof DynamicIntroductionAdvice) {
|
||||
// We need an IntroductionAdvisor for this kind of introduction.
|
||||
@@ -506,8 +506,8 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
this.advisorChainFactory = other.advisorChainFactory;
|
||||
this.interfaces = new ArrayList<>(other.interfaces);
|
||||
for (Advisor advisor : advisors) {
|
||||
if (advisor instanceof IntroductionAdvisor) {
|
||||
validateIntroductionAdvisor((IntroductionAdvisor) advisor);
|
||||
if (advisor instanceof IntroductionAdvisor introductionAdvisor) {
|
||||
validateIntroductionAdvisor(introductionAdvisor);
|
||||
}
|
||||
Assert.notNull(advisor, "Advisor must not be null");
|
||||
this.advisors.add(advisor);
|
||||
@@ -580,8 +580,8 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object other) {
|
||||
return (this == other || (other instanceof MethodCacheKey &&
|
||||
this.method == ((MethodCacheKey) other).method));
|
||||
return (this == other || (other instanceof MethodCacheKey methodCacheKey &&
|
||||
this.method == methodCacheKey.method));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* 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.
|
||||
@@ -257,23 +257,21 @@ public abstract class AopProxyUtils {
|
||||
if (ObjectUtils.isEmpty(arguments)) {
|
||||
return new Object[0];
|
||||
}
|
||||
if (method.isVarArgs()) {
|
||||
if (method.getParameterCount() == arguments.length) {
|
||||
Class<?>[] paramTypes = method.getParameterTypes();
|
||||
int varargIndex = paramTypes.length - 1;
|
||||
Class<?> varargType = paramTypes[varargIndex];
|
||||
if (varargType.isArray()) {
|
||||
Object varargArray = arguments[varargIndex];
|
||||
if (varargArray instanceof Object[] && !varargType.isInstance(varargArray)) {
|
||||
Object[] newArguments = new Object[arguments.length];
|
||||
System.arraycopy(arguments, 0, newArguments, 0, varargIndex);
|
||||
Class<?> targetElementType = varargType.getComponentType();
|
||||
int varargLength = Array.getLength(varargArray);
|
||||
Object newVarargArray = Array.newInstance(targetElementType, varargLength);
|
||||
System.arraycopy(varargArray, 0, newVarargArray, 0, varargLength);
|
||||
newArguments[varargIndex] = newVarargArray;
|
||||
return newArguments;
|
||||
}
|
||||
if (method.isVarArgs() && (method.getParameterCount() == arguments.length)) {
|
||||
Class<?>[] paramTypes = method.getParameterTypes();
|
||||
int varargIndex = paramTypes.length - 1;
|
||||
Class<?> varargType = paramTypes[varargIndex];
|
||||
if (varargType.isArray()) {
|
||||
Object varargArray = arguments[varargIndex];
|
||||
if (varargArray instanceof Object[] && !varargType.isInstance(varargArray)) {
|
||||
Object[] newArguments = new Object[arguments.length];
|
||||
System.arraycopy(arguments, 0, newArguments, 0, varargIndex);
|
||||
Class<?> targetElementType = varargType.getComponentType();
|
||||
int varargLength = Array.getLength(varargArray);
|
||||
Object newVarargArray = Array.newInstance(targetElementType, varargLength);
|
||||
System.arraycopy(varargArray, 0, newVarargArray, 0, varargLength);
|
||||
newArguments[varargIndex] = newVarargArray;
|
||||
return newArguments;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* 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.
|
||||
@@ -188,8 +188,8 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
Enhancer enhancer = createEnhancer();
|
||||
if (classLoader != null) {
|
||||
enhancer.setClassLoader(classLoader);
|
||||
if (classLoader instanceof SmartClassLoader &&
|
||||
((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
|
||||
if (classLoader instanceof SmartClassLoader smartClassLoader &&
|
||||
smartClassLoader.isClassReloadable(proxySuperClass)) {
|
||||
enhancer.setUseCache(false);
|
||||
}
|
||||
}
|
||||
@@ -251,11 +251,10 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
private void validateClassIfNecessary(Class<?> proxySuperClass, @Nullable ClassLoader proxyClassLoader) {
|
||||
if (!this.advised.isOptimize() && logger.isInfoEnabled()) {
|
||||
synchronized (validatedClasses) {
|
||||
if (!validatedClasses.containsKey(proxySuperClass)) {
|
||||
doValidateClass(proxySuperClass, proxyClassLoader,
|
||||
ClassUtils.getAllInterfacesForClassAsSet(proxySuperClass));
|
||||
validatedClasses.put(proxySuperClass, Boolean.TRUE);
|
||||
}
|
||||
validatedClasses.computeIfAbsent(proxySuperClass, clazz -> {
|
||||
doValidateClass(clazz, proxyClassLoader, ClassUtils.getAllInterfacesForClassAsSet(clazz));
|
||||
return Boolean.TRUE;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -365,8 +364,8 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object other) {
|
||||
return (this == other || (other instanceof CglibAopProxy &&
|
||||
AopProxyUtils.equalsInProxy(this.advised, ((CglibAopProxy) other).advised)));
|
||||
return (this == other || (other instanceof CglibAopProxy cglibAopProxy &&
|
||||
AopProxyUtils.equalsInProxy(this.advised, cglibAopProxy.advised)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -590,12 +589,12 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
if (proxy == other) {
|
||||
return true;
|
||||
}
|
||||
if (other instanceof Factory) {
|
||||
Callback callback = ((Factory) other).getCallback(INVOKE_EQUALS);
|
||||
if (!(callback instanceof EqualsInterceptor)) {
|
||||
if (other instanceof Factory factory) {
|
||||
Callback callback = factory.getCallback(INVOKE_EQUALS);
|
||||
if (!(callback instanceof EqualsInterceptor equalsInterceptor)) {
|
||||
return false;
|
||||
}
|
||||
AdvisedSupport otherAdvised = ((EqualsInterceptor) callback).advised;
|
||||
AdvisedSupport otherAdvised = equalsInterceptor.advised;
|
||||
return AopProxyUtils.equalsInProxy(this.advised, otherAdvised);
|
||||
}
|
||||
else {
|
||||
@@ -702,8 +701,7 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
// We need to create a method invocation...
|
||||
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
|
||||
}
|
||||
retVal = processReturnType(proxy, target, method, retVal);
|
||||
return retVal;
|
||||
return processReturnType(proxy, target, method, retVal);
|
||||
}
|
||||
finally {
|
||||
if (target != null && !targetSource.isStatic()) {
|
||||
@@ -719,8 +717,8 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
@Override
|
||||
public boolean equals(@Nullable Object other) {
|
||||
return (this == other ||
|
||||
(other instanceof DynamicAdvisedInterceptor &&
|
||||
this.advised.equals(((DynamicAdvisedInterceptor) other).advised)));
|
||||
(other instanceof DynamicAdvisedInterceptor dynamicAdvisedInterceptor &&
|
||||
this.advised.equals(dynamicAdvisedInterceptor.advised)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -962,9 +960,9 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
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) ||
|
||||
(b instanceof PointcutAdvisor &&
|
||||
ObjectUtils.nullSafeEquals(((PointcutAdvisor) a).getPointcut(), ((PointcutAdvisor) b).getPointcut())));
|
||||
return (!(a instanceof PointcutAdvisor pointcutAdvisor1) ||
|
||||
(b instanceof PointcutAdvisor pointcutAdvisor2 &&
|
||||
ObjectUtils.nullSafeEquals(pointcutAdvisor1.getPointcut(), pointcutAdvisor2.getPointcut())));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+5
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* 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.
|
||||
@@ -65,11 +65,11 @@ public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializ
|
||||
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
|
||||
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
|
||||
boolean match;
|
||||
if (mm instanceof IntroductionAwareMethodMatcher) {
|
||||
if (mm instanceof IntroductionAwareMethodMatcher iamm) {
|
||||
if (hasIntroductions == null) {
|
||||
hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
|
||||
}
|
||||
match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
|
||||
match = iamm.matches(method, actualClass, hasIntroductions);
|
||||
}
|
||||
else {
|
||||
match = mm.matches(method, actualClass);
|
||||
@@ -109,10 +109,8 @@ public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializ
|
||||
*/
|
||||
private static boolean hasMatchingIntroductions(Advisor[] advisors, Class<?> actualClass) {
|
||||
for (Advisor advisor : advisors) {
|
||||
if (advisor instanceof IntroductionAdvisor ia) {
|
||||
if (ia.getClassFilter().matches(actualClass)) {
|
||||
return true;
|
||||
}
|
||||
if (advisor instanceof IntroductionAdvisor ia && ia.getClassFilter().matches(actualClass)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -262,15 +262,15 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa
|
||||
}
|
||||
|
||||
JdkDynamicAopProxy otherProxy;
|
||||
if (other instanceof JdkDynamicAopProxy) {
|
||||
otherProxy = (JdkDynamicAopProxy) other;
|
||||
if (other instanceof JdkDynamicAopProxy jdkDynamicAopProxy) {
|
||||
otherProxy = jdkDynamicAopProxy;
|
||||
}
|
||||
else if (Proxy.isProxyClass(other.getClass())) {
|
||||
InvocationHandler ih = Proxy.getInvocationHandler(other);
|
||||
if (!(ih instanceof JdkDynamicAopProxy)) {
|
||||
if (!(ih instanceof JdkDynamicAopProxy jdkDynamicAopProxy)) {
|
||||
return false;
|
||||
}
|
||||
otherProxy = (JdkDynamicAopProxy) ih;
|
||||
otherProxy = jdkDynamicAopProxy;
|
||||
}
|
||||
else {
|
||||
// Not a valid comparison...
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -55,8 +55,8 @@ public class AdvisorAdapterRegistrationManager implements BeanPostProcessor {
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (bean instanceof AdvisorAdapter){
|
||||
this.advisorAdapterRegistry.registerAdvisorAdapter((AdvisorAdapter) bean);
|
||||
if (bean instanceof AdvisorAdapter advisorAdapter) {
|
||||
this.advisorAdapterRegistry.registerAdvisorAdapter(advisorAdapter);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -55,8 +55,8 @@ public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Se
|
||||
|
||||
@Override
|
||||
public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
|
||||
if (adviceObject instanceof Advisor) {
|
||||
return (Advisor) adviceObject;
|
||||
if (adviceObject instanceof Advisor advisor) {
|
||||
return advisor;
|
||||
}
|
||||
if (!(adviceObject instanceof Advice advice)) {
|
||||
throw new UnknownAdviceTypeException(adviceObject);
|
||||
@@ -78,8 +78,8 @@ public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Se
|
||||
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
|
||||
List<MethodInterceptor> interceptors = new ArrayList<>(3);
|
||||
Advice advice = advisor.getAdvice();
|
||||
if (advice instanceof MethodInterceptor) {
|
||||
interceptors.add((MethodInterceptor) advice);
|
||||
if (advice instanceof MethodInterceptor methodInterceptor) {
|
||||
interceptors.add(methodInterceptor);
|
||||
}
|
||||
for (AdvisorAdapter adapter : this.adapters) {
|
||||
if (adapter.supportsAdvice(advice)) {
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -58,11 +58,11 @@ public abstract class AbstractAdvisorAutoProxyCreator extends AbstractAutoProxyC
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
super.setBeanFactory(beanFactory);
|
||||
if (!(beanFactory instanceof ConfigurableListableBeanFactory)) {
|
||||
if (!(beanFactory instanceof ConfigurableListableBeanFactory clbf)) {
|
||||
throw new IllegalArgumentException(
|
||||
"AdvisorAutoProxyCreator requires a ConfigurableListableBeanFactory: " + beanFactory);
|
||||
}
|
||||
initBeanFactory((ConfigurableListableBeanFactory) beanFactory);
|
||||
initBeanFactory(clbf);
|
||||
}
|
||||
|
||||
protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
|
||||
|
||||
+7
-7
@@ -473,8 +473,8 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
|
||||
private Object buildProxy(Class<?> beanClass, @Nullable String beanName,
|
||||
@Nullable Object[] specificInterceptors, TargetSource targetSource, boolean classOnly) {
|
||||
|
||||
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
|
||||
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
|
||||
if (this.beanFactory instanceof ConfigurableListableBeanFactory clbf) {
|
||||
AutoProxyUtils.exposeTargetClass(clbf, beanName, beanClass);
|
||||
}
|
||||
|
||||
ProxyFactory proxyFactory = new ProxyFactory();
|
||||
@@ -511,8 +511,8 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
|
||||
|
||||
// Use original ClassLoader if bean class not locally loaded in overriding class loader
|
||||
ClassLoader classLoader = getProxyClassLoader();
|
||||
if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) {
|
||||
classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
|
||||
if (classLoader instanceof SmartClassLoader smartClassLoader && classLoader != beanClass.getClassLoader()) {
|
||||
classLoader = smartClassLoader.getOriginalClassLoader();
|
||||
}
|
||||
return (classOnly ? proxyFactory.getProxyClass(classLoader) : proxyFactory.getProxy(classLoader));
|
||||
}
|
||||
@@ -527,8 +527,8 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
|
||||
* @see AutoProxyUtils#shouldProxyTargetClass
|
||||
*/
|
||||
protected boolean shouldProxyTargetClass(Class<?> beanClass, @Nullable String beanName) {
|
||||
return (this.beanFactory instanceof ConfigurableListableBeanFactory &&
|
||||
AutoProxyUtils.shouldProxyTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName));
|
||||
return (this.beanFactory instanceof ConfigurableListableBeanFactory clbf &&
|
||||
AutoProxyUtils.shouldProxyTargetClass(clbf, beanName));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -592,7 +592,7 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
|
||||
*/
|
||||
private Advisor[] resolveInterceptorNames() {
|
||||
BeanFactory bf = this.beanFactory;
|
||||
ConfigurableBeanFactory cbf = (bf instanceof ConfigurableBeanFactory ? (ConfigurableBeanFactory) bf : null);
|
||||
ConfigurableBeanFactory cbf = (bf instanceof ConfigurableBeanFactory _cbf ? _cbf : null);
|
||||
List<Advisor> advisors = new ArrayList<>();
|
||||
for (String beanName : this.interceptorNames) {
|
||||
if (cbf == null || !cbf.isCurrentlyInCreation(beanName)) {
|
||||
|
||||
+2
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 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.
|
||||
@@ -46,8 +46,7 @@ public abstract class AbstractBeanFactoryAwareAdvisingPostProcessor extends Abst
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
this.beanFactory = (beanFactory instanceof ConfigurableListableBeanFactory ?
|
||||
(ConfigurableListableBeanFactory) beanFactory : null);
|
||||
this.beanFactory = (beanFactory instanceof ConfigurableListableBeanFactory clbf ? clbf : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -67,11 +67,11 @@ public abstract class AbstractBeanFactoryBasedTargetSourceCreator
|
||||
|
||||
@Override
|
||||
public final void setBeanFactory(BeanFactory beanFactory) {
|
||||
if (!(beanFactory instanceof ConfigurableBeanFactory)) {
|
||||
if (!(beanFactory instanceof ConfigurableBeanFactory clbf)) {
|
||||
throw new IllegalStateException("Cannot do auto-TargetSource creation with a BeanFactory " +
|
||||
"that doesn't implement ConfigurableBeanFactory: " + beanFactory.getClass());
|
||||
}
|
||||
this.beanFactory = (ConfigurableBeanFactory) beanFactory;
|
||||
this.beanFactory = clbf;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+3
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -66,9 +66,8 @@ public class LazyInitTargetSourceCreator extends AbstractBeanFactoryBasedTargetS
|
||||
protected AbstractBeanFactoryBasedTargetSource createBeanFactoryBasedTargetSource(
|
||||
Class<?> beanClass, String beanName) {
|
||||
|
||||
if (getBeanFactory() instanceof ConfigurableListableBeanFactory) {
|
||||
BeanDefinition definition =
|
||||
((ConfigurableListableBeanFactory) getBeanFactory()).getBeanDefinition(beanName);
|
||||
if (getBeanFactory() instanceof ConfigurableListableBeanFactory clbf) {
|
||||
BeanDefinition definition = clbf.getBeanDefinition(beanName);
|
||||
if (definition.isLazyInit()) {
|
||||
return new LazyInitTargetSource();
|
||||
}
|
||||
|
||||
+2
-2
@@ -182,8 +182,8 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
|
||||
if (targetExecutor == null) {
|
||||
return null;
|
||||
}
|
||||
executor = (targetExecutor instanceof AsyncTaskExecutor ?
|
||||
(AsyncTaskExecutor) targetExecutor : new TaskExecutorAdapter(targetExecutor));
|
||||
executor = (targetExecutor instanceof AsyncTaskExecutor asyncTaskExecutor ?
|
||||
asyncTaskExecutor : new TaskExecutorAdapter(targetExecutor));
|
||||
this.executors.put(method, executor);
|
||||
}
|
||||
return executor;
|
||||
|
||||
+16
-13
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* 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.
|
||||
@@ -54,9 +54,9 @@ import org.springframework.util.ClassUtils;
|
||||
* return type however, such exceptions cannot be transmitted back. In that case an
|
||||
* {@link AsyncUncaughtExceptionHandler} can be registered to process such exceptions.
|
||||
*
|
||||
* <p>As of Spring 3.1.2 the {@code AnnotationAsyncExecutionInterceptor} subclass is
|
||||
* preferred for use due to its support for executor qualification in conjunction with
|
||||
* Spring's {@code @Async} annotation.
|
||||
* <p>Note: the {@code AnnotationAsyncExecutionInterceptor} subclass is preferred
|
||||
* due to its support for executor qualification in conjunction with Spring's
|
||||
* {@code @Async} annotation.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
@@ -71,8 +71,8 @@ public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport imple
|
||||
/**
|
||||
* Create a new instance with a default {@link AsyncUncaughtExceptionHandler}.
|
||||
* @param defaultExecutor the {@link Executor} (typically a Spring {@link AsyncTaskExecutor}
|
||||
* or {@link java.util.concurrent.ExecutorService}) to delegate to;
|
||||
* as of 4.2.6, a local executor for this interceptor will be built otherwise
|
||||
* or {@link java.util.concurrent.ExecutorService}) to delegate to; a local
|
||||
* executor for this interceptor will be built otherwise
|
||||
*/
|
||||
public AsyncExecutionInterceptor(@Nullable Executor defaultExecutor) {
|
||||
super(defaultExecutor);
|
||||
@@ -81,8 +81,8 @@ public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport imple
|
||||
/**
|
||||
* Create a new {@code AsyncExecutionInterceptor}.
|
||||
* @param defaultExecutor the {@link Executor} (typically a Spring {@link AsyncTaskExecutor}
|
||||
* or {@link java.util.concurrent.ExecutorService}) to delegate to;
|
||||
* as of 4.2.6, a local executor for this interceptor will be built otherwise
|
||||
* or {@link java.util.concurrent.ExecutorService}) to delegate to; a local
|
||||
* executor for this interceptor will be built otherwise
|
||||
* @param exceptionHandler the {@link AsyncUncaughtExceptionHandler} to use
|
||||
*/
|
||||
public AsyncExecutionInterceptor(@Nullable Executor defaultExecutor, AsyncUncaughtExceptionHandler exceptionHandler) {
|
||||
@@ -113,8 +113,8 @@ public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport imple
|
||||
Callable<Object> task = () -> {
|
||||
try {
|
||||
Object result = invocation.proceed();
|
||||
if (result instanceof Future) {
|
||||
return ((Future<?>) result).get();
|
||||
if (result instanceof Future<?> future) {
|
||||
return future.get();
|
||||
}
|
||||
}
|
||||
catch (ExecutionException ex) {
|
||||
@@ -130,9 +130,12 @@ public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport imple
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation is a no-op for compatibility in Spring 3.1.2.
|
||||
* Subclasses may override to provide support for extracting qualifier information,
|
||||
* e.g. via an annotation on the given method.
|
||||
* Get the qualifier for a specific executor to use when executing the given
|
||||
* method.
|
||||
* <p>The default implementation of this method is effectively a no-op.
|
||||
* <p>Subclasses may override this method to provide support for extracting
|
||||
* qualifier information — for example, via an annotation on the given
|
||||
* method.
|
||||
* @return always {@code null}
|
||||
* @since 3.1.2
|
||||
* @see #determineAsyncExecutor(Method)
|
||||
|
||||
+1
-1
@@ -75,7 +75,7 @@ class ScopedProxyBeanRegistrationAotProcessor implements BeanRegistrationAotProc
|
||||
@Nullable
|
||||
private String getTargetBeanName(BeanDefinition beanDefinition) {
|
||||
Object value = beanDefinition.getPropertyValues().get("targetBeanName");
|
||||
return (value instanceof String ? (String) value : null);
|
||||
return (value instanceof String targetBeanName ? targetBeanName : null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -82,8 +82,8 @@ public abstract class AbstractBeanFactoryPointcutAdvisor extends AbstractPointcu
|
||||
}
|
||||
|
||||
private void resetAdviceMonitor() {
|
||||
if (this.beanFactory instanceof ConfigurableBeanFactory) {
|
||||
this.adviceMonitor = ((ConfigurableBeanFactory) this.beanFactory).getSingletonMutex();
|
||||
if (this.beanFactory instanceof ConfigurableBeanFactory cbf) {
|
||||
this.adviceMonitor = cbf.getSingletonMutex();
|
||||
}
|
||||
else {
|
||||
this.adviceMonitor = new Object();
|
||||
|
||||
@@ -107,8 +107,8 @@ public abstract class AopUtils {
|
||||
public static Class<?> getTargetClass(Object candidate) {
|
||||
Assert.notNull(candidate, "Candidate object must not be null");
|
||||
Class<?> result = null;
|
||||
if (candidate instanceof TargetClassAware) {
|
||||
result = ((TargetClassAware) candidate).getTargetClass();
|
||||
if (candidate instanceof TargetClassAware targetClassAware) {
|
||||
result = targetClassAware.getTargetClass();
|
||||
}
|
||||
if (result == null) {
|
||||
result = (isCglibProxy(candidate) ? candidate.getClass().getSuperclass() : candidate.getClass());
|
||||
@@ -234,8 +234,8 @@ public abstract class AopUtils {
|
||||
}
|
||||
|
||||
IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
|
||||
if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
|
||||
introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
|
||||
if (methodMatcher instanceof IntroductionAwareMethodMatcher iamm) {
|
||||
introductionAwareMethodMatcher = iamm;
|
||||
}
|
||||
|
||||
Set<Class<?>> classes = new LinkedHashSet<>();
|
||||
@@ -281,8 +281,8 @@ public abstract class AopUtils {
|
||||
* @return whether the pointcut can apply on any method
|
||||
*/
|
||||
public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
|
||||
if (advisor instanceof IntroductionAdvisor) {
|
||||
return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
|
||||
if (advisor instanceof IntroductionAdvisor ia) {
|
||||
return ia.getClassFilter().matches(targetClass);
|
||||
}
|
||||
else if (advisor instanceof PointcutAdvisor pca) {
|
||||
return canApply(pca.getPointcut(), targetClass, hasIntroductions);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -109,9 +109,9 @@ public abstract class ClassFilters {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object other) {
|
||||
return (this == other || (other instanceof UnionClassFilter &&
|
||||
ObjectUtils.nullSafeEquals(this.filters, ((UnionClassFilter) other).filters)));
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
return (this == obj || (obj instanceof UnionClassFilter that &&
|
||||
ObjectUtils.nullSafeEquals(this.filters, that.filters)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -150,9 +150,9 @@ public abstract class ClassFilters {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object other) {
|
||||
return (this == other || (other instanceof IntersectionClassFilter &&
|
||||
ObjectUtils.nullSafeEquals(this.filters, ((IntersectionClassFilter) other).filters)));
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
return (this == obj || (obj instanceof IntersectionClassFilter that &&
|
||||
ObjectUtils.nullSafeEquals(this.filters, that.filters)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -56,7 +56,7 @@ public class DefaultIntroductionAdvisor implements IntroductionAdvisor, ClassFil
|
||||
* @see #addInterface
|
||||
*/
|
||||
public DefaultIntroductionAdvisor(Advice advice) {
|
||||
this(advice, (advice instanceof IntroductionInfo ? (IntroductionInfo) advice : null));
|
||||
this(advice, (advice instanceof IntroductionInfo introductionInfo ? introductionInfo : null));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,8 +112,8 @@ public class DefaultIntroductionAdvisor implements IntroductionAdvisor, ClassFil
|
||||
@Override
|
||||
public void validateInterfaces() throws IllegalArgumentException {
|
||||
for (Class<?> ifc : this.interfaces) {
|
||||
if (this.advice instanceof DynamicIntroductionAdvice &&
|
||||
!((DynamicIntroductionAdvice) this.advice).implementsInterface(ifc)) {
|
||||
if (this.advice instanceof DynamicIntroductionAdvice dynamicIntroductionAdvice &&
|
||||
!dynamicIntroductionAdvice.implementsInterface(ifc)) {
|
||||
throw new IllegalArgumentException("DynamicIntroductionAdvice [" + this.advice + "] " +
|
||||
"does not implement interface [" + ifc.getName() + "] specified for introduction");
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -98,8 +98,8 @@ public class DelegatePerTargetObjectIntroductionInterceptor extends Introduction
|
||||
|
||||
// Massage return value if possible: if the delegate returned itself,
|
||||
// we really want to return the proxy.
|
||||
if (retVal == delegate && mi instanceof ProxyMethodInvocation) {
|
||||
retVal = ((ProxyMethodInvocation) mi).getProxy();
|
||||
if (retVal == delegate && mi instanceof ProxyMethodInvocation pmi) {
|
||||
retVal = pmi.getProxy();
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -112,8 +112,8 @@ public class DelegatingIntroductionInterceptor extends IntroductionInfoSupport
|
||||
|
||||
// Massage return value if possible: if the delegate returned itself,
|
||||
// we really want to return the proxy.
|
||||
if (retVal == this.delegate && mi instanceof ProxyMethodInvocation) {
|
||||
Object proxy = ((ProxyMethodInvocation) mi).getProxy();
|
||||
if (retVal == this.delegate && mi instanceof ProxyMethodInvocation pmi) {
|
||||
Object proxy = pmi.getProxy();
|
||||
if (mi.getMethod().getReturnType().isInstance(proxy)) {
|
||||
retVal = proxy;
|
||||
}
|
||||
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -100,9 +100,9 @@ public class NameMatchMethodPointcut extends StaticMethodMatcherPointcut impleme
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object other) {
|
||||
return (this == other || (other instanceof NameMatchMethodPointcut &&
|
||||
this.mappedNames.equals(((NameMatchMethodPointcut) other).mappedNames)));
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
return (this == obj || (obj instanceof NameMatchMethodPointcut that &&
|
||||
this.mappedNames.equals(that.mappedNames)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -45,9 +45,9 @@ public class RootClassFilter implements ClassFilter, Serializable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return (this == other || (other instanceof RootClassFilter &&
|
||||
this.clazz.equals(((RootClassFilter) other).clazz)));
|
||||
public boolean equals(Object obj) {
|
||||
return (this == obj || (obj instanceof RootClassFilter that &&
|
||||
this.clazz.equals(that.clazz)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 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.
|
||||
@@ -77,12 +77,12 @@ public abstract class AbstractPrototypeBasedTargetSource extends AbstractBeanFac
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Destroying instance of bean '" + getTargetBeanName() + "'");
|
||||
}
|
||||
if (getBeanFactory() instanceof ConfigurableBeanFactory) {
|
||||
((ConfigurableBeanFactory) getBeanFactory()).destroyBean(getTargetBeanName(), target);
|
||||
if (getBeanFactory() instanceof ConfigurableBeanFactory cbf) {
|
||||
cbf.destroyBean(getTargetBeanName(), target);
|
||||
}
|
||||
else if (target instanceof DisposableBean) {
|
||||
else if (target instanceof DisposableBean disposableBean) {
|
||||
try {
|
||||
((DisposableBean) target).destroy();
|
||||
disposableBean.destroy();
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
logger.warn("Destroy method on bean with name '" + getTargetBeanName() + "' threw an exception", ex);
|
||||
|
||||
+3
-3
@@ -100,9 +100,9 @@ public class HotSwappableTargetSource implements TargetSource, Serializable {
|
||||
* objects are equal.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return (this == other || (other instanceof HotSwappableTargetSource &&
|
||||
this.target.equals(((HotSwappableTargetSource) other).target)));
|
||||
public boolean equals(Object obj) {
|
||||
return (this == obj || (obj instanceof HotSwappableTargetSource that &&
|
||||
this.target.equals(that.target)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+144
-130
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* 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.
|
||||
@@ -20,6 +20,7 @@ import java.io.FileNotFoundException;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.UndeclaredThrowableException;
|
||||
import java.rmi.RemoteException;
|
||||
import java.util.ArrayList;
|
||||
@@ -76,24 +77,21 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
|
||||
/**
|
||||
* To be overridden by concrete test subclasses.
|
||||
* @return the fixture
|
||||
*/
|
||||
protected abstract AspectJAdvisorFactory getFixture();
|
||||
protected abstract AspectJAdvisorFactory getAdvisorFactory();
|
||||
|
||||
|
||||
@Test
|
||||
void rejectsPerCflowAspect() {
|
||||
assertThatExceptionOfType(AopConfigException.class).isThrownBy(() ->
|
||||
getFixture().getAdvisors(
|
||||
new SingletonMetadataAwareAspectInstanceFactory(new PerCflowAspect(), "someBean")))
|
||||
assertThatExceptionOfType(AopConfigException.class)
|
||||
.isThrownBy(() -> getAdvisorFactory().getAdvisors(aspectInstanceFactory(new PerCflowAspect(), "someBean")))
|
||||
.withMessageContaining("PERCFLOW");
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsPerCflowBelowAspect() {
|
||||
assertThatExceptionOfType(AopConfigException.class).isThrownBy(() ->
|
||||
getFixture().getAdvisors(
|
||||
new SingletonMetadataAwareAspectInstanceFactory(new PerCflowBelowAspect(), "someBean")))
|
||||
assertThatExceptionOfType(AopConfigException.class)
|
||||
.isThrownBy(() -> getAdvisorFactory().getAdvisors(aspectInstanceFactory(new PerCflowBelowAspect(), "someBean")))
|
||||
.withMessageContaining("PERCFLOWBELOW");
|
||||
}
|
||||
|
||||
@@ -102,9 +100,8 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
TestBean target = new TestBean();
|
||||
int realAge = 65;
|
||||
target.setAge(realAge);
|
||||
TestBean itb = (TestBean) createProxy(target,
|
||||
getFixture().getAdvisors(new SingletonMetadataAwareAspectInstanceFactory(new PerTargetAspect(), "someBean")),
|
||||
TestBean.class);
|
||||
TestBean itb = createProxy(target, TestBean.class,
|
||||
getAdvisorFactory().getAdvisors(aspectInstanceFactory(new PerTargetAspect(), "someBean")));
|
||||
assertThat(itb.getAge()).as("Around advice must NOT apply").isEqualTo(realAge);
|
||||
|
||||
Advised advised = (Advised) itb;
|
||||
@@ -139,15 +136,13 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
PerTargetAspect aspect1 = new PerTargetAspect();
|
||||
aspect1.count = 100;
|
||||
aspect1.setOrder(10);
|
||||
advisors.addAll(
|
||||
getFixture().getAdvisors(new SingletonMetadataAwareAspectInstanceFactory(aspect1, "someBean1")));
|
||||
advisors.addAll(getAdvisorFactory().getAdvisors(aspectInstanceFactory(aspect1, "someBean1")));
|
||||
PerTargetAspect aspect2 = new PerTargetAspect();
|
||||
aspect2.setOrder(5);
|
||||
advisors.addAll(
|
||||
getFixture().getAdvisors(new SingletonMetadataAwareAspectInstanceFactory(aspect2, "someBean2")));
|
||||
advisors.addAll(getAdvisorFactory().getAdvisors(aspectInstanceFactory(aspect2, "someBean2")));
|
||||
OrderComparator.sort(advisors);
|
||||
|
||||
TestBean itb = (TestBean) createProxy(target, advisors, TestBean.class);
|
||||
TestBean itb = createProxy(target, TestBean.class, advisors);
|
||||
assertThat(itb.getAge()).as("Around advice must NOT apply").isEqualTo(realAge);
|
||||
|
||||
// Hit the method in the per clause to instantiate the aspect
|
||||
@@ -166,14 +161,12 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
List<Advisor> advisors = new ArrayList<>();
|
||||
PerTargetAspectWithOrderAnnotation10 aspect1 = new PerTargetAspectWithOrderAnnotation10();
|
||||
aspect1.count = 100;
|
||||
advisors.addAll(
|
||||
getFixture().getAdvisors(new SingletonMetadataAwareAspectInstanceFactory(aspect1, "someBean1")));
|
||||
advisors.addAll(getAdvisorFactory().getAdvisors(aspectInstanceFactory(aspect1, "someBean1")));
|
||||
PerTargetAspectWithOrderAnnotation5 aspect2 = new PerTargetAspectWithOrderAnnotation5();
|
||||
advisors.addAll(
|
||||
getFixture().getAdvisors(new SingletonMetadataAwareAspectInstanceFactory(aspect2, "someBean2")));
|
||||
advisors.addAll(getAdvisorFactory().getAdvisors(aspectInstanceFactory(aspect2, "someBean2")));
|
||||
OrderComparator.sort(advisors);
|
||||
|
||||
TestBean itb = (TestBean) createProxy(target, advisors, TestBean.class);
|
||||
TestBean itb = createProxy(target, TestBean.class, advisors);
|
||||
assertThat(itb.getAge()).as("Around advice must NOT apply").isEqualTo(realAge);
|
||||
|
||||
// Hit the method in the per clause to instantiate the aspect
|
||||
@@ -188,9 +181,8 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
TestBean target = new TestBean();
|
||||
int realAge = 65;
|
||||
target.setAge(realAge);
|
||||
TestBean itb = (TestBean) createProxy(target,
|
||||
getFixture().getAdvisors(new SingletonMetadataAwareAspectInstanceFactory(new PerThisAspect(), "someBean")),
|
||||
TestBean.class);
|
||||
TestBean itb = createProxy(target, TestBean.class,
|
||||
getAdvisorFactory().getAdvisors(aspectInstanceFactory(new PerThisAspect(), "someBean")));
|
||||
assertThat(itb.getAge()).as("Around advice must NOT apply").isEqualTo(realAge);
|
||||
|
||||
Advised advised = (Advised) itb;
|
||||
@@ -225,7 +217,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
int realAge = 65;
|
||||
target.setAge(realAge);
|
||||
PerTypeWithinAspectInstanceFactory aif = new PerTypeWithinAspectInstanceFactory();
|
||||
TestBean itb = (TestBean) createProxy(target, getFixture().getAdvisors(aif), TestBean.class);
|
||||
TestBean itb = createProxy(target, TestBean.class, getAdvisorFactory().getAdvisors(aif));
|
||||
assertThat(aif.getInstantiationCount()).as("No method calls").isEqualTo(0);
|
||||
assertThat(itb.getAge()).as("Around advice must now apply").isEqualTo(0);
|
||||
|
||||
@@ -254,7 +246,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
assertThat(itb.getAge()).as("Around advice must still apply").isEqualTo(1);
|
||||
assertThat(itb.getAge()).as("Around advice must still apply").isEqualTo(2);
|
||||
|
||||
TestBean itb2 = (TestBean) createProxy(target, getFixture().getAdvisors(aif), TestBean.class);
|
||||
TestBean itb2 = createProxy(target, TestBean.class, getAdvisorFactory().getAdvisors(aif));
|
||||
assertThat(aif.getInstantiationCount()).isEqualTo(1);
|
||||
assertThat(itb2.getAge()).as("Around advice be independent for second instance").isEqualTo(0);
|
||||
assertThat(aif.getInstantiationCount()).isEqualTo(2);
|
||||
@@ -278,10 +270,9 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
@Test
|
||||
void namedPointcutFromAspectLibraryWithBinding() {
|
||||
TestBean target = new TestBean();
|
||||
ITestBean itb = (ITestBean) createProxy(target,
|
||||
getFixture().getAdvisors(new SingletonMetadataAwareAspectInstanceFactory(
|
||||
new NamedPointcutAspectFromLibraryWithBinding(), "someBean")),
|
||||
ITestBean.class);
|
||||
ITestBean itb = createProxy(target, ITestBean.class,
|
||||
getAdvisorFactory().getAdvisors(aspectInstanceFactory(
|
||||
new NamedPointcutAspectFromLibraryWithBinding(), "someBean")));
|
||||
itb.setAge(10);
|
||||
assertThat(itb.getAge()).as("Around advice must apply").isEqualTo(20);
|
||||
assertThat(target.getAge()).isEqualTo(20);
|
||||
@@ -291,9 +282,8 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
TestBean target = new TestBean();
|
||||
int realAge = 65;
|
||||
target.setAge(realAge);
|
||||
ITestBean itb = (ITestBean) createProxy(target,
|
||||
getFixture().getAdvisors(new SingletonMetadataAwareAspectInstanceFactory(aspectInstance, "someBean")),
|
||||
ITestBean.class);
|
||||
ITestBean itb = createProxy(target, ITestBean.class,
|
||||
getAdvisorFactory().getAdvisors(aspectInstanceFactory(aspectInstance, "someBean")));
|
||||
assertThat(itb.getAge()).as("Around advice must apply").isEqualTo(-1);
|
||||
assertThat(target.getAge()).isEqualTo(realAge);
|
||||
}
|
||||
@@ -301,10 +291,8 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
@Test
|
||||
void bindingWithSingleArg() {
|
||||
TestBean target = new TestBean();
|
||||
ITestBean itb = (ITestBean) createProxy(target,
|
||||
getFixture().getAdvisors(
|
||||
new SingletonMetadataAwareAspectInstanceFactory(new BindingAspectWithSingleArg(), "someBean")),
|
||||
ITestBean.class);
|
||||
ITestBean itb = createProxy(target, ITestBean.class,
|
||||
getAdvisorFactory().getAdvisors(aspectInstanceFactory(new BindingAspectWithSingleArg(), "someBean")));
|
||||
itb.setAge(10);
|
||||
assertThat(itb.getAge()).as("Around advice must apply").isEqualTo(20);
|
||||
assertThat(target.getAge()).isEqualTo(20);
|
||||
@@ -313,10 +301,8 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
@Test
|
||||
void bindingWithMultipleArgsDifferentlyOrdered() {
|
||||
ManyValuedArgs target = new ManyValuedArgs();
|
||||
ManyValuedArgs mva = (ManyValuedArgs) createProxy(target,
|
||||
getFixture().getAdvisors(
|
||||
new SingletonMetadataAwareAspectInstanceFactory(new ManyValuedArgs(), "someBean")),
|
||||
ManyValuedArgs.class);
|
||||
ManyValuedArgs mva = createProxy(target, ManyValuedArgs.class,
|
||||
getAdvisorFactory().getAdvisors(aspectInstanceFactory(new ManyValuedArgs(), "someBean")));
|
||||
|
||||
String a = "a";
|
||||
int b = 12;
|
||||
@@ -334,10 +320,8 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
void introductionOnTargetNotImplementingInterface() {
|
||||
NotLockable notLockableTarget = new NotLockable();
|
||||
assertThat(notLockableTarget instanceof Lockable).isFalse();
|
||||
NotLockable notLockable1 = (NotLockable) createProxy(notLockableTarget,
|
||||
getFixture().getAdvisors(
|
||||
new SingletonMetadataAwareAspectInstanceFactory(new MakeLockable(), "someBean")),
|
||||
NotLockable.class);
|
||||
NotLockable notLockable1 = createProxy(notLockableTarget, NotLockable.class,
|
||||
getAdvisorFactory().getAdvisors(aspectInstanceFactory(new MakeLockable(), "someBean")));
|
||||
assertThat(notLockable1 instanceof Lockable).isTrue();
|
||||
Lockable lockable = (Lockable) notLockable1;
|
||||
assertThat(lockable.locked()).isFalse();
|
||||
@@ -345,72 +329,64 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
assertThat(lockable.locked()).isTrue();
|
||||
|
||||
NotLockable notLockable2Target = new NotLockable();
|
||||
NotLockable notLockable2 = (NotLockable) createProxy(notLockable2Target,
|
||||
getFixture().getAdvisors(
|
||||
new SingletonMetadataAwareAspectInstanceFactory(new MakeLockable(), "someBean")),
|
||||
NotLockable.class);
|
||||
NotLockable notLockable2 = createProxy(notLockable2Target, NotLockable.class,
|
||||
getAdvisorFactory().getAdvisors(aspectInstanceFactory(new MakeLockable(), "someBean")));
|
||||
assertThat(notLockable2 instanceof Lockable).isTrue();
|
||||
Lockable lockable2 = (Lockable) notLockable2;
|
||||
assertThat(lockable2.locked()).isFalse();
|
||||
notLockable2.setIntValue(1);
|
||||
lockable2.lock();
|
||||
assertThatIllegalStateException().isThrownBy(() ->
|
||||
notLockable2.setIntValue(32));
|
||||
assertThatIllegalStateException().isThrownBy(() -> notLockable2.setIntValue(32));
|
||||
assertThat(lockable2.locked()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void introductionAdvisorExcludedFromTargetImplementingInterface() {
|
||||
assertThat(AopUtils.findAdvisorsThatCanApply(
|
||||
getFixture().getAdvisors(
|
||||
new SingletonMetadataAwareAspectInstanceFactory(new MakeLockable(), "someBean")),
|
||||
getAdvisorFactory().getAdvisors(
|
||||
aspectInstanceFactory(new MakeLockable(), "someBean")),
|
||||
CannotBeUnlocked.class).isEmpty()).isTrue();
|
||||
assertThat(AopUtils.findAdvisorsThatCanApply(getFixture().getAdvisors(
|
||||
new SingletonMetadataAwareAspectInstanceFactory(new MakeLockable(),"someBean")), NotLockable.class)).hasSize(2);
|
||||
assertThat(AopUtils.findAdvisorsThatCanApply(getAdvisorFactory().getAdvisors(
|
||||
aspectInstanceFactory(new MakeLockable(),"someBean")), NotLockable.class)).hasSize(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void introductionOnTargetImplementingInterface() {
|
||||
CannotBeUnlocked target = new CannotBeUnlocked();
|
||||
Lockable proxy = (Lockable) createProxy(target,
|
||||
Lockable proxy = createProxy(target, CannotBeUnlocked.class,
|
||||
// Ensure that we exclude
|
||||
AopUtils.findAdvisorsThatCanApply(
|
||||
getFixture().getAdvisors(
|
||||
new SingletonMetadataAwareAspectInstanceFactory(new MakeLockable(), "someBean")),
|
||||
CannotBeUnlocked.class
|
||||
),
|
||||
CannotBeUnlocked.class);
|
||||
getAdvisorFactory().getAdvisors(aspectInstanceFactory(new MakeLockable(), "someBean")),
|
||||
CannotBeUnlocked.class));
|
||||
assertThat(proxy).isInstanceOf(Lockable.class);
|
||||
Lockable lockable = proxy;
|
||||
assertThat(lockable.locked()).as("Already locked").isTrue();
|
||||
lockable.lock();
|
||||
assertThat(lockable.locked()).as("Real target ignores locking").isTrue();
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() ->
|
||||
lockable.unlock());
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> lockable.unlock());
|
||||
}
|
||||
|
||||
@Test
|
||||
void introductionOnTargetExcludedByTypePattern() {
|
||||
ArrayList<Object> target = new ArrayList<>();
|
||||
List<?> proxy = (List<?>) createProxy(target,
|
||||
List<?> proxy = createProxy(target, List.class,
|
||||
AopUtils.findAdvisorsThatCanApply(
|
||||
getFixture().getAdvisors(new SingletonMetadataAwareAspectInstanceFactory(new MakeLockable(), "someBean")),
|
||||
List.class
|
||||
),
|
||||
List.class);
|
||||
getAdvisorFactory().getAdvisors(aspectInstanceFactory(new MakeLockable(), "someBean")),
|
||||
List.class));
|
||||
assertThat(proxy instanceof Lockable).as("Type pattern must have excluded mixin").isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void introductionBasedOnAnnotationMatch_SPR5307() {
|
||||
void introductionBasedOnAnnotationMatch() { // gh-9980
|
||||
AnnotatedTarget target = new AnnotatedTargetImpl();
|
||||
List<Advisor> advisors = getFixture().getAdvisors(
|
||||
new SingletonMetadataAwareAspectInstanceFactory(new MakeAnnotatedTypeModifiable(), "someBean"));
|
||||
Object proxy = createProxy(target, advisors, AnnotatedTarget.class);
|
||||
System.out.println(advisors.get(1));
|
||||
assertThat(proxy instanceof Lockable).isTrue();
|
||||
Lockable lockable = (Lockable)proxy;
|
||||
lockable.locked();
|
||||
List<Advisor> advisors = getAdvisorFactory().getAdvisors(
|
||||
aspectInstanceFactory(new MakeAnnotatedTypeModifiable(), "someBean"));
|
||||
Object proxy = createProxy(target, AnnotatedTarget.class, advisors);
|
||||
assertThat(proxy).isInstanceOf(Lockable.class);
|
||||
Lockable lockable = (Lockable) proxy;
|
||||
assertThat(lockable.locked()).isFalse();
|
||||
lockable.lock();
|
||||
assertThat(lockable.locked()).isTrue();
|
||||
}
|
||||
|
||||
// TODO: Why does this test fail? It hasn't been run before, so it maybe never actually passed...
|
||||
@@ -419,12 +395,12 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
void introductionWithArgumentBinding() {
|
||||
TestBean target = new TestBean();
|
||||
|
||||
List<Advisor> advisors = getFixture().getAdvisors(
|
||||
new SingletonMetadataAwareAspectInstanceFactory(new MakeITestBeanModifiable(), "someBean"));
|
||||
advisors.addAll(getFixture().getAdvisors(
|
||||
new SingletonMetadataAwareAspectInstanceFactory(new MakeLockable(), "someBean")));
|
||||
List<Advisor> advisors = getAdvisorFactory().getAdvisors(
|
||||
aspectInstanceFactory(new MakeITestBeanModifiable(), "someBean"));
|
||||
advisors.addAll(getAdvisorFactory().getAdvisors(
|
||||
aspectInstanceFactory(new MakeLockable(), "someBean")));
|
||||
|
||||
Modifiable modifiable = (Modifiable) createProxy(target, advisors, ITestBean.class);
|
||||
Modifiable modifiable = (Modifiable) createProxy(target, ITestBean.class, advisors);
|
||||
assertThat(modifiable).isInstanceOf(Modifiable.class);
|
||||
Lockable lockable = (Lockable) modifiable;
|
||||
assertThat(lockable.locked()).isFalse();
|
||||
@@ -443,8 +419,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
|
||||
lockable.lock();
|
||||
assertThat(lockable.locked()).isTrue();
|
||||
assertThatIllegalStateException().as("Should be locked").isThrownBy(() ->
|
||||
itb.setName("Else"));
|
||||
assertThatIllegalStateException().as("Should be locked").isThrownBy(() -> itb.setName("Else"));
|
||||
lockable.unlock();
|
||||
itb.setName("Tony");
|
||||
}
|
||||
@@ -453,12 +428,11 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
void aspectMethodThrowsExceptionLegalOnSignature() {
|
||||
TestBean target = new TestBean();
|
||||
UnsupportedOperationException expectedException = new UnsupportedOperationException();
|
||||
List<Advisor> advisors = getFixture().getAdvisors(
|
||||
new SingletonMetadataAwareAspectInstanceFactory(new ExceptionThrowingAspect(expectedException), "someBean"));
|
||||
List<Advisor> advisors = getAdvisorFactory().getAdvisors(
|
||||
aspectInstanceFactory(new ExceptionThrowingAspect(expectedException), "someBean"));
|
||||
assertThat(advisors.size()).as("One advice method was found").isEqualTo(1);
|
||||
ITestBean itb = (ITestBean) createProxy(target, advisors, ITestBean.class);
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(
|
||||
itb::getAge);
|
||||
ITestBean itb = createProxy(target, ITestBean.class, advisors);
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(itb::getAge);
|
||||
}
|
||||
|
||||
// TODO document this behaviour.
|
||||
@@ -467,39 +441,23 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
void aspectMethodThrowsExceptionIllegalOnSignature() {
|
||||
TestBean target = new TestBean();
|
||||
RemoteException expectedException = new RemoteException();
|
||||
List<Advisor> advisors = getFixture().getAdvisors(
|
||||
new SingletonMetadataAwareAspectInstanceFactory(new ExceptionThrowingAspect(expectedException), "someBean"));
|
||||
List<Advisor> advisors = getAdvisorFactory().getAdvisors(
|
||||
aspectInstanceFactory(new ExceptionThrowingAspect(expectedException), "someBean"));
|
||||
assertThat(advisors.size()).as("One advice method was found").isEqualTo(1);
|
||||
ITestBean itb = (ITestBean) createProxy(target, advisors, ITestBean.class);
|
||||
assertThatExceptionOfType(UndeclaredThrowableException.class).isThrownBy(
|
||||
itb::getAge).withCause(expectedException);
|
||||
}
|
||||
|
||||
protected Object createProxy(Object target, List<Advisor> advisors, Class<?>... interfaces) {
|
||||
ProxyFactory pf = new ProxyFactory(target);
|
||||
if (interfaces.length > 1 || interfaces[0].isInterface()) {
|
||||
pf.setInterfaces(interfaces);
|
||||
}
|
||||
else {
|
||||
pf.setProxyTargetClass(true);
|
||||
}
|
||||
|
||||
// Required everywhere we use AspectJ proxies
|
||||
pf.addAdvice(ExposeInvocationInterceptor.INSTANCE);
|
||||
pf.addAdvisors(advisors);
|
||||
|
||||
pf.setExposeProxy(true);
|
||||
return pf.getProxy();
|
||||
ITestBean itb = createProxy(target, ITestBean.class, advisors);
|
||||
assertThatExceptionOfType(UndeclaredThrowableException.class)
|
||||
.isThrownBy(itb::getAge)
|
||||
.withCause(expectedException);
|
||||
}
|
||||
|
||||
@Test
|
||||
void twoAdvicesOnOneAspect() {
|
||||
TestBean target = new TestBean();
|
||||
TwoAdviceAspect twoAdviceAspect = new TwoAdviceAspect();
|
||||
List<Advisor> advisors = getFixture().getAdvisors(
|
||||
new SingletonMetadataAwareAspectInstanceFactory(twoAdviceAspect, "someBean"));
|
||||
List<Advisor> advisors = getAdvisorFactory().getAdvisors(
|
||||
aspectInstanceFactory(twoAdviceAspect, "someBean"));
|
||||
assertThat(advisors.size()).as("Two advice methods found").isEqualTo(2);
|
||||
ITestBean itb = (ITestBean) createProxy(target, advisors, ITestBean.class);
|
||||
ITestBean itb = createProxy(target, ITestBean.class, advisors);
|
||||
itb.setName("");
|
||||
assertThat(itb.getAge()).isEqualTo(0);
|
||||
int newAge = 32;
|
||||
@@ -510,40 +468,76 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
@Test
|
||||
void afterAdviceTypes() throws Exception {
|
||||
InvocationTrackingAspect aspect = new InvocationTrackingAspect();
|
||||
List<Advisor> advisors = getFixture().getAdvisors(
|
||||
new SingletonMetadataAwareAspectInstanceFactory(aspect, "exceptionHandlingAspect"));
|
||||
Echo echo = (Echo) createProxy(new Echo(), advisors, Echo.class);
|
||||
List<Advisor> advisors = getAdvisorFactory().getAdvisors(
|
||||
aspectInstanceFactory(aspect, "exceptionHandlingAspect"));
|
||||
Echo echo = createProxy(new Echo(), Echo.class, advisors);
|
||||
|
||||
assertThat(aspect.invocations).isEmpty();
|
||||
assertThat(echo.echo(42)).isEqualTo(42);
|
||||
assertThat(aspect.invocations).containsExactly("around - start", "before", "after returning", "after", "around - end");
|
||||
|
||||
aspect.invocations.clear();
|
||||
assertThatExceptionOfType(FileNotFoundException.class).isThrownBy(() -> echo.echo(new FileNotFoundException()));
|
||||
assertThatExceptionOfType(FileNotFoundException.class)
|
||||
.isThrownBy(() -> echo.echo(new FileNotFoundException()));
|
||||
assertThat(aspect.invocations).containsExactly("around - start", "before", "after throwing", "after", "around - end");
|
||||
}
|
||||
|
||||
@Test
|
||||
void nonAbstractParentAspect() throws Exception {
|
||||
IncrementingAspect aspect = new IncrementingAspect();
|
||||
|
||||
// Precondition:
|
||||
assertThat(Modifier.isAbstract(aspect.getClass().getSuperclass().getModifiers())).isFalse();
|
||||
|
||||
List<Advisor> advisors = getAdvisorFactory().getAdvisors(aspectInstanceFactory(aspect, "incrementingAspect"));
|
||||
|
||||
ITestBean proxy = createProxy(new TestBean("Jane", 42), ITestBean.class, advisors);
|
||||
assertThat(proxy.getAge()).isEqualTo(86); // (42 + 1) * 2
|
||||
}
|
||||
|
||||
@Test
|
||||
void failureWithoutExplicitDeclarePrecedence() {
|
||||
TestBean target = new TestBean();
|
||||
MetadataAwareAspectInstanceFactory aspectInstanceFactory = new SingletonMetadataAwareAspectInstanceFactory(
|
||||
MetadataAwareAspectInstanceFactory aspectInstanceFactory = aspectInstanceFactory(
|
||||
new NoDeclarePrecedenceShouldFail(), "someBean");
|
||||
ITestBean itb = (ITestBean) createProxy(target,
|
||||
getFixture().getAdvisors(aspectInstanceFactory), ITestBean.class);
|
||||
itb.getAge();
|
||||
ITestBean itb = createProxy(target, ITestBean.class, getAdvisorFactory().getAdvisors(aspectInstanceFactory));
|
||||
assertThat(itb.getAge()).isEqualTo(42);
|
||||
}
|
||||
|
||||
@Test
|
||||
void declarePrecedenceNotSupported() {
|
||||
TestBean target = new TestBean();
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> {
|
||||
MetadataAwareAspectInstanceFactory aspectInstanceFactory = new SingletonMetadataAwareAspectInstanceFactory(
|
||||
MetadataAwareAspectInstanceFactory aspectInstanceFactory = aspectInstanceFactory(
|
||||
new DeclarePrecedenceShouldSucceed(), "someBean");
|
||||
createProxy(target, getFixture().getAdvisors(aspectInstanceFactory), ITestBean.class);
|
||||
createProxy(target, ITestBean.class, getAdvisorFactory().getAdvisors(aspectInstanceFactory));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private static MetadataAwareAspectInstanceFactory aspectInstanceFactory(Object aspectInstance, String aspectName) {
|
||||
return new SingletonMetadataAwareAspectInstanceFactory(aspectInstance, aspectName);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> T createProxy(Object target, Class<T> type, List<Advisor> advisors) {
|
||||
ProxyFactory pf = new ProxyFactory(target);
|
||||
if (type.isInterface()) {
|
||||
pf.addInterface(type);
|
||||
}
|
||||
else {
|
||||
pf.setProxyTargetClass(true);
|
||||
}
|
||||
|
||||
// Required everywhere we use AspectJ proxies
|
||||
pf.addAdvice(ExposeInvocationInterceptor.INSTANCE);
|
||||
pf.addAdvisors(advisors);
|
||||
|
||||
pf.setExposeProxy(true);
|
||||
return (T) pf.getProxy();
|
||||
}
|
||||
|
||||
|
||||
@Aspect("percflow(execution(* *(..)))")
|
||||
static class PerCflowAspect {
|
||||
}
|
||||
@@ -758,15 +752,35 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
|
||||
static class Echo {
|
||||
|
||||
Object echo(Object o) throws Exception {
|
||||
if (o instanceof Exception) {
|
||||
throw (Exception) o;
|
||||
Object echo(Object obj) throws Exception {
|
||||
if (obj instanceof Exception ex) {
|
||||
throw ex;
|
||||
}
|
||||
return o;
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Aspect
|
||||
class DoublingAspect {
|
||||
|
||||
@Around("execution(* getAge())")
|
||||
public Object doubleAge(ProceedingJoinPoint pjp) throws Throwable {
|
||||
return ((int) pjp.proceed()) * 2;
|
||||
}
|
||||
}
|
||||
|
||||
@Aspect
|
||||
class IncrementingAspect extends DoublingAspect {
|
||||
|
||||
@Around("execution(* getAge())")
|
||||
public int incrementAge(ProceedingJoinPoint pjp) throws Throwable {
|
||||
return ((int) pjp.proceed()) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Aspect
|
||||
private static class InvocationTrackingAspect {
|
||||
|
||||
@@ -824,7 +838,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
|
||||
@Around("getAge()")
|
||||
int preventExecution(ProceedingJoinPoint pjp) {
|
||||
return 666;
|
||||
return 42;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -844,7 +858,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
|
||||
@Around("getAge()")
|
||||
int preventExecution(ProceedingJoinPoint pjp) {
|
||||
return 666;
|
||||
return 42;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* 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.
|
||||
@@ -27,7 +27,7 @@ package org.springframework.aop.aspectj.annotation;
|
||||
class ReflectiveAspectJAdvisorFactoryTests extends AbstractAspectJAdvisorFactoryTests {
|
||||
|
||||
@Override
|
||||
protected AspectJAdvisorFactory getFixture() {
|
||||
protected AspectJAdvisorFactory getAdvisorFactory() {
|
||||
return new ReflectiveAspectJAdvisorFactory();
|
||||
}
|
||||
|
||||
|
||||
+6
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* 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.
|
||||
@@ -50,7 +50,7 @@ public class ThrowsAdviceInterceptorTests {
|
||||
MyThrowsHandler th = new MyThrowsHandler();
|
||||
ThrowsAdviceInterceptor ti = new ThrowsAdviceInterceptor(th);
|
||||
Object ret = new Object();
|
||||
MethodInvocation mi = mock(MethodInvocation.class);
|
||||
MethodInvocation mi = mock();
|
||||
given(mi.proceed()).willReturn(ret);
|
||||
assertThat(ti.invoke(mi)).isEqualTo(ret);
|
||||
assertThat(th.getCalls()).isEqualTo(0);
|
||||
@@ -62,7 +62,7 @@ public class ThrowsAdviceInterceptorTests {
|
||||
ThrowsAdviceInterceptor ti = new ThrowsAdviceInterceptor(th);
|
||||
assertThat(ti.getHandlerMethodCount()).isEqualTo(2);
|
||||
Exception ex = new Exception();
|
||||
MethodInvocation mi = mock(MethodInvocation.class);
|
||||
MethodInvocation mi = mock();
|
||||
given(mi.proceed()).willThrow(ex);
|
||||
assertThatException().isThrownBy(() -> ti.invoke(mi)).isSameAs(ex);
|
||||
assertThat(th.getCalls()).isEqualTo(0);
|
||||
@@ -73,7 +73,7 @@ public class ThrowsAdviceInterceptorTests {
|
||||
MyThrowsHandler th = new MyThrowsHandler();
|
||||
ThrowsAdviceInterceptor ti = new ThrowsAdviceInterceptor(th);
|
||||
FileNotFoundException ex = new FileNotFoundException();
|
||||
MethodInvocation mi = mock(MethodInvocation.class);
|
||||
MethodInvocation mi = mock();
|
||||
given(mi.getMethod()).willReturn(Object.class.getMethod("hashCode"));
|
||||
given(mi.getThis()).willReturn(new Object());
|
||||
given(mi.proceed()).willThrow(ex);
|
||||
@@ -90,7 +90,7 @@ public class ThrowsAdviceInterceptorTests {
|
||||
ThrowsAdviceInterceptor ti = new ThrowsAdviceInterceptor(th);
|
||||
// Extends RemoteException
|
||||
ConnectException ex = new ConnectException("");
|
||||
MethodInvocation mi = mock(MethodInvocation.class);
|
||||
MethodInvocation mi = mock();
|
||||
given(mi.proceed()).willThrow(ex);
|
||||
assertThatExceptionOfType(ConnectException.class).isThrownBy(() ->
|
||||
ti.invoke(mi))
|
||||
@@ -115,7 +115,7 @@ public class ThrowsAdviceInterceptorTests {
|
||||
ThrowsAdviceInterceptor ti = new ThrowsAdviceInterceptor(th);
|
||||
// Extends RemoteException
|
||||
ConnectException ex = new ConnectException("");
|
||||
MethodInvocation mi = mock(MethodInvocation.class);
|
||||
MethodInvocation mi = mock();
|
||||
given(mi.proceed()).willThrow(ex);
|
||||
assertThatExceptionOfType(Throwable.class).isThrownBy(() ->
|
||||
ti.invoke(mi))
|
||||
|
||||
+7
-10
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* 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.
|
||||
@@ -94,12 +94,11 @@ public class CustomizableTraceInterceptorTests {
|
||||
|
||||
@Test
|
||||
public void testSunnyDayPathLogsCorrectly() throws Throwable {
|
||||
|
||||
MethodInvocation methodInvocation = mock(MethodInvocation.class);
|
||||
MethodInvocation methodInvocation = mock();
|
||||
given(methodInvocation.getMethod()).willReturn(String.class.getMethod("toString"));
|
||||
given(methodInvocation.getThis()).willReturn(this);
|
||||
|
||||
Log log = mock(Log.class);
|
||||
Log log = mock();
|
||||
given(log.isTraceEnabled()).willReturn(true);
|
||||
|
||||
CustomizableTraceInterceptor interceptor = new StubCustomizableTraceInterceptor(log);
|
||||
@@ -110,15 +109,14 @@ public class CustomizableTraceInterceptorTests {
|
||||
|
||||
@Test
|
||||
public void testExceptionPathLogsCorrectly() throws Throwable {
|
||||
|
||||
MethodInvocation methodInvocation = mock(MethodInvocation.class);
|
||||
MethodInvocation methodInvocation = mock();
|
||||
|
||||
IllegalArgumentException exception = new IllegalArgumentException();
|
||||
given(methodInvocation.getMethod()).willReturn(String.class.getMethod("toString"));
|
||||
given(methodInvocation.getThis()).willReturn(this);
|
||||
given(methodInvocation.proceed()).willThrow(exception);
|
||||
|
||||
Log log = mock(Log.class);
|
||||
Log log = mock();
|
||||
given(log.isTraceEnabled()).willReturn(true);
|
||||
|
||||
CustomizableTraceInterceptor interceptor = new StubCustomizableTraceInterceptor(log);
|
||||
@@ -131,15 +129,14 @@ public class CustomizableTraceInterceptorTests {
|
||||
|
||||
@Test
|
||||
public void testSunnyDayPathLogsCorrectlyWithPrettyMuchAllPlaceholdersMatching() throws Throwable {
|
||||
|
||||
MethodInvocation methodInvocation = mock(MethodInvocation.class);
|
||||
MethodInvocation methodInvocation = mock();
|
||||
|
||||
given(methodInvocation.getMethod()).willReturn(String.class.getMethod("toString", new Class[0]));
|
||||
given(methodInvocation.getThis()).willReturn(this);
|
||||
given(methodInvocation.getArguments()).willReturn(new Object[]{"$ One \\$", 2L});
|
||||
given(methodInvocation.proceed()).willReturn("Hello!");
|
||||
|
||||
Log log = mock(Log.class);
|
||||
Log log = mock();
|
||||
given(log.isTraceEnabled()).willReturn(true);
|
||||
|
||||
CustomizableTraceInterceptor interceptor = new StubCustomizableTraceInterceptor(log);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user