mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 73a1f4f82e |
@@ -1,17 +0,0 @@
|
||||
name: Print JVM thread dumps
|
||||
description: Prints a thread dump for all running JVMs
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- if: ${{ runner.os == 'Linux' }}
|
||||
shell: bash
|
||||
run: |
|
||||
for jvm_pid in $(jps -q -J-XX:+PerfDisableSharedMem); do
|
||||
jcmd $java_pid Thread.print
|
||||
done
|
||||
- if: ${{ runner.os == 'Windows' }}
|
||||
shell: powershell
|
||||
run: |
|
||||
foreach ($jvm_pid in $(jps -q -J-XX:+PerfDisableSharedMem)) {
|
||||
jcmd $jvm_pid Thread.print
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
name: Send notification
|
||||
description: Sends a Google Chat message as a notification of the job's outcome
|
||||
inputs:
|
||||
webhook-url:
|
||||
description: 'Google Chat Webhook URL'
|
||||
required: true
|
||||
status:
|
||||
description: 'Status of the job'
|
||||
required: true
|
||||
build-scan-url:
|
||||
description: 'URL of the build scan to include in the notification'
|
||||
run-name:
|
||||
description: 'Name of the run to include in the notification'
|
||||
default: ${{ format('{0} {1}', github.ref_name, github.job) }}
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- shell: bash
|
||||
run: |
|
||||
echo "BUILD_SCAN=${{ inputs.build-scan-url == '' && ' [build scan unavailable]' || format(' [<{0}|Build Scan>]', inputs.build-scan-url) }}" >> "$GITHUB_ENV"
|
||||
echo "RUN_URL=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" >> "$GITHUB_ENV"
|
||||
- shell: bash
|
||||
if: ${{ inputs.status == 'success' }}
|
||||
run: |
|
||||
curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: "<${{ env.RUN_URL }}|${{ inputs.run-name }}> was successful ${{ env.BUILD_SCAN }}"}' || true
|
||||
- shell: bash
|
||||
if: ${{ inputs.status == 'failure' }}
|
||||
run: |
|
||||
curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: "<users/all> *<${{ env.RUN_URL }}|${{ inputs.run-name }}> failed* ${{ env.BUILD_SCAN }}"}' || true
|
||||
- shell: bash
|
||||
if: ${{ inputs.status == 'cancelled' }}
|
||||
run: |
|
||||
curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: "<${{ env.RUN_URL }}|${{ inputs.run-name }}> was cancelled"}' || true
|
||||
@@ -18,13 +18,11 @@ jobs:
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up Java
|
||||
uses: actions/setup-java@v4
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'liberica'
|
||||
java-version: 17
|
||||
distribution: 'temurin'
|
||||
java-version: '17'
|
||||
- name: Download BackportBot
|
||||
run: wget https://github.com/spring-io/backport-bot/releases/download/latest/backport-bot-0.0.1-SNAPSHOT.jar
|
||||
- name: Backport
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
name: Build and deploy snapshot
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
jobs:
|
||||
build-and-deploy-snapshot:
|
||||
if: ${{ github.repository == 'spring-projects/spring-framework' }}
|
||||
name: Build and deploy snapshot
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'liberica'
|
||||
java-version: 17
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up Gradle
|
||||
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
|
||||
with:
|
||||
cache-read-only: false
|
||||
- name: Configure Gradle properties
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p $HOME/.gradle
|
||||
echo 'systemProp.user.name=spring-builds+github' >> $HOME/.gradle/gradle.properties
|
||||
echo 'systemProp.org.gradle.internal.launcher.welcomeMessageEnabled=false' >> $HOME/.gradle/gradle.properties
|
||||
echo 'org.gradle.daemon=false' >> $HOME/.gradle/gradle.properties
|
||||
echo 'org.gradle.daemon=4' >> $HOME/.gradle/gradle.properties
|
||||
- name: Build and publish
|
||||
id: build
|
||||
env:
|
||||
CI: 'true'
|
||||
GRADLE_ENTERPRISE_URL: 'https://ge.spring.io'
|
||||
DEVELOCITY_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
|
||||
run: ./gradlew -PdeploymentRepository=$(pwd)/deployment-repository build publishAllPublicationsToDeploymentRepository
|
||||
- name: Deploy
|
||||
uses: spring-io/artifactory-deploy-action@v0.0.1
|
||||
with:
|
||||
uri: 'https://repo.spring.io'
|
||||
username: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||
password: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
build-name: ${{ format('spring-framework-{0}', github.ref_name)}}
|
||||
repository: 'libs-snapshot-local'
|
||||
folder: 'deployment-repository'
|
||||
signing-key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
signing-passphrase: ${{ secrets.GPG_PASSPHRASE }}
|
||||
artifact-properties: |
|
||||
/**/framework-api-*.zip::zip.name=spring-framework,zip.deployed=false
|
||||
/**/framework-api-*-docs.zip::zip.type=docs
|
||||
/**/framework-api-*-schema.zip::zip.type=schema
|
||||
- name: Send notification
|
||||
uses: ./.github/actions/send-notification
|
||||
if: always()
|
||||
with:
|
||||
webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
|
||||
status: ${{ job.status }}
|
||||
build-scan-url: ${{ steps.build.outputs.build-scan-url }}
|
||||
run-name: ${{ format('{0} | Linux | Java 17', github.ref_name) }}
|
||||
@@ -1,43 +0,0 @@
|
||||
name: Build Pull Request
|
||||
on: pull_request
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build pull request
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository == 'spring-projects/spring-framework' }}
|
||||
steps:
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'liberica'
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Validate Gradle wrapper
|
||||
uses: gradle/wrapper-validation-action@699bb18358f12c5b78b37bb0111d3a0e2276e0e2
|
||||
|
||||
- name: Set up Gradle
|
||||
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
|
||||
|
||||
- name: Build
|
||||
env:
|
||||
CI: 'true'
|
||||
GRADLE_ENTERPRISE_URL: 'https://ge.spring.io'
|
||||
run: ./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false --no-daemon --no-parallel --continue build
|
||||
|
||||
- name: Print JVM thread dumps when cancelled
|
||||
uses: ./.github/actions/print-jvm-thread-dumps
|
||||
if: cancelled()
|
||||
|
||||
- name: Upload build reports
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: build-reports
|
||||
path: '**/build/reports/'
|
||||
@@ -1,77 +0,0 @@
|
||||
name: CI
|
||||
on:
|
||||
schedule:
|
||||
- cron: '30 9 * * *'
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
jobs:
|
||||
ci:
|
||||
if: ${{ github.repository == 'spring-projects/spring-framework' }}
|
||||
strategy:
|
||||
matrix:
|
||||
os:
|
||||
- id: ubuntu-latest
|
||||
name: Linux
|
||||
java:
|
||||
- version: 17
|
||||
toolchain: false
|
||||
- version: 21
|
||||
toolchain: true
|
||||
exclude:
|
||||
- os:
|
||||
name: Linux
|
||||
java:
|
||||
version: 17
|
||||
name: '${{ matrix.os.name}} | Java ${{ matrix.java.version}}'
|
||||
runs-on: ${{ matrix.os.id }}
|
||||
steps:
|
||||
- name: Set up Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'liberica'
|
||||
java-version: |
|
||||
${{ matrix.java.version }}
|
||||
${{ matrix.java.toolchain && '17' || '' }}
|
||||
- name: Prepare Windows runner
|
||||
if: ${{ runner.os == 'Windows' }}
|
||||
run: |
|
||||
git config --global core.autocrlf true
|
||||
git config --global core.longPaths true
|
||||
Stop-Service -name Docker
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up Gradle
|
||||
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
|
||||
with:
|
||||
cache-read-only: false
|
||||
- name: Configure Gradle properties
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p $HOME/.gradle
|
||||
echo 'systemProp.user.name=spring-builds+github' >> $HOME/.gradle/gradle.properties
|
||||
echo 'systemProp.org.gradle.internal.launcher.welcomeMessageEnabled=false' >> $HOME/.gradle/gradle.properties
|
||||
echo 'org.gradle.daemon=false' >> $HOME/.gradle/gradle.properties
|
||||
echo 'org.gradle.daemon=4' >> $HOME/.gradle/gradle.properties
|
||||
- name: Configure toolchain properties
|
||||
if: ${{ matrix.java.toolchain }}
|
||||
shell: bash
|
||||
run: |
|
||||
echo toolchainVersion=${{ matrix.java.version }} >> $HOME/.gradle/gradle.properties
|
||||
echo systemProp.org.gradle.java.installations.auto-detect=false >> $HOME/.gradle/gradle.properties
|
||||
echo systemProp.org.gradle.java.installations.auto-download=false >> $HOME/.gradle/gradle.properties
|
||||
echo systemProp.org.gradle.java.installations.paths=${{ format('$JAVA_HOME_{0}_X64', matrix.java.version) }} >> $HOME/.gradle/gradle.properties
|
||||
- name: Build
|
||||
id: build
|
||||
env:
|
||||
CI: 'true'
|
||||
GRADLE_ENTERPRISE_URL: 'https://ge.spring.io'
|
||||
DEVELOCITY_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
|
||||
run: ./gradlew check antora
|
||||
- name: Send notification
|
||||
uses: ./.github/actions/send-notification
|
||||
if: always()
|
||||
with:
|
||||
webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
|
||||
status: ${{ job.status }}
|
||||
build-scan-url: ${{ steps.build.outputs.build-scan-url }}
|
||||
run-name: ${{ format('{0} | {1} | Java {2}', github.ref_name, matrix.os.name, matrix.java.version) }}
|
||||
@@ -1,14 +1,12 @@
|
||||
name: Deploy Docs
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
- '*.x'
|
||||
- '!gh-pages'
|
||||
tags:
|
||||
- 'v*'
|
||||
branches-ignore: [ gh-pages ]
|
||||
tags: '**'
|
||||
repository_dispatch:
|
||||
types: request-build-reference # legacy
|
||||
schedule:
|
||||
- cron: '0 10 * * *' # Once per day at 10am UTC
|
||||
workflow_dispatch:
|
||||
permissions:
|
||||
actions: write
|
||||
@@ -17,8 +15,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository_owner == 'spring-projects'
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: docs-build
|
||||
fetch-depth: 1
|
||||
|
||||
@@ -9,5 +9,5 @@ jobs:
|
||||
name: "Validation"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: gradle/wrapper-validation-action@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: gradle/wrapper-validation-action@v1
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
Juergen Hoeller <jhoeller@vmware.com>
|
||||
Juergen Hoeller <jhoeller@vmware.com> <jhoeller@pivotal.io>
|
||||
Juergen Hoeller <jhoeller@vmware.com> <jhoeller@gopivotal.com>
|
||||
Rossen Stoyanchev <rstoyanchev@vmware.com>
|
||||
Rossen Stoyanchev <rstoyanchev@vmware.com> <rstoyanchev@pivotal.io>
|
||||
Rossen Stoyanchev <rstoyanchev@vmware.com> <rstoyanchev@gopivotal.com>
|
||||
Phillip Webb <pwebb@vmware.com>
|
||||
Phillip Webb <pwebb@vmware.com> <pwebb@pivotal.io>
|
||||
Phillip Webb <pwebb@vmware.com> <pwebb@gopivotal.com>
|
||||
Chris Beams <cbeams@vmware.com>
|
||||
Chris Beams <cbeams@vmware.com> <cbeams@pivotal.io>
|
||||
Chris Beams <cbeams@vmware.com> <cbeams@gopivotal.com>
|
||||
Arjen Poutsma <poutsmaa@vmware.com>
|
||||
Arjen Poutsma <poutsmaa@vmware.com> <apoutsma@pivotal.io>
|
||||
Arjen Poutsma <poutsmaa@vmware.com> <apoutsma@gopivotal.com>
|
||||
Arjen Poutsma <poutsmaa@vmware.com> <poutsma@mac.com>
|
||||
Arjen Poutsma <poutsmaa@vmware.com> <apoutsma@vmware.com>
|
||||
Oliver Drotbohm <odrotbohm@vmware.com>
|
||||
Oliver Drotbohm <odrotbohm@vmware.com> <ogierke@vmware.com>
|
||||
Oliver Drotbohm <odrotbohm@vmware.com> <ogierke@pivotal.io>
|
||||
Oliver Drotbohm <odrotbohm@vmware.com> <ogierke@gopivotal.com>
|
||||
Dave Syer <dsyer@vmware.com>
|
||||
Dave Syer <dsyer@vmware.com> <dsyer@pivotal.io>
|
||||
Dave Syer <dsyer@vmware.com> <dsyer@gopivotal.com>
|
||||
Dave Syer <dsyer@vmware.com> <david_syer@hotmail.com>
|
||||
Andy Clement <aclement@vmware.com>
|
||||
Andy Clement <aclement@vmware.com> <aclement@pivotal.io>
|
||||
Andy Clement <aclement@vmware.com> <aclement@gopivotal.com>
|
||||
Andy Clement <aclement@vmware.com> <andrew.clement@gmail.com>
|
||||
Sam Brannen <sbrannen@vmware.com>
|
||||
Sam Brannen <sbrannen@vmware.com> <sbrannen@pivotal.io>
|
||||
Sam Brannen <sbrannen@vmware.com> <sam@sambrannen.com>
|
||||
Simon Basle <sbasle@vmware.com>
|
||||
Simon Baslé <sbasle@vmware.com>
|
||||
<dmitry.katsubo@gmail.com> <dmitry.katsubo@gmai.com>
|
||||
Nick Williams <nicholas@nicholaswilliams.net>
|
||||
@@ -1,3 +1,3 @@
|
||||
# Enable auto-env through the sdkman_auto_env config
|
||||
# Add key=value pairs of SDKs to use below
|
||||
java=17.0.11-librca
|
||||
java=17.0.7-librca
|
||||
|
||||
+6
-6
@@ -123,13 +123,13 @@ define the source file coding standards we use along with some IDEA editor setti
|
||||
|
||||
### Reference Docs
|
||||
|
||||
The reference documentation is authored in [Asciidoctor](https://asciidoctor.org/) format
|
||||
using [Antora](https://docs.antora.org/antora/latest/). The source files for the documentation
|
||||
reside in the [framework-docs/modules/ROOT](framework-docs/modules/ROOT) directory. For
|
||||
trivial changes, you may be able to browse, edit source files, and submit directly from GitHub.
|
||||
The reference documentation is in the [framework-docs/src/docs/asciidoc](framework-docs/src/docs/asciidoc) directory, in
|
||||
[Asciidoctor](https://asciidoctor.org/) format. For trivial changes, you may be able to browse,
|
||||
edit source files, and submit directly from GitHub.
|
||||
|
||||
When making changes locally, execute `./gradlew antora` and then browse the results under
|
||||
`framework-docs/build/site/index.html`.
|
||||
When making changes locally, execute `./gradlew :framework-docs:asciidoctor` and then browse the result under
|
||||
`framework-docs/build/docs/ref-docs/html5/index.html`.
|
||||
|
||||
Asciidoctor also supports live editing. For more details see
|
||||
[AsciiDoc Tooling](https://docs.asciidoctor.org/asciidoctor/latest/tooling/).
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# <img src="framework-docs/src/docs/spring-framework.png" width="80" height="80"> Spring Framework [](https://github.com/spring-projects/spring-framework/actions/workflows/build-and-deploy-snapshot.yml?query=branch%3Amain) [](https://ge.spring.io/scans?search.rootProjectNames=spring)
|
||||
# <img src="framework-docs/src/docs/spring-framework.png" width="80" height="80"> Spring Framework [](https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-6.0.x?groups=Build") [](https://ge.spring.io/scans?search.rootProjectNames=spring)
|
||||
|
||||
This is the home of the Spring Framework: the foundation for all [Spring projects](https://spring.io/projects). Collectively the Spring Framework and the family of Spring projects are often referred to simply as "Spring".
|
||||
|
||||
Spring provides everything required beyond the Java programming language for creating enterprise applications for a wide range of scenarios and architectures. Please read the [Overview](https://docs.spring.io/spring-framework/reference/overview.html) section of the reference documentation for a more complete introduction.
|
||||
Spring provides everything required beyond the Java programming language for creating enterprise applications for a wide range of scenarios and architectures. Please read the [Overview](https://docs.spring.io/spring/docs/current/spring-framework-reference/overview.html#spring-introduction) section as reference for a more complete introduction.
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
@@ -14,7 +14,7 @@ For access to artifacts or a distribution zip, see the [Spring Framework Artifac
|
||||
|
||||
## Documentation
|
||||
|
||||
The Spring Framework maintains reference documentation ([published](https://docs.spring.io/spring-framework/reference/) and [source](framework-docs/modules/ROOT)), GitHub [wiki pages](https://github.com/spring-projects/spring-framework/wiki), and an
|
||||
The Spring Framework maintains reference documentation ([published](https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/) and [source](framework-docs/src/docs/asciidoc)), GitHub [wiki pages](https://github.com/spring-projects/spring-framework/wiki), and an
|
||||
[API reference](https://docs.spring.io/spring-framework/docs/current/javadoc-api/). There are also [guides and tutorials](https://spring.io/guides) across Spring projects.
|
||||
|
||||
## Micro-Benchmarks
|
||||
@@ -31,7 +31,7 @@ Information regarding CI builds can be found in the [Spring Framework Concourse
|
||||
|
||||
## Stay in Touch
|
||||
|
||||
Follow [@SpringCentral](https://twitter.com/springcentral), [@SpringFramework](https://twitter.com/springframework), and its [team members](https://twitter.com/springframework/lists/team/members) on 𝕏. In-depth articles can be found at [The Spring Blog](https://spring.io/blog/), and releases are announced via our [releases feed](https://spring.io/blog/category/releases).
|
||||
Follow [@SpringCentral](https://twitter.com/springcentral), [@SpringFramework](https://twitter.com/springframework), and its [team members](https://twitter.com/springframework/lists/team/members) on Twitter. In-depth articles can be found at [The Spring Blog](https://spring.io/blog/), and releases are announced via our [news feed](https://spring.io/blog/category/news).
|
||||
|
||||
## License
|
||||
|
||||
|
||||
+80
-29
@@ -1,27 +1,22 @@
|
||||
plugins {
|
||||
id 'io.freefair.aspectj' version '8.4' apply false
|
||||
id 'io.spring.nohttp' version '0.0.11'
|
||||
id 'io.freefair.aspectj' version '8.0.1' apply false
|
||||
// kotlinVersion is managed in gradle.properties
|
||||
id 'org.jetbrains.kotlin.plugin.serialization' version "${kotlinVersion}" apply false
|
||||
id 'org.jetbrains.dokka' version '1.9.20'
|
||||
id 'org.jetbrains.dokka' version '1.8.10'
|
||||
id 'org.unbroken-dome.xjc' version '2.0.0' apply false
|
||||
id 'com.github.ben-manes.versions' version '0.51.0'
|
||||
id 'com.github.ben-manes.versions' version '0.46.0'
|
||||
id 'com.github.johnrengelman.shadow' version '8.1.1' apply false
|
||||
id 'de.undercouch.download' version '5.4.0'
|
||||
id 'me.champeau.jmh' version '0.7.2' apply false
|
||||
id 'me.champeau.mrjar' version '0.1.1'
|
||||
id "net.ltgt.errorprone" version "3.1.0" apply false
|
||||
id 'me.champeau.jmh' version '0.7.1' apply false
|
||||
}
|
||||
|
||||
ext {
|
||||
moduleProjects = subprojects.findAll { it.name.startsWith("spring-") }
|
||||
javaProjects = subprojects.findAll { !it.name.startsWith("framework-") }
|
||||
javaProjects = subprojects - project(":framework-bom") - project(":framework-platform")
|
||||
}
|
||||
|
||||
description = "Spring Framework"
|
||||
|
||||
configure(allprojects) { project ->
|
||||
apply plugin: "org.springframework.build.localdev"
|
||||
group = "org.springframework"
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven {
|
||||
@@ -46,7 +41,16 @@ configure(allprojects) { project ->
|
||||
}
|
||||
}
|
||||
|
||||
configure(allprojects - project(":framework-platform")) {
|
||||
configure([rootProject] + javaProjects) { project ->
|
||||
group = "org.springframework"
|
||||
|
||||
apply plugin: "java"
|
||||
apply plugin: "java-test-fixtures"
|
||||
apply plugin: "checkstyle"
|
||||
apply plugin: 'org.springframework.build.conventions'
|
||||
apply from: "${rootDir}/gradle/toolchains.gradle"
|
||||
apply from: "${rootDir}/gradle/ide.gradle"
|
||||
|
||||
configurations {
|
||||
dependencyManagement {
|
||||
canBeConsumed = false
|
||||
@@ -55,19 +59,36 @@ configure(allprojects - project(":framework-platform")) {
|
||||
}
|
||||
matching { it.name.endsWith("Classpath") }.all { it.extendsFrom(dependencyManagement) }
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
include(["**/*Tests.class", "**/*Test.class"])
|
||||
systemProperty("java.awt.headless", "true")
|
||||
systemProperty("testGroups", project.properties.get("testGroups"))
|
||||
systemProperty("io.netty.leakDetection.level", "paranoid")
|
||||
systemProperty("io.netty5.leakDetectionLevel", "paranoid")
|
||||
systemProperty("io.netty5.leakDetection.targetRecords", "32")
|
||||
systemProperty("io.netty5.buffer.lifecycleTracingEnabled", "true")
|
||||
systemProperty("io.netty5.buffer.leakDetectionEnabled", "true")
|
||||
jvmArgs(["--add-opens=java.base/java.lang=ALL-UNNAMED",
|
||||
"--add-opens=java.base/java.util=ALL-UNNAMED"])
|
||||
}
|
||||
|
||||
checkstyle {
|
||||
toolVersion = "10.12.3"
|
||||
configDirectory.set(rootProject.file("src/checkstyle"))
|
||||
}
|
||||
|
||||
tasks.named("checkstyleMain").configure {
|
||||
maxHeapSize = "1g"
|
||||
}
|
||||
|
||||
tasks.named("checkstyleTest").configure {
|
||||
maxHeapSize = "1g"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
dependencyManagement(enforcedPlatform(dependencies.project(path: ":framework-platform")))
|
||||
}
|
||||
}
|
||||
|
||||
configure([rootProject] + javaProjects) { project ->
|
||||
apply plugin: "java"
|
||||
apply plugin: "java-test-fixtures"
|
||||
apply plugin: 'org.springframework.build.conventions'
|
||||
apply from: "${rootDir}/gradle/toolchains.gradle"
|
||||
apply from: "${rootDir}/gradle/ide.gradle"
|
||||
|
||||
dependencies {
|
||||
testImplementation("org.junit.jupiter:junit-jupiter-api")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter-params")
|
||||
testImplementation("org.junit.platform:junit-platform-suite-api")
|
||||
@@ -85,36 +106,66 @@ configure([rootProject] + javaProjects) { project ->
|
||||
// JSR-305 only used for non-required meta-annotations
|
||||
compileOnly("com.google.code.findbugs:jsr305")
|
||||
testCompileOnly("com.google.code.findbugs:jsr305")
|
||||
checkstyle("io.spring.javaformat:spring-javaformat-checkstyle:0.0.39")
|
||||
}
|
||||
|
||||
ext.javadocLinks = [
|
||||
"https://docs.oracle.com/en/java/javase/17/docs/api/",
|
||||
"https://jakarta.ee/specifications/platform/9/apidocs/",
|
||||
"https://docs.oracle.com/cd/E13222_01/wls/docs90/javadocs/", // CommonJ and weblogic.* packages
|
||||
"https://www.ibm.com/docs/api/v1/content/SSEQTP_8.5.5/com.ibm.websphere.javadoc.doc/web/apidocs/", // com.ibm.*
|
||||
"https://docs.jboss.org/jbossas/javadoc/4.0.5/connector/", // org.jboss.resource.*
|
||||
"https://docs.jboss.org/hibernate/orm/5.6/javadocs/",
|
||||
"https://eclipse.dev/aspectj/doc/released/aspectj5rt-api",
|
||||
"https://www.quartz-scheduler.org/api/2.3.0/",
|
||||
"https://fasterxml.github.io/jackson-core/javadoc/2.14/",
|
||||
"https://fasterxml.github.io/jackson-databind/javadoc/2.14/",
|
||||
"https://fasterxml.github.io/jackson-dataformat-xml/javadoc/2.14/",
|
||||
"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 execute Gradle with Java 18+.
|
||||
// See https://github.com/spring-projects/spring-framework/issues/27497
|
||||
//
|
||||
// "https://junit.org/junit5/docs/5.10.2/api/",
|
||||
// "https://junit.org/junit5/docs/5.9.3/api/",
|
||||
"https://www.reactive-streams.org/reactive-streams-1.0.3-javadoc/",
|
||||
//"https://javadoc.io/static/io.rsocket/rsocket-core/1.1.1/",
|
||||
"https://javadoc.io/static/io.rsocket/rsocket-core/1.1.1/",
|
||||
"https://r2dbc.io/spec/1.0.0.RELEASE/api/",
|
||||
// Previously there could be a split-package issue between JSR250 and JSR305 javax.annotation packages,
|
||||
// but since 6.0 JSR 250 annotations such as @Resource and @PostConstruct have been replaced by their
|
||||
// JakartaEE equivalents in the jakarta.annotation package.
|
||||
//"https://www.javadoc.io/doc/com.google.code.findbugs/jsr305/3.0.2/"
|
||||
"https://www.javadoc.io/doc/com.google.code.findbugs/jsr305/3.0.2/"
|
||||
] as String[]
|
||||
}
|
||||
|
||||
configure(moduleProjects) { project ->
|
||||
apply from: "${rootDir}/gradle/spring-module.gradle"
|
||||
}
|
||||
|
||||
configure(rootProject) {
|
||||
description = "Spring Framework"
|
||||
|
||||
apply plugin: "io.spring.nohttp"
|
||||
apply plugin: 'org.springframework.build.api-diff'
|
||||
|
||||
nohttp {
|
||||
source.exclude "**/test-output/**"
|
||||
source.exclude "**/.gradle/**"
|
||||
allowlistFile = project.file("src/nohttp/allowlist.lines")
|
||||
def rootPath = file(rootDir).toPath()
|
||||
def projectDirs = allprojects.collect { it.projectDir } + "${rootDir}/buildSrc"
|
||||
projectDirs.forEach { dir ->
|
||||
[ 'bin', 'build', 'out', '.settings' ]
|
||||
.collect { rootPath.relativize(new File(dir, it).toPath()) }
|
||||
.forEach { source.exclude "$it/**" }
|
||||
[ '.classpath', '.project' ]
|
||||
.collect { rootPath.relativize(new File(dir, it).toPath()) }
|
||||
.forEach { source.exclude "$it" }
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named("checkstyleNohttp").configure {
|
||||
maxHeapSize = "1g"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,6 +22,21 @@ but doesn't affect the classpath of dependent projects.
|
||||
This plugin does not provide a `provided` configuration, as the native `compileOnly` and `testCompileOnly`
|
||||
configurations are preferred.
|
||||
|
||||
### API Diff
|
||||
|
||||
This plugin uses the [Gradle JApiCmp](https://github.com/melix/japicmp-gradle-plugin) plugin
|
||||
to generate API Diff reports for each Spring Framework module. This plugin is applied once on the root
|
||||
project and creates tasks in each framework module. Unlike previous versions of this part of the build,
|
||||
there is no need for checking out a specific tag. The plugin will fetch the JARs we want to compare the
|
||||
current working version with. You can generate the reports for all modules or a single module:
|
||||
|
||||
```
|
||||
./gradlew apiDiff -PbaselineVersion=5.1.0.RELEASE
|
||||
./gradlew :spring-core:apiDiff -PbaselineVersion=5.1.0.RELEASE
|
||||
```
|
||||
|
||||
The reports are located under `build/reports/api-diff/$OLDVERSION_to_$NEWVERSION/`.
|
||||
|
||||
|
||||
### RuntimeHints Java Agent
|
||||
|
||||
|
||||
+8
-11
@@ -1,6 +1,5 @@
|
||||
plugins {
|
||||
id 'java-gradle-plugin'
|
||||
id 'checkstyle'
|
||||
}
|
||||
|
||||
repositories {
|
||||
@@ -18,24 +17,22 @@ ext {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
checkstyle "io.spring.javaformat:spring-javaformat-checkstyle:${javaFormatVersion}"
|
||||
implementation "org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}"
|
||||
implementation "org.jetbrains.kotlin:kotlin-compiler-embeddable:${kotlinVersion}"
|
||||
implementation "org.gradle:test-retry-gradle-plugin:1.5.6"
|
||||
implementation "io.spring.javaformat:spring-javaformat-gradle-plugin:${javaFormatVersion}"
|
||||
implementation "io.spring.nohttp:nohttp-gradle:0.0.11"
|
||||
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}")
|
||||
implementation("org.jetbrains.kotlin:kotlin-compiler-embeddable:${kotlinVersion}")
|
||||
implementation "me.champeau.gradle:japicmp-gradle-plugin:0.3.0"
|
||||
implementation "org.gradle:test-retry-gradle-plugin:1.4.1"
|
||||
}
|
||||
|
||||
gradlePlugin {
|
||||
plugins {
|
||||
apiDiffPlugin {
|
||||
id = "org.springframework.build.api-diff"
|
||||
implementationClass = "org.springframework.build.api.ApiDiffPlugin"
|
||||
}
|
||||
conventionsPlugin {
|
||||
id = "org.springframework.build.conventions"
|
||||
implementationClass = "org.springframework.build.ConventionsPlugin"
|
||||
}
|
||||
localDevPlugin {
|
||||
id = "org.springframework.build.localdev"
|
||||
implementationClass = "org.springframework.build.dev.LocalDevelopmentPlugin"
|
||||
}
|
||||
optionalDependenciesPlugin {
|
||||
id = "org.springframework.build.optional-dependencies"
|
||||
implementationClass = "org.springframework.build.optional.OptionalDependenciesPlugin"
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE module PUBLIC "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN" "https://checkstyle.org/dtds/configuration_1_3.dtd">
|
||||
<module name="com.puppycrawl.tools.checkstyle.Checker">
|
||||
|
||||
<!-- Root Checks -->
|
||||
<module name="io.spring.javaformat.checkstyle.check.SpringHeaderCheck">
|
||||
<property name="fileExtensions" value="java"/>
|
||||
<property name="headerType" value="apache2"/>
|
||||
<property name="headerCopyrightPattern" value="20\d\d-20\d\d"/>
|
||||
<property name="packageInfoHeaderType" value="none"/>
|
||||
</module>
|
||||
<module name="com.puppycrawl.tools.checkstyle.checks.NewlineAtEndOfFileCheck"/>
|
||||
|
||||
<!-- TreeWalker Checks -->
|
||||
<module name="com.puppycrawl.tools.checkstyle.TreeWalker">
|
||||
|
||||
<!-- Imports -->
|
||||
<module name="com.puppycrawl.tools.checkstyle.checks.imports.UnusedImportsCheck">
|
||||
<property name="processJavadoc" value="true"/>
|
||||
</module>
|
||||
|
||||
<!-- Modifiers -->
|
||||
<module name="com.puppycrawl.tools.checkstyle.checks.modifier.ModifierOrderCheck"/>
|
||||
|
||||
</module>
|
||||
|
||||
</module>
|
||||
@@ -1,2 +1 @@
|
||||
org.gradle.caching=true
|
||||
javaFormatVersion=0.0.41
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.build;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
import io.spring.javaformat.gradle.SpringJavaFormatPlugin;
|
||||
import io.spring.nohttp.gradle.NoHttpExtension;
|
||||
import io.spring.nohttp.gradle.NoHttpPlugin;
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.DependencySet;
|
||||
import org.gradle.api.plugins.JavaBasePlugin;
|
||||
import org.gradle.api.plugins.quality.Checkstyle;
|
||||
import org.gradle.api.plugins.quality.CheckstyleExtension;
|
||||
import org.gradle.api.plugins.quality.CheckstylePlugin;
|
||||
|
||||
/**
|
||||
* {@link Plugin} that applies conventions for checkstyle.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
public class CheckstyleConventions {
|
||||
|
||||
/**
|
||||
* Applies the Spring Java Format and Checkstyle plugins with the project conventions.
|
||||
* @param project the current project
|
||||
*/
|
||||
public void apply(Project project) {
|
||||
project.getPlugins().withType(JavaBasePlugin.class, (java) -> {
|
||||
if (project.getRootProject() == project) {
|
||||
configureNoHttpPlugin(project);
|
||||
}
|
||||
project.getPlugins().apply(CheckstylePlugin.class);
|
||||
project.getTasks().withType(Checkstyle.class).forEach(checkstyle -> checkstyle.getMaxHeapSize().set("1g"));
|
||||
CheckstyleExtension checkstyle = project.getExtensions().getByType(CheckstyleExtension.class);
|
||||
checkstyle.setToolVersion("10.16.0");
|
||||
checkstyle.getConfigDirectory().set(project.getRootProject().file("src/checkstyle"));
|
||||
String version = SpringJavaFormatPlugin.class.getPackage().getImplementationVersion();
|
||||
DependencySet checkstyleDependencies = project.getConfigurations().getByName("checkstyle").getDependencies();
|
||||
checkstyleDependencies.add(
|
||||
project.getDependencies().create("io.spring.javaformat:spring-javaformat-checkstyle:" + version));
|
||||
});
|
||||
}
|
||||
|
||||
private static void configureNoHttpPlugin(Project project) {
|
||||
project.getPlugins().apply(NoHttpPlugin.class);
|
||||
NoHttpExtension noHttp = project.getExtensions().getByType(NoHttpExtension.class);
|
||||
noHttp.setAllowlistFile(project.file("src/nohttp/allowlist.lines"));
|
||||
noHttp.getSource().exclude("**/test-output/**", "**/.settings/**",
|
||||
"**/.classpath", "**/.project", "**/.gradle/**");
|
||||
List<String> buildFolders = List.of("bin", "build", "out");
|
||||
project.allprojects(subproject -> {
|
||||
Path rootPath = project.getRootDir().toPath();
|
||||
Path projectPath = rootPath.relativize(subproject.getProjectDir().toPath());
|
||||
for (String buildFolder : buildFolders) {
|
||||
Path innerBuildDir = projectPath.resolve(buildFolder);
|
||||
noHttp.getSource().exclude(innerBuildDir + File.separator + "**");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -25,8 +25,10 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinBasePlugin;
|
||||
* Plugin to apply conventions to projects that are part of Spring Framework's build.
|
||||
* Conventions are applied in response to various plugins being applied.
|
||||
*
|
||||
* <p>When the {@link JavaBasePlugin} is applied, the conventions in {@link CheckstyleConventions},
|
||||
* {@link TestConventions} and {@link JavaConventions} are applied.
|
||||
* When the {@link JavaBasePlugin} is applied, the conventions in {@link TestConventions}
|
||||
* are applied.
|
||||
* When the {@link JavaBasePlugin} is applied, the conventions in {@link JavaConventions}
|
||||
* are applied.
|
||||
* When the {@link KotlinBasePlugin} is applied, the conventions in {@link KotlinConventions}
|
||||
* are applied.
|
||||
*
|
||||
@@ -36,10 +38,8 @@ public class ConventionsPlugin implements Plugin<Project> {
|
||||
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
new CheckstyleConventions().apply(project);
|
||||
new JavaConventions().apply(project);
|
||||
new KotlinConventions().apply(project);
|
||||
new TestConventions().apply(project);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -24,10 +24,7 @@ import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.plugins.JavaBasePlugin;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
import org.gradle.api.plugins.JavaPluginExtension;
|
||||
import org.gradle.api.tasks.compile.JavaCompile;
|
||||
import org.gradle.jvm.toolchain.JavaLanguageVersion;
|
||||
import org.gradle.jvm.toolchain.JvmVendorSpec;
|
||||
|
||||
/**
|
||||
* {@link Plugin} that applies conventions for compiling Java sources in Spring Framework.
|
||||
@@ -71,10 +68,6 @@ public class JavaConventions {
|
||||
* @param project the current project
|
||||
*/
|
||||
private void applyJavaCompileConventions(Project project) {
|
||||
project.getExtensions().getByType(JavaPluginExtension.class).toolchain(toolchain -> {
|
||||
toolchain.getVendor().set(JvmVendorSpec.BELLSOFT);
|
||||
toolchain.getLanguageVersion().set(JavaLanguageVersion.of(17));
|
||||
});
|
||||
project.getTasks().withType(JavaCompile.class)
|
||||
.matching(compileTask -> compileTask.getName().equals(JavaPlugin.COMPILE_JAVA_TASK_NAME))
|
||||
.forEach(compileTask -> {
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package org.springframework.build;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.plugins.JavaBasePlugin;
|
||||
import org.gradle.api.tasks.testing.Test;
|
||||
@@ -43,36 +41,11 @@ class TestConventions {
|
||||
|
||||
private void configureTestConventions(Project project) {
|
||||
project.getTasks().withType(Test.class,
|
||||
test -> {
|
||||
configureTests(project, test);
|
||||
configureTestRetryPlugin(project, test);
|
||||
});
|
||||
}
|
||||
|
||||
private void configureTests(Project project, Test test) {
|
||||
test.useJUnitPlatform();
|
||||
test.include("**/*Tests.class", "**/*Test.class");
|
||||
test.setSystemProperties(Map.of(
|
||||
"java.awt.headless", "true",
|
||||
"io.netty.leakDetection.level", "paranoid",
|
||||
"io.netty5.leakDetectionLevel", "paranoid",
|
||||
"io.netty5.leakDetection.targetRecords", "32",
|
||||
"io.netty5.buffer.lifecycleTracingEnabled", "true"
|
||||
));
|
||||
if (project.hasProperty("testGroups")) {
|
||||
test.systemProperty("testGroups", project.getProperties().get("testGroups"));
|
||||
}
|
||||
test.jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED",
|
||||
"--add-opens=java.base/java.util=ALL-UNNAMED",
|
||||
"-Djava.locale.providers=COMPAT");
|
||||
}
|
||||
|
||||
private void configureTestRetryPlugin(Project project, Test test) {
|
||||
project.getPlugins().withType(TestRetryPlugin.class, testRetryPlugin -> {
|
||||
TestRetryTaskExtension testRetry = test.getExtensions().getByType(TestRetryTaskExtension.class);
|
||||
testRetry.getFailOnPassedAfterRetry().set(true);
|
||||
testRetry.getMaxRetries().set(isCi() ? 3 : 0);
|
||||
});
|
||||
test -> project.getPlugins().withType(TestRetryPlugin.class, testRetryPlugin -> {
|
||||
TestRetryTaskExtension testRetry = test.getExtensions().getByType(TestRetryTaskExtension.class);
|
||||
testRetry.getFailOnPassedAfterRetry().set(true);
|
||||
testRetry.getMaxRetries().set(isCi() ? 3 : 0);
|
||||
}));
|
||||
}
|
||||
|
||||
private boolean isCi() {
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.build.api;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import me.champeau.gradle.japicmp.JapicmpPlugin;
|
||||
import me.champeau.gradle.japicmp.JapicmpTask;
|
||||
import org.gradle.api.GradleException;
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.artifacts.Dependency;
|
||||
import org.gradle.api.plugins.JavaBasePlugin;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
import org.gradle.api.publish.maven.plugins.MavenPublishPlugin;
|
||||
import org.gradle.api.tasks.TaskProvider;
|
||||
import org.gradle.jvm.tasks.Jar;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* {@link Plugin} that applies the {@code "japicmp-gradle-plugin"}
|
||||
* and create tasks for all subprojects named {@code "spring-*"}, diffing the public API one by one
|
||||
* and creating the reports in {@code "build/reports/api-diff/$OLDVERSION_to_$NEWVERSION/"}.
|
||||
* <p>{@code "./gradlew apiDiff -PbaselineVersion=5.1.0.RELEASE"} will output the
|
||||
* reports for the API diff between the baseline version and the current one for all modules.
|
||||
* You can limit the report to a single module with
|
||||
* {@code "./gradlew :spring-core:apiDiff -PbaselineVersion=5.1.0.RELEASE"}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
public class ApiDiffPlugin implements Plugin<Project> {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ApiDiffPlugin.class);
|
||||
|
||||
public static final String TASK_NAME = "apiDiff";
|
||||
|
||||
private static final String BASELINE_VERSION_PROPERTY = "baselineVersion";
|
||||
|
||||
private static final List<String> PACKAGE_INCLUDES = Collections.singletonList("org.springframework.*");
|
||||
|
||||
private static final URI SPRING_MILESTONE_REPOSITORY = URI.create("https://repo.spring.io/milestone");
|
||||
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
if (project.hasProperty(BASELINE_VERSION_PROPERTY) && project.equals(project.getRootProject())) {
|
||||
project.getPluginManager().apply(JapicmpPlugin.class);
|
||||
project.getPlugins().withType(JapicmpPlugin.class,
|
||||
plugin -> applyApiDiffConventions(project));
|
||||
}
|
||||
}
|
||||
|
||||
private void applyApiDiffConventions(Project project) {
|
||||
String baselineVersion = project.property(BASELINE_VERSION_PROPERTY).toString();
|
||||
project.subprojects(subProject -> {
|
||||
if (subProject.getName().startsWith("spring-")) {
|
||||
createApiDiffTask(baselineVersion, subProject);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void createApiDiffTask(String baselineVersion, Project project) {
|
||||
if (isProjectEligible(project)) {
|
||||
// Add Spring Milestone repository for generating diffs against previous milestones
|
||||
project.getRootProject()
|
||||
.getRepositories()
|
||||
.maven(mavenArtifactRepository -> mavenArtifactRepository.setUrl(SPRING_MILESTONE_REPOSITORY));
|
||||
JapicmpTask apiDiff = project.getTasks().create(TASK_NAME, JapicmpTask.class);
|
||||
apiDiff.setDescription("Generates an API diff report with japicmp");
|
||||
apiDiff.setGroup(JavaBasePlugin.DOCUMENTATION_GROUP);
|
||||
|
||||
apiDiff.setOldClasspath(createBaselineConfiguration(baselineVersion, project));
|
||||
TaskProvider<Jar> jar = project.getTasks().withType(Jar.class).named("jar");
|
||||
apiDiff.setNewArchives(project.getLayout().files(jar.get().getArchiveFile().get().getAsFile()));
|
||||
apiDiff.setNewClasspath(getRuntimeClassPath(project));
|
||||
apiDiff.setPackageIncludes(PACKAGE_INCLUDES);
|
||||
apiDiff.setOnlyModified(true);
|
||||
apiDiff.setIgnoreMissingClasses(true);
|
||||
// Ignore Kotlin metadata annotations since they contain
|
||||
// illegal HTML characters and fail the report generation
|
||||
apiDiff.setAnnotationExcludes(Collections.singletonList("@kotlin.Metadata"));
|
||||
|
||||
apiDiff.setHtmlOutputFile(getOutputFile(baselineVersion, project));
|
||||
|
||||
apiDiff.dependsOn(project.getTasks().getByName("jar"));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isProjectEligible(Project project) {
|
||||
return project.getPlugins().hasPlugin(JavaPlugin.class)
|
||||
&& project.getPlugins().hasPlugin(MavenPublishPlugin.class);
|
||||
}
|
||||
|
||||
private Configuration createBaselineConfiguration(String baselineVersion, Project project) {
|
||||
String baseline = String.join(":",
|
||||
project.getGroup().toString(), project.getName(), baselineVersion);
|
||||
Dependency baselineDependency = project.getDependencies().create(baseline + "@jar");
|
||||
Configuration baselineConfiguration = project.getRootProject().getConfigurations().detachedConfiguration(baselineDependency);
|
||||
try {
|
||||
// eagerly resolve the baseline configuration to check whether this is a new Spring module
|
||||
baselineConfiguration.resolve();
|
||||
return baselineConfiguration;
|
||||
}
|
||||
catch (GradleException exception) {
|
||||
logger.warn("Could not resolve {} - assuming this is a new Spring module.", baseline);
|
||||
}
|
||||
return project.getRootProject().getConfigurations().detachedConfiguration();
|
||||
}
|
||||
|
||||
private Configuration getRuntimeClassPath(Project project) {
|
||||
return project.getConfigurations().getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME);
|
||||
}
|
||||
|
||||
private File getOutputFile(String baseLineVersion, Project project) {
|
||||
Path outDir = Paths.get(project.getRootProject().getBuildDir().getAbsolutePath(),
|
||||
"reports", "api-diff",
|
||||
baseLineVersion + "_to_" + project.getRootProject().getVersion());
|
||||
return project.file(outDir.resolve(project.getName() + ".html").toString());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.build.dev;
|
||||
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.plugins.JavaBasePlugin;
|
||||
|
||||
/**
|
||||
* {@link Plugin} that skips documentation tasks when the {@code "-PskipDocs"} property is defined.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
public class LocalDevelopmentPlugin implements Plugin<Project> {
|
||||
|
||||
private static final String SKIP_DOCS_PROPERTY = "skipDocs";
|
||||
|
||||
@Override
|
||||
public void apply(Project target) {
|
||||
if (target.hasProperty(SKIP_DOCS_PROPERTY)) {
|
||||
skipDocumentationTasks(target);
|
||||
target.subprojects(this::skipDocumentationTasks);
|
||||
}
|
||||
}
|
||||
|
||||
private void skipDocumentationTasks(Project project) {
|
||||
project.afterEvaluate(p -> {
|
||||
p.getTasks().matching(task -> {
|
||||
return JavaBasePlugin.DOCUMENTATION_GROUP.equals(task.getGroup())
|
||||
|| "distribution".equals(task.getGroup());
|
||||
})
|
||||
.forEach(task -> task.setEnabled(false));
|
||||
});
|
||||
}
|
||||
}
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.build.hint;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.gradle.api.file.ConfigurableFileCollection;
|
||||
import org.gradle.api.provider.SetProperty;
|
||||
import org.gradle.api.tasks.Classpath;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.process.CommandLineArgumentProvider;
|
||||
|
||||
/**
|
||||
* Argument provider for registering the runtime hints agent with a Java process.
|
||||
*/
|
||||
public interface RuntimeHintsAgentArgumentProvider extends CommandLineArgumentProvider {
|
||||
|
||||
@Classpath
|
||||
ConfigurableFileCollection getAgentJar();
|
||||
|
||||
@Input
|
||||
SetProperty<String> getIncludedPackages();
|
||||
|
||||
@Input
|
||||
SetProperty<String> getExcludedPackages();
|
||||
|
||||
@Override
|
||||
default Iterable<String> asArguments() {
|
||||
StringBuilder packages = new StringBuilder();
|
||||
getIncludedPackages().get().forEach(packageName -> packages.append('+').append(packageName).append(','));
|
||||
getExcludedPackages().get().forEach(packageName -> packages.append('-').append(packageName).append(','));
|
||||
return Collections.singleton("-javaagent:" + getAgentJar().getSingleFile() + "=" + packages);
|
||||
}
|
||||
}
|
||||
+27
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,15 +16,38 @@
|
||||
|
||||
package org.springframework.build.hint;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.gradle.api.model.ObjectFactory;
|
||||
import org.gradle.api.provider.SetProperty;
|
||||
|
||||
/**
|
||||
* Entry point to the DSL extension for the {@link RuntimeHintsAgentPlugin} Gradle plugin.
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
public interface RuntimeHintsAgentExtension {
|
||||
public class RuntimeHintsAgentExtension {
|
||||
|
||||
SetProperty<String> getIncludedPackages();
|
||||
private final SetProperty<String> includedPackages;
|
||||
|
||||
SetProperty<String> getExcludedPackages();
|
||||
private final SetProperty<String> excludedPackages;
|
||||
|
||||
public RuntimeHintsAgentExtension(ObjectFactory objectFactory) {
|
||||
this.includedPackages = objectFactory.setProperty(String.class).convention(Collections.singleton("org.springframework"));
|
||||
this.excludedPackages = objectFactory.setProperty(String.class).convention(Collections.emptySet());
|
||||
}
|
||||
|
||||
public SetProperty<String> getIncludedPackages() {
|
||||
return this.includedPackages;
|
||||
}
|
||||
|
||||
public SetProperty<String> getExcludedPackages() {
|
||||
return this.excludedPackages;
|
||||
}
|
||||
|
||||
String asJavaAgentArgument() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
this.includedPackages.get().forEach(packageName -> builder.append('+').append(packageName).append(','));
|
||||
this.excludedPackages.get().forEach(packageName -> builder.append('-').append(packageName).append(','));
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,79 +16,41 @@
|
||||
|
||||
package org.springframework.build.hint;
|
||||
|
||||
import org.gradle.api.JavaVersion;
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.attributes.Bundling;
|
||||
import org.gradle.api.attributes.Category;
|
||||
import org.gradle.api.attributes.LibraryElements;
|
||||
import org.gradle.api.attributes.Usage;
|
||||
import org.gradle.api.attributes.java.TargetJvmVersion;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
import org.gradle.api.tasks.bundling.Jar;
|
||||
import org.gradle.api.tasks.testing.Test;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* {@link Plugin} that configures the {@code RuntimeHints} Java agent to test tasks.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
public class RuntimeHintsAgentPlugin implements Plugin<Project> {
|
||||
|
||||
public static final String RUNTIMEHINTS_TEST_TASK = "runtimeHintsTest";
|
||||
private static final String EXTENSION_NAME = "runtimeHintsAgent";
|
||||
private static final String CONFIGURATION_NAME = "testRuntimeHintsAgentJar";
|
||||
|
||||
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
|
||||
project.getPlugins().withType(JavaPlugin.class, javaPlugin -> {
|
||||
RuntimeHintsAgentExtension agentExtension = createRuntimeHintsAgentExtension(project);
|
||||
RuntimeHintsAgentExtension agentExtension = project.getExtensions().create(EXTENSION_NAME,
|
||||
RuntimeHintsAgentExtension.class, project.getObjects());
|
||||
Test agentTest = project.getTasks().create(RUNTIMEHINTS_TEST_TASK, Test.class, test -> {
|
||||
test.useJUnitPlatform(options -> {
|
||||
options.includeTags("RuntimeHintsTests");
|
||||
});
|
||||
test.include("**/*Tests.class", "**/*Test.class");
|
||||
test.systemProperty("java.awt.headless", "true");
|
||||
test.systemProperty("org.graalvm.nativeimage.imagecode", "runtime");
|
||||
test.getJvmArgumentProviders().add(createRuntimeHintsAgentArgumentProvider(project, agentExtension));
|
||||
});
|
||||
project.afterEvaluate(p -> {
|
||||
Jar jar = project.getRootProject().project("spring-core-test").getTasks().withType(Jar.class).named("jar").get();
|
||||
agentTest.jvmArgs("-javaagent:" + jar.getArchiveFile().get().getAsFile() + "=" + agentExtension.asJavaAgentArgument());
|
||||
});
|
||||
project.getTasks().getByName("check", task -> task.dependsOn(agentTest));
|
||||
project.getDependencies().add(CONFIGURATION_NAME, project.project(":spring-core-test"));
|
||||
});
|
||||
}
|
||||
|
||||
private static RuntimeHintsAgentExtension createRuntimeHintsAgentExtension(Project project) {
|
||||
RuntimeHintsAgentExtension agentExtension = project.getExtensions().create(EXTENSION_NAME, RuntimeHintsAgentExtension.class);
|
||||
agentExtension.getIncludedPackages().convention(Collections.singleton("org.springframework"));
|
||||
agentExtension.getExcludedPackages().convention(Collections.emptySet());
|
||||
return agentExtension;
|
||||
}
|
||||
|
||||
private static RuntimeHintsAgentArgumentProvider createRuntimeHintsAgentArgumentProvider(
|
||||
Project project, RuntimeHintsAgentExtension agentExtension) {
|
||||
RuntimeHintsAgentArgumentProvider agentArgumentProvider = project.getObjects().newInstance(RuntimeHintsAgentArgumentProvider.class);
|
||||
agentArgumentProvider.getAgentJar().from(createRuntimeHintsAgentConfiguration(project));
|
||||
agentArgumentProvider.getIncludedPackages().set(agentExtension.getIncludedPackages());
|
||||
agentArgumentProvider.getExcludedPackages().set(agentExtension.getExcludedPackages());
|
||||
return agentArgumentProvider;
|
||||
}
|
||||
|
||||
private static Configuration createRuntimeHintsAgentConfiguration(Project project) {
|
||||
return project.getConfigurations().create(CONFIGURATION_NAME, configuration -> {
|
||||
configuration.setCanBeConsumed(false);
|
||||
configuration.setTransitive(false); // Only the built artifact is required
|
||||
configuration.attributes(attributes -> {
|
||||
attributes.attribute(Bundling.BUNDLING_ATTRIBUTE, project.getObjects().named(Bundling.class, Bundling.EXTERNAL));
|
||||
attributes.attribute(Category.CATEGORY_ATTRIBUTE, project.getObjects().named(Category.class, Category.LIBRARY));
|
||||
attributes.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, project.getObjects().named(LibraryElements.class, LibraryElements.JAR));
|
||||
attributes.attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, Integer.valueOf(JavaVersion.current().getMajorVersion()));
|
||||
attributes.attribute(Usage.USAGE_ATTRIBUTE, project.getObjects().named(Usage.class, Usage.JAVA_RUNTIME));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -19,7 +19,7 @@ package org.springframework.build.optional;
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.plugins.JavaBasePlugin;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
import org.gradle.api.plugins.JavaPluginExtension;
|
||||
import org.gradle.api.tasks.SourceSetContainer;
|
||||
|
||||
@@ -40,10 +40,10 @@ public class OptionalDependenciesPlugin implements Plugin<Project> {
|
||||
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
Configuration optional = project.getConfigurations().create(OPTIONAL_CONFIGURATION_NAME);
|
||||
Configuration optional = project.getConfigurations().create("optional");
|
||||
optional.setCanBeConsumed(false);
|
||||
optional.setCanBeResolved(false);
|
||||
project.getPlugins().withType(JavaBasePlugin.class, (javaBasePlugin) -> {
|
||||
project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> {
|
||||
SourceSetContainer sourceSets = project.getExtensions().getByType(JavaPluginExtension.class)
|
||||
.getSourceSets();
|
||||
sourceSets.all((sourceSet) -> {
|
||||
@@ -53,4 +53,4 @@ public class OptionalDependenciesPlugin implements Plugin<Project> {
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,3 @@
|
||||
/*
|
||||
* 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.build.shadow;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
+2
-4
@@ -1,10 +1,8 @@
|
||||
== Spring Framework Concourse pipeline
|
||||
|
||||
NOTE: CI is being migrated to GitHub Actions.
|
||||
|
||||
The Spring Framework uses https://concourse-ci.org/[Concourse] for its CI build and other automated tasks.
|
||||
The Spring team has a dedicated Concourse instance available at https://ci.spring.io with a build pipeline
|
||||
for https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-6.2.x[Spring Framework 6.2.x].
|
||||
for https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-6.0.x[Spring Framework 6.0.x].
|
||||
|
||||
=== Setting up your development environment
|
||||
|
||||
@@ -53,7 +51,7 @@ The pipeline can be deployed using the following command:
|
||||
|
||||
[source]
|
||||
----
|
||||
$ fly -t spring set-pipeline -p spring-framework-6.2.x -c ci/pipeline.yml -l ci/parameters.yml
|
||||
$ fly -t spring set-pipeline -p spring-framework-6.0.x -c ci/pipeline.yml -l ci/parameters.yml
|
||||
----
|
||||
|
||||
NOTE: This assumes that you have credhub integration configured with the appropriate secrets.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM ubuntu:jammy-20240125
|
||||
FROM ubuntu:jammy-20230816
|
||||
|
||||
ADD setup.sh /setup.sh
|
||||
ADD get-jdk-url.sh /get-jdk-url.sh
|
||||
@@ -6,7 +6,6 @@ RUN ./setup.sh
|
||||
|
||||
ENV JAVA_HOME /opt/openjdk/java17
|
||||
ENV JDK17 /opt/openjdk/java17
|
||||
ENV JDK21 /opt/openjdk/java21
|
||||
ENV JDK23 /opt/openjdk/java23
|
||||
ENV JDK20 /opt/openjdk/java20
|
||||
|
||||
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.10%2B13/bellsoft-jdk17.0.10+13-linux-amd64.tar.gz"
|
||||
echo "https://github.com/bell-sw/Liberica/releases/download/17.0.7+7/bellsoft-jdk17.0.7+7-linux-amd64.tar.gz"
|
||||
;;
|
||||
java21)
|
||||
echo "https://github.com/bell-sw/Liberica/releases/download/21.0.2%2B14/bellsoft-jdk21.0.2+14-linux-amd64.tar.gz"
|
||||
;;
|
||||
java23)
|
||||
echo "https://download.java.net/java/early_access/jdk23/17/GPL/openjdk-23-ea+17_linux-x64_bin.tar.gz"
|
||||
java20)
|
||||
echo "https://github.com/bell-sw/Liberica/releases/download/20.0.1+10/bellsoft-jdk20.0.1+10-linux-amd64.tar.gz"
|
||||
;;
|
||||
*)
|
||||
echo $"Unknown java version"
|
||||
|
||||
+1
-1
@@ -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 java21 java23
|
||||
for jdk in java17 java20
|
||||
do
|
||||
JDK_URL=$( /get-jdk-url.sh $jdk )
|
||||
mkdir $jdk
|
||||
|
||||
+3
-2
@@ -3,8 +3,9 @@ github-repo-name: "spring-projects/spring-framework"
|
||||
sonatype-staging-profile: "org.springframework"
|
||||
docker-hub-organization: "springci"
|
||||
artifactory-server: "https://repo.spring.io"
|
||||
branch: "main"
|
||||
milestone: "6.2.x"
|
||||
branch: "6.0.x"
|
||||
milestone: "6.0.x"
|
||||
build-name: "spring-framework"
|
||||
pipeline-name: "spring-framework"
|
||||
concourse-url: "https://ci.spring.io"
|
||||
task-timeout: 1h00m
|
||||
|
||||
+138
-28
@@ -5,7 +5,9 @@ anchors:
|
||||
password: ((github-ci-release-token))
|
||||
branch: ((branch))
|
||||
gradle-enterprise-task-params: &gradle-enterprise-task-params
|
||||
DEVELOCITY_ACCESS_KEY: ((gradle_enterprise_secret_access_key))
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY: ((gradle_enterprise_secret_access_key))
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME: ((gradle_enterprise_cache_user.username))
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD: ((gradle_enterprise_cache_user.password))
|
||||
sonatype-task-params: &sonatype-task-params
|
||||
SONATYPE_USERNAME: ((sonatype-username))
|
||||
SONATYPE_PASSWORD: ((sonatype-password))
|
||||
@@ -21,6 +23,14 @@ anchors:
|
||||
docker-resource-source: &docker-resource-source
|
||||
username: ((docker-hub-username))
|
||||
password: ((docker-hub-password))
|
||||
slack-fail-params: &slack-fail-params
|
||||
text: >
|
||||
:concourse-failed: <https://ci.spring.io/teams/${BUILD_TEAM_NAME}/pipelines/${BUILD_PIPELINE_NAME}/jobs/${BUILD_JOB_NAME}/builds/${BUILD_NAME}|${BUILD_PIPELINE_NAME} ${BUILD_JOB_NAME} failed!>
|
||||
[$TEXT_FILE_CONTENT]
|
||||
text_file: git-repo/build/build-scan-uri.txt
|
||||
silent: true
|
||||
icon_emoji: ":concourse:"
|
||||
username: concourse-ci
|
||||
changelog-task-params: &changelog-task-params
|
||||
name: generated-changelog/tag
|
||||
tag: generated-changelog/tag
|
||||
@@ -35,7 +45,7 @@ resource_types:
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: concourse/registry-image-resource
|
||||
tag: 1.8.0
|
||||
tag: 1.5.0
|
||||
- name: artifactory-resource
|
||||
type: registry-image
|
||||
source:
|
||||
@@ -47,13 +57,19 @@ resource_types:
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: concourse/github-release-resource
|
||||
tag: 1.8.0
|
||||
tag: 1.5.5
|
||||
- name: github-status-resource
|
||||
type: registry-image
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: dpb587/github-status-resource
|
||||
tag: master
|
||||
- name: slack-notification
|
||||
type: registry-image
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: cfcommunity/slack-notification-resource
|
||||
tag: latest
|
||||
resources:
|
||||
- name: git-repo
|
||||
type: git
|
||||
@@ -74,6 +90,13 @@ resources:
|
||||
<<: *docker-resource-source
|
||||
repository: ((docker-hub-organization))/spring-framework-ci
|
||||
tag: ((milestone))
|
||||
- name: every-morning
|
||||
type: time
|
||||
icon: alarm
|
||||
source:
|
||||
start: 8:00 AM
|
||||
stop: 9:00 AM
|
||||
location: Europe/Vienna
|
||||
- name: artifactory-repo
|
||||
type: artifactory-resource
|
||||
icon: package-variant
|
||||
@@ -82,6 +105,27 @@ resources:
|
||||
username: ((artifactory-username))
|
||||
password: ((artifactory-password))
|
||||
build_name: ((build-name))
|
||||
- name: repo-status-build
|
||||
type: github-status-resource
|
||||
icon: eye-check-outline
|
||||
source:
|
||||
repository: ((github-repo-name))
|
||||
access_token: ((github-ci-status-token))
|
||||
branch: ((branch))
|
||||
context: build
|
||||
- name: repo-status-jdk20-build
|
||||
type: github-status-resource
|
||||
icon: eye-check-outline
|
||||
source:
|
||||
repository: ((github-repo-name))
|
||||
access_token: ((github-ci-status-token))
|
||||
branch: ((branch))
|
||||
context: jdk20-build
|
||||
- name: slack-alert
|
||||
type: slack-notification
|
||||
icon: slack
|
||||
source:
|
||||
url: ((slack-webhook-url))
|
||||
- name: github-pre-release
|
||||
type: github-release
|
||||
icon: briefcase-download-outline
|
||||
@@ -116,6 +160,91 @@ jobs:
|
||||
- put: ci-image
|
||||
params:
|
||||
image: ci-image/image.tar
|
||||
- name: build
|
||||
serial: true
|
||||
public: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
trigger: true
|
||||
- put: repo-status-build
|
||||
params: { state: "pending", commit: "git-repo" }
|
||||
- do:
|
||||
- task: build-project
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/build-project.yml
|
||||
privileged: true
|
||||
timeout: ((task-timeout))
|
||||
params:
|
||||
<<: *build-project-task-params
|
||||
on_failure:
|
||||
do:
|
||||
- put: repo-status-build
|
||||
params: { state: "failure", commit: "git-repo" }
|
||||
- put: slack-alert
|
||||
params:
|
||||
<<: *slack-fail-params
|
||||
- put: repo-status-build
|
||||
params: { state: "success", commit: "git-repo" }
|
||||
- put: artifactory-repo
|
||||
params: &artifactory-params
|
||||
signing_key: ((signing-key))
|
||||
signing_passphrase: ((signing-passphrase))
|
||||
repo: libs-snapshot-local
|
||||
folder: distribution-repository
|
||||
build_uri: "https://ci.spring.io/teams/${BUILD_TEAM_NAME}/pipelines/${BUILD_PIPELINE_NAME}/jobs/${BUILD_JOB_NAME}/builds/${BUILD_NAME}"
|
||||
build_number: "${BUILD_PIPELINE_NAME}-${BUILD_JOB_NAME}-${BUILD_NAME}"
|
||||
disable_checksum_uploads: true
|
||||
threads: 8
|
||||
artifact_set:
|
||||
- include:
|
||||
- "/**/framework-docs-*.zip"
|
||||
properties:
|
||||
"zip.name": "spring-framework"
|
||||
"zip.displayname": "Spring Framework"
|
||||
"zip.deployed": "false"
|
||||
- include:
|
||||
- "/**/framework-docs-*-docs.zip"
|
||||
properties:
|
||||
"zip.type": "docs"
|
||||
- include:
|
||||
- "/**/framework-docs-*-dist.zip"
|
||||
properties:
|
||||
"zip.type": "dist"
|
||||
- include:
|
||||
- "/**/framework-docs-*-schema.zip"
|
||||
properties:
|
||||
"zip.type": "schema"
|
||||
get_params:
|
||||
threads: 8
|
||||
- name: jdk20-build
|
||||
serial: true
|
||||
public: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
- get: every-morning
|
||||
trigger: true
|
||||
- put: repo-status-jdk20-build
|
||||
params: { state: "pending", commit: "git-repo" }
|
||||
- do:
|
||||
- task: check-project
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/check-project.yml
|
||||
privileged: true
|
||||
timeout: ((task-timeout))
|
||||
params:
|
||||
TEST_TOOLCHAIN: 20
|
||||
<<: *build-project-task-params
|
||||
on_failure:
|
||||
do:
|
||||
- put: repo-status-jdk20-build
|
||||
params: { state: "failure", commit: "git-repo" }
|
||||
- put: slack-alert
|
||||
params:
|
||||
<<: *slack-fail-params
|
||||
- put: repo-status-jdk20-build
|
||||
params: { state: "success", commit: "git-repo" }
|
||||
- name: stage-milestone
|
||||
serial: true
|
||||
plan:
|
||||
@@ -129,32 +258,9 @@ jobs:
|
||||
RELEASE_TYPE: M
|
||||
<<: *gradle-enterprise-task-params
|
||||
- put: artifactory-repo
|
||||
params: &artifactory-params
|
||||
signing_key: ((signing-key))
|
||||
signing_passphrase: ((signing-passphrase))
|
||||
params:
|
||||
<<: *artifactory-params
|
||||
repo: libs-staging-local
|
||||
folder: distribution-repository
|
||||
build_uri: "https://ci.spring.io/teams/${BUILD_TEAM_NAME}/pipelines/${BUILD_PIPELINE_NAME}/jobs/${BUILD_JOB_NAME}/builds/${BUILD_NAME}"
|
||||
build_number: "${BUILD_PIPELINE_NAME}-${BUILD_JOB_NAME}-${BUILD_NAME}"
|
||||
disable_checksum_uploads: true
|
||||
threads: 8
|
||||
artifact_set:
|
||||
- include:
|
||||
- "/**/framework-api-*.zip"
|
||||
properties:
|
||||
"zip.name": "spring-framework"
|
||||
"zip.displayname": "Spring Framework"
|
||||
"zip.deployed": "false"
|
||||
- include:
|
||||
- "/**/framework-api-*-docs.zip"
|
||||
properties:
|
||||
"zip.type": "docs"
|
||||
- include:
|
||||
- "/**/framework-api-*-schema.zip"
|
||||
properties:
|
||||
"zip.type": "schema"
|
||||
get_params:
|
||||
threads: 8
|
||||
- put: git-repo
|
||||
params:
|
||||
repository: stage-git-repo
|
||||
@@ -199,6 +305,7 @@ jobs:
|
||||
- put: artifactory-repo
|
||||
params:
|
||||
<<: *artifactory-params
|
||||
repo: libs-staging-local
|
||||
- put: git-repo
|
||||
params:
|
||||
repository: stage-git-repo
|
||||
@@ -243,6 +350,7 @@ jobs:
|
||||
- put: artifactory-repo
|
||||
params:
|
||||
<<: *artifactory-params
|
||||
repo: libs-staging-local
|
||||
- put: git-repo
|
||||
params:
|
||||
repository: stage-git-repo
|
||||
@@ -287,6 +395,8 @@ jobs:
|
||||
<<: *changelog-task-params
|
||||
|
||||
groups:
|
||||
- name: "builds"
|
||||
jobs: ["build", "jdk20-build"]
|
||||
- name: "releases"
|
||||
jobs: ["stage-milestone", "stage-rc", "stage-release", "promote-milestone", "promote-rc", "promote-release", "create-github-release"]
|
||||
- name: "ci-images"
|
||||
|
||||
Executable
+8
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
source $(dirname $0)/common.sh
|
||||
|
||||
pushd git-repo > /dev/null
|
||||
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false --no-daemon --max-workers=4 check
|
||||
popd > /dev/null
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
source $(dirname $0)/common.sh
|
||||
repository=$(pwd)/distribution-repository
|
||||
|
||||
pushd git-repo > /dev/null
|
||||
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false --no-daemon --max-workers=4 -PdeploymentRepository=${repository} build publishAllPublicationsToDeploymentRepository
|
||||
popd > /dev/null
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
source $(dirname $0)/common.sh
|
||||
|
||||
pushd git-repo > /dev/null
|
||||
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false -Porg.gradle.java.installations.fromEnv=JDK17,JDK20 \
|
||||
-PmainToolchain=${MAIN_TOOLCHAIN} -PtestToolchain=${TEST_TOOLCHAIN} --no-daemon --max-workers=4 check antora
|
||||
popd > /dev/null
|
||||
@@ -35,8 +35,7 @@ git add gradle.properties > /dev/null
|
||||
git commit -m"Release v$stageVersion" > /dev/null
|
||||
git tag -a "v$stageVersion" -m"Release v$stageVersion" > /dev/null
|
||||
|
||||
./gradlew --no-daemon --max-workers=4 -PdeploymentRepository=${repository} -Porg.gradle.java.installations.fromEnv=JDK17,JDK21 \
|
||||
build publishAllPublicationsToDeploymentRepository
|
||||
./gradlew --no-daemon --max-workers=4 -PdeploymentRepository=${repository} build publishAllPublicationsToDeploymentRepository
|
||||
|
||||
git reset --hard HEAD^ > /dev/null
|
||||
if [[ $nextVersion != $snapshotVersion ]]; then
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
platform: linux
|
||||
inputs:
|
||||
- name: git-repo
|
||||
caches:
|
||||
- path: gradle
|
||||
params:
|
||||
BRANCH:
|
||||
CI: true
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY:
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME:
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD:
|
||||
GRADLE_ENTERPRISE_URL: https://ge.spring.io
|
||||
run:
|
||||
path: bash
|
||||
args:
|
||||
- -ec
|
||||
- |
|
||||
${PWD}/git-repo/ci/scripts/build-pr.sh
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
platform: linux
|
||||
inputs:
|
||||
- name: git-repo
|
||||
outputs:
|
||||
- name: distribution-repository
|
||||
- name: git-repo
|
||||
caches:
|
||||
- path: gradle
|
||||
params:
|
||||
BRANCH:
|
||||
CI: true
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY:
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME:
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD:
|
||||
GRADLE_ENTERPRISE_URL: https://ge.spring.io
|
||||
run:
|
||||
path: bash
|
||||
args:
|
||||
- -ec
|
||||
- |
|
||||
${PWD}/git-repo/ci/scripts/build-project.sh
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
platform: linux
|
||||
inputs:
|
||||
- name: git-repo
|
||||
outputs:
|
||||
- name: distribution-repository
|
||||
- name: git-repo
|
||||
caches:
|
||||
- path: gradle
|
||||
params:
|
||||
BRANCH:
|
||||
CI: true
|
||||
MAIN_TOOLCHAIN:
|
||||
TEST_TOOLCHAIN:
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY:
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME:
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD:
|
||||
GRADLE_ENTERPRISE_URL: https://ge.spring.io
|
||||
run:
|
||||
path: bash
|
||||
args:
|
||||
- -ec
|
||||
- |
|
||||
${PWD}/git-repo/ci/scripts/check-project.sh
|
||||
@@ -4,7 +4,7 @@ image_resource:
|
||||
type: registry-image
|
||||
source:
|
||||
repository: springio/concourse-release-scripts
|
||||
tag: '0.4.0'
|
||||
tag: '0.3.4'
|
||||
username: ((docker-hub-username))
|
||||
password: ((docker-hub-password))
|
||||
inputs:
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
plugins {
|
||||
id 'java-platform'
|
||||
id 'io.freefair.aggregate-javadoc' version '8.3'
|
||||
}
|
||||
|
||||
description = "Spring Framework API Docs"
|
||||
|
||||
apply from: "${rootDir}/gradle/publications.gradle"
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
url "https://repo.spring.io/release"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
moduleProjects.each { moduleProject ->
|
||||
javadoc moduleProject
|
||||
}
|
||||
}
|
||||
|
||||
javadoc {
|
||||
title = "${rootProject.description} ${version} API"
|
||||
options {
|
||||
encoding = "UTF-8"
|
||||
memberLevel = JavadocMemberLevel.PROTECTED
|
||||
author = true
|
||||
header = rootProject.description
|
||||
use = true
|
||||
overview = project.relativePath("$rootProject.rootDir/framework-docs/src/docs/api/overview.html")
|
||||
destinationDir = file("$project.docsDir/javadoc-api")
|
||||
splitIndex = true
|
||||
links(rootProject.ext.javadocLinks)
|
||||
addBooleanOption('Xdoclint:syntax,reference', true) // only check syntax and reference with doclint
|
||||
addBooleanOption('Werror', true) // fail build on Javadoc warnings
|
||||
}
|
||||
maxMemory = "1024m"
|
||||
doFirst {
|
||||
classpath += files(
|
||||
// ensure the javadoc process can resolve types compiled from .aj sources
|
||||
project(":spring-aspects").sourceSets.main.output
|
||||
)
|
||||
classpath += files(moduleProjects.collect { it.sourceSets.main.compileClasspath })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce KDoc for all Spring Framework modules in "build/docs/kdoc"
|
||||
*/
|
||||
rootProject.tasks.dokkaHtmlMultiModule.configure {
|
||||
dependsOn {
|
||||
tasks.named("javadoc")
|
||||
}
|
||||
moduleName.set("spring-framework")
|
||||
outputDirectory.set(file("$docsDir/kdoc-api"))
|
||||
includes.from("$rootProject.rootDir/framework-docs/src/docs/api/dokka-overview.md")
|
||||
}
|
||||
|
||||
/**
|
||||
* Zip all Java docs (javadoc & kdoc) into a single archive
|
||||
*/
|
||||
tasks.register('docsZip', Zip) {
|
||||
dependsOn = ['javadoc', rootProject.tasks.dokkaHtmlMultiModule]
|
||||
group = "distribution"
|
||||
description = "Builds -${archiveClassifier} archive containing api and reference " +
|
||||
"for deployment at https://docs.spring.io/spring-framework/docs/."
|
||||
|
||||
archiveBaseName.set("spring-framework")
|
||||
archiveClassifier.set("docs")
|
||||
from("src/dist") {
|
||||
include "changelog.txt"
|
||||
}
|
||||
from(javadoc) {
|
||||
into "javadoc-api"
|
||||
}
|
||||
from(rootProject.tasks.dokkaHtmlMultiModule.outputDirectory) {
|
||||
into "kdoc-api"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Zip all Spring Framework schemas into a single archive
|
||||
*/
|
||||
tasks.register('schemaZip', Zip) {
|
||||
group = "distribution"
|
||||
archiveBaseName.set("spring-framework")
|
||||
archiveClassifier.set("schema")
|
||||
description = "Builds -${archiveClassifier} archive containing all " +
|
||||
"XSDs for deployment at https://springframework.org/schema."
|
||||
duplicatesStrategy DuplicatesStrategy.EXCLUDE
|
||||
moduleProjects.each { module ->
|
||||
def Properties schemas = new Properties();
|
||||
|
||||
module.sourceSets.main.resources.find {
|
||||
(it.path.endsWith("META-INF/spring.schemas") || it.path.endsWith("META-INF\\spring.schemas"))
|
||||
}?.withInputStream { schemas.load(it) }
|
||||
|
||||
for (def key : schemas.keySet()) {
|
||||
def shortName = key.replaceAll(/http.*schema.(.*).spring-.*/, '$1')
|
||||
assert shortName != key
|
||||
File xsdFile = module.sourceSets.main.resources.find {
|
||||
(it.path.endsWith(schemas.get(key)) || it.path.endsWith(schemas.get(key).replaceAll('\\/', '\\\\')))
|
||||
}
|
||||
assert xsdFile != null
|
||||
into(shortName) {
|
||||
from xsdFile.path
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
artifact docsZip
|
||||
artifact schemaZip
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,76 +17,16 @@ asciidoc:
|
||||
# FIXME: the copyright is not removed
|
||||
# FIXME: The package is not renamed
|
||||
chomp: 'all'
|
||||
fold: 'all'
|
||||
table-stripes: 'odd'
|
||||
include-java: 'example$docs-src/main/java/org/springframework/docs'
|
||||
include-kotlin: 'example$docs-src/main/kotlin/org/springframework/docs'
|
||||
include-xml: 'example$docs-src/main/resources/org/springframework/docs'
|
||||
spring-site: 'https://spring.io'
|
||||
spring-site-blog: '{spring-site}/blog'
|
||||
spring-site-cve: "{spring-site}/security"
|
||||
spring-site-guides: '{spring-site}/guides'
|
||||
spring-site-projects: '{spring-site}/projects'
|
||||
spring-site-tools: "{spring-site}/tools"
|
||||
spring-org: 'spring-projects'
|
||||
spring-github-org: "https://github.com/{spring-org}"
|
||||
spring-framework-github: "https://github.com/{spring-org}/spring-framework"
|
||||
spring-framework-code: '{spring-framework-github}/tree/main'
|
||||
spring-framework-issues: '{spring-framework-github}/issues'
|
||||
spring-framework-wiki: '{spring-framework-github}/wiki'
|
||||
# Docs
|
||||
spring-framework-main-code: 'https://github.com/spring-projects/spring-framework/tree/main'
|
||||
docs-site: 'https://docs.spring.io'
|
||||
spring-framework-docs-root: '{docs-site}/spring-framework/docs'
|
||||
spring-framework-api: '{spring-framework-docs-root}/{spring-version}/javadoc-api/org/springframework'
|
||||
spring-framework-api-kdoc: '{spring-framework-docs-root}/{spring-version}/kdoc-api'
|
||||
spring-framework-reference: '{spring-framework-docs-root}/{spring-version}/reference'
|
||||
#
|
||||
# Other Spring portfolio projects
|
||||
spring-boot-docs: '{docs-site}/spring-boot/docs/current/reference/html'
|
||||
spring-boot-issues: '{spring-github-org}/spring-boot/issues'
|
||||
# TODO add more projects / links or just build up on {docs-site}?
|
||||
# TODO rename the below using new conventions
|
||||
docs-spring: "{docs-site}/spring-framework/docs/{spring-version}"
|
||||
docs-spring-framework: '{docs-site}/spring-framework/docs/{spring-version}'
|
||||
api-spring-framework: '{docs-spring-framework}/javadoc-api/org/springframework'
|
||||
docs-graalvm: 'https://www.graalvm.org/22.3/reference-manual'
|
||||
docs-spring-boot: '{docs-site}/spring-boot/docs/current/reference'
|
||||
docs-spring-gemfire: '{docs-site}/spring-gemfire/docs/current/reference'
|
||||
docs-spring-security: '{docs-site}/spring-security/reference'
|
||||
docs-spring-session: '{docs-site}/spring-session/reference'
|
||||
#
|
||||
# External projects URLs and related attributes
|
||||
aspectj-site: 'https://www.eclipse.org/aspectj'
|
||||
aspectj-docs: "{aspectj-site}/doc/released"
|
||||
aspectj-api: "{aspectj-docs}/runtime-api"
|
||||
aspectj-docs-devguide: "{aspectj-docs}/devguide"
|
||||
aspectj-docs-progguide: "{aspectj-docs}/progguide"
|
||||
assertj-docs: 'https://assertj.github.io/doc'
|
||||
baeldung-blog: 'https://www.baeldung.com'
|
||||
bean-validation-site: 'https://beanvalidation.org'
|
||||
graalvm-docs: 'https://www.graalvm.org/22.3/reference-manual'
|
||||
hibernate-validator-site: 'https://hibernate.org/validator/'
|
||||
jackson-docs: 'https://fasterxml.github.io'
|
||||
jackson-github-org: 'https://github.com/FasterXML'
|
||||
java-api: 'https://docs.oracle.com/en/java/javase/17/docs/api'
|
||||
java-tutorial: 'https://docs.oracle.com/javase/tutorial'
|
||||
JSR: 'https://www.jcp.org/en/jsr/detail?id='
|
||||
kotlin-site: 'https://kotlinlang.org'
|
||||
kotlin-docs: '{kotlin-site}/docs'
|
||||
kotlin-api: '{kotlin-site}/api/latest'
|
||||
kotlin-coroutines-api: '{kotlin-site}/api/kotlinx.coroutines'
|
||||
kotlin-github-org: 'https://github.com/Kotlin'
|
||||
kotlin-issues: 'https://youtrack.jetbrains.com/issue'
|
||||
reactive-streams-site: 'https://www.reactive-streams.org'
|
||||
reactive-streams-spec: 'https://github.com/reactive-streams/reactive-streams-jvm/blob/master/README.md#specification'
|
||||
reactor-github-org: 'https://github.com/reactor'
|
||||
reactor-site: 'https://projectreactor.io'
|
||||
rsocket-github-org: 'https://github.com/rsocket'
|
||||
rsocket-java: '{rsocket-github-org}/rsocket-java'
|
||||
rsocket-java-code: '{rsocket-java}/tree/master/'
|
||||
rsocket-protocol-extensions: '{rsocket-github-org}/rsocket/tree/master/Extensions'
|
||||
rsocket-site: 'https://rsocket.io'
|
||||
rfc-site: 'https://datatracker.ietf.org/doc/html'
|
||||
sockjs-client: 'https://github.com/sockjs/sockjs-client'
|
||||
sockjs-protocol: 'https://github.com/sockjs/sockjs-protocol'
|
||||
sockjs-protocol-site: "https://sockjs.github.io/sockjs-protocol"
|
||||
stackoverflow-site: 'https://stackoverflow.com'
|
||||
stackoverflow-questions: '{stackoverflow-site}/questions'
|
||||
stackoverflow-spring-tag: "{stackoverflow-questions}/tagged/spring"
|
||||
stackoverflow-spring-kotlin-tags: "{stackoverflow-spring-tag}+kotlin"
|
||||
testcontainers-site: 'https://www.testcontainers.org'
|
||||
gh-rsocket: 'https://github.com/rsocket'
|
||||
gh-rsocket-extensions: '{gh-rsocket}/rsocket/blob/master/Extensions'
|
||||
gh-rsocket-java: '{gh-rsocket}/rsocket-java{gh-rsocket}/rsocket-java'
|
||||
@@ -6,9 +6,9 @@ plugins {
|
||||
|
||||
description = "Spring Framework Docs"
|
||||
|
||||
apply from: "${rootDir}/gradle/ide.gradle"
|
||||
apply from: "${rootDir}/gradle/publications.gradle"
|
||||
|
||||
|
||||
antora {
|
||||
version = '3.2.0-alpha.2'
|
||||
playbook = 'cached-antora-playbook.yml'
|
||||
@@ -28,22 +28,29 @@ antora {
|
||||
'@antora/atlas-extension': '1.0.0-alpha.1',
|
||||
'@antora/collector-extension': '1.0.0-alpha.3',
|
||||
'@asciidoctor/tabs': '1.0.0-beta.3',
|
||||
'@opendevise/antora-release-line-extension': '1.0.0',
|
||||
'@springio/antora-extensions': '1.8.2',
|
||||
'@springio/asciidoctor-extensions': '1.0.0-alpha.10'
|
||||
'@opendevise/antora-release-line-extension': '1.0.0-alpha.2',
|
||||
'@springio/antora-extensions': '1.3.0',
|
||||
'@springio/asciidoctor-extensions': '1.0.0-alpha.9'
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
tasks.named("generateAntoraYml") {
|
||||
asciidocAttributes = project.provider( {
|
||||
return ["spring-version": project.version ]
|
||||
} )
|
||||
}
|
||||
|
||||
tasks.register("generateAntoraResources") {
|
||||
tasks.create("generateAntoraResources") {
|
||||
dependsOn 'generateAntoraYml'
|
||||
}
|
||||
|
||||
// Commented out for now:
|
||||
// https://github.com/spring-projects/spring-framework/issues/30481
|
||||
// tasks.named("check") {
|
||||
// dependsOn 'antora'
|
||||
// }
|
||||
|
||||
jar {
|
||||
enabled = false
|
||||
}
|
||||
@@ -60,29 +67,166 @@ repositories {
|
||||
|
||||
dependencies {
|
||||
api(project(":spring-context"))
|
||||
api(project(":spring-jdbc"))
|
||||
api(project(":spring-jms"))
|
||||
api(project(":spring-web"))
|
||||
api(project(":spring-webmvc"))
|
||||
api(project(":spring-context-support"))
|
||||
api(project(":spring-aspects"))
|
||||
api(project(":spring-websocket"))
|
||||
|
||||
api("org.jetbrains.kotlin:kotlin-stdlib")
|
||||
api("jakarta.jms:jakarta.jms-api")
|
||||
api("jakarta.servlet:jakarta.servlet-api")
|
||||
api("org.apache.commons:commons-dbcp2:2.11.0")
|
||||
api("com.mchange:c3p0:0.9.5.5")
|
||||
api("com.fasterxml.jackson.core:jackson-databind")
|
||||
api("com.fasterxml.jackson.module:jackson-module-parameter-names")
|
||||
api("jakarta.validation:jakarta.validation-api")
|
||||
api("org.aspectj:aspectjweaver")
|
||||
api("io.projectreactor.netty:reactor-netty-http")
|
||||
api("org.eclipse.jetty.websocket:jetty-websocket-jetty-api")
|
||||
api("javax.cache:cache-api")
|
||||
api("jakarta.resource:jakarta.resource-api")
|
||||
api("org.apache.activemq:activemq-ra:6.1.2")
|
||||
|
||||
implementation(project(":spring-core-test"))
|
||||
implementation("org.assertj:assertj-core")
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce Javadoc for all Spring Framework modules in "build/docs/javadoc"
|
||||
*/
|
||||
task api(type: Javadoc) {
|
||||
group = "Documentation"
|
||||
description = "Generates aggregated Javadoc API documentation."
|
||||
title = "${rootProject.description} ${version} API"
|
||||
|
||||
dependsOn {
|
||||
moduleProjects.collect {
|
||||
it.tasks.getByName("jar")
|
||||
}
|
||||
}
|
||||
doFirst {
|
||||
classpath = files(
|
||||
// ensure the javadoc process can resolve types compiled from .aj sources
|
||||
project(":spring-aspects").sourceSets.main.output
|
||||
)
|
||||
classpath += files(moduleProjects.collect { it.sourceSets.main.compileClasspath })
|
||||
}
|
||||
|
||||
options {
|
||||
encoding = "UTF-8"
|
||||
memberLevel = JavadocMemberLevel.PROTECTED
|
||||
author = true
|
||||
header = rootProject.description
|
||||
use = true
|
||||
overview = "framework-docs/src/docs/api/overview.html"
|
||||
splitIndex = true
|
||||
links(project.ext.javadocLinks)
|
||||
addBooleanOption('Xdoclint:syntax,reference', true) // only check syntax and reference with doclint
|
||||
addBooleanOption('Werror', true) // fail build on Javadoc warnings
|
||||
}
|
||||
source moduleProjects.collect { project ->
|
||||
project.sourceSets.main.allJava
|
||||
}
|
||||
maxMemory = "1024m"
|
||||
destinationDir = file("$buildDir/docs/javadoc")
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce KDoc for all Spring Framework modules in "build/docs/kdoc"
|
||||
*/
|
||||
rootProject.tasks.dokkaHtmlMultiModule.configure {
|
||||
dependsOn {
|
||||
tasks.getByName("api")
|
||||
}
|
||||
moduleName.set("spring-framework")
|
||||
outputDirectory.set(project.file("$buildDir/docs/kdoc"))
|
||||
}
|
||||
|
||||
/**
|
||||
* Zip all Java docs (javadoc & kdoc) into a single archive
|
||||
*/
|
||||
task docsZip(type: Zip, dependsOn: ['api', rootProject.tasks.dokkaHtmlMultiModule]) {
|
||||
group = "Distribution"
|
||||
description = "Builds -${archiveClassifier} archive containing api and reference " +
|
||||
"for deployment at https://docs.spring.io/spring-framework/docs/."
|
||||
|
||||
archiveBaseName.set("spring-framework")
|
||||
archiveClassifier.set("docs")
|
||||
from("src/dist") {
|
||||
include "changelog.txt"
|
||||
}
|
||||
from (api) {
|
||||
into "javadoc-api"
|
||||
}
|
||||
from (rootProject.tasks.dokkaHtmlMultiModule.outputDirectory) {
|
||||
into "kdoc-api"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Zip all Spring Framework schemas into a single archive
|
||||
*/
|
||||
task schemaZip(type: Zip) {
|
||||
group = "Distribution"
|
||||
archiveBaseName.set("spring-framework")
|
||||
archiveClassifier.set("schema")
|
||||
description = "Builds -${archiveClassifier} archive containing all " +
|
||||
"XSDs for deployment at https://springframework.org/schema."
|
||||
duplicatesStrategy DuplicatesStrategy.EXCLUDE
|
||||
moduleProjects.each { module ->
|
||||
def Properties schemas = new Properties();
|
||||
|
||||
module.sourceSets.main.resources.find {
|
||||
(it.path.endsWith("META-INF/spring.schemas") || it.path.endsWith("META-INF\\spring.schemas"))
|
||||
}?.withInputStream { schemas.load(it) }
|
||||
|
||||
for (def key : schemas.keySet()) {
|
||||
def shortName = key.replaceAll(/http.*schema.(.*).spring-.*/, '$1')
|
||||
assert shortName != key
|
||||
File xsdFile = module.sourceSets.main.resources.find {
|
||||
(it.path.endsWith(schemas.get(key)) || it.path.endsWith(schemas.get(key).replaceAll('\\/','\\\\')))
|
||||
}
|
||||
assert xsdFile != null
|
||||
into (shortName) {
|
||||
from xsdFile.path
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a distribution zip with everything:
|
||||
* docs, schemas, jars, source jars, javadoc jars
|
||||
*/
|
||||
task distZip(type: Zip, dependsOn: [docsZip, schemaZip]) {
|
||||
group = "Distribution"
|
||||
archiveBaseName.set("spring-framework")
|
||||
archiveClassifier.set("dist")
|
||||
description = "Builds -${archiveClassifier} archive, containing all jars and docs, " +
|
||||
"suitable for community download page."
|
||||
|
||||
ext.baseDir = "spring-framework-${project.version}";
|
||||
|
||||
from("src/docs/dist") {
|
||||
include "readme.txt"
|
||||
include "license.txt"
|
||||
include "notice.txt"
|
||||
into "${baseDir}"
|
||||
expand(copyright: new Date().format("yyyy"), version: project.version)
|
||||
}
|
||||
|
||||
from(zipTree(docsZip.archiveFile)) {
|
||||
into "${baseDir}/docs"
|
||||
}
|
||||
|
||||
from(zipTree(schemaZip.archiveFile)) {
|
||||
into "${baseDir}/schema"
|
||||
}
|
||||
|
||||
moduleProjects.each { module ->
|
||||
into ("${baseDir}/libs") {
|
||||
from module.jar
|
||||
if (module.tasks.findByPath("sourcesJar")) {
|
||||
from module.sourcesJar
|
||||
}
|
||||
if (module.tasks.findByPath("javadocJar")) {
|
||||
from module.javadocJar
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
distZip.mustRunAfter moduleProjects.check
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
artifact docsZip
|
||||
artifact schemaZip
|
||||
artifact distZip
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,8 +39,8 @@
|
||||
** xref:core/resources.adoc[]
|
||||
** xref:core/validation.adoc[]
|
||||
*** xref:core/validation/validator.adoc[]
|
||||
*** xref:core/validation/beans-beans.adoc[]
|
||||
*** xref:core/validation/conversion.adoc[]
|
||||
*** xref:core/validation/beans-beans.adoc[]
|
||||
*** xref:core/validation/convert.adoc[]
|
||||
*** xref:core/validation/format.adoc[]
|
||||
*** xref:core/validation/format-configuring-formatting-globaldatetimeformat.adoc[]
|
||||
@@ -122,7 +122,6 @@
|
||||
**** xref:testing/testcontext-framework/ctx-management/groovy.adoc[]
|
||||
**** xref:testing/testcontext-framework/ctx-management/javaconfig.adoc[]
|
||||
**** xref:testing/testcontext-framework/ctx-management/mixed-config.adoc[]
|
||||
**** xref:testing/testcontext-framework/ctx-management/context-customizers.adoc[]
|
||||
**** xref:testing/testcontext-framework/ctx-management/initializers.adoc[]
|
||||
**** xref:testing/testcontext-framework/ctx-management/inheritance.adoc[]
|
||||
**** xref:testing/testcontext-framework/ctx-management/env-profiles.adoc[]
|
||||
@@ -131,10 +130,8 @@
|
||||
**** xref:testing/testcontext-framework/ctx-management/web.adoc[]
|
||||
**** xref:testing/testcontext-framework/ctx-management/web-mocks.adoc[]
|
||||
**** xref:testing/testcontext-framework/ctx-management/caching.adoc[]
|
||||
**** xref:testing/testcontext-framework/ctx-management/failure-threshold.adoc[]
|
||||
**** xref:testing/testcontext-framework/ctx-management/hierarchies.adoc[]
|
||||
*** xref:testing/testcontext-framework/fixture-di.adoc[]
|
||||
*** xref:testing/testcontext-framework/bean-overriding.adoc[]
|
||||
*** xref:testing/testcontext-framework/web-scoped-beans.adoc[]
|
||||
*** xref:testing/testcontext-framework/tx.adoc[]
|
||||
*** xref:testing/testcontext-framework/executing-sql.adoc[]
|
||||
@@ -168,12 +165,9 @@
|
||||
***** xref:testing/annotations/integration-spring/annotation-contextconfiguration.adoc[]
|
||||
***** xref:testing/annotations/integration-spring/annotation-webappconfiguration.adoc[]
|
||||
***** xref:testing/annotations/integration-spring/annotation-contexthierarchy.adoc[]
|
||||
***** xref:testing/annotations/integration-spring/annotation-contextcustomizerfactories.adoc[]
|
||||
***** xref:testing/annotations/integration-spring/annotation-activeprofiles.adoc[]
|
||||
***** xref:testing/annotations/integration-spring/annotation-testpropertysource.adoc[]
|
||||
***** xref:testing/annotations/integration-spring/annotation-dynamicpropertysource.adoc[]
|
||||
***** xref:testing/annotations/integration-spring/annotation-testbean.adoc[]
|
||||
***** xref:testing/annotations/integration-spring/annotation-mockitobean.adoc[]
|
||||
***** xref:testing/annotations/integration-spring/annotation-dirtiescontext.adoc[]
|
||||
***** xref:testing/annotations/integration-spring/annotation-testexecutionlisteners.adoc[]
|
||||
***** xref:testing/annotations/integration-spring/annotation-recordapplicationevents.adoc[]
|
||||
@@ -185,7 +179,6 @@
|
||||
***** xref:testing/annotations/integration-spring/annotation-sqlconfig.adoc[]
|
||||
***** xref:testing/annotations/integration-spring/annotation-sqlmergemode.adoc[]
|
||||
***** xref:testing/annotations/integration-spring/annotation-sqlgroup.adoc[]
|
||||
***** xref:testing/annotations/integration-spring/annotation-disabledinaotmode.adoc[]
|
||||
**** xref:testing/annotations/integration-junit4.adoc[]
|
||||
**** xref:testing/annotations/integration-junit-jupiter.adoc[]
|
||||
**** xref:testing/annotations/integration-meta.adoc[]
|
||||
@@ -273,7 +266,6 @@
|
||||
***** xref:web/webmvc/mvc-controller/ann-methods/jackson.adoc[]
|
||||
**** xref:web/webmvc/mvc-controller/ann-modelattrib-methods.adoc[]
|
||||
**** xref:web/webmvc/mvc-controller/ann-initbinder.adoc[]
|
||||
**** xref:web/webmvc/mvc-controller/ann-validation.adoc[]
|
||||
**** xref:web/webmvc/mvc-controller/ann-exceptionhandler.adoc[]
|
||||
**** xref:web/webmvc/mvc-controller/ann-advice.adoc[]
|
||||
*** xref:web/webmvc-functional.adoc[]
|
||||
@@ -368,7 +360,6 @@
|
||||
***** xref:web/webflux/controller/ann-methods/jackson.adoc[]
|
||||
**** xref:web/webflux/controller/ann-modelattrib-methods.adoc[]
|
||||
**** xref:web/webflux/controller/ann-initbinder.adoc[]
|
||||
**** xref:web/webflux/controller/ann-validation.adoc[]
|
||||
**** xref:web/webflux/controller/ann-exceptions.adoc[]
|
||||
**** xref:web/webflux/controller/ann-advice.adoc[]
|
||||
*** xref:web/webflux-functional.adoc[]
|
||||
@@ -423,8 +414,6 @@
|
||||
*** xref:integration/cache/plug.adoc[]
|
||||
*** xref:integration/cache/specific-config.adoc[]
|
||||
** xref:integration/observability.adoc[]
|
||||
** xref:integration/checkpoint-restore.adoc[]
|
||||
** xref:integration/cds.adoc[]
|
||||
** xref:integration/appendix.adoc[]
|
||||
* xref:languages.adoc[]
|
||||
** xref:languages/kotlin.adoc[]
|
||||
@@ -442,5 +431,4 @@
|
||||
** xref:languages/groovy.adoc[]
|
||||
** xref:languages/dynamic.adoc[]
|
||||
* xref:appendix.adoc[]
|
||||
* {spring-framework-wiki}[Wiki]
|
||||
|
||||
* https://github.com/spring-projects/spring-framework/wiki[Wiki]
|
||||
@@ -8,7 +8,7 @@ within the core Spring Framework.
|
||||
[[appendix-spring-properties]]
|
||||
== Spring Properties
|
||||
|
||||
{spring-framework-api}/core/SpringProperties.html[`SpringProperties`] is a static holder
|
||||
{api-spring-framework}/core/SpringProperties.html[`SpringProperties`] is a static holder
|
||||
for properties that control certain low-level aspects of the Spring Framework. Users can
|
||||
configure these properties via JVM system properties or programmatically via the
|
||||
`SpringProperties.setProperty(String key, String value)` method. The latter may be
|
||||
@@ -19,53 +19,15 @@ of the classpath -- for example, deployed within the application's JAR file.
|
||||
The following table lists all currently supported Spring properties.
|
||||
|
||||
.Supported Spring Properties
|
||||
[cols="1,1"]
|
||||
|===
|
||||
| Name | Description
|
||||
|
||||
| `spring.aot.enabled`
|
||||
| Indicates the application should run with AOT generated artifacts. See
|
||||
xref:core/aot.adoc[Ahead of Time Optimizations] and
|
||||
{spring-framework-api}++/aot/AotDetector.html#AOT_ENABLED++[`AotDetector`]
|
||||
for details.
|
||||
|
||||
| `spring.beaninfo.ignore`
|
||||
| Instructs Spring to use the `Introspector.IGNORE_ALL_BEANINFO` mode when calling the
|
||||
JavaBeans `Introspector`. See
|
||||
{spring-framework-api}++/beans/StandardBeanInfoFactory.html#IGNORE_BEANINFO_PROPERTY_NAME++[`CachedIntrospectionResults`]
|
||||
{api-spring-framework}++/beans/StandardBeanInfoFactory.html#IGNORE_BEANINFO_PROPERTY_NAME++[`CachedIntrospectionResults`]
|
||||
for details.
|
||||
|
||||
| `spring.cache.reactivestreams.ignore`
|
||||
| Instructs Spring's caching infrastructure to ignore the presence of Reactive Streams,
|
||||
in particular Reactor's `Mono`/`Flux` in `@Cacheable` method return type declarations. See
|
||||
{spring-framework-api}++/cache/interceptor/CacheAspectSupport.html#IGNORE_REACTIVESTREAMS_PROPERTY_NAME++[`CacheAspectSupport`]
|
||||
for details.
|
||||
|
||||
| `spring.classformat.ignore`
|
||||
| Instructs Spring to ignore class format exceptions during classpath scanning, in
|
||||
particular for unsupported class file versions. See
|
||||
{spring-framework-api}++/context/annotation/ClassPathScanningCandidateComponentProvider.html#IGNORE_CLASSFORMAT_PROPERTY_NAME++[`ClassPathScanningCandidateComponentProvider`]
|
||||
for details.
|
||||
|
||||
| `spring.context.checkpoint`
|
||||
| Property that specifies a common context checkpoint. See
|
||||
xref:integration/checkpoint-restore.adoc#_automatic_checkpointrestore_at_startup[Automatic
|
||||
checkpoint/restore at startup] and
|
||||
{spring-framework-api}++/context/support/DefaultLifecycleProcessor.html#CHECKPOINT_PROPERTY_NAME++[`DefaultLifecycleProcessor`]
|
||||
for details.
|
||||
|
||||
| `spring.context.exit`
|
||||
| Property for terminating the JVM when the context reaches a specific phase. See
|
||||
xref:integration/checkpoint-restore.adoc#_automatic_checkpointrestore_at_startup[Automatic
|
||||
checkpoint/restore at startup] and
|
||||
{spring-framework-api}++/context/support/DefaultLifecycleProcessor.html#EXIT_PROPERTY_NAME++[`DefaultLifecycleProcessor`]
|
||||
for details.
|
||||
|
||||
| `spring.context.expression.maxLength`
|
||||
| The maximum length for
|
||||
xref:core/expressions/evaluation.adoc#expressions-parser-configuration[Spring Expression Language]
|
||||
expressions used in XML bean definitions, `@Value`, etc.
|
||||
|
||||
| `spring.expression.compiler.mode`
|
||||
| The mode to use when compiling expressions for the
|
||||
xref:core/expressions/evaluation.adoc#expressions-compiler-configuration[Spring Expression Language].
|
||||
@@ -74,9 +36,14 @@ xref:core/expressions/evaluation.adoc#expressions-compiler-configuration[Spring
|
||||
| Instructs Spring to ignore operating system environment variables if a Spring
|
||||
`Environment` property -- for example, a placeholder in a configuration String -- isn't
|
||||
resolvable otherwise. See
|
||||
{spring-framework-api}++/core/env/AbstractEnvironment.html#IGNORE_GETENV_PROPERTY_NAME++[`AbstractEnvironment`]
|
||||
{api-spring-framework}++/core/env/AbstractEnvironment.html#IGNORE_GETENV_PROPERTY_NAME++[`AbstractEnvironment`]
|
||||
for details.
|
||||
|
||||
| `spring.index.ignore`
|
||||
| Instructs Spring to ignore the components index located in
|
||||
`META-INF/spring.components`. See xref:core/beans/classpath-scanning.adoc#beans-scanning-index[Generating an Index of Candidate Components]
|
||||
.
|
||||
|
||||
| `spring.jdbc.getParameterType.ignore`
|
||||
| Instructs Spring to ignore `java.sql.ParameterMetaData.getParameterType` completely.
|
||||
See the note in xref:data-access/jdbc/advanced.adoc#jdbc-batch-list[Batch Operations with a List of Objects].
|
||||
@@ -85,35 +52,27 @@ See the note in xref:data-access/jdbc/advanced.adoc#jdbc-batch-list[Batch Operat
|
||||
| Instructs Spring to ignore a default JNDI environment, as an optimization for scenarios
|
||||
where nothing is ever to be found for such JNDI fallback searches to begin with, avoiding
|
||||
the repeated JNDI lookup overhead. See
|
||||
{spring-framework-api}++/jndi/JndiLocatorDelegate.html#IGNORE_JNDI_PROPERTY_NAME++[`JndiLocatorDelegate`]
|
||||
{api-spring-framework}++/jndi/JndiLocatorDelegate.html#IGNORE_JNDI_PROPERTY_NAME++[`JndiLocatorDelegate`]
|
||||
for details.
|
||||
|
||||
| `spring.objenesis.ignore`
|
||||
| Instructs Spring to ignore Objenesis, not even attempting to use it. See
|
||||
{spring-framework-api}++/objenesis/SpringObjenesis.html#IGNORE_OBJENESIS_PROPERTY_NAME++[`SpringObjenesis`]
|
||||
{api-spring-framework}++/objenesis/SpringObjenesis.html#IGNORE_OBJENESIS_PROPERTY_NAME++[`SpringObjenesis`]
|
||||
for details.
|
||||
|
||||
| `spring.test.aot.processing.failOnError`
|
||||
| A boolean flag that controls whether errors encountered during AOT processing in the
|
||||
_Spring TestContext Framework_ should result in an exception that fails the overall process.
|
||||
See xref:testing/testcontext-framework/aot.adoc[Ahead of Time Support for Tests].
|
||||
|
||||
| `spring.test.constructor.autowire.mode`
|
||||
| The default _test constructor autowire mode_ to use if `@TestConstructor` is not present
|
||||
on a test class. See xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-testconstructor[Changing the default test constructor autowire mode].
|
||||
on a test class. See xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-testconstructor[Changing the default test constructor autowire mode]
|
||||
.
|
||||
|
||||
| `spring.test.context.cache.maxSize`
|
||||
| The maximum size of the context cache in the _Spring TestContext Framework_. See
|
||||
xref:testing/testcontext-framework/ctx-management/caching.adoc[Context Caching].
|
||||
|
||||
| `spring.test.context.failure.threshold`
|
||||
| The failure threshold for errors encountered while attempting to load an `ApplicationContext`
|
||||
in the _Spring TestContext Framework_. See
|
||||
xref:testing/testcontext-framework/ctx-management/failure-threshold.adoc[Context Failure Threshold].
|
||||
|
||||
| `spring.test.enclosing.configuration`
|
||||
| The default _enclosing configuration inheritance mode_ to use if
|
||||
`@NestedTestConfiguration` is not present on a test class. See
|
||||
xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-nestedtestconfiguration[Changing the default enclosing configuration inheritance mode].
|
||||
xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-nestedtestconfiguration[Changing the default enclosing configuration inheritance mode]
|
||||
.
|
||||
|
||||
|===
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// Spring Portfolio
|
||||
:docs-site: https://docs.spring.io
|
||||
:docs-spring-boot: {docs-site}/spring-boot/docs/current/reference
|
||||
:docs-spring-gemfire: {docs-site}/spring-gemfire/docs/current/reference
|
||||
:docs-spring-security: {docs-site}/spring-security/reference
|
||||
// spring-asciidoctor-backends Settings
|
||||
:chomp: default headers packages
|
||||
:fold: all
|
||||
// Spring Framework
|
||||
:docs-spring-framework: {docs-site}/spring-framework/docs/{spring-version}
|
||||
:api-spring-framework: {docs-spring-framework}/javadoc-api/org/springframework
|
||||
:docs-java: {docdir}/../../main/java/org/springframework/docs
|
||||
:docs-kotlin: {docdir}/../../main/kotlin/org/springframework/docs
|
||||
:docs-resources: {docdir}/../../main/resources
|
||||
:spring-framework-main-code: https://github.com/spring-projects/spring-framework/tree/main
|
||||
// Third-party Links
|
||||
:docs-graalvm: https://www.graalvm.org/22.3/reference-manual
|
||||
:gh-rsocket: https://github.com/rsocket
|
||||
:gh-rsocket-extensions: {gh-rsocket}/rsocket/blob/master/Extensions
|
||||
:gh-rsocket-java: {gh-rsocket}/rsocket-java
|
||||
@@ -12,5 +12,5 @@ support for new custom advice types be added without changing the core framework
|
||||
The only constraint on a custom `Advice` type is that it must implement the
|
||||
`org.aopalliance.aop.Advice` marker interface.
|
||||
|
||||
See the {spring-framework-api}/aop/framework/adapter/package-summary.html[`org.springframework.aop.framework.adapter`]
|
||||
See the {api-spring-framework}/aop/framework/adapter/package-summary.html[`org.springframework.aop.framework.adapter`]
|
||||
javadoc for further information.
|
||||
|
||||
@@ -205,7 +205,7 @@ Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
val person = factory.getBean("person") as Person
|
||||
val person = factory.getBean("person") as Person;
|
||||
----
|
||||
======
|
||||
|
||||
@@ -291,8 +291,6 @@ to consider:
|
||||
* `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.
|
||||
* Methods that are not visible, typically package private methods in a parent class
|
||||
from a different package, cannot be advised because they are effectively private.
|
||||
|
||||
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
|
||||
|
||||
@@ -128,7 +128,18 @@ the resulting pointcut is effectively the union of the specified patterns.)
|
||||
|
||||
The following example shows how to use `JdkRegexpMethodPointcut`:
|
||||
|
||||
include-code::./JdkRegexpConfiguration[tag=snippet,indent=0]
|
||||
[source,xml,indent=0,subs="verbatim"]
|
||||
----
|
||||
<bean id="settersAndAbsquatulatePointcut"
|
||||
class="org.springframework.aop.support.JdkRegexpMethodPointcut">
|
||||
<property name="patterns">
|
||||
<list>
|
||||
<value>.*set.*</value>
|
||||
<value>.*absquatulate</value>
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
----
|
||||
|
||||
Spring provides a convenience class named `RegexpMethodPointcutAdvisor`, which lets us
|
||||
also reference an `Advice` (remember that an `Advice` can be an interceptor, before advice,
|
||||
@@ -136,7 +147,21 @@ throws advice, and others). Behind the scenes, Spring uses a `JdkRegexpMethodPoi
|
||||
Using `RegexpMethodPointcutAdvisor` simplifies wiring, as the one bean encapsulates both
|
||||
pointcut and advice, as the following example shows:
|
||||
|
||||
include-code::./RegexpConfiguration[tag=snippet,indent=0]
|
||||
[source,xml,indent=0,subs="verbatim"]
|
||||
----
|
||||
<bean id="settersAndAbsquatulateAdvisor"
|
||||
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
|
||||
<property name="advice">
|
||||
<ref bean="beanNameOfAopAllianceInterceptor"/>
|
||||
</property>
|
||||
<property name="patterns">
|
||||
<list>
|
||||
<value>.*set.*</value>
|
||||
<value>.*absquatulate</value>
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
----
|
||||
|
||||
You can use `RegexpMethodPointcutAdvisor` with any `Advice` type.
|
||||
|
||||
|
||||
@@ -119,7 +119,7 @@ The following listing shows an example configuration:
|
||||
|
||||
Note that the target object (`businessObjectTarget` in the preceding example) must be a
|
||||
prototype. This lets the `PoolingTargetSource` implementation create new instances
|
||||
of the target to grow the pool as necessary. See the {spring-framework-api}/aop/target/AbstractPoolingTargetSource.html[javadoc of
|
||||
of the target to grow the pool as necessary. See the {api-spring-framework}/aop/target/AbstractPoolingTargetSource.html[javadoc of
|
||||
`AbstractPoolingTargetSource`] and the concrete subclass you wish to use for information
|
||||
about its properties. `maxSize` is the most basic and is always guaranteed to be present.
|
||||
|
||||
@@ -168,7 +168,7 @@ Kotlin::
|
||||
======
|
||||
|
||||
NOTE: Pooling stateless service objects is not usually necessary. We do not believe it should
|
||||
be the default choice, as most stateless objects are naturally thread-safe, and instance
|
||||
be the default choice, as most stateless objects are naturally thread safe, and instance
|
||||
pooling is problematic if resources are cached.
|
||||
|
||||
Simpler pooling is available by using auto-proxying. You can set the `TargetSource` implementations
|
||||
|
||||
@@ -52,7 +52,7 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
See the {spring-framework-api}/aop/aspectj/annotation/AspectJProxyFactory.html[javadoc] for more information.
|
||||
See the {api-spring-framework}/aop/aspectj/annotation/AspectJProxyFactory.html[javadoc] for more information.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
@AspectJ refers to a style of declaring aspects as regular Java classes annotated with
|
||||
annotations. The @AspectJ style was introduced by the
|
||||
{aspectj-site}[AspectJ project] as part of the AspectJ 5 release. Spring
|
||||
https://www.eclipse.org/aspectj[AspectJ project] as part of the AspectJ 5 release. Spring
|
||||
interprets the same annotations as AspectJ 5, using a library supplied by AspectJ
|
||||
for pointcut parsing and matching. The AOP runtime is still pure Spring AOP, though, and
|
||||
there is no dependency on the AspectJ compiler or weaver.
|
||||
|
||||
@@ -482,7 +482,7 @@ The `JoinPoint` interface provides a number of useful methods:
|
||||
* `getSignature()`: Returns a description of the method that is being advised.
|
||||
* `toString()`: Prints a useful description of the method being advised.
|
||||
|
||||
See the {aspectj-api}/org/aspectj/lang/JoinPoint.html[javadoc] for more detail.
|
||||
See the https://www.eclipse.org/aspectj/doc/released/runtime-api/org/aspectj/lang/JoinPoint.html[javadoc] for more detail.
|
||||
|
||||
[[aop-ataspectj-advice-params-passing]]
|
||||
=== Passing Parameters to Advice
|
||||
@@ -728,9 +728,14 @@ of determining parameter names, an exception will be thrown.
|
||||
`StandardReflectionParameterNameDiscoverer` :: Uses the standard `java.lang.reflect.Parameter`
|
||||
API to determine parameter names. Requires that code be compiled with the `-parameters`
|
||||
flag for `javac`. Recommended approach on Java 8+.
|
||||
`LocalVariableTableParameterNameDiscoverer` :: Analyzes the local variable table available
|
||||
in the byte code of the advice class to determine parameter names from debug information.
|
||||
Requires that code be compiled with debug symbols (`-g:vars` at a minimum). Deprecated
|
||||
as of Spring Framework 6.0 for removal in Spring Framework 6.1 in favor of compiling
|
||||
code with `-parameters`. Not supported in a GraalVM native image.
|
||||
`AspectJAdviceParameterNameDiscoverer` :: Deduces parameter names from the pointcut
|
||||
expression, `returning`, and `throwing` clauses. See the
|
||||
{spring-framework-api}/aop/aspectj/AspectJAdviceParameterNameDiscoverer.html[javadoc]
|
||||
{api-spring-framework}/aop/aspectj/AspectJAdviceParameterNameDiscoverer.html[javadoc]
|
||||
for details on the algorithm used.
|
||||
|
||||
[[aop-ataspectj-advice-params-names-explicit]]
|
||||
|
||||
@@ -8,8 +8,54 @@ determines that a bean is advised by one or more aspects, it automatically gener
|
||||
a proxy for that bean to intercept method invocations and ensures that advice is run
|
||||
as needed.
|
||||
|
||||
The @AspectJ support can be enabled with programmatic or XML configuration. In either
|
||||
case, you also need to ensure that AspectJ's `org.aspectj:aspectjweaver` library is on the
|
||||
classpath of your application (version 1.9 or later).
|
||||
The @AspectJ support can be enabled with XML- or Java-style configuration. In either
|
||||
case, you also need to ensure that AspectJ's `aspectjweaver.jar` library is on the
|
||||
classpath of your application (version 1.9 or later). This library is available in the
|
||||
`lib` directory of an AspectJ distribution or from the Maven Central repository.
|
||||
|
||||
|
||||
[[aop-enable-aspectj-java]]
|
||||
== Enabling @AspectJ Support with Java Configuration
|
||||
|
||||
To enable @AspectJ support with Java `@Configuration`, add the `@EnableAspectJAutoProxy`
|
||||
annotation, as the following example shows:
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim",role="primary"]
|
||||
----
|
||||
@Configuration
|
||||
@EnableAspectJAutoProxy
|
||||
public class AppConfig {
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
|
||||
----
|
||||
@Configuration
|
||||
@EnableAspectJAutoProxy
|
||||
class AppConfig
|
||||
----
|
||||
======
|
||||
|
||||
[[aop-enable-aspectj-xml]]
|
||||
== Enabling @AspectJ Support with XML Configuration
|
||||
|
||||
To enable @AspectJ support with XML-based configuration, use the `aop:aspectj-autoproxy`
|
||||
element, as the following example shows:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim"]
|
||||
----
|
||||
<aop:aspectj-autoproxy/>
|
||||
----
|
||||
|
||||
This assumes that you use schema support as described in
|
||||
xref:core/appendix/xsd-schemas.adoc[XML Schema-based configuration].
|
||||
See xref:core/appendix/xsd-schemas.adoc#aop[the AOP schema] for how to
|
||||
import the tags in the `aop` namespace.
|
||||
|
||||
|
||||
|
||||
include-code::./ApplicationConfiguration[tag=snippet,indent=0]
|
||||
|
||||
@@ -9,12 +9,43 @@ minimal steps required for a not-very-useful aspect.
|
||||
The first of the two examples shows a regular bean definition in the application context
|
||||
that points to a bean class that is annotated with `@Aspect`:
|
||||
|
||||
include-code::./ApplicationConfiguration[tag=snippet,indent=0]
|
||||
[source,xml,indent=0,subs="verbatim"]
|
||||
----
|
||||
<bean id="myAspect" class="com.xyz.NotVeryUsefulAspect">
|
||||
<!-- configure properties of the aspect here -->
|
||||
</bean>
|
||||
----
|
||||
|
||||
The second of the two examples shows the `NotVeryUsefulAspect` class definition, which is
|
||||
annotated with `@Aspect`:
|
||||
|
||||
include-code::./NotVeryUsefulAspect[tag=snippet,indent=0]
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim",role="primary",chomp="-packages",fold="none"]
|
||||
----
|
||||
package com.xyz;
|
||||
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
|
||||
@Aspect
|
||||
public class NotVeryUsefulAspect {
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages",fold="none"]
|
||||
----
|
||||
package com.xyz
|
||||
|
||||
import org.aspectj.lang.annotation.Aspect
|
||||
|
||||
@Aspect
|
||||
class NotVeryUsefulAspect
|
||||
----
|
||||
======
|
||||
|
||||
Aspects (classes annotated with `@Aspect`) can have methods and fields, the same as any
|
||||
other class. They can also contain pointcut, advice, and introduction (inter-type)
|
||||
|
||||
@@ -16,9 +16,93 @@ aspect.
|
||||
Because we want to retry the operation, we need to use around advice so that we can
|
||||
call `proceed` multiple times. The following listing shows the basic aspect implementation:
|
||||
|
||||
include-code::./ConcurrentOperationExecutor[tag=snippet,indent=0]
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim",role="primary"]
|
||||
----
|
||||
@Aspect
|
||||
public class ConcurrentOperationExecutor implements Ordered {
|
||||
|
||||
`@Around("com.xyz.CommonPointcuts.businessService()")` references the `businessService` named pointcut defined in xref:core/aop/ataspectj/pointcuts.adoc#aop-common-pointcuts[Sharing Named Pointcut Definitions].
|
||||
private static final int DEFAULT_MAX_RETRIES = 2;
|
||||
|
||||
private int maxRetries = DEFAULT_MAX_RETRIES;
|
||||
private int order = 1;
|
||||
|
||||
public void setMaxRetries(int maxRetries) {
|
||||
this.maxRetries = maxRetries;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
@Around("com.xyz.CommonPointcuts.businessService()") // <1>
|
||||
public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
|
||||
int numAttempts = 0;
|
||||
PessimisticLockingFailureException lockFailureException;
|
||||
do {
|
||||
numAttempts++;
|
||||
try {
|
||||
return pjp.proceed();
|
||||
}
|
||||
catch(PessimisticLockingFailureException ex) {
|
||||
lockFailureException = ex;
|
||||
}
|
||||
} while(numAttempts <= this.maxRetries);
|
||||
throw lockFailureException;
|
||||
}
|
||||
}
|
||||
----
|
||||
<1> References the `businessService` named pointcut defined in xref:core/aop/ataspectj/pointcuts.adoc#aop-common-pointcuts[Sharing Named Pointcut Definitions].
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
|
||||
----
|
||||
@Aspect
|
||||
class ConcurrentOperationExecutor : Ordered {
|
||||
|
||||
private val DEFAULT_MAX_RETRIES = 2
|
||||
private var maxRetries = DEFAULT_MAX_RETRIES
|
||||
private var order = 1
|
||||
|
||||
fun setMaxRetries(maxRetries: Int) {
|
||||
this.maxRetries = maxRetries
|
||||
}
|
||||
|
||||
override fun getOrder(): Int {
|
||||
return this.order
|
||||
}
|
||||
|
||||
fun setOrder(order: Int) {
|
||||
this.order = order
|
||||
}
|
||||
|
||||
@Around("com.xyz.CommonPointcuts.businessService()") // <1>
|
||||
fun doConcurrentOperation(pjp: ProceedingJoinPoint): Any? {
|
||||
var numAttempts = 0
|
||||
var lockFailureException: PessimisticLockingFailureException
|
||||
do {
|
||||
numAttempts++
|
||||
try {
|
||||
return pjp.proceed()
|
||||
} catch (ex: PessimisticLockingFailureException) {
|
||||
lockFailureException = ex
|
||||
}
|
||||
|
||||
} while (numAttempts <= this.maxRetries)
|
||||
throw lockFailureException
|
||||
}
|
||||
}
|
||||
----
|
||||
<1> References the `businessService` named pointcut defined in xref:core/aop/ataspectj/pointcuts.adoc#aop-common-pointcuts[Sharing Named Pointcut Definitions].
|
||||
======
|
||||
|
||||
Note that the aspect implements the `Ordered` interface so that we can set the precedence of
|
||||
the aspect higher than the transaction advice (we want a fresh transaction each time we
|
||||
@@ -30,15 +114,70 @@ we have exhausted all of our retry attempts.
|
||||
|
||||
The corresponding Spring configuration follows:
|
||||
|
||||
include-code::./ApplicationConfiguration[tag=snippet,indent=0]
|
||||
[source,xml,indent=0,subs="verbatim"]
|
||||
----
|
||||
<aop:aspectj-autoproxy/>
|
||||
|
||||
<bean id="concurrentOperationExecutor"
|
||||
class="com.xyz.service.impl.ConcurrentOperationExecutor">
|
||||
<property name="maxRetries" value="3"/>
|
||||
<property name="order" value="100"/>
|
||||
</bean>
|
||||
----
|
||||
|
||||
To refine the aspect so that it retries only idempotent operations, we might define the following
|
||||
`Idempotent` annotation:
|
||||
|
||||
include-code::./service/Idempotent[tag=snippet,indent=0]
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim",role="primary"]
|
||||
----
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
// marker annotation
|
||||
public @interface Idempotent {
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
|
||||
----
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
// marker annotation
|
||||
annotation class Idempotent
|
||||
----
|
||||
======
|
||||
|
||||
We can then use the annotation to annotate the implementation of service operations. The change
|
||||
to the aspect to retry only idempotent operations involves refining the pointcut
|
||||
expression so that only `@Idempotent` operations match, as follows:
|
||||
|
||||
include-code::./service/SampleService[tag=snippet,indent=0]
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim",role="primary"]
|
||||
----
|
||||
@Around("execution(* com.xyz..service.*.*(..)) && " +
|
||||
"@annotation(com.xyz.service.Idempotent)")
|
||||
public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
|
||||
// ...
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
|
||||
----
|
||||
@Around("execution(* com.xyz..service.*.*(..)) && " +
|
||||
"@annotation(com.xyz.service.Idempotent)")
|
||||
fun doConcurrentOperation(pjp: ProceedingJoinPoint): Any? {
|
||||
// ...
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ it until later.
|
||||
|
||||
By default, there is a single instance of each aspect within the application
|
||||
context. AspectJ calls this the singleton instantiation model. It is possible to define
|
||||
aspects with alternate lifecycles. Spring supports AspectJ's `perthis`, `pertarget`, and
|
||||
`pertypewithin` instantiation models; `percflow` and `percflowbelow` are not currently
|
||||
aspects with alternate lifecycles. Spring supports AspectJ's `perthis` and `pertarget`
|
||||
instantiation models; `percflow`, `percflowbelow`, and `pertypewithin` are not currently
|
||||
supported.
|
||||
|
||||
You can declare a `perthis` aspect by specifying a `perthis` clause in the `@Aspect`
|
||||
|
||||
@@ -72,7 +72,7 @@ Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
|
||||
----
|
||||
val usageTracked = context.getBean<UsageTracked>("myService")
|
||||
val usageTracked = context.getBean("myService", UsageTracked.class)
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
@@ -36,9 +36,9 @@ Kotlin::
|
||||
|
||||
The pointcut expression that forms the value of the `@Pointcut` annotation is a regular
|
||||
AspectJ pointcut expression. For a full discussion of AspectJ's pointcut language, see
|
||||
the {aspectj-docs-progguide}/index.html[AspectJ
|
||||
the https://www.eclipse.org/aspectj/doc/released/progguide/index.html[AspectJ
|
||||
Programming Guide] (and, for extensions, the
|
||||
{aspectj-docs}/adk15notebook/index.html[AspectJ 5
|
||||
https://www.eclipse.org/aspectj/doc/released/adk15notebook/index.html[AspectJ 5
|
||||
Developer's Notebook]) or one of the books on AspectJ (such as _Eclipse AspectJ_, by Colyer
|
||||
et al., or _AspectJ in Action_, by Ramnivas Laddad).
|
||||
|
||||
@@ -392,7 +392,7 @@ method that takes no parameters, whereas `(..)` matches any number (zero or more
|
||||
The `({asterisk})` pattern matches a method that takes one parameter of any type.
|
||||
`(*,String)` matches a method that takes two parameters. The first can be of any type, while the
|
||||
second must be a `String`. Consult the
|
||||
{aspectj-docs-progguide}/semantics-pointcuts.html[Language
|
||||
https://www.eclipse.org/aspectj/doc/released/progguide/semantics-pointcuts.html[Language
|
||||
Semantics] section of the AspectJ Programming Guide for more information.
|
||||
|
||||
The following examples show some common pointcut expressions:
|
||||
|
||||
@@ -31,7 +31,7 @@ However, it would be even more confusing if Spring used its own terminology.
|
||||
the "advised object". Since Spring AOP is implemented by using runtime proxies, this
|
||||
object is always a proxied object.
|
||||
* AOP proxy: An object created by the AOP framework in order to implement the aspect
|
||||
contracts (advice method executions and so on). In the Spring Framework, an AOP proxy
|
||||
contracts (advise method executions and so on). In the Spring Framework, an AOP proxy
|
||||
is a JDK dynamic proxy or a CGLIB proxy.
|
||||
* Weaving: linking aspects with other application types or objects to create an
|
||||
advised object. This can be done at compile time (using the AspectJ compiler, for
|
||||
|
||||
@@ -19,10 +19,6 @@ you can do so. However, you should consider the following issues:
|
||||
since the CGLIB proxy instance is created through Objenesis. Only if your JVM does
|
||||
not allow for constructor bypassing, you might see double invocations and
|
||||
corresponding debug log entries from Spring's AOP support.
|
||||
* Your CGLIB proxy usage may face limitations with the JDK 9+ platform module system.
|
||||
As a typical case, you cannot create a CGLIB proxy for a class from the `java.lang`
|
||||
package when deploying on the module path. Such cases require a JVM bootstrap flag
|
||||
`--add-opens=java.base/java.lang=ALL-UNNAMED` which is not available for modules.
|
||||
|
||||
To force the use of CGLIB proxies, set the value of the `proxy-target-class` attribute
|
||||
of the `<aop:config>` element to true, as follows:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
= Further Resources
|
||||
:page-section-summary-toc: 1
|
||||
|
||||
More information on AspectJ can be found on the {aspectj-site}[AspectJ website].
|
||||
More information on AspectJ can be found on the https://www.eclipse.org/aspectj[AspectJ website].
|
||||
|
||||
_Eclipse AspectJ_ by Adrian Colyer et. al. (Addison-Wesley, 2005) provides a
|
||||
comprehensive introduction and reference for the AspectJ language.
|
||||
|
||||
@@ -66,7 +66,7 @@ Kotlin::
|
||||
When used as a marker interface in this way, Spring configures new instances of the
|
||||
annotated type (`Account`, in this case) by using a bean definition (typically
|
||||
prototype-scoped) with the same name as the fully-qualified type name
|
||||
(`com.xyz.domain.Account`). Since the default name for a bean defined via XML is the
|
||||
(`com.xyz.domain.Account`). Since the default name for a bean is the
|
||||
fully-qualified name of its type, a convenient way to declare the prototype definition
|
||||
is to omit the `id` attribute, as the following example shows:
|
||||
|
||||
@@ -136,7 +136,7 @@ using Spring in accordance with the properties of the annotation". In this conte
|
||||
"initialization" refers to newly instantiated objects (for example, objects instantiated
|
||||
with the `new` operator) as well as to `Serializable` objects that are undergoing
|
||||
deserialization (for example, through
|
||||
{java-api}/java.base/java/io/Serializable.html[readResolve()]).
|
||||
https://docs.oracle.com/javase/8/docs/api/java/io/Serializable.html[readResolve()]).
|
||||
|
||||
[NOTE]
|
||||
=====
|
||||
@@ -168,19 +168,51 @@ Kotlin::
|
||||
|
||||
You can find more information about the language semantics of the various pointcut
|
||||
types in AspectJ
|
||||
{aspectj-docs-progguide}/semantics-joinPoints.html[in this appendix] of the
|
||||
{aspectj-docs-progguide}/index.html[AspectJ Programming Guide].
|
||||
https://www.eclipse.org/aspectj/doc/next/progguide/semantics-joinPoints.html[in this
|
||||
appendix] of the https://www.eclipse.org/aspectj/doc/next/progguide/index.html[AspectJ
|
||||
Programming Guide].
|
||||
=====
|
||||
|
||||
For this to work, the annotated types must be woven with the AspectJ weaver. You can
|
||||
either use a build-time Ant or Maven task to do this (see, for example, the
|
||||
{aspectj-docs-devguide}/antTasks.html[AspectJ Development
|
||||
https://www.eclipse.org/aspectj/doc/released/devguide/antTasks.html[AspectJ Development
|
||||
Environment Guide]) or load-time weaving (see xref:core/aop/using-aspectj.adoc#aop-aj-ltw[Load-time Weaving with AspectJ in the Spring Framework]). The
|
||||
`AnnotationBeanConfigurerAspect` itself needs to be configured by Spring (in order to obtain
|
||||
a reference to the bean factory that is to be used to configure new objects). You can define
|
||||
the related configuration as follows:
|
||||
a reference to the bean factory that is to be used to configure new objects). If you
|
||||
use Java-based configuration, you can add `@EnableSpringConfigured` to any
|
||||
`@Configuration` class, as follows:
|
||||
|
||||
include-code::./ApplicationConfiguration[tag=snippet,indent=0]
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim",role="primary"]
|
||||
----
|
||||
@Configuration
|
||||
@EnableSpringConfigured
|
||||
public class AppConfig {
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
|
||||
----
|
||||
@Configuration
|
||||
@EnableSpringConfigured
|
||||
class AppConfig {
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
If you prefer XML based configuration, the Spring
|
||||
xref:core/appendix/xsd-schemas.adoc#context[`context` namespace]
|
||||
defines a convenient `context:spring-configured` element, which you can use as follows:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim"]
|
||||
----
|
||||
<context:spring-configured/>
|
||||
----
|
||||
|
||||
Instances of `@Configurable` objects created before the aspect has been configured
|
||||
result in a message being issued to the debug log and no configuration of the
|
||||
@@ -367,7 +399,7 @@ The focus of this section is on configuring and using LTW in the specific contex
|
||||
Spring Framework. This section is not a general introduction to LTW. For full details on
|
||||
the specifics of LTW and configuring LTW with only AspectJ (with Spring not being
|
||||
involved at all), see the
|
||||
{aspectj-docs-devguide}/ltw.html[LTW section of the AspectJ
|
||||
https://www.eclipse.org/aspectj/doc/released/devguide/ltw.html[LTW section of the AspectJ
|
||||
Development Environment Guide].
|
||||
|
||||
The value that the Spring Framework brings to AspectJ LTW is in enabling much
|
||||
@@ -389,7 +421,7 @@ who typically are in charge of the deployment configuration, such as the launch
|
||||
Now that the sales pitch is over, let us first walk through a quick example of AspectJ
|
||||
LTW that uses Spring, followed by detailed specifics about elements introduced in the
|
||||
example. For a complete example, see the
|
||||
{spring-github-org}/spring-petclinic[Petclinic sample application].
|
||||
https://github.com/spring-projects/spring-petclinic[Petclinic sample application].
|
||||
|
||||
|
||||
[[aop-aj-ltw-first-example]]
|
||||
@@ -490,8 +522,8 @@ standard AspectJ. The following example shows the `aop.xml` file:
|
||||
<aspectj>
|
||||
|
||||
<weaver>
|
||||
<!-- only weave classes in our application-specific packages and sub-packages -->
|
||||
<include within="com.xyz..*"/>
|
||||
<!-- only weave classes in our application-specific packages -->
|
||||
<include within="com.xyz.*"/>
|
||||
</weaver>
|
||||
|
||||
<aspects>
|
||||
@@ -502,11 +534,6 @@ standard AspectJ. The following example shows the `aop.xml` file:
|
||||
</aspectj>
|
||||
----
|
||||
|
||||
NOTE: It is recommended to only weave specific classes (typically those in the
|
||||
application packages, as shown in the `aop.xml` example above) in order
|
||||
to avoid side effects such as AspectJ dump files and warnings.
|
||||
This is also a best practice from an efficiency perspective.
|
||||
|
||||
Now we can move on to the Spring-specific portion of the configuration. We need
|
||||
to configure a `LoadTimeWeaver` (explained later). This load-time weaver is the
|
||||
essential component responsible for weaving the aspect configuration in one or
|
||||
@@ -594,7 +621,7 @@ java -javaagent:C:/projects/xyz/lib/spring-instrument.jar com.xyz.Main
|
||||
----
|
||||
|
||||
The `-javaagent` is a flag for specifying and enabling
|
||||
{java-api}/java.instrument/java/lang/instrument/package-summary.html[agents
|
||||
https://docs.oracle.com/javase/8/docs/api/java/lang/instrument/package-summary.html[agents
|
||||
to instrument programs that run on the JVM]. The Spring Framework ships with such an
|
||||
agent, the `InstrumentationSavingAgent`, which is packaged in the
|
||||
`spring-instrument.jar` that was supplied as the value of the `-javaagent` argument in
|
||||
@@ -688,32 +715,13 @@ Furthermore, the compiled aspect classes need to be available on the classpath.
|
||||
|
||||
|
||||
[[aop-aj-ltw-aop_dot_xml]]
|
||||
=== `META-INF/aop.xml`
|
||||
=== 'META-INF/aop.xml'
|
||||
|
||||
The AspectJ LTW infrastructure is configured by using one or more `META-INF/aop.xml`
|
||||
files that are on the Java classpath (either directly or, more typically, in jar files).
|
||||
For example:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim"]
|
||||
----
|
||||
<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "https://www.eclipse.org/aspectj/dtd/aspectj.dtd">
|
||||
<aspectj>
|
||||
|
||||
<weaver>
|
||||
<!-- only weave classes in our application-specific packages and sub-packages -->
|
||||
<include within="com.xyz..*"/>
|
||||
</weaver>
|
||||
|
||||
</aspectj>
|
||||
----
|
||||
|
||||
NOTE: It is recommended to only weave specific classes (typically those in the
|
||||
application packages, as shown in the `aop.xml` example above) in order
|
||||
to avoid side effects such as AspectJ dump files and warnings.
|
||||
This is also a best practice from an efficiency perspective.
|
||||
|
||||
The structure and contents of this file is detailed in the LTW part of the
|
||||
{aspectj-docs-devguide}/ltw-configuration.html[AspectJ reference
|
||||
https://www.eclipse.org/aspectj/doc/released/devguide/ltw-configuration.html[AspectJ reference
|
||||
documentation]. Because the `aop.xml` file is 100% AspectJ, we do not describe it further here.
|
||||
|
||||
|
||||
@@ -752,9 +760,52 @@ adding one line. (Note that you almost certainly need to use an
|
||||
`ApplicationContext` as your Spring container -- typically, a `BeanFactory` is not
|
||||
enough because the LTW support uses `BeanFactoryPostProcessors`.)
|
||||
|
||||
To enable the Spring Framework's LTW support, you need to configure a `LoadTimeWeaver` as follows:
|
||||
To enable the Spring Framework's LTW support, you need to configure a `LoadTimeWeaver`,
|
||||
which typically is done by using the `@EnableLoadTimeWeaving` annotation, as follows:
|
||||
|
||||
include-code::./ApplicationConfiguration[tag=snippet,indent=0]
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim",role="primary"]
|
||||
----
|
||||
@Configuration
|
||||
@EnableLoadTimeWeaving
|
||||
public class AppConfig {
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
|
||||
----
|
||||
@Configuration
|
||||
@EnableLoadTimeWeaving
|
||||
class AppConfig {
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
Alternatively, if you prefer XML-based configuration, use the
|
||||
`<context:load-time-weaver/>` element. Note that the element is defined in the
|
||||
`context` namespace. The following example shows how to use `<context:load-time-weaver/>`:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim"]
|
||||
----
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/beans
|
||||
https://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/context
|
||||
https://www.springframework.org/schema/context/spring-context.xsd">
|
||||
|
||||
<context:load-time-weaver/>
|
||||
|
||||
</beans>
|
||||
----
|
||||
|
||||
The preceding configuration automatically defines and registers a number of LTW-specific
|
||||
infrastructure beans, such as a `LoadTimeWeaver` and an `AspectJWeavingEnabler`, for you.
|
||||
@@ -790,12 +841,63 @@ Note that the table lists only the `LoadTimeWeavers` that are autodetected when
|
||||
use the `DefaultContextLoadTimeWeaver`. You can specify exactly which `LoadTimeWeaver`
|
||||
implementation to use.
|
||||
|
||||
To configure a specific `LoadTimeWeaver`, implement the
|
||||
`LoadTimeWeavingConfigurer` interface and override the `getLoadTimeWeaver()` method
|
||||
(or use the XML equivalent).
|
||||
To specify a specific `LoadTimeWeaver` with Java configuration, implement the
|
||||
`LoadTimeWeavingConfigurer` interface and override the `getLoadTimeWeaver()` method.
|
||||
The following example specifies a `ReflectiveLoadTimeWeaver`:
|
||||
|
||||
include-code::./CustomWeaverConfiguration[tag=snippet,indent=0]
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim",role="primary"]
|
||||
----
|
||||
@Configuration
|
||||
@EnableLoadTimeWeaving
|
||||
public class AppConfig implements LoadTimeWeavingConfigurer {
|
||||
|
||||
@Override
|
||||
public LoadTimeWeaver getLoadTimeWeaver() {
|
||||
return new ReflectiveLoadTimeWeaver();
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
|
||||
----
|
||||
@Configuration
|
||||
@EnableLoadTimeWeaving
|
||||
class AppConfig : LoadTimeWeavingConfigurer {
|
||||
|
||||
override fun getLoadTimeWeaver(): LoadTimeWeaver {
|
||||
return ReflectiveLoadTimeWeaver()
|
||||
}
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
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`:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim"]
|
||||
----
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/beans
|
||||
https://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/context
|
||||
https://www.springframework.org/schema/context/spring-context.xsd">
|
||||
|
||||
<context:load-time-weaver
|
||||
weaver-class="org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver"/>
|
||||
|
||||
</beans>
|
||||
----
|
||||
|
||||
The `LoadTimeWeaver` that is defined and registered by the configuration can be later
|
||||
retrieved from the Spring container by using the well known name, `loadTimeWeaver`.
|
||||
|
||||
@@ -15,13 +15,10 @@ Applying such optimizations early implies the following restrictions:
|
||||
|
||||
* The classpath is fixed and fully defined at build time.
|
||||
* The beans defined in your application cannot change at runtime, meaning:
|
||||
** `@Profile`, in particular profile-specific configuration, needs to be chosen at build time and is automatically enabled at runtime when AOT is enabled.
|
||||
** `@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) cannot be transformed ahead-of-time.
|
||||
* Beans registered as singletons (using `registerSingleton`, typically from
|
||||
`ConfigurableListableBeanFactory`) cannot be transformed ahead-of-time either.
|
||||
* As we cannot rely on the instance, make sure that the bean type is as precise as
|
||||
possible.
|
||||
* Bean definitions with instance suppliers (lambdas or method references) cannot be transformed ahead-of-time (see related https://github.com/spring-projects/spring-framework/issues/29555[spring-framework#29555] issue).
|
||||
* Make sure that the bean type is as precise as possible.
|
||||
|
||||
TIP: See also the xref:core/aot.adoc#aot.bestpractices[] section.
|
||||
|
||||
@@ -30,7 +27,7 @@ A Spring AOT processed application typically generates:
|
||||
|
||||
* Java source code
|
||||
* Bytecode (usually for dynamic proxies)
|
||||
* {spring-framework-api}/aot/hint/RuntimeHints.html[`RuntimeHints`] for the use of reflection, resource loading, serialization, and JDK proxies
|
||||
* {api-spring-framework}/aot/hint/RuntimeHints.html[`RuntimeHints`] for the use of reflection, resource loading, serialization, and JDK proxies.
|
||||
|
||||
NOTE: At the moment, AOT is focused on allowing Spring applications to be deployed as native images using GraalVM.
|
||||
We intend to support more JVM-based use cases in future generations.
|
||||
@@ -38,7 +35,7 @@ We intend to support more JVM-based use cases in future generations.
|
||||
[[aot.basics]]
|
||||
== AOT engine overview
|
||||
|
||||
The entry point of the AOT engine for processing an `ApplicationContext` is `ApplicationContextAotGenerator`. It takes care of the following steps, based on a `GenericApplicationContext` that represents the application to optimize and a {spring-framework-api}/aot/generate/GenerationContext.html[`GenerationContext`]:
|
||||
The entry point of the AOT engine for processing an `ApplicationContext` arrangement is `ApplicationContextAotGenerator`. It takes care of the following steps, based on a `GenericApplicationContext` that represents the application to optimize and a {api-spring-framework}/aot/generate/GenerationContext.html[`GenerationContext`]:
|
||||
|
||||
* Refresh an `ApplicationContext` for AOT processing. Contrary to a traditional refresh, this version only creates bean definitions, not bean instances.
|
||||
* Invoke the available `BeanFactoryInitializationAotProcessor` implementations and apply their contributions against the `GenerationContext`.
|
||||
@@ -70,14 +67,7 @@ include-code::./AotProcessingSample[tag=aotcontext]
|
||||
In this mode, xref:core/beans/factory-extension.adoc#beans-factory-extension-factory-postprocessors[`BeanFactoryPostProcessor` implementations] are invoked as usual.
|
||||
This includes configuration class parsing, import selectors, classpath scanning, etc.
|
||||
Such steps make sure that the `BeanRegistry` contains the relevant bean definitions for the application.
|
||||
If bean definitions are guarded by conditions (such as `@Profile`), these are evaluated,
|
||||
and bean definitions that don't match their conditions are discarded at this stage.
|
||||
|
||||
If custom code needs to register extra beans programmatically, make sure that custom
|
||||
registration code uses `BeanDefinitionRegistry` instead of `BeanFactory` as only bean
|
||||
definitions are taken into account. A good pattern is to implement
|
||||
`ImportBeanDefinitionRegistrar` and register it via an `@Import` on one of your
|
||||
configuration classes.
|
||||
If bean definitions are guarded by conditions (such as `@Profile`), these are discarded at this stage.
|
||||
|
||||
Because this mode does not actually create bean instances, `BeanPostProcessor` implementations are not invoked, except for specific variants that are relevant for AOT processing.
|
||||
These are:
|
||||
@@ -91,15 +81,15 @@ Once this part completes, the `BeanFactory` contains the bean definitions that a
|
||||
[[aot.bean-factory-initialization-contributions]]
|
||||
== Bean Factory Initialization AOT Contributions
|
||||
|
||||
Components that want to participate in this step can implement the {spring-framework-api}/beans/factory/aot/BeanFactoryInitializationAotProcessor.html[`BeanFactoryInitializationAotProcessor`] interface.
|
||||
Components that want to participate in this step can implement the {api-spring-framework}/beans/factory/aot/BeanFactoryInitializationAotProcessor.html[`BeanFactoryInitializationAotProcessor`] interface.
|
||||
Each implementation can return an AOT contribution, based on the state of the bean factory.
|
||||
|
||||
An AOT contribution is a component that contributes generated code which reproduces a particular behavior.
|
||||
An AOT contribution is a component that contributes generated code that reproduces a particular behavior.
|
||||
It can also contribute `RuntimeHints` to indicate the need for reflection, resource loading, serialization, or JDK proxies.
|
||||
|
||||
A `BeanFactoryInitializationAotProcessor` implementation can be registered in `META-INF/spring/aot.factories` with a key equal to the fully-qualified name of the interface.
|
||||
A `BeanFactoryInitializationAotProcessor` implementation can be registered in `META-INF/spring/aot.factories` with a key equal to the fully qualified name of the interface.
|
||||
|
||||
The `BeanFactoryInitializationAotProcessor` interface can also be implemented directly by a bean.
|
||||
A `BeanFactoryInitializationAotProcessor` can also be implemented directly by a bean.
|
||||
In this mode, the bean provides an AOT contribution equivalent to the feature it provides with a regular runtime.
|
||||
Consequently, such a bean is automatically excluded from the AOT-optimized context.
|
||||
|
||||
@@ -121,7 +111,7 @@ This interface is used as follows:
|
||||
|
||||
* Implemented by a `BeanPostProcessor` bean, to replace its runtime behavior.
|
||||
For instance xref:core/beans/factory-extension.adoc#beans-factory-extension-bpp-examples-aabpp[`AutowiredAnnotationBeanPostProcessor`] implements this interface to generate code that injects members annotated with `@Autowired`.
|
||||
* Implemented by a type registered in `META-INF/spring/aot.factories` with a key equal to the fully-qualified name of the interface.
|
||||
* Implemented by a type registered in `META-INF/spring/aot.factories` with a key equal to the fully qualified name of the interface.
|
||||
Typically used when the bean definition needs to be tuned for specific features of the core framework.
|
||||
|
||||
[NOTE]
|
||||
@@ -152,23 +142,8 @@ Java::
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
class DataSourceConfiguration {
|
||||
|
||||
@Bean
|
||||
fun dataSource() = SimpleDataSource()
|
||||
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
WARNING: Kotlin class names with backticks that use invalid Java identifiers (not starting with a letter, containing spaces, etc.) are not supported.
|
||||
|
||||
Since there isn't any particular condition on this class, `dataSourceConfiguration` and `dataSource` are identified as candidates.
|
||||
The AOT engine will convert the configuration class above to code similar to the following:
|
||||
|
||||
@@ -181,7 +156,6 @@ Java::
|
||||
/**
|
||||
* Bean definitions for {@link DataSourceConfiguration}
|
||||
*/
|
||||
@Generated
|
||||
public class DataSourceConfiguration__BeanDefinitions {
|
||||
/**
|
||||
* Get the bean definition for 'dataSourceConfiguration'
|
||||
@@ -216,25 +190,11 @@ Java::
|
||||
|
||||
NOTE: The exact code generated may differ depending on the exact nature of your bean definitions.
|
||||
|
||||
TIP: Each generated class is annotated with `org.springframework.aot.generate.Generated` to
|
||||
identify them if they need to be excluded, for instance by static analysis tools.
|
||||
|
||||
The generated code above creates bean definitions equivalent to the `@Configuration` class, but in a direct way and without the use of reflection if at all possible.
|
||||
There is a bean definition for `dataSourceConfiguration` and one for `dataSourceBean`.
|
||||
When a `datasource` instance is required, a `BeanInstanceSupplier` is called.
|
||||
This supplier invokes the `dataSource()` method on the `dataSourceConfiguration` bean.
|
||||
|
||||
[[aot.running]]
|
||||
== Running with AOT optimizations
|
||||
|
||||
AOT is a mandatory step to transform a Spring application to a native executable, so it
|
||||
is automatically enabled when running in this mode. It is possible to use those optimizations
|
||||
on the JVM by setting the `spring.aot.enabled` System property to `true`.
|
||||
|
||||
NOTE: When AOT optimizations are included, some decisions that have been taken at build-time
|
||||
are hard-coded in the application setup. For instance, profiles that have been enabled at
|
||||
build-time are automatically enabled at runtime as well.
|
||||
|
||||
[[aot.bestpractices]]
|
||||
== Best Practices
|
||||
|
||||
@@ -243,33 +203,11 @@ However, keep in mind that some optimizations are made at build time based on a
|
||||
|
||||
This section lists the best practices that make sure your application is ready for AOT.
|
||||
|
||||
[[aot.bestpractices.bean-registration]]
|
||||
== Programmatic bean registration
|
||||
|
||||
The AOT engine takes care of the `@Configuration` model and any callback that might be
|
||||
invoked as part of processing your configuration. If you need to register additional
|
||||
beans programmatically, make sure to use a `BeanDefinitionRegistry` to register
|
||||
bean definitions.
|
||||
|
||||
This can typically be done via a `BeanDefinitionRegistryPostProcessor`. Note that, if it
|
||||
is registered itself as a bean, it will be invoked again at runtime unless you make
|
||||
sure to implement `BeanFactoryInitializationAotProcessor` as well. A more idiomatic
|
||||
way is to implement `ImportBeanDefinitionRegistrar` and register it using `@Import` on
|
||||
one of your configuration classes. This invokes your custom code as part of configuration
|
||||
class parsing.
|
||||
|
||||
If you declare additional beans programmatically using a different callback, they are
|
||||
likely not going to be handled by the AOT engine, and therefore no hints are going to be
|
||||
generated for them. Depending on the environment, those beans may not be registered at
|
||||
all. For instance, classpath scanning does not work in a native image as there is no
|
||||
notion of a classpath. For cases like this, it is crucial that the scanning happens at
|
||||
build time.
|
||||
|
||||
[[aot.bestpractices.bean-type]]
|
||||
=== Expose The Most Precise Bean Type
|
||||
|
||||
While your application may interact with an interface that a bean implements, it is still very important to declare the most precise type.
|
||||
The AOT engine performs additional checks on the bean type, such as detecting the presence of `@Autowired` members or lifecycle callback methods.
|
||||
The AOT engine performs additional checks on the bean type, such as detecting the presence of `@Autowired` members, or lifecycle callback methods.
|
||||
|
||||
For `@Configuration` classes, make sure that the return type of the factory `@Bean` method is as precise as possible.
|
||||
Consider the following example:
|
||||
@@ -318,27 +256,6 @@ Java::
|
||||
|
||||
If you are registering bean definitions programmatically, consider using `RootBeanBefinition` as it allows to specify a `ResolvableType` that handles generics.
|
||||
|
||||
[[aot.bestpractices.constructors]]
|
||||
=== Avoid Multiple Constructors
|
||||
|
||||
The container is able to choose the most appropriate constructor to use based on several candidates.
|
||||
However, this is not a best practice and flagging the preferred constructor with `@Autowired` if necessary is preferred.
|
||||
|
||||
In case you are working on a code base that you cannot modify, you can set the {spring-framework-api}/beans/factory/support/AbstractBeanDefinition.html#PREFERRED_CONSTRUCTORS_ATTRIBUTE[`preferredConstructors` attribute] on the related bean definition to indicate which constructor should be used.
|
||||
|
||||
[[aot.bestpractices.custom-arguments]]
|
||||
=== Avoid Creating Bean with Custom Arguments
|
||||
|
||||
Spring AOT detects what needs to be done to create a bean and translates that in generated code using an instance supplier.
|
||||
The container also supports creating a bean with {spring-framework-api}++/beans/factory/BeanFactory.html#getBean(java.lang.String,java.lang.Object...)++[custom arguments] that leads to several issues with AOT:
|
||||
|
||||
. The custom arguments require dynamic introspection of a matching constructor or factory method.
|
||||
Those arguments cannot be detected by AOT, so the necessary reflection hints will have to be provided manually.
|
||||
. By-passing the instance supplier means that all other optimizations after creation are skipped as well.
|
||||
For instance, autowiring on fields and methods will be skipped as they are handled in the instance supplier.
|
||||
|
||||
Rather than having prototype-scoped beans created with custom arguments, we recommend a manual factory pattern where a bean is responsible for the creation of the instance.
|
||||
|
||||
[[aot.bestpractices.factory-bean]]
|
||||
=== FactoryBean
|
||||
|
||||
@@ -355,7 +272,7 @@ Java::
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
public class ClientFactoryBean<T extends AbstractClient> implements FactoryBean<T> {
|
||||
// ...
|
||||
|
||||
}
|
||||
----
|
||||
======
|
||||
@@ -452,10 +369,10 @@ Java::
|
||||
|
||||
Running an application as a native image requires additional information compared to a regular JVM runtime.
|
||||
For instance, GraalVM needs to know ahead of time if a component uses reflection.
|
||||
Similarly, classpath resources are not included in a native image unless specified explicitly.
|
||||
Similarly, classpath resources are not shipped in a native image unless specified explicitly.
|
||||
Consequently, if the application needs to load a resource, it must be referenced from the corresponding GraalVM native image configuration file.
|
||||
|
||||
The {spring-framework-api}/aot/hint/RuntimeHints.html[`RuntimeHints`] API collects the need for reflection, resource loading, serialization, and JDK proxies at runtime.
|
||||
The {api-spring-framework}/aot/hint/RuntimeHints.html[`RuntimeHints`] API collects the need for reflection, resource loading, serialization, and JDK proxies at runtime.
|
||||
The following example makes sure that `config/app.properties` can be loaded from the classpath at runtime within a native image:
|
||||
|
||||
[tabs]
|
||||
@@ -487,16 +404,16 @@ include-code::./SpellCheckService[]
|
||||
If at all possible, `@ImportRuntimeHints` should be used as close as possible to the component that requires the hints.
|
||||
This way, if the component is not contributed to the `BeanFactory`, the hints won't be contributed either.
|
||||
|
||||
It is also possible to register an implementation statically by adding an entry in `META-INF/spring/aot.factories` with a key equal to the fully-qualified name of the `RuntimeHintsRegistrar` interface.
|
||||
It is also possible to register an implementation statically by adding an entry in `META-INF/spring/aot.factories` with a key equal to the fully qualified name of the `RuntimeHintsRegistrar` interface.
|
||||
|
||||
|
||||
[[aot.hints.reflective]]
|
||||
=== `@Reflective`
|
||||
|
||||
{spring-framework-api}/aot/hint/annotation/Reflective.html[`@Reflective`] provides an idiomatic way to flag the need for reflection on an annotated element.
|
||||
{api-spring-framework}/aot/hint/annotation/Reflective.html[`@Reflective`] provides an idiomatic way to flag the need for reflection on an annotated element.
|
||||
For instance, `@EventListener` is meta-annotated with `@Reflective` since the underlying implementation invokes the annotated method using reflection.
|
||||
|
||||
By default, only Spring beans are considered, and an invocation hint is registered for the annotated element.
|
||||
By default, only Spring beans are considered and an invocation hint is registered for the annotated element.
|
||||
This can be tuned by specifying a custom `ReflectiveProcessor` implementation via the
|
||||
`@Reflective` annotation.
|
||||
|
||||
@@ -507,7 +424,7 @@ If components other than Spring beans need to be processed, a `BeanFactoryInitia
|
||||
[[aot.hints.register-reflection-for-binding]]
|
||||
=== `@RegisterReflectionForBinding`
|
||||
|
||||
{spring-framework-api}/aot/hint/annotation/RegisterReflectionForBinding.html[`@RegisterReflectionForBinding`] is a specialization of `@Reflective` that registers the need for serializing arbitrary types.
|
||||
{api-spring-framework}/aot/hint/annotation/RegisterReflectionForBinding.html[`@RegisterReflectionForBinding`] is a specialization of `@Reflective` that registers the need for serializing arbitrary types.
|
||||
A typical use case is the use of DTOs that the container cannot infer, such as using a web client within a method body.
|
||||
|
||||
`@RegisterReflectionForBinding` can be applied to any Spring bean at the class level, but it can also be applied directly to a method, field, or constructor to better indicate where the hints are actually required.
|
||||
@@ -543,7 +460,7 @@ include-code::./SpellCheckServiceTests[tag=hintspredicates]
|
||||
With `RuntimeHintsPredicates`, we can check for reflection, resource, serialization, or proxy generation hints.
|
||||
This approach works well for unit tests but implies that the runtime behavior of a component is well known.
|
||||
|
||||
You can learn more about the global runtime behavior of an application by running its test suite (or the app itself) with the {graalvm-docs}/native-image/metadata/AutomaticMetadataCollection/[GraalVM tracing agent].
|
||||
You can learn more about the global runtime behavior of an application by running its test suite (or the app itself) with the {docs-graalvm}/native-image/metadata/AutomaticMetadataCollection/[GraalVM tracing agent].
|
||||
This agent will record all relevant calls requiring GraalVM hints at runtime and write them out as JSON configuration files.
|
||||
|
||||
For more targeted discovery and testing, Spring Framework ships a dedicated module with core AOT testing utilities, `"org.springframework:spring-core-test"`.
|
||||
@@ -575,4 +492,4 @@ io.spring.runtimehintstesting.SampleReflectionRuntimeHintsTests#lambda$shouldReg
|
||||
|
||||
There are various ways to configure this Java agent in your build, so please refer to the documentation of your build tool and test execution plugin.
|
||||
The agent itself can be configured to instrument specific packages (by default, only `org.springframework` is instrumented).
|
||||
You'll find more details in the {spring-framework-code}/buildSrc/README.md[Spring Framework `buildSrc` README] file.
|
||||
You'll find more details in the {spring-framework-main-code}/buildSrc/README.md[Spring Framework `buildSrc` README] file.
|
||||
|
||||
@@ -765,7 +765,7 @@ want to add an additional attribute to the existing bean definition element.
|
||||
|
||||
By way of another example, suppose that you define a bean definition for a
|
||||
service object that (unknown to it) accesses a clustered
|
||||
{JSR}107[JCache], and you want to ensure that the
|
||||
https://jcp.org/en/jsr/detail?id=107[JCache], and you want to ensure that the
|
||||
named JCache instance is eagerly started within the surrounding cluster.
|
||||
The following listing shows such a definition:
|
||||
|
||||
|
||||
@@ -66,13 +66,13 @@ developer's intent ("`inject this constant value`"), and it reads better:
|
||||
[[xsd-schemas-util-frfb]]
|
||||
==== Setting a Bean Property or Constructor Argument from a Field Value
|
||||
|
||||
{spring-framework-api}/beans/factory/config/FieldRetrievingFactoryBean.html[`FieldRetrievingFactoryBean`]
|
||||
{api-spring-framework}/beans/factory/config/FieldRetrievingFactoryBean.html[`FieldRetrievingFactoryBean`]
|
||||
is a `FactoryBean` that retrieves a `static` or non-static field value. It is typically
|
||||
used for retrieving `public` `static` `final` constants, which may then be used to set a
|
||||
property value or constructor argument for another bean.
|
||||
|
||||
The following example shows how a `static` field is exposed, by using the
|
||||
{spring-framework-api}/beans/factory/config/FieldRetrievingFactoryBean.html#setStaticField(java.lang.String)[`staticField`]
|
||||
{api-spring-framework}/beans/factory/config/FieldRetrievingFactoryBean.html#setStaticField(java.lang.String)[`staticField`]
|
||||
property:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
@@ -109,7 +109,7 @@ to be specified for the bean reference, as the following example shows:
|
||||
|
||||
You can also access a non-static (instance) field of another bean, as
|
||||
described in the API documentation for the
|
||||
{spring-framework-api}/beans/factory/config/FieldRetrievingFactoryBean.html[`FieldRetrievingFactoryBean`]
|
||||
{api-spring-framework}/beans/factory/config/FieldRetrievingFactoryBean.html[`FieldRetrievingFactoryBean`]
|
||||
class.
|
||||
|
||||
Injecting enumeration values into beans as either property or constructor arguments is
|
||||
@@ -565,17 +565,6 @@ is a convenience mechanism that sets up a xref:core/beans/factory-extension.adoc
|
||||
for you. If you need more control over the specific
|
||||
`PropertySourcesPlaceholderConfigurer` setup, you can explicitly define it as a bean yourself.
|
||||
|
||||
[WARNING]
|
||||
=====
|
||||
Only one such element should be defined for a given application with the properties
|
||||
that it needs. Several property placeholders can be configured as long as they have distinct
|
||||
placeholder syntax (`${...}`).
|
||||
|
||||
If you need to modularize the source of properties used for the replacement, you should
|
||||
not create multiple properties placeholders. Rather, each module should contribute a
|
||||
`PropertySource` to the `Environment`. Alternatively, you can create your own
|
||||
`PropertySourcesPlaceholderConfigurer` bean that gathers the properties to use.
|
||||
=====
|
||||
|
||||
[[xsd-schemas-context-ac]]
|
||||
=== Using `<annotation-config/>`
|
||||
|
||||
@@ -1,16 +1,33 @@
|
||||
[[beans-annotation-config]]
|
||||
= Annotation-based Container Configuration
|
||||
|
||||
Spring provides comprehensive support for annotation-based configuration, operating on
|
||||
metadata in the component class itself by using annotations on the relevant class,
|
||||
method, or field declaration. As mentioned in
|
||||
xref:core/beans/factory-extension.adoc#beans-factory-extension-bpp-examples-aabpp[Example: The `AutowiredAnnotationBeanPostProcessor`],
|
||||
Spring uses `BeanPostProcessors` in conjunction with annotations to make the core IOC
|
||||
container aware of specific annotations.
|
||||
.Are annotations better than XML for configuring Spring?
|
||||
****
|
||||
The introduction of annotation-based configuration raised the question of whether this
|
||||
approach is "`better`" than XML. The short answer is "`it depends.`" The long answer is
|
||||
that each approach has its pros and cons, and, usually, it is up to the developer to
|
||||
decide which strategy suits them better. Due to the way they are defined, annotations
|
||||
provide a lot of context in their declaration, leading to shorter and more concise
|
||||
configuration. However, XML excels at wiring up components without touching their source
|
||||
code or recompiling them. Some developers prefer having the wiring close to the source
|
||||
while others argue that annotated classes are no longer POJOs and, furthermore, that the
|
||||
configuration becomes decentralized and harder to control.
|
||||
|
||||
For example, the xref:core/beans/annotation-config/autowired.adoc[`@Autowired`]
|
||||
annotation provides the same capabilities as described in
|
||||
xref:core/beans/dependencies/factory-autowire.adoc[Autowiring Collaborators] but
|
||||
No matter the choice, Spring can accommodate both styles and even mix them together.
|
||||
It is worth pointing out that through its xref:core/beans/java.adoc[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
|
||||
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 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 xref:core/beans/factory-extension.adoc#beans-factory-extension-bpp-examples-aabpp[Example: The `AutowiredAnnotationBeanPostProcessor`], using a
|
||||
`BeanPostProcessor` in conjunction with annotations is a common means of extending the
|
||||
Spring IoC container. For example, the xref:core/beans/annotation-config/autowired.adoc[`@Autowired`]
|
||||
annotation provides the same capabilities as described in xref:core/beans/dependencies/factory-autowire.adoc[Autowiring Collaborators] 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
|
||||
@@ -19,16 +36,13 @@ can be found in the xref:core/beans/standard-annotations.adoc[relevant section].
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
Annotation injection is performed before external property injection. Thus, external
|
||||
configuration (e.g. XML-specified bean properties) effectively overrides the annotations
|
||||
for properties when wired through mixed approaches.
|
||||
Annotation injection is performed before XML injection. Thus, the XML configuration
|
||||
overrides the annotations for properties wired through both approaches.
|
||||
====
|
||||
|
||||
Technically, you can register the post-processors as individual bean definitions, but they
|
||||
are implicitly registered in an `AnnotationConfigApplicationContext` already.
|
||||
|
||||
In an XML-based Spring setup, you may include the following configuration tag to enable
|
||||
mixing and matching with annotation-based configuration:
|
||||
As always, you can register the post-processors as individual bean definitions, but they
|
||||
can also be implicitly registered by including the following tag in an XML-based Spring
|
||||
configuration (notice the inclusion of the `context` namespace):
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@@ -48,11 +62,11 @@ mixing and matching with annotation-based configuration:
|
||||
|
||||
The `<context:annotation-config/>` element implicitly registers the following post-processors:
|
||||
|
||||
* {spring-framework-api}/context/annotation/ConfigurationClassPostProcessor.html[`ConfigurationClassPostProcessor`]
|
||||
* {spring-framework-api}/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.html[`AutowiredAnnotationBeanPostProcessor`]
|
||||
* {spring-framework-api}/context/annotation/CommonAnnotationBeanPostProcessor.html[`CommonAnnotationBeanPostProcessor`]
|
||||
* {spring-framework-api}/orm/jpa/support/PersistenceAnnotationBeanPostProcessor.html[`PersistenceAnnotationBeanPostProcessor`]
|
||||
* {spring-framework-api}/context/event/EventListenerMethodProcessor.html[`EventListenerMethodProcessor`]
|
||||
* {api-spring-framework}/context/annotation/ConfigurationClassPostProcessor.html[`ConfigurationClassPostProcessor`]
|
||||
* {api-spring-framework}/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.html[`AutowiredAnnotationBeanPostProcessor`]
|
||||
* {api-spring-framework}/context/annotation/CommonAnnotationBeanPostProcessor.html[`CommonAnnotationBeanPostProcessor`]
|
||||
* {api-spring-framework}/orm/jpa/support/PersistenceAnnotationBeanPostProcessor.html[`PersistenceAnnotationBeanPostProcessor`]
|
||||
* {api-spring-framework}/context/event/EventListenerMethodProcessor.html[`EventListenerMethodProcessor`]
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
|
||||
+3
-46
@@ -1,5 +1,5 @@
|
||||
[[beans-autowired-annotation-primary]]
|
||||
= Fine-tuning Annotation-based Autowiring with `@Primary` or `@Fallback`
|
||||
= Fine-tuning Annotation-based Autowiring with `@Primary`
|
||||
|
||||
Because autowiring by type may lead to multiple candidates, it is often necessary to have
|
||||
more control over the selection process. One way to accomplish this is with Spring's
|
||||
@@ -50,51 +50,8 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
Alternatively, as of 6.2, there is a `@Fallback` annotation for demarcating
|
||||
any beans other than the regular ones to be injected. If only one regular
|
||||
bean is left, it is effectively primary as well:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
@Configuration
|
||||
public class MovieConfiguration {
|
||||
|
||||
@Bean
|
||||
public MovieCatalog firstMovieCatalog() { ... }
|
||||
|
||||
@Bean
|
||||
@Fallback
|
||||
public MovieCatalog secondMovieCatalog() { ... }
|
||||
|
||||
// ...
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
@Configuration
|
||||
class MovieConfiguration {
|
||||
|
||||
@Bean
|
||||
fun firstMovieCatalog(): MovieCatalog { ... }
|
||||
|
||||
@Bean
|
||||
@Fallback
|
||||
fun secondMovieCatalog(): MovieCatalog { ... }
|
||||
|
||||
// ...
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
With both variants of the preceding configuration, the following
|
||||
`MovieRecommender` is autowired with the `firstMovieCatalog`:
|
||||
With the preceding configuration, the following `MovieRecommender` is autowired with the
|
||||
`firstMovieCatalog`:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
|
||||
+6
-9
@@ -1,13 +1,12 @@
|
||||
[[beans-autowired-annotation-qualifiers]]
|
||||
= Fine-tuning Annotation-based Autowiring with Qualifiers
|
||||
|
||||
`@Primary` and `@Fallback` are effective ways to use autowiring by type with several
|
||||
instances when one primary (or non-fallback) candidate can be determined.
|
||||
|
||||
When you need more control over the selection process, you can use Spring's `@Qualifier`
|
||||
annotation. You can associate qualifier values with specific arguments, narrowing the set
|
||||
of type matches so that a specific bean is chosen for each argument. In the simplest case,
|
||||
this can be a plain descriptive value, as shown in the following example:
|
||||
`@Primary` is an effective way to use autowiring by type with several instances when one
|
||||
primary candidate can be determined. When you need more control over the selection process,
|
||||
you can use Spring's `@Qualifier` annotation. You can associate qualifier values
|
||||
with specific arguments, narrowing the set of type matches so that a specific bean is
|
||||
chosen for each argument. In the simplest case, this can be a plain descriptive value, as
|
||||
shown in the following example:
|
||||
|
||||
--
|
||||
[tabs]
|
||||
@@ -156,8 +155,6 @@ If there is no other resolution indicator (such as a qualifier or a primary mark
|
||||
for a non-unique dependency situation, Spring matches the injection point name
|
||||
(that is, the field name or parameter name) against the target bean names and chooses the
|
||||
same-named candidate, if any.
|
||||
|
||||
Since version 6.1, this requires the `-parameters` Java compiler flag to be present.
|
||||
====
|
||||
|
||||
That said, if you intend to express annotation-driven injection by name, do not
|
||||
|
||||
@@ -186,15 +186,6 @@ implementation type, consider declaring the most specific return type on your fa
|
||||
method (at least as specific as required by the injection points referring to your bean).
|
||||
====
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
As of 4.3, `@Autowired` also considers self references for injection (that is, references
|
||||
back to the bean that is currently injected). Note that self injection is a fallback.
|
||||
In practice, you should use self references as a last resort only (for example, for
|
||||
calling other methods on the same instance through the bean's transactional proxy).
|
||||
Consider factoring out the affected methods to a separate delegate bean in such a scenario.
|
||||
====
|
||||
|
||||
You can also instruct Spring to provide all beans of a particular type from the
|
||||
`ApplicationContext` by adding the `@Autowired` annotation to a field or method that
|
||||
expects an array of that type, as the following example shows:
|
||||
@@ -277,12 +268,6 @@ use the same bean class). `@Order` values may influence priorities at injection
|
||||
but be aware that they do not influence singleton startup order, which is an
|
||||
orthogonal concern determined by dependency relationships and `@DependsOn` declarations.
|
||||
|
||||
Note that `@Order` annotations on configuration classes just influence the evaluation
|
||||
order within the overall set of configuration classes on startup. Such configuration-level
|
||||
order values do not affect the contained `@Bean` methods at all. For bean-level ordering,
|
||||
each `@Bean` method needs to have its own `@Order` annotation which applies within a
|
||||
set of multiple matches for the specific bean type (as returned by the factory method).
|
||||
|
||||
Note that the standard `jakarta.annotation.Priority` annotation is not available at the
|
||||
`@Bean` level, since it cannot be declared on methods. Its semantics can be modeled
|
||||
through `@Order` values in combination with `@Primary` on a single bean for each type.
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
[[beans-custom-autowire-configurer]]
|
||||
= Using `CustomAutowireConfigurer`
|
||||
|
||||
{spring-framework-api}/beans/factory/annotation/CustomAutowireConfigurer.html[`CustomAutowireConfigurer`]
|
||||
{api-spring-framework}/beans/factory/annotation/CustomAutowireConfigurer.html[`CustomAutowireConfigurer`]
|
||||
is a `BeanFactoryPostProcessor` that lets you register your own custom qualifier
|
||||
annotation types, even if they are not annotated with Spring's `@Qualifier` annotation.
|
||||
The following example shows how to use `CustomAutowireConfigurer`:
|
||||
|
||||
@@ -84,7 +84,7 @@ Kotlin::
|
||||
NOTE: The name provided with the annotation is resolved as a bean name by the
|
||||
`ApplicationContext` of which the `CommonAnnotationBeanPostProcessor` is aware.
|
||||
The names can be resolved through JNDI if you configure Spring's
|
||||
{spring-framework-api}/jndi/support/SimpleJndiBeanFactory.html[`SimpleJndiBeanFactory`]
|
||||
{api-spring-framework}/jndi/support/SimpleJndiBeanFactory.html[`SimpleJndiBeanFactory`]
|
||||
explicitly. However, we recommend that you rely on the default behavior and
|
||||
use Spring's JNDI lookup capabilities to preserve the level of indirection.
|
||||
|
||||
|
||||
+2
-2
@@ -101,8 +101,8 @@ NOTE: When configuring a `PropertySourcesPlaceholderConfigurer` using JavaConfig
|
||||
|
||||
Using the above configuration ensures Spring initialization failure if any `${}`
|
||||
placeholder could not be resolved. It is also possible to use methods like
|
||||
`setPlaceholderPrefix`, `setPlaceholderSuffix`, `setValueSeparator`, or
|
||||
`setEscapeCharacter` to customize placeholders.
|
||||
`setPlaceholderPrefix`, `setPlaceholderSuffix`, or `setValueSeparator` to customize
|
||||
placeholders.
|
||||
|
||||
NOTE: Spring Boot configures by default a `PropertySourcesPlaceholderConfigurer` bean that
|
||||
will get properties from `application.properties` and `application.yml` files.
|
||||
|
||||
@@ -2,28 +2,36 @@
|
||||
= Container Overview
|
||||
|
||||
The `org.springframework.context.ApplicationContext` interface represents the Spring IoC
|
||||
container and is responsible for instantiating, configuring, and assembling the beans.
|
||||
The container gets its instructions on the components to instantiate, configure, and
|
||||
assemble by reading configuration metadata. The configuration metadata can be represented
|
||||
as annotated component classes, configuration classes with factory methods, or external
|
||||
XML files or Groovy scripts. With either format, you may compose your application and the
|
||||
rich interdependencies between those components.
|
||||
container and is responsible for instantiating, configuring, and assembling the
|
||||
beans. The container gets its instructions on what objects to
|
||||
instantiate, configure, and assemble by reading configuration metadata. The
|
||||
configuration metadata is represented in XML, Java annotations, or Java code. It lets
|
||||
you express the objects that compose your application and the rich interdependencies
|
||||
between those objects.
|
||||
|
||||
Several implementations of the `ApplicationContext` interface are part of core Spring.
|
||||
In stand-alone applications, it is common to create an instance of
|
||||
{spring-framework-api}/context/annotation/AnnotationConfigApplicationContext.html[`AnnotationConfigApplicationContext`]
|
||||
or {spring-framework-api}/context/support/ClassPathXmlApplicationContext.html[`ClassPathXmlApplicationContext`].
|
||||
Several implementations of the `ApplicationContext` interface are supplied
|
||||
with Spring. In stand-alone applications, it is common to create an
|
||||
instance of
|
||||
{api-spring-framework}/context/support/ClassPathXmlApplicationContext.html[`ClassPathXmlApplicationContext`]
|
||||
or {api-spring-framework}/context/support/FileSystemXmlApplicationContext.html[`FileSystemXmlApplicationContext`].
|
||||
While XML has been the traditional format for defining configuration metadata, you can
|
||||
instruct the container to use Java annotations or code as the metadata format by
|
||||
providing a small amount of XML configuration to declaratively enable support for these
|
||||
additional metadata formats.
|
||||
|
||||
In most application scenarios, explicit user code is not required to instantiate one or
|
||||
more instances of a Spring IoC container. For example, in a plain web application scenario,
|
||||
a simple boilerplate web descriptor XML in the `web.xml` file of the application suffices (see
|
||||
more instances of a Spring IoC container. For example, in a web application scenario, a
|
||||
simple eight (or so) lines of boilerplate web descriptor XML in the `web.xml` file
|
||||
of the application typically suffices (see
|
||||
xref:core/beans/context-introduction.adoc#context-create[Convenient ApplicationContext Instantiation for Web Applications]).
|
||||
In a Spring Boot scenario, the application context is implicitly bootstrapped for you
|
||||
based on common setup conventions.
|
||||
If you use the https://spring.io/tools[Spring Tools for Eclipse] (an Eclipse-powered
|
||||
development environment), you can easily create this boilerplate configuration with a
|
||||
few mouse clicks or keystrokes.
|
||||
|
||||
The following diagram shows a high-level view of how Spring works. Your application classes
|
||||
are combined with configuration metadata so that, after the `ApplicationContext` is
|
||||
created and initialized, you have a fully configured and executable system or application.
|
||||
created and initialized, you have a fully configured and executable system or
|
||||
application.
|
||||
|
||||
.The Spring IoC container
|
||||
image::container-magic.png[]
|
||||
@@ -35,25 +43,33 @@ image::container-magic.png[]
|
||||
|
||||
As the preceding diagram shows, the Spring IoC container consumes a form of
|
||||
configuration metadata. This configuration metadata represents how you, as an
|
||||
application developer, tell the Spring container to instantiate, configure,
|
||||
and assemble the components in your application.
|
||||
application developer, tell the Spring container to instantiate, configure, and assemble
|
||||
the objects in your application.
|
||||
|
||||
Configuration metadata is traditionally supplied in a simple and intuitive XML format,
|
||||
which is what most of this chapter uses to convey key concepts and features of the
|
||||
Spring IoC container.
|
||||
|
||||
NOTE: XML-based metadata is not the only allowed form of configuration metadata.
|
||||
The Spring IoC container itself is totally decoupled from the format in which this
|
||||
configuration metadata is actually written. These days, many developers choose
|
||||
xref:core/beans/java.adoc[Java-based configuration] for their Spring applications:
|
||||
xref:core/beans/java.adoc[Java-based configuration] for their Spring applications.
|
||||
|
||||
For information about using other forms of metadata with the Spring container, see:
|
||||
|
||||
* xref:core/beans/annotation-config.adoc[Annotation-based configuration]: define beans using
|
||||
annotation-based configuration metadata on your application's component classes.
|
||||
annotation-based configuration metadata.
|
||||
* xref:core/beans/java.adoc[Java-based configuration]: define beans external to your application
|
||||
classes by using Java-based configuration classes. To use these features, see the
|
||||
{spring-framework-api}/context/annotation/Configuration.html[`@Configuration`],
|
||||
{spring-framework-api}/context/annotation/Bean.html[`@Bean`],
|
||||
{spring-framework-api}/context/annotation/Import.html[`@Import`],
|
||||
and {spring-framework-api}/context/annotation/DependsOn.html[`@DependsOn`] annotations.
|
||||
classes by using Java rather than XML files. To use these features, see the
|
||||
{api-spring-framework}/context/annotation/Configuration.html[`@Configuration`],
|
||||
{api-spring-framework}/context/annotation/Bean.html[`@Bean`],
|
||||
{api-spring-framework}/context/annotation/Import.html[`@Import`],
|
||||
and {api-spring-framework}/context/annotation/DependsOn.html[`@DependsOn`] annotations.
|
||||
|
||||
Spring configuration consists of at least one and typically more than one bean definition
|
||||
that the container must manage. Java configuration typically uses `@Bean`-annotated
|
||||
methods within a `@Configuration` class, each corresponding to one bean definition.
|
||||
Spring configuration consists of at least one and typically more than one bean
|
||||
definition that the container must manage. XML-based configuration metadata configures these
|
||||
beans as `<bean/>` elements inside a top-level `<beans/>` element. Java
|
||||
configuration typically uses `@Bean`-annotated methods within a `@Configuration` class.
|
||||
|
||||
These bean definitions correspond to the actual objects that make up your application.
|
||||
Typically, you define service layer objects, persistence layer objects such as
|
||||
@@ -63,14 +79,7 @@ Typically, one does not configure fine-grained domain objects in the container,
|
||||
it is usually the responsibility of repositories and business logic to create and load
|
||||
domain objects.
|
||||
|
||||
|
||||
|
||||
[[beans-factory-xml]]
|
||||
=== XML as an External Configuration DSL
|
||||
|
||||
XML-based configuration metadata configures these beans as `<bean/>` elements inside
|
||||
a top-level `<beans/>` element. The following example shows the basic structure of
|
||||
XML-based configuration metadata:
|
||||
The following example shows the basic structure of XML-based configuration metadata:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@@ -101,9 +110,14 @@ The value of the `id` attribute can be used to refer to collaborating objects. T
|
||||
for referring to collaborating objects is not shown in this example. See
|
||||
xref:core/beans/dependencies.adoc[Dependencies] for more information.
|
||||
|
||||
For instantiating a container, the location path or paths to the XML resource files
|
||||
need to be supplied to a `ClassPathXmlApplicationContext` constructor that let the
|
||||
container load configuration metadata from a variety of external resources, such
|
||||
|
||||
|
||||
[[beans-factory-instantiation]]
|
||||
== Instantiating a Container
|
||||
|
||||
The location path or paths
|
||||
supplied to an `ApplicationContext` constructor are resource strings that let
|
||||
the container load configuration metadata from a variety of external resources, such
|
||||
as the local file system, the Java `CLASSPATH`, and so on.
|
||||
|
||||
[tabs]
|
||||
@@ -127,7 +141,7 @@ Kotlin::
|
||||
====
|
||||
After you learn about Spring's IoC container, you may want to know more about Spring's
|
||||
`Resource` abstraction (as described in
|
||||
xref:core/resources.adoc[Resources])
|
||||
xref:web/webflux-webclient/client-builder.adoc#webflux-client-builder-reactor-resources[Resources])
|
||||
which provides a convenient mechanism for reading an InputStream from locations defined
|
||||
in a URI syntax. In particular, `Resource` paths are used to construct applications contexts,
|
||||
as described in xref:core/resources.adoc#resources-app-ctx[Application Contexts and Resource Paths].
|
||||
@@ -195,9 +209,9 @@ xref:core/beans/dependencies.adoc[Dependencies].
|
||||
It can be useful to have bean definitions span multiple XML files. Often, each individual
|
||||
XML configuration file represents a logical layer or module in your architecture.
|
||||
|
||||
You can use the `ClassPathXmlApplicationContext` constructor to load bean definitions from
|
||||
You can use the application context constructor to load bean definitions from all these
|
||||
XML fragments. This constructor takes multiple `Resource` locations, as was shown in the
|
||||
xref:core/beans/basics.adoc#beans-factory-xml[previous section]. Alternatively,
|
||||
xref:core/beans/basics.adoc#beans-factory-instantiation[previous section]. Alternatively,
|
||||
use one or more occurrences of the `<import/>` element to load bean definitions from
|
||||
another file or files. The following example shows how to do so:
|
||||
|
||||
@@ -245,7 +259,7 @@ configuration features beyond plain bean definitions are available in a selectio
|
||||
of XML namespaces provided by Spring -- for example, the `context` and `util` namespaces.
|
||||
|
||||
|
||||
[[beans-factory-groovy]]
|
||||
[[groovy-bean-definition-dsl]]
|
||||
=== The Groovy Bean Definition DSL
|
||||
|
||||
As a further example for externalized configuration metadata, bean definitions can also
|
||||
@@ -406,3 +420,4 @@ a dependency on a specific bean through metadata (such as an autowiring annotati
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ use these features.
|
||||
== `@Component` and Further Stereotype Annotations
|
||||
|
||||
The `@Repository` annotation is a marker for any class that fulfills the role or
|
||||
_stereotype_ of a repository (also known as Data Access Object or DAO). Among the uses
|
||||
stereotype of a repository (also known as Data Access Object or DAO). Among the uses
|
||||
of this marker is the automatic translation of exceptions, as described in
|
||||
xref:data-access/orm/general.adoc#orm-exception-translation[Exception Translation].
|
||||
|
||||
@@ -39,7 +39,7 @@ layers, respectively). Therefore, you can annotate your component classes with
|
||||
`@Component`, but, by annotating them with `@Repository`, `@Service`, or `@Controller`
|
||||
instead, your classes are more properly suited for processing by tools or associating
|
||||
with aspects. For example, these stereotype annotations make ideal targets for
|
||||
pointcuts. `@Repository`, `@Service`, and `@Controller` may also
|
||||
pointcuts. `@Repository`, `@Service`, and `@Controller` can also
|
||||
carry additional semantics in future releases of the Spring Framework. Thus, if you are
|
||||
choosing between using `@Component` or `@Service` for your service layer, `@Service` is
|
||||
clearly the better choice. Similarly, as stated earlier, `@Repository` is already
|
||||
@@ -191,7 +191,7 @@ Kotlin::
|
||||
======
|
||||
|
||||
For further details, see the
|
||||
{spring-framework-wiki}/Spring-Annotation-Programming-Model[Spring Annotation Programming Model]
|
||||
https://github.com/spring-projects/spring-framework/wiki/Spring-Annotation-Programming-Model[Spring Annotation Programming Model]
|
||||
wiki page.
|
||||
|
||||
|
||||
@@ -315,7 +315,7 @@ entries in the classpath. When you build JARs with Ant, make sure that you do no
|
||||
activate the files-only switch of the JAR task. Also, classpath directories may not be
|
||||
exposed based on security policies in some environments -- for example, standalone apps on
|
||||
JDK 1.7.0_45 and higher (which requires 'Trusted-Library' setup in your manifests -- see
|
||||
{stackoverflow-questions}/19394570/java-jre-7u45-breaks-classloader-getresources).
|
||||
https://stackoverflow.com/questions/19394570/java-jre-7u45-breaks-classloader-getresources).
|
||||
|
||||
On JDK 9's module path (Jigsaw), Spring's classpath scanning generally works as expected.
|
||||
However, make sure that your component classes are exported in your `module-info`
|
||||
@@ -664,36 +664,15 @@ analogous to how the container selects between multiple `@Autowired` constructor
|
||||
== Naming Autodetected Components
|
||||
|
||||
When a component is autodetected as part of the scanning process, its bean name is
|
||||
generated by the `BeanNameGenerator` strategy known to that scanner.
|
||||
generated by the `BeanNameGenerator` strategy known to that scanner. By default, any
|
||||
Spring stereotype annotation (`@Component`, `@Repository`, `@Service`, and
|
||||
`@Controller`) that contains a name `value` thereby provides that name to the
|
||||
corresponding bean definition.
|
||||
|
||||
By default, the `AnnotationBeanNameGenerator` is used. For Spring
|
||||
xref:core/beans/classpath-scanning.adoc#beans-stereotype-annotations[stereotype annotations],
|
||||
if you supply a name via the annotation's `value` attribute that name will be used as
|
||||
the name in the corresponding bean definition. This convention also applies when the
|
||||
following JSR-250 and JSR-330 annotations are used instead of Spring stereotype
|
||||
annotations: `@jakarta.annotation.ManagedBean`, `@javax.annotation.ManagedBean`,
|
||||
`@jakarta.inject.Named`, and `@javax.inject.Named`.
|
||||
|
||||
As of Spring Framework 6.1, the name of the annotation attribute that is used to specify
|
||||
the bean name is no longer required to be `value`. Custom stereotype annotations can
|
||||
declare an attribute with a different name (such as `name`) and annotate that attribute
|
||||
with `@AliasFor(annotation = Component.class, attribute = "value")`. See the source code
|
||||
declaration of `ControllerAdvice#name()` for a concrete example.
|
||||
|
||||
[WARNING]
|
||||
====
|
||||
As of Spring Framework 6.1, support for convention-based stereotype names is deprecated
|
||||
and will be removed in a future version of the framework. Consequently, custom stereotype
|
||||
annotations must use `@AliasFor` to declare an explicit alias for the `value` attribute
|
||||
in `@Component`. See the source code declaration of `Repository#value()` and
|
||||
`ControllerAdvice#name()` for concrete examples.
|
||||
====
|
||||
|
||||
If an explicit bean name cannot be derived from such an annotation or for any other
|
||||
detected component (such as those discovered by custom filters), the default bean name
|
||||
generator returns the uncapitalized non-qualified class name. For example, if the
|
||||
following component classes were detected, the names would be `myMovieLister` and
|
||||
`movieFinderImpl`.
|
||||
If such an annotation contains no name `value` or for any other detected component
|
||||
(such as those discovered by custom filters), the default bean name generator returns
|
||||
the uncapitalized non-qualified class name. For example, if the following component
|
||||
classes were detected, the names would be `myMovieLister` and `movieFinderImpl`:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
@@ -743,7 +722,7 @@ Kotlin::
|
||||
|
||||
If you do not want to rely on the default bean-naming strategy, you can provide a custom
|
||||
bean-naming strategy. First, implement the
|
||||
{spring-framework-api}/beans/factory/support/BeanNameGenerator.html[`BeanNameGenerator`]
|
||||
{api-spring-framework}/beans/factory/support/BeanNameGenerator.html[`BeanNameGenerator`]
|
||||
interface, and be sure to include a default no-arg constructor. Then, provide the fully
|
||||
qualified class name when configuring the scanner, as the following example annotation
|
||||
and bean definition show.
|
||||
@@ -840,7 +819,7 @@ possibly also declaring a custom scoped-proxy mode.
|
||||
|
||||
NOTE: To provide a custom strategy for scope resolution rather than relying on the
|
||||
annotation-based approach, you can implement the
|
||||
{spring-framework-api}/context/annotation/ScopeMetadataResolver.html[`ScopeMetadataResolver`]
|
||||
{api-spring-framework}/context/annotation/ScopeMetadataResolver.html[`ScopeMetadataResolver`]
|
||||
interface. Be sure to include a default no-arg constructor. Then you can provide the
|
||||
fully qualified class name when configuring the scanner, as the following example of both
|
||||
an annotation and a bean definition shows:
|
||||
@@ -1010,4 +989,68 @@ metadata is provided per-instance rather than per-class.
|
||||
|
||||
|
||||
|
||||
[[beans-scanning-index]]
|
||||
== Generating an Index of Candidate Components
|
||||
|
||||
While classpath scanning is very fast, it is possible to improve the startup performance
|
||||
of large applications by creating a static list of candidates at compilation time. In this
|
||||
mode, all modules that are targets of component scanning must use this mechanism.
|
||||
|
||||
NOTE: Your existing `@ComponentScan` or `<context:component-scan/>` directives must remain
|
||||
unchanged to request the context to scan candidates in certain packages. When the
|
||||
`ApplicationContext` detects such an index, it automatically uses it rather than scanning
|
||||
the classpath.
|
||||
|
||||
To generate the index, add an additional dependency to each module that contains
|
||||
components that are targets for component scan directives. The following example shows
|
||||
how to do so with Maven:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes,attributes"]
|
||||
----
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context-indexer</artifactId>
|
||||
<version>{spring-version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
----
|
||||
|
||||
With Gradle 4.5 and earlier, the dependency should be declared in the `compileOnly`
|
||||
configuration, as shown in the following example:
|
||||
|
||||
[source,groovy,indent=0,subs="verbatim,quotes,attributes"]
|
||||
----
|
||||
dependencies {
|
||||
compileOnly "org.springframework:spring-context-indexer:{spring-version}"
|
||||
}
|
||||
----
|
||||
|
||||
With Gradle 4.6 and later, the dependency should be declared in the `annotationProcessor`
|
||||
configuration, as shown in the following example:
|
||||
|
||||
[source,groovy,indent=0,subs="verbatim,quotes,attributes"]
|
||||
----
|
||||
dependencies {
|
||||
annotationProcessor "org.springframework:spring-context-indexer:{spring-version}"
|
||||
}
|
||||
----
|
||||
|
||||
The `spring-context-indexer` artifact generates a `META-INF/spring.components` file that
|
||||
is included in the jar file.
|
||||
|
||||
NOTE: When working with this mode in your IDE, the `spring-context-indexer` must be
|
||||
registered as an annotation processor to make sure the index is up-to-date when
|
||||
candidate components are updated.
|
||||
|
||||
TIP: The index is enabled automatically when a `META-INF/spring.components` file is found
|
||||
on the classpath. If an index is partially available for some libraries (or use cases)
|
||||
but could not be built for the whole application, you can fall back to a regular classpath
|
||||
arrangement (as though no index were present at all) by setting `spring.index.ignore` to
|
||||
`true`, either as a JVM system property or via the
|
||||
xref:appendix.adoc#appendix-spring-properties[`SpringProperties`] mechanism.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
As discussed in the xref:web/webmvc-view/mvc-xslt.adoc#mvc-view-xslt-beandefs[chapter introduction], the `org.springframework.beans.factory`
|
||||
package provides basic functionality for managing and manipulating beans, including in a
|
||||
programmatic way. The `org.springframework.context` package adds the
|
||||
{spring-framework-api}/context/ApplicationContext.html[`ApplicationContext`]
|
||||
{api-spring-framework}/context/ApplicationContext.html[`ApplicationContext`]
|
||||
interface, which extends the `BeanFactory` interface, in addition to extending other
|
||||
interfaces to provide additional functionality in a more application
|
||||
framework-oriented style. Many people use the `ApplicationContext` in a completely
|
||||
@@ -269,7 +269,7 @@ file format but is more flexible than the standard JDK based
|
||||
`ResourceBundleMessageSource` implementation. In particular, it allows for reading
|
||||
files from any Spring resource location (not only from the classpath) and supports hot
|
||||
reloading of bundle property files (while efficiently caching them in between).
|
||||
See the {spring-framework-api}/context/support/ReloadableResourceBundleMessageSource.html[`ReloadableResourceBundleMessageSource`]
|
||||
See the {api-spring-framework}/context/support/ReloadableResourceBundleMessageSource.html[`ReloadableResourceBundleMessageSource`]
|
||||
javadoc for details.
|
||||
|
||||
|
||||
@@ -478,21 +478,20 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
Notice that `ApplicationListener` is generically parameterized with the type of your custom event (`BlockedListEvent` in the preceding example).
|
||||
This means that the `onApplicationEvent()` method can remain type-safe, avoiding any need for downcasting.
|
||||
You can register as many event listeners as you wish, but note that, by default, event listeners receive events synchronously.
|
||||
This means that the `publishEvent()` method blocks until all listeners have finished processing the event.
|
||||
One advantage of this synchronous and single-threaded approach is that, when a listener receives an event,
|
||||
it operates inside the transaction context of the publisher if a transaction context is available.
|
||||
If another strategy for event publication becomes necessary, e.g. asynchronous event processing by default,
|
||||
see the javadoc for Spring's {spring-framework-api}/context/event/ApplicationEventMulticaster.html[`ApplicationEventMulticaster`] interface
|
||||
and {spring-framework-api}/context/event/SimpleApplicationEventMulticaster.html[`SimpleApplicationEventMulticaster`] implementation
|
||||
for configuration options which can be applied to a custom "applicationEventMulticaster" bean definition.
|
||||
In these cases, ThreadLocals and logging context are not propagated for the event processing.
|
||||
See xref:integration/observability.adoc#observability.application-events[the `@EventListener` Observability section]
|
||||
for more information on Observability concerns.
|
||||
|
||||
|
||||
Notice that `ApplicationListener` is generically parameterized with the type of your
|
||||
custom event (`BlockedListEvent` in the preceding example). This means that the
|
||||
`onApplicationEvent()` method can remain type-safe, avoiding any need for downcasting.
|
||||
You can register as many event listeners as you wish, but note that, by default, event
|
||||
listeners receive events synchronously. This means that the `publishEvent()` method
|
||||
blocks until all listeners have finished processing the event. One advantage of this
|
||||
synchronous and single-threaded approach is that, when a listener receives an event,
|
||||
it operates inside the transaction context of the publisher if a transaction context
|
||||
is available. If another strategy for event publication becomes necessary, e.g.
|
||||
asynchronous event processing by default, see the javadoc for Spring's
|
||||
{api-spring-framework}/context/event/ApplicationEventMulticaster.html[`ApplicationEventMulticaster`] interface
|
||||
and {api-spring-framework}/context/event/SimpleApplicationEventMulticaster.html[`SimpleApplicationEventMulticaster`]
|
||||
implementation for configuration options which can be applied to a custom
|
||||
"applicationEventMulticaster" bean definition.
|
||||
|
||||
The following example shows the bean definitions used to register and configure each of
|
||||
the classes above:
|
||||
@@ -529,7 +528,7 @@ notify appropriate parties.
|
||||
NOTE: Spring's eventing mechanism is designed for simple communication between Spring beans
|
||||
within the same application context. However, for more sophisticated enterprise
|
||||
integration needs, the separately maintained
|
||||
{spring-site-projects}/spring-integration/[Spring Integration] project provides
|
||||
https://projects.spring.io/spring-integration/[Spring Integration] project provides
|
||||
complete support for building lightweight,
|
||||
https://www.enterpriseintegrationpatterns.com[pattern-oriented], event-driven
|
||||
architectures that build upon the well-known Spring programming model.
|
||||
@@ -742,15 +741,12 @@ Be aware of the following limitations when using asynchronous events:
|
||||
|
||||
* If an asynchronous event listener throws an `Exception`, it is not propagated to the
|
||||
caller. See
|
||||
{spring-framework-api}/aop/interceptor/AsyncUncaughtExceptionHandler.html[`AsyncUncaughtExceptionHandler`]
|
||||
{api-spring-framework}/aop/interceptor/AsyncUncaughtExceptionHandler.html[`AsyncUncaughtExceptionHandler`]
|
||||
for more details.
|
||||
* Asynchronous event listener methods cannot publish a subsequent event by returning a
|
||||
value. If you need to publish another event as the result of the processing, inject an
|
||||
{spring-framework-api}/context/ApplicationEventPublisher.html[`ApplicationEventPublisher`]
|
||||
{api-spring-framework}/context/ApplicationEventPublisher.html[`ApplicationEventPublisher`]
|
||||
to publish the event manually.
|
||||
* ThreadLocals and logging context are not propagated by default for the event processing.
|
||||
See xref:integration/observability.adoc#observability.application-events[the `@EventListener` Observability section]
|
||||
for more information on Observability concerns.
|
||||
|
||||
|
||||
[[context-functionality-events-order]]
|
||||
@@ -1040,7 +1036,7 @@ and JMX support facilities. Application components can also interact with the ap
|
||||
server's JCA `WorkManager` through Spring's `TaskExecutor` abstraction.
|
||||
|
||||
See the javadoc of the
|
||||
{spring-framework-api}/jca/context/SpringContextResourceAdapter.html[`SpringContextResourceAdapter`]
|
||||
{api-spring-framework}/jca/context/SpringContextResourceAdapter.html[`SpringContextResourceAdapter`]
|
||||
class for the configuration details involved in RAR deployment.
|
||||
|
||||
For a simple deployment of a Spring ApplicationContext as a Jakarta EE RAR file:
|
||||
@@ -1050,7 +1046,7 @@ all application classes into a RAR file (which is a standard JAR file with a dif
|
||||
file extension).
|
||||
. Add all required library JARs into the root of the RAR archive.
|
||||
. Add a
|
||||
`META-INF/ra.xml` deployment descriptor (as shown in the {spring-framework-api}/jca/context/SpringContextResourceAdapter.html[javadoc for `SpringContextResourceAdapter`])
|
||||
`META-INF/ra.xml` deployment descriptor (as shown in the {api-spring-framework}/jca/context/SpringContextResourceAdapter.html[javadoc for `SpringContextResourceAdapter`])
|
||||
and the corresponding Spring XML bean definition file(s) (typically
|
||||
`META-INF/applicationContext.xml`).
|
||||
. Drop the resulting RAR file into your
|
||||
|
||||
@@ -44,7 +44,7 @@ weaver instance. This is particularly useful in combination with
|
||||
xref:data-access/orm/jpa.adoc[Spring's JPA support] where load-time weaving may be
|
||||
necessary for JPA class transformation.
|
||||
Consult the
|
||||
{spring-framework-api}/orm/jpa/LocalContainerEntityManagerFactoryBean.html[`LocalContainerEntityManagerFactoryBean`]
|
||||
{api-spring-framework}/orm/jpa/LocalContainerEntityManagerFactoryBean.html[`LocalContainerEntityManagerFactoryBean`]
|
||||
javadoc for more detail. For more on AspectJ load-time weaving, see xref:core/aop/using-aspectj.adoc#aop-aj-ltw[Load-time Weaving with AspectJ in the Spring Framework].
|
||||
|
||||
|
||||
|
||||
@@ -73,23 +73,7 @@ runtime (concurrently with live access to the factory) is not officially support
|
||||
lead to concurrent access exceptions, inconsistent state in the bean container, or both.
|
||||
====
|
||||
|
||||
[[beans-definition-overriding]]
|
||||
== Overriding Beans
|
||||
|
||||
Bean overriding is happening when a bean is registered using an identifier that is
|
||||
already allocated. While bean overriding is possible, it makes the configuration harder
|
||||
to read and this feature will be deprecated in a future release.
|
||||
|
||||
To disable bean overriding altogether, you can set the `allowBeanDefinitionOverriding`
|
||||
to `false` on the `ApplicationContext` before it is refreshed. In such setup, an
|
||||
exception is thrown if bean overriding is used.
|
||||
|
||||
By default, the container logs every bean overriding at `INFO` level so that you can
|
||||
adapt your configuration accordingly. While not recommended, you can silence those logs
|
||||
by setting the `allowBeanDefinitionOverriding` flag to `true`.
|
||||
|
||||
NOTE: We acknowledge that overriding beans in a test is convenient, and there is
|
||||
explicit support for this. For more details please refer to xref:testing/testcontext-framework/bean-overriding.adoc[this section].
|
||||
|
||||
[[beans-beanname]]
|
||||
== Naming Beans
|
||||
@@ -250,10 +234,6 @@ For details about the mechanism for supplying arguments to the constructor (if r
|
||||
and setting object instance properties after the object is constructed, see
|
||||
xref:core/beans/dependencies/factory-collaborators.adoc[Injecting Dependencies].
|
||||
|
||||
NOTE: In the case of constructor arguments, the container can select a corresponding
|
||||
constructor among several overloaded constructors. That said, to avoid ambiguities,
|
||||
it is recommended to keep your constructor signatures as straightforward as possible.
|
||||
|
||||
|
||||
[[beans-factory-class-static-factory-method]]
|
||||
=== Instantiation with a Static Factory Method
|
||||
@@ -314,24 +294,6 @@ For details about the mechanism for supplying (optional) arguments to the factor
|
||||
and setting object instance properties after the object is returned from the factory,
|
||||
see xref:core/beans/dependencies/factory-properties-detailed.adoc[Dependencies and Configuration in Detail].
|
||||
|
||||
NOTE: In the case of factory method arguments, the container can select a corresponding
|
||||
method among several overloaded methods of the same name. That said, to avoid ambiguities,
|
||||
it is recommended to keep your factory method signatures as straightforward as possible.
|
||||
|
||||
[TIP]
|
||||
====
|
||||
A typical problematic case with factory method overloading is Mockito with its many
|
||||
overloads of the `mock` method. Choose the most specific variant of `mock` possible:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
<bean id="clientService" class="org.mockito.Mockito" factory-method="mock">
|
||||
<constructor-arg type="java.lang.Class" value="examples.ClientService"/>
|
||||
<constructor-arg type="java.lang.String" value="clientService"/>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
[[beans-factory-class-instance-factory-method]]
|
||||
=== Instantiation by Using an Instance Factory Method
|
||||
@@ -454,8 +416,8 @@ Kotlin::
|
||||
======
|
||||
|
||||
This approach shows that the factory bean itself can be managed and configured through
|
||||
dependency injection (DI).
|
||||
See xref:core/beans/dependencies/factory-properties-detailed.adoc[Dependencies and Configuration in Detail].
|
||||
dependency injection (DI). See xref:core/beans/dependencies/factory-properties-detailed.adoc[Dependencies and Configuration in Detail]
|
||||
.
|
||||
|
||||
NOTE: In Spring documentation, "factory bean" refers to a bean that is configured in the
|
||||
Spring container and that creates objects through an
|
||||
@@ -482,3 +444,5 @@ cases into account and returns the type of object that a `BeanFactory.getBean` c
|
||||
going to return for the same bean name.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -10,25 +10,33 @@ pre-instantiation of a singleton bean by marking the bean definition as being
|
||||
lazy-initialized. A lazy-initialized bean tells the IoC container to create a bean
|
||||
instance when it is first requested, rather than at startup.
|
||||
|
||||
This behavior is controlled by the `@Lazy` annotation or in XML the `lazy-init` attribute on the `<bean/>` element, as
|
||||
the following example shows:
|
||||
In XML, this behavior is controlled by the `lazy-init` attribute on the `<bean/>`
|
||||
element, as the following example shows:
|
||||
|
||||
include-code::./ApplicationConfiguration[tag=snippet,indent=0]
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
<bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/>
|
||||
<bean name="not.lazy" class="com.something.AnotherBean"/>
|
||||
----
|
||||
|
||||
When the preceding configuration is consumed by an `ApplicationContext`, the `lazy` bean
|
||||
is not eagerly pre-instantiated when the `ApplicationContext` starts,
|
||||
whereas the `notLazy` one is eagerly pre-instantiated.
|
||||
whereas the `not.lazy` bean is eagerly pre-instantiated.
|
||||
|
||||
However, when a lazy-initialized bean is a dependency of a singleton bean that is
|
||||
not lazy-initialized, the `ApplicationContext` creates the lazy-initialized bean at
|
||||
startup, because it must satisfy the singleton's dependencies. The lazy-initialized bean
|
||||
is injected into a singleton bean elsewhere that is not lazy-initialized.
|
||||
|
||||
You can also control lazy-initialization for a set of beans by using the `@Lazy` annotation on your `@Configuration`
|
||||
annotated class or in XML using the `default-lazy-init` attribute on the `<beans/>` element, as the following example
|
||||
shows:
|
||||
You can also control lazy-initialization at the container level by using the
|
||||
`default-lazy-init` attribute on the `<beans/>` element, as the following example shows:
|
||||
|
||||
include-code::./LazyConfiguration[tag=snippet,indent=0]
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
<beans default-lazy-init="true">
|
||||
<!-- no beans will be pre-instantiated... -->
|
||||
</beans>
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -99,7 +99,7 @@ container, lets you handle this use case cleanly.
|
||||
|
||||
****
|
||||
You can read more about the motivation for Method Injection in
|
||||
{spring-site-blog}/2004/08/06/method-injection/[this blog entry].
|
||||
https://spring.io/blog/2004/08/06/method-injection/[this blog entry].
|
||||
****
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -51,7 +51,7 @@ XML configuration:
|
||||
|
||||
The preceding XML is more succinct. However, typos are discovered at runtime rather than
|
||||
design time, unless you use an IDE (such as https://www.jetbrains.com/idea/[IntelliJ
|
||||
IDEA] or the {spring-site-tools}[Spring Tools for Eclipse])
|
||||
IDEA] or the https://spring.io/tools[Spring Tools for Eclipse])
|
||||
that supports automatic property completion when you create bean definitions. Such IDE
|
||||
assistance is highly recommended.
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[[beans-environment]]
|
||||
= Environment Abstraction
|
||||
|
||||
The {spring-framework-api}/core/env/Environment.html[`Environment`] interface
|
||||
The {api-spring-framework}/core/env/Environment.html[`Environment`] interface
|
||||
is an abstraction integrated in the container that models two key
|
||||
aspects of the application environment: xref:core/beans/environment.adoc#beans-definition-profiles[profiles]
|
||||
and xref:core/beans/environment.adoc#beans-property-source-abstraction[properties].
|
||||
@@ -118,7 +118,7 @@ situation B. We start by updating our configuration to reflect this need.
|
||||
[[beans-definition-profiles-java]]
|
||||
=== Using `@Profile`
|
||||
|
||||
The {spring-framework-api}/context/annotation/Profile.html[`@Profile`]
|
||||
The {api-spring-framework}/context/annotation/Profile.html[`@Profile`]
|
||||
annotation lets you indicate that a component is eligible for registration
|
||||
when one or more specified profiles are active. Using our preceding example, we
|
||||
can rewrite the `dataSource` configuration as follows:
|
||||
@@ -516,8 +516,8 @@ as the following example shows:
|
||||
[[beans-definition-profiles-default]]
|
||||
=== Default Profile
|
||||
|
||||
The default profile represents the profile that is enabled if no profile is active. Consider
|
||||
the following example:
|
||||
The default profile represents the profile that is enabled by default. Consider the
|
||||
following example:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
@@ -558,13 +558,12 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
If xref:#beans-definition-profiles-enable[no profile is active], the `dataSource` is
|
||||
created. You can see this as a way to provide a default definition for one or more
|
||||
beans. If any profile is enabled, the default profile does not apply.
|
||||
If no profile is active, the `dataSource` is created. You can see this
|
||||
as a way to provide a default definition for one or more beans. If any
|
||||
profile is enabled, the default profile does not apply.
|
||||
|
||||
The name of the default profile is `default`. You can change the name of
|
||||
the default profile by using `setDefaultProfiles()` on the `Environment` or,
|
||||
declaratively, by using the `spring.profiles.default` property.
|
||||
You can change the name of the default profile by using `setDefaultProfiles()` on
|
||||
the `Environment` or, declaratively, by using the `spring.profiles.default` property.
|
||||
|
||||
|
||||
|
||||
@@ -599,17 +598,17 @@ Kotlin::
|
||||
|
||||
In the preceding snippet, we see a high-level way of asking Spring whether the `my-property` property is
|
||||
defined for the current environment. To answer this question, the `Environment` object performs
|
||||
a search over a set of {spring-framework-api}/core/env/PropertySource.html[`PropertySource`]
|
||||
a search over a set of {api-spring-framework}/core/env/PropertySource.html[`PropertySource`]
|
||||
objects. A `PropertySource` is a simple abstraction over any source of key-value pairs, and
|
||||
Spring's {spring-framework-api}/core/env/StandardEnvironment.html[`StandardEnvironment`]
|
||||
Spring's {api-spring-framework}/core/env/StandardEnvironment.html[`StandardEnvironment`]
|
||||
is configured with two PropertySource objects -- one representing the set of JVM system properties
|
||||
(`System.getProperties()`) and one representing the set of system environment variables
|
||||
(`System.getenv()`).
|
||||
|
||||
NOTE: These default property sources are present for `StandardEnvironment`, for use in standalone
|
||||
applications. {spring-framework-api}/web/context/support/StandardServletEnvironment.html[`StandardServletEnvironment`]
|
||||
applications. {api-spring-framework}/web/context/support/StandardServletEnvironment.html[`StandardServletEnvironment`]
|
||||
is populated with additional default property sources including servlet config, servlet
|
||||
context parameters, and a {spring-framework-api}/jndi/JndiPropertySource.html[`JndiPropertySource`]
|
||||
context parameters, and a {api-spring-framework}/jndi/JndiPropertySource.html[`JndiPropertySource`]
|
||||
if JNDI is available.
|
||||
|
||||
Concretely, when you use the `StandardEnvironment`, the call to `env.containsProperty("my-property")`
|
||||
@@ -663,7 +662,7 @@ Kotlin::
|
||||
In the preceding code, `MyPropertySource` has been added with highest precedence in the
|
||||
search. If it contains a `my-property` property, the property is detected and returned, in favor of
|
||||
any `my-property` property in any other `PropertySource`. The
|
||||
{spring-framework-api}/core/env/MutablePropertySources.html[`MutablePropertySources`]
|
||||
{api-spring-framework}/core/env/MutablePropertySources.html[`MutablePropertySources`]
|
||||
API exposes a number of methods that allow for precise manipulation of the set of
|
||||
property sources.
|
||||
|
||||
@@ -672,7 +671,7 @@ property sources.
|
||||
[[beans-using-propertysource]]
|
||||
== Using `@PropertySource`
|
||||
|
||||
The {spring-framework-api}/context/annotation/PropertySource.html[`@PropertySource`]
|
||||
The {api-spring-framework}/context/annotation/PropertySource.html[`@PropertySource`]
|
||||
annotation provides a convenient and declarative mechanism for adding a `PropertySource`
|
||||
to Spring's `Environment`.
|
||||
|
||||
@@ -772,9 +771,11 @@ resolved to the corresponding value. If not, then `default/path` is used
|
||||
as a default. If no default is specified and a property cannot be resolved, an
|
||||
`IllegalArgumentException` is thrown.
|
||||
|
||||
NOTE: `@PropertySource` can be used as a repeatable annotation. `@PropertySource`
|
||||
may also be used as a meta-annotation to create custom composed annotations with
|
||||
attribute overrides.
|
||||
NOTE: The `@PropertySource` annotation is repeatable, according to Java 8 conventions.
|
||||
However, all such `@PropertySource` annotations need to be declared at the same
|
||||
level, either directly on the configuration class or as meta-annotations within the
|
||||
same custom annotation. Mixing direct annotations and meta-annotations is not
|
||||
recommended, since direct annotations effectively override meta-annotations.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -22,8 +22,8 @@ in which these `BeanPostProcessor` instances run by setting the `order` property
|
||||
You can set this property only if the `BeanPostProcessor` implements the `Ordered`
|
||||
interface. If you write your own `BeanPostProcessor`, you should consider implementing
|
||||
the `Ordered` interface, too. For further details, see the javadoc of the
|
||||
{spring-framework-api}/beans/factory/config/BeanPostProcessor.html[`BeanPostProcessor`]
|
||||
and {spring-framework-api}/core/Ordered.html[`Ordered`] interfaces. See also the note on
|
||||
{api-spring-framework}/beans/factory/config/BeanPostProcessor.html[`BeanPostProcessor`]
|
||||
and {api-spring-framework}/core/Ordered.html[`Ordered`] interfaces. See also the note on
|
||||
xref:core/beans/factory-extension.adoc#beans-factory-programmatically-registering-beanpostprocessors[programmatic registration of `BeanPostProcessor` instances].
|
||||
|
||||
[NOTE]
|
||||
@@ -272,8 +272,8 @@ which these `BeanFactoryPostProcessor` instances run by setting the `order` prop
|
||||
However, you can only set this property if the `BeanFactoryPostProcessor` implements the
|
||||
`Ordered` interface. If you write your own `BeanFactoryPostProcessor`, you should
|
||||
consider implementing the `Ordered` interface, too. See the javadoc of the
|
||||
{spring-framework-api}/beans/factory/config/BeanFactoryPostProcessor.html[`BeanFactoryPostProcessor`]
|
||||
and {spring-framework-api}/core/Ordered.html[`Ordered`] interfaces for more details.
|
||||
{api-spring-framework}/beans/factory/config/BeanFactoryPostProcessor.html[`BeanFactoryPostProcessor`]
|
||||
and {api-spring-framework}/core/Ordered.html[`Ordered`] interfaces for more details.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
@@ -372,17 +372,6 @@ The `PropertySourcesPlaceholderConfigurer` not only looks for properties in the
|
||||
file you specify. By default, if it cannot find a property in the specified properties files,
|
||||
it checks against Spring `Environment` properties and regular Java `System` properties.
|
||||
|
||||
[WARNING]
|
||||
=====
|
||||
Only one such element should be defined for a given application with the properties
|
||||
that it needs. Several property placeholders can be configured as long as they have distinct
|
||||
placeholder syntax (`${...}`).
|
||||
|
||||
If you need to modularize the source of properties used for the replacement, you should
|
||||
not create multiple properties placeholders. Rather, you should create your own
|
||||
`PropertySourcesPlaceholderConfigurer` bean that gathers the properties to use.
|
||||
=====
|
||||
|
||||
[TIP]
|
||||
=====
|
||||
You can use the `PropertySourcesPlaceholderConfigurer` to substitute class names, which
|
||||
|
||||
@@ -592,40 +592,6 @@ Kotlin::
|
||||
|
||||
|
||||
|
||||
[[beans-factory-thread-safety]]
|
||||
=== Thread Safety and Visibility
|
||||
|
||||
The Spring core container publishes created singleton instances in a thread-safe manner,
|
||||
guarding access through a singleton lock and guaranteeing visibility in other threads.
|
||||
|
||||
As a consequence, application-provided bean classes do not have to be concerned about the
|
||||
visibility of their initialization state. Regular configuration fields do not have to be
|
||||
marked as `volatile` as long as they are only mutated during the initialization phase,
|
||||
providing visibility guarantees similar to `final` even for setter-based configuration
|
||||
state that is mutable during that initial phase. If such fields get changed after the
|
||||
bean creation phase and its subsequent initial publication, they need to be declared as
|
||||
`volatile` or guarded by a common lock whenever accessed.
|
||||
|
||||
Note that concurrent access to such configuration state in singleton bean instances,
|
||||
e.g. for controller instances or repository instances, is perfectly thread-safe after
|
||||
such safe initial publication from the container side. This includes common singleton
|
||||
`FactoryBean` instances which are processed within the general singleton lock as well.
|
||||
|
||||
For destruction callbacks, the configuration state remains thread-safe but any runtime
|
||||
state accumulated between initialization and destruction should be kept in thread-safe
|
||||
structures (or in `volatile` fields for simple cases) as per common Java guidelines.
|
||||
|
||||
Deeper `Lifecycle` integration as shown above involves runtime-mutable state such as
|
||||
a `runnable` field which will have to be declared as `volatile`. While the common
|
||||
lifecycle callbacks follow a certain order, e.g. a start callback is guaranteed to
|
||||
only happen after full initialization and a stop callback only after an initial start,
|
||||
there is a special case with the common stop before destroy arrangement: It is strongly
|
||||
recommended that the internal state in any such bean also allows for an immediate
|
||||
destroy callback without a preceding stop since this may happen during an extraordinary
|
||||
shutdown after a cancelled bootstrap or in case of a stop timeout caused by another bean.
|
||||
|
||||
|
||||
|
||||
[[beans-factory-aware]]
|
||||
== `ApplicationContextAware` and `BeanNameAware`
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ The following table describes the supported scopes:
|
||||
|
||||
NOTE: A thread scope is available but is not registered by default. For more information,
|
||||
see the documentation for
|
||||
{spring-framework-api}/context/support/SimpleThreadScope.html[`SimpleThreadScope`].
|
||||
{api-spring-framework}/context/support/SimpleThreadScope.html[`SimpleThreadScope`].
|
||||
For instructions on how to register this or any other custom scope, see
|
||||
xref:core/beans/factory-scopes.adoc#beans-factory-scopes-custom-using[Using a Custom Scope].
|
||||
|
||||
@@ -324,6 +324,7 @@ Kotlin::
|
||||
|
||||
|
||||
|
||||
|
||||
[[beans-factory-scopes-application]]
|
||||
=== Application Scope
|
||||
|
||||
@@ -373,6 +374,7 @@ Kotlin::
|
||||
|
||||
|
||||
|
||||
|
||||
[[beans-factory-scopes-websocket]]
|
||||
=== WebSocket Scope
|
||||
|
||||
@@ -382,6 +384,7 @@ xref:web/websocket/stomp/scope.adoc[WebSocket scope] for more details.
|
||||
|
||||
|
||||
|
||||
|
||||
[[beans-factory-scopes-other-injection]]
|
||||
=== Scoped Beans as Dependencies
|
||||
|
||||
@@ -511,8 +514,8 @@ the `<aop:scoped-proxy/>` element, a CGLIB-based class proxy is created.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
CGLIB proxies do not intercept private methods. Attempting to call a private method
|
||||
on such a proxy will not delegate to the actual scoped target object.
|
||||
CGLIB proxies intercept only public method calls! Do not call non-public methods
|
||||
on such a proxy. They are not delegated to the actual scoped target object.
|
||||
====
|
||||
|
||||
Alternatively, you can configure the Spring container to create standard JDK
|
||||
@@ -541,19 +544,6 @@ see xref:core/aop/proxying.adoc[Proxying Mechanisms].
|
||||
|
||||
|
||||
|
||||
[[beans-factory-scopes-injection]]
|
||||
=== Injecting Request/Session References Directly
|
||||
|
||||
As an alternative to factory scopes, a Spring `WebApplicationContext` also supports
|
||||
the injection of `HttpServletRequest`, `HttpServletResponse`, `HttpSession`,
|
||||
`WebRequest` and (if JSF is present) `FacesContext` and `ExternalContext` into
|
||||
Spring-managed beans, simply through type-based autowiring next to regular injection
|
||||
points for other beans. Spring generally injects proxies for such request and session
|
||||
objects which has the advantage of working in singleton beans and serializable beans
|
||||
as well, similar to scoped proxies for factory-scoped beans.
|
||||
|
||||
|
||||
|
||||
[[beans-factory-scopes-custom]]
|
||||
== Custom Scopes
|
||||
|
||||
@@ -569,7 +559,7 @@ To integrate your custom scopes into the Spring container, you need to implement
|
||||
`org.springframework.beans.factory.config.Scope` interface, which is described in this
|
||||
section. For an idea of how to implement your own scopes, see the `Scope`
|
||||
implementations that are supplied with the Spring Framework itself and the
|
||||
{spring-framework-api}/beans/factory/config/Scope.html[`Scope`] javadoc,
|
||||
{api-spring-framework}/beans/factory/config/Scope.html[`Scope`] javadoc,
|
||||
which explains the methods you need to implement in more detail.
|
||||
|
||||
The `Scope` interface has four methods to get objects from the scope, remove them from
|
||||
@@ -639,7 +629,7 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
See the {spring-framework-api}/beans/factory/config/Scope.html#registerDestructionCallback[javadoc]
|
||||
See the {api-spring-framework}/beans/factory/config/Scope.html#registerDestructionCallback[javadoc]
|
||||
or a Spring scope implementation for more information on destruction callbacks.
|
||||
|
||||
The following method obtains the conversation identifier for the underlying scope:
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
[[beans-introduction]]
|
||||
= Introduction to the Spring IoC Container and Beans
|
||||
|
||||
This chapter covers the Spring Framework implementation of the Inversion of Control (IoC)
|
||||
principle. Dependency injection (DI) is a specialized form of IoC, whereby objects define
|
||||
their dependencies (that is, the other objects they work with) only through constructor
|
||||
arguments, arguments to a factory method, or properties that are set on the object
|
||||
instance after it is constructed or returned from a factory method. The IoC container
|
||||
This chapter covers the Spring Framework implementation of the Inversion of Control
|
||||
(IoC) principle. IoC is also known as dependency injection (DI). It is a process whereby
|
||||
objects define their dependencies (that is, the other objects they work with) only through
|
||||
constructor arguments, arguments to a factory method, or properties that are set on the
|
||||
object instance after it is constructed or returned from a factory method. The container
|
||||
then injects those dependencies when it creates the bean. This process is fundamentally
|
||||
the inverse (hence the name, Inversion of Control) of the bean itself controlling the
|
||||
instantiation or location of its dependencies by using direct construction of classes or
|
||||
a mechanism such as the Service Locator pattern.
|
||||
the inverse (hence the name, Inversion of Control) of the bean itself
|
||||
controlling the instantiation or location of its dependencies by using direct
|
||||
construction of classes or a mechanism such as the Service Locator pattern.
|
||||
|
||||
The `org.springframework.beans` and `org.springframework.context` packages are the basis
|
||||
for Spring Framework's IoC container. The
|
||||
{spring-framework-api}/beans/factory/BeanFactory.html[`BeanFactory`]
|
||||
{api-spring-framework}/beans/factory/BeanFactory.html[`BeanFactory`]
|
||||
interface provides an advanced configuration mechanism capable of managing any type of
|
||||
object.
|
||||
{spring-framework-api}/context/ApplicationContext.html[`ApplicationContext`]
|
||||
{api-spring-framework}/context/ApplicationContext.html[`ApplicationContext`]
|
||||
is a sub-interface of `BeanFactory`. It adds:
|
||||
|
||||
* Easier integration with Spring's AOP features
|
||||
|
||||
@@ -3,5 +3,17 @@
|
||||
:page-section-summary-toc: 1
|
||||
|
||||
This section covers how to use annotations in your Java code to configure the Spring
|
||||
container.
|
||||
container. It includes the following topics:
|
||||
|
||||
* xref:core/beans/java/basic-concepts.adoc[Basic Concepts: `@Bean` and `@Configuration`]
|
||||
* xref:core/beans/java/instantiating-container.adoc[Instantiating the Spring Container by Using `AnnotationConfigApplicationContext`]
|
||||
* xref:core/beans/java/bean-annotation.adoc[Using the `@Bean` Annotation]
|
||||
* xref:core/beans/java/configuration-annotation.adoc[Using the `@Configuration` annotation]
|
||||
* xref:core/beans/java/composing-configuration-classes.adoc[Composing Java-based Configurations]
|
||||
* xref:core/beans/environment.adoc#beans-definition-profiles[Bean Definition Profiles]
|
||||
* xref:core/beans/environment.adoc#beans-property-source-abstraction[`PropertySource` Abstraction]
|
||||
* xref:core/beans/environment.adoc#beans-using-propertysource[Using `@PropertySource`]
|
||||
* xref:core/beans/environment.adoc#beans-placeholder-resolution-in-statements[Placeholder Resolution in Statements]
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -55,34 +55,33 @@ The preceding `AppConfig` class is equivalent to the following Spring `<beans/>`
|
||||
</beans>
|
||||
----
|
||||
|
||||
.@Configuration classes with or without local calls between @Bean methods?
|
||||
.Full @Configuration vs "`lite`" @Bean mode?
|
||||
****
|
||||
In common scenarios, `@Bean` methods are to be declared within `@Configuration` classes,
|
||||
ensuring that full configuration class processing applies and that cross-method
|
||||
references therefore get redirected to the container's lifecycle management.
|
||||
This prevents the same `@Bean` method from accidentally being invoked through a regular
|
||||
Java method call, which helps to reduce subtle bugs that can be hard to track down.
|
||||
|
||||
When `@Bean` methods are declared within classes that are not annotated with
|
||||
`@Configuration` - or when `@Configuration(proxyBeanMethods=false)` is declared -,
|
||||
they are referred to as being processed in a "lite" mode. In such scenarios,
|
||||
`@Bean` methods are effectively a general-purpose factory method mechanism without
|
||||
special runtime processing (that is, without generating a CGLIB subclass for it).
|
||||
A custom Java call to such a method will not get intercepted by the container and
|
||||
therefore behaves just like a regular method call, creating a new instance every time
|
||||
rather than reusing an existing singleton (or scoped) instance for the given bean.
|
||||
`@Configuration`, they are referred to as being processed in a "`lite`" mode. Bean methods
|
||||
declared in a `@Component` or even in a plain old class are considered to be "`lite`",
|
||||
with a different primary purpose of the containing class and a `@Bean` method
|
||||
being a sort of bonus there. For example, service components may expose management views
|
||||
to the container through an additional `@Bean` method on each applicable component class.
|
||||
In such scenarios, `@Bean` methods are a general-purpose factory method mechanism.
|
||||
|
||||
As a consequence, `@Bean` methods on classes without runtime proxying are not meant to
|
||||
declare inter-bean dependencies at all. Instead, they are expected to operate on their
|
||||
containing component's fields and, optionally, on arguments that a factory method may
|
||||
declare in order to receive autowired collaborators. Such a `@Bean` method therefore
|
||||
never needs to invoke other `@Bean` methods; every such call can be expressed through
|
||||
a factory method argument instead. The positive side-effect here is that no CGLIB
|
||||
subclassing has to be applied at runtime, reducing the overhead and the footprint.
|
||||
Unlike full `@Configuration`, lite `@Bean` methods cannot declare inter-bean dependencies.
|
||||
Instead, they operate on their containing component's internal state and, optionally, on
|
||||
arguments that they may declare. Such a `@Bean` method should therefore not invoke other
|
||||
`@Bean` methods. Each such method is literally only a factory method for a particular
|
||||
bean reference, without any special runtime semantics. The positive side-effect here is
|
||||
that no CGLIB subclassing has to be applied at runtime, so there are no limitations in
|
||||
terms of class design (that is, the containing class may be `final` and so forth).
|
||||
|
||||
In common scenarios, `@Bean` methods are to be declared within `@Configuration` classes,
|
||||
ensuring that "`full`" mode is always used and that cross-method references therefore
|
||||
get redirected to the container's lifecycle management. This prevents the same
|
||||
`@Bean` method from accidentally being invoked through a regular Java call, which helps
|
||||
to reduce subtle bugs that can be hard to track down when operating in "`lite`" mode.
|
||||
****
|
||||
|
||||
The `@Bean` and `@Configuration` annotations are discussed in depth in the following sections.
|
||||
First, however, we cover the various ways of creating a Spring container by using
|
||||
First, however, we cover the various ways of creating a spring container by using
|
||||
Java-based configuration.
|
||||
|
||||
|
||||
|
||||
@@ -552,7 +552,7 @@ Sometimes, it is helpful to provide a more detailed textual description of a bea
|
||||
be particularly useful when beans are exposed (perhaps through JMX) for monitoring purposes.
|
||||
|
||||
To add a description to a `@Bean`, you can use the
|
||||
{spring-framework-api}/context/annotation/Description.html[`@Description`]
|
||||
{api-spring-framework}/context/annotation/Description.html[`@Description`]
|
||||
annotation, as the following example shows:
|
||||
|
||||
[tabs]
|
||||
|
||||
+10
-52
@@ -113,8 +113,7 @@ issue, because no compiler is involved, and you can declare
|
||||
When using `@Configuration` classes, the Java compiler places constraints on
|
||||
the configuration model, in that references to other beans must be valid Java syntax.
|
||||
|
||||
Fortunately, solving this problem is simple. As
|
||||
xref:core/beans/java/bean-annotation.adoc#beans-java-dependencies[we already discussed],
|
||||
Fortunately, solving this problem is simple. As xref:core/beans/java/bean-annotation.adoc#beans-java-dependencies[we already discussed],
|
||||
a `@Bean` method can have an arbitrary number of parameters that describe the bean
|
||||
dependencies. Consider the following more real-world scenario with several `@Configuration`
|
||||
classes, each depending on beans declared in the others:
|
||||
@@ -205,6 +204,7 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
There is another way to achieve the same result. Remember that `@Configuration` classes are
|
||||
ultimately only another bean in the container: This means that they can take advantage of
|
||||
`@Autowired` and `@Value` injection and other features the same as any other bean.
|
||||
@@ -216,16 +216,11 @@ classes are processed quite early during the initialization of the context, and
|
||||
to be injected this way may lead to unexpected early initialization. Whenever possible, resort to
|
||||
parameter-based injection, as in the preceding example.
|
||||
|
||||
Avoid access to locally defined beans within a `@PostConstruct` method on the same configuration
|
||||
class. This effectively leads to a circular reference since non-static `@Bean` methods semantically
|
||||
require a fully initialized configuration class instance to be called on. With circular references
|
||||
disallowed (e.g. in Spring Boot 2.6+), this may trigger a `BeanCurrentlyInCreationException`.
|
||||
|
||||
Also, be particularly careful with `BeanPostProcessor` and `BeanFactoryPostProcessor` definitions
|
||||
through `@Bean`. Those should usually be declared as `static @Bean` methods, not triggering the
|
||||
instantiation of their containing configuration class. Otherwise, `@Autowired` and `@Value` may not
|
||||
work on the configuration class itself, since it is possible to create it as a bean instance earlier than
|
||||
{spring-framework-api}/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.html[`AutowiredAnnotationBeanPostProcessor`].
|
||||
{api-spring-framework}/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.html[`AutowiredAnnotationBeanPostProcessor`].
|
||||
====
|
||||
|
||||
The following example shows how one bean can be autowired to another bean:
|
||||
@@ -338,7 +333,7 @@ modularity, but determining exactly where the autowired bean definitions are dec
|
||||
still somewhat ambiguous. For example, as a developer looking at `ServiceConfig`, how do
|
||||
you know exactly where the `@Autowired AccountRepository` bean is declared? It is not
|
||||
explicit in the code, and this may be just fine. Remember that the
|
||||
{spring-site-tools}[Spring Tools for Eclipse] provides tooling that
|
||||
https://spring.io/tools[Spring Tools for Eclipse] provides tooling that
|
||||
can render graphs showing how everything is wired, which may be all you need. Also,
|
||||
your Java IDE can easily find all declarations and uses of the `AccountRepository` type
|
||||
and quickly show you the location of `@Bean` methods that return that type.
|
||||
@@ -503,47 +498,10 @@ way, navigating `@Configuration` classes and their dependencies becomes no diffe
|
||||
than the usual process of navigating interface-based code.
|
||||
--
|
||||
|
||||
|
||||
[[beans-java-startup]]
|
||||
== Influencing the Startup of `@Bean`-defined Singletons
|
||||
|
||||
If you want to influence the startup creation order of certain singleton beans, consider
|
||||
declaring some of them as `@Lazy` for creation on first access instead of on startup.
|
||||
|
||||
`@DependsOn` forces certain other beans to be initialized first, making sure that
|
||||
the specified beans are created before the current bean, beyond what the latter's
|
||||
direct dependencies imply.
|
||||
|
||||
[[beans-java-startup-background]]
|
||||
=== Background Initialization
|
||||
|
||||
As of 6.2, there is a background initialization option: `@Bean(bootstrap=BACKGROUND)`
|
||||
allows for singling out specific beans for background initialization, covering the
|
||||
entire bean creation step for each such bean on context startup.
|
||||
|
||||
Dependent beans with non-lazy injection points automatically wait for the bean instance
|
||||
to be completed. All regular background initializations are forced to complete at the end
|
||||
of context startup. Only beans additionally marked as `@Lazy` are allowed to be completed
|
||||
later (up until the first actual access).
|
||||
|
||||
Background initialization typically goes together with `@Lazy` (or `ObjectProvider`)
|
||||
injection points in dependent beans. Otherwise, the main bootstrap thread is going to
|
||||
block when an actual background-initialized bean instance needs to be injected early.
|
||||
|
||||
This form of concurrent startup applies to individual beans: if such a bean depends on
|
||||
other beans, they need to have been initialized already, either simply through being
|
||||
declared earlier or through `@DependsOn` which enforces initialization in the main
|
||||
bootstrap thread before background initialization for the affected bean is triggered.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
A `bootstrapExecutor` bean of type `Executor` must be declared for background
|
||||
bootstrapping to be actually active. Otherwise, the background markers will be ignored at
|
||||
runtime.
|
||||
|
||||
The bootstrap executor may be a bounded executor just for startup purposes or a shared
|
||||
thread pool which serves for other purposes as well.
|
||||
====
|
||||
TIP: If you want to influence the startup creation order of certain beans, consider
|
||||
declaring some of them as `@Lazy` (for creation on first access instead of on startup)
|
||||
or as `@DependsOn` certain other beans (making sure that specific other beans are
|
||||
created before the current bean, beyond what the latter's direct dependencies imply).
|
||||
|
||||
|
||||
[[beans-java-conditional]]
|
||||
@@ -556,7 +514,7 @@ profile has been enabled in the Spring `Environment` (see xref:core/beans/enviro
|
||||
for details).
|
||||
|
||||
The `@Profile` annotation is actually implemented by using a much more flexible annotation
|
||||
called {spring-framework-api}/context/annotation/Conditional.html[`@Conditional`].
|
||||
called {api-spring-framework}/context/annotation/Conditional.html[`@Conditional`].
|
||||
The `@Conditional` annotation indicates specific
|
||||
`org.springframework.context.annotation.Condition` implementations that should be
|
||||
consulted before a `@Bean` is registered.
|
||||
@@ -607,7 +565,7 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
See the {spring-framework-api}/context/annotation/Conditional.html[`@Conditional`]
|
||||
See the {api-spring-framework}/context/annotation/Conditional.html[`@Conditional`]
|
||||
javadoc for more detail.
|
||||
|
||||
|
||||
|
||||
@@ -278,7 +278,7 @@ init-param):
|
||||
|
||||
NOTE: For programmatic use cases, a `GenericWebApplicationContext` can be used as an
|
||||
alternative to `AnnotationConfigWebApplicationContext`. See the
|
||||
{spring-framework-api}/web/context/support/GenericWebApplicationContext.html[`GenericWebApplicationContext`]
|
||||
{api-spring-framework}/web/context/support/GenericWebApplicationContext.html[`GenericWebApplicationContext`]
|
||||
javadoc for details.
|
||||
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ alternate between read and write.
|
||||
== `PooledDataBuffer`
|
||||
|
||||
As explained in the Javadoc for
|
||||
{java-api}/java.base/java/nio/ByteBuffer.html[ByteBuffer],
|
||||
https://docs.oracle.com/javase/8/docs/api/java/nio/ByteBuffer.html[ByteBuffer],
|
||||
byte buffers can be direct or non-direct. Direct buffers may reside outside the Java heap
|
||||
which eliminates the need for copying for native I/O operations. That makes direct buffers
|
||||
particularly useful for receiving and sending data over a socket, but they're also more
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
[[expressions]]
|
||||
= Spring Expression Language (SpEL)
|
||||
|
||||
The Spring Expression Language ("SpEL" for short) is a powerful expression language that
|
||||
The Spring Expression Language ("`SpEL`" for short) is a powerful expression language that
|
||||
supports querying and manipulating an object graph at runtime. The language syntax is
|
||||
similar to the https://jakarta.ee/specifications/expression-language/[Jakarta Expression
|
||||
Language] but offers additional features, most notably method invocation and basic string
|
||||
templating functionality.
|
||||
similar to Unified EL but offers additional features, most notably method invocation and
|
||||
basic string templating functionality.
|
||||
|
||||
While there are several other Java expression languages available -- OGNL, MVEL, and JBoss
|
||||
EL, to name a few -- the Spring Expression Language was created to provide the Spring
|
||||
community with a single well supported expression language that can be used across all
|
||||
the products in the Spring portfolio. Its language features are driven by the
|
||||
requirements of the projects in the Spring portfolio, including tooling requirements
|
||||
for code completion support within the {spring-site-tools}[Spring Tools for Eclipse].
|
||||
for code completion support within the https://spring.io/tools[Spring Tools for Eclipse].
|
||||
That said, SpEL is based on a technology-agnostic API that lets other expression language
|
||||
implementations be integrated, should the need arise.
|
||||
|
||||
@@ -34,24 +33,24 @@ populate them are listed at the end of the chapter.
|
||||
The expression language supports the following functionality:
|
||||
|
||||
* Literal expressions
|
||||
* Boolean and relational operators
|
||||
* Regular expressions
|
||||
* Class expressions
|
||||
* Accessing properties, arrays, lists, and maps
|
||||
* Method invocation
|
||||
* Assignment
|
||||
* Calling constructors
|
||||
* Bean references
|
||||
* Array construction
|
||||
* Inline lists
|
||||
* Inline maps
|
||||
* Array construction
|
||||
* Relational operators
|
||||
* Regular expressions
|
||||
* Logical operators
|
||||
* String operators
|
||||
* Mathematical operators
|
||||
* Assignment
|
||||
* Type expressions
|
||||
* Method invocation
|
||||
* Constructor invocation
|
||||
* Ternary operator
|
||||
* Variables
|
||||
* User-defined functions
|
||||
* Bean references
|
||||
* Ternary, Elvis, and safe-navigation operators
|
||||
* Collection projection
|
||||
* Collection selection
|
||||
* Templated expressions
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,34 +1,219 @@
|
||||
[[expressions-beandef]]
|
||||
= Expressions in Bean Definitions
|
||||
|
||||
You can use SpEL expressions with configuration metadata for defining bean instances. In both
|
||||
cases, the syntax to define the expression is of the form `#{ <expression string> }`.
|
||||
You can use SpEL expressions with XML-based or annotation-based configuration metadata for
|
||||
defining `BeanDefinition` instances. In both cases, the syntax to define the expression is of the
|
||||
form `#{ <expression string> }`.
|
||||
|
||||
|
||||
|
||||
[[expressions-beandef-xml-based]]
|
||||
== XML Configuration
|
||||
|
||||
A property or constructor argument value can be set by using expressions, as the following
|
||||
example shows:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim"]
|
||||
----
|
||||
<bean id="numberGuess" class="org.spring.samples.NumberGuess">
|
||||
<property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }"/>
|
||||
|
||||
<!-- other properties -->
|
||||
</bean>
|
||||
----
|
||||
|
||||
All beans in the application context are available as predefined variables with their
|
||||
common bean name. This includes standard context beans such as `environment` (of type
|
||||
`org.springframework.core.env.Environment`) as well as `systemProperties` and
|
||||
`systemEnvironment` (of type `Map<String, Object>`) for access to the runtime environment.
|
||||
|
||||
To specify a default value, you can place the `@Value` annotation on fields, methods,
|
||||
and method or constructor parameters (or XML equivalent).
|
||||
The following example shows access to the `systemProperties` bean as a SpEL variable:
|
||||
|
||||
The following example sets the default value of a field:
|
||||
[source,xml,indent=0,subs="verbatim"]
|
||||
----
|
||||
<bean id="taxCalculator" class="org.spring.samples.TaxCalculator">
|
||||
<property name="defaultLocale" value="#{ systemProperties['user.region'] }"/>
|
||||
|
||||
include-code::./FieldValueTestBean[tag=snippet,indent=0]
|
||||
<!-- other properties -->
|
||||
</bean>
|
||||
----
|
||||
|
||||
Note that you do not have to prefix the predefined variable with the `#` symbol here.
|
||||
|
||||
You can also refer to other bean properties by name, as the following example shows:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim"]
|
||||
----
|
||||
<bean id="numberGuess" class="org.spring.samples.NumberGuess">
|
||||
<property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }"/>
|
||||
|
||||
<!-- other properties -->
|
||||
</bean>
|
||||
|
||||
<bean id="shapeGuess" class="org.spring.samples.ShapeGuess">
|
||||
<property name="initialShapeSeed" value="#{ numberGuess.randomNumber }"/>
|
||||
|
||||
<!-- other properties -->
|
||||
</bean>
|
||||
----
|
||||
|
||||
|
||||
|
||||
[[expressions-beandef-annotation-based]]
|
||||
== Annotation Configuration
|
||||
|
||||
To specify a default value, you can place the `@Value` annotation on fields, methods,
|
||||
and method or constructor parameters.
|
||||
|
||||
The following example sets the default value of a field:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
public class FieldValueTestBean {
|
||||
|
||||
@Value("#{ systemProperties['user.region'] }")
|
||||
private String defaultLocale;
|
||||
|
||||
public void setDefaultLocale(String defaultLocale) {
|
||||
this.defaultLocale = defaultLocale;
|
||||
}
|
||||
|
||||
public String getDefaultLocale() {
|
||||
return this.defaultLocale;
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
class FieldValueTestBean {
|
||||
|
||||
@Value("#{ systemProperties['user.region'] }")
|
||||
var defaultLocale: String? = null
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
The following example shows the equivalent but on a property setter method:
|
||||
|
||||
include-code::./PropertyValueTestBean[tag=snippet,indent=0]
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
public class PropertyValueTestBean {
|
||||
|
||||
private String defaultLocale;
|
||||
|
||||
@Value("#{ systemProperties['user.region'] }")
|
||||
public void setDefaultLocale(String defaultLocale) {
|
||||
this.defaultLocale = defaultLocale;
|
||||
}
|
||||
|
||||
public String getDefaultLocale() {
|
||||
return this.defaultLocale;
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
class PropertyValueTestBean {
|
||||
|
||||
@Value("#{ systemProperties['user.region'] }")
|
||||
var defaultLocale: String? = null
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
Autowired methods and constructors can also use the `@Value` annotation, as the following
|
||||
examples show:
|
||||
|
||||
include-code::./SimpleMovieLister[tag=snippet,indent=0]
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
public class SimpleMovieLister {
|
||||
|
||||
private MovieFinder movieFinder;
|
||||
private String defaultLocale;
|
||||
|
||||
@Autowired
|
||||
public void configure(MovieFinder movieFinder,
|
||||
@Value("#{ systemProperties['user.region'] }") String defaultLocale) {
|
||||
this.movieFinder = movieFinder;
|
||||
this.defaultLocale = defaultLocale;
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
class SimpleMovieLister {
|
||||
|
||||
private lateinit var movieFinder: MovieFinder
|
||||
private lateinit var defaultLocale: String
|
||||
|
||||
@Autowired
|
||||
fun configure(movieFinder: MovieFinder,
|
||||
@Value("#{ systemProperties['user.region'] }") defaultLocale: String) {
|
||||
this.movieFinder = movieFinder
|
||||
this.defaultLocale = defaultLocale
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
public class MovieRecommender {
|
||||
|
||||
private String defaultLocale;
|
||||
|
||||
private CustomerPreferenceDao customerPreferenceDao;
|
||||
|
||||
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao,
|
||||
@Value("#{systemProperties['user.country']}") String defaultLocale) {
|
||||
this.customerPreferenceDao = customerPreferenceDao;
|
||||
this.defaultLocale = defaultLocale;
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
class MovieRecommender(private val customerPreferenceDao: CustomerPreferenceDao,
|
||||
@Value("#{systemProperties['user.country']}") private val defaultLocale: String) {
|
||||
// ...
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
include-code::./MovieRecommender[tag=snippet,indent=0]
|
||||
|
||||
You can also refer to other bean properties by name, as the following example shows:
|
||||
|
||||
include-code::./ShapeGuess[tag=snippet,indent=0]
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
[[expressions-evaluation]]
|
||||
= Evaluation
|
||||
|
||||
This section introduces programmatic use of SpEL's interfaces and its expression language.
|
||||
The complete language reference can be found in the
|
||||
This section introduces the simple use of SpEL interfaces and its expression language.
|
||||
The complete language reference can be found in
|
||||
xref:core/expressions/language-ref.adoc[Language Reference].
|
||||
|
||||
The following code demonstrates how to use the SpEL API to evaluate the literal string
|
||||
expression, `Hello World`.
|
||||
The following code introduces the SpEL API to evaluate the literal string expression,
|
||||
`Hello World`.
|
||||
|
||||
[tabs]
|
||||
======
|
||||
@@ -18,7 +18,7 @@ Java::
|
||||
Expression exp = parser.parseExpression("'Hello World'"); // <1>
|
||||
String message = (String) exp.getValue();
|
||||
----
|
||||
<1> The value of the message variable is `"Hello World"`.
|
||||
<1> The value of the message variable is `'Hello World'`.
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
@@ -28,24 +28,24 @@ Kotlin::
|
||||
val exp = parser.parseExpression("'Hello World'") // <1>
|
||||
val message = exp.value as String
|
||||
----
|
||||
<1> The value of the message variable is `"Hello World"`.
|
||||
<1> The value of the message variable is `'Hello World'`.
|
||||
======
|
||||
|
||||
|
||||
The SpEL classes and interfaces you are most likely to use are located in the
|
||||
`org.springframework.expression` package and its sub-packages, such as `spel.support`.
|
||||
|
||||
The `ExpressionParser` interface is responsible for parsing an expression string. In the
|
||||
preceding example, the expression string is a string literal denoted by the surrounding
|
||||
single quotation marks. The `Expression` interface is responsible for evaluating the
|
||||
defined expression string. The two types of exceptions that can be thrown when calling
|
||||
`parser.parseExpression(...)` and `exp.getValue(...)` are `ParseException` and
|
||||
`EvaluationException`, respectively.
|
||||
The `ExpressionParser` interface is responsible for parsing an expression string. In
|
||||
the preceding example, the expression string is a string literal denoted by the surrounding single
|
||||
quotation marks. The `Expression` interface is responsible for evaluating the previously defined
|
||||
expression string. Two exceptions that can be thrown, `ParseException` and
|
||||
`EvaluationException`, when calling `parser.parseExpression` and `exp.getValue`,
|
||||
respectively.
|
||||
|
||||
SpEL supports a wide range of features such as calling methods, accessing properties,
|
||||
SpEL supports a wide range of features, such as calling methods, accessing properties,
|
||||
and calling constructors.
|
||||
|
||||
In the following method invocation example, we call the `concat` method on the string
|
||||
literal, `Hello World`.
|
||||
In the following example of method invocation, we call the `concat` method on the string literal:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
@@ -57,7 +57,7 @@ Java::
|
||||
Expression exp = parser.parseExpression("'Hello World'.concat('!')"); // <1>
|
||||
String message = (String) exp.getValue();
|
||||
----
|
||||
<1> The value of `message` is now `"Hello World!"`.
|
||||
<1> The value of `message` is now 'Hello World!'.
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
@@ -67,11 +67,10 @@ Kotlin::
|
||||
val exp = parser.parseExpression("'Hello World'.concat('!')") // <1>
|
||||
val message = exp.value as String
|
||||
----
|
||||
<1> The value of `message` is now `"Hello World!"`.
|
||||
<1> The value of `message` is now 'Hello World!'.
|
||||
======
|
||||
|
||||
The following example demonstrates how to access the `Bytes` JavaBean property of the
|
||||
string literal, `Hello World`.
|
||||
The following example of calling a JavaBean property calls the `String` property `Bytes`:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
@@ -101,10 +100,10 @@ Kotlin::
|
||||
======
|
||||
|
||||
SpEL also supports nested properties by using the standard dot notation (such as
|
||||
`prop1.prop2.prop3`) as well as the corresponding setting of property values.
|
||||
`prop1.prop2.prop3`) and also the corresponding setting of property values.
|
||||
Public fields may also be accessed.
|
||||
|
||||
The following example shows how to use dot notation to get the length of a string literal.
|
||||
The following example shows how to use dot notation to get the length of a literal:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
@@ -134,7 +133,7 @@ Kotlin::
|
||||
======
|
||||
|
||||
The String's constructor can be called instead of using a string literal, as the following
|
||||
example shows.
|
||||
example shows:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
@@ -146,7 +145,7 @@ Java::
|
||||
Expression exp = parser.parseExpression("new String('hello world').toUpperCase()"); // <1>
|
||||
String message = exp.getValue(String.class);
|
||||
----
|
||||
<1> Construct a new `String` from the literal and convert it to upper case.
|
||||
<1> Construct a new `String` from the literal and make it be upper case.
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
@@ -156,9 +155,10 @@ Kotlin::
|
||||
val exp = parser.parseExpression("new String('hello world').toUpperCase()") // <1>
|
||||
val message = exp.getValue(String::class.java)
|
||||
----
|
||||
<1> Construct a new `String` from the literal and convert it to upper case.
|
||||
<1> Construct a new `String` from the literal and make it be upper case.
|
||||
======
|
||||
|
||||
|
||||
Note the use of the generic method: `public <T> T getValue(Class<T> desiredResultType)`.
|
||||
Using this method removes the need to cast the value of the expression to the desired
|
||||
result type. An `EvaluationException` is thrown if the value cannot be cast to the
|
||||
@@ -166,8 +166,8 @@ type `T` or converted by using the registered type converter.
|
||||
|
||||
The more common usage of SpEL is to provide an expression string that is evaluated
|
||||
against a specific object instance (called the root object). The following example shows
|
||||
how to retrieve the `name` property from an instance of the `Inventor` class and how to
|
||||
reference the `name` property in a boolean expression.
|
||||
how to retrieve the `name` property from an instance of the `Inventor` class or
|
||||
create a boolean condition:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
@@ -240,7 +240,7 @@ It excludes Java type references, constructors, and bean references. It also req
|
||||
you to explicitly choose the level of support for properties and methods in expressions.
|
||||
By default, the `create()` static factory method enables only read access to properties.
|
||||
You can also obtain a builder to configure the exact level of support needed, targeting
|
||||
one or some combination of the following.
|
||||
one or some combination of the following:
|
||||
|
||||
* Custom `PropertyAccessor` only (no reflection)
|
||||
* Data binding properties for read-only access
|
||||
@@ -252,15 +252,16 @@ one or some combination of the following.
|
||||
|
||||
By default, SpEL uses the conversion service available in Spring core
|
||||
(`org.springframework.core.convert.ConversionService`). This conversion service comes
|
||||
with many built-in converters for common conversions, but is also fully extensible so
|
||||
that you can add custom conversions between types. Additionally, it is generics-aware.
|
||||
This means that, when you work with generic types in expressions, SpEL attempts
|
||||
conversions to maintain type correctness for any objects it encounters.
|
||||
with many built-in converters for common conversions but is also fully extensible so that
|
||||
you can add custom conversions between types. Additionally, it is
|
||||
generics-aware. This means that, when you work with generic types in
|
||||
expressions, SpEL attempts conversions to maintain type correctness for any objects
|
||||
it encounters.
|
||||
|
||||
What does this mean in practice? Suppose assignment, using `setValue()`, is being used
|
||||
to set a `List` property. The type of the property is actually `List<Boolean>`. SpEL
|
||||
recognizes that the elements of the list need to be converted to `Boolean` before
|
||||
being placed in it. The following example shows how to do so.
|
||||
being placed in it. The following example shows how to do so:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
@@ -324,7 +325,7 @@ constructor before setting the specified value. If the element type does not hav
|
||||
default constructor, `null` will be added to the array or list. If there is no built-in
|
||||
or custom converter that knows how to set the value, `null` will remain in the array or
|
||||
list at the specified index. The following example demonstrates how to automatically grow
|
||||
the list.
|
||||
the list:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
@@ -379,25 +380,16 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
By default, a SpEL expression cannot contain more than 10,000 characters; however, the
|
||||
`maxExpressionLength` is configurable. If you create a `SpelExpressionParser`
|
||||
programmatically, you can specify a custom `maxExpressionLength` when creating the
|
||||
`SpelParserConfiguration` that you provide to the `SpelExpressionParser`. If you wish to
|
||||
set the `maxExpressionLength` used for parsing SpEL expressions within an
|
||||
`ApplicationContext` -- for example, in XML bean definitions, `@Value`, etc. -- you can
|
||||
set a JVM system property or Spring property named `spring.context.expression.maxLength`
|
||||
to the maximum expression length needed by your application (see
|
||||
xref:appendix.adoc#appendix-spring-properties[Supported Spring Properties]).
|
||||
|
||||
|
||||
[[expressions-spel-compilation]]
|
||||
== SpEL Compilation
|
||||
|
||||
Spring provides a basic compiler for SpEL expressions. Expressions are usually
|
||||
interpreted, which provides a lot of dynamic flexibility during evaluation but does not
|
||||
provide optimum performance. For occasional expression usage, this is fine, but, when
|
||||
used by other components such as Spring Integration, performance can be very important,
|
||||
and there is no real need for the dynamism.
|
||||
Spring Framework 4.1 includes a basic expression compiler. Expressions are usually
|
||||
interpreted, which provides a lot of dynamic flexibility during evaluation but
|
||||
does not provide optimum performance. For occasional expression usage,
|
||||
this is fine, but, when used by other components such as Spring Integration,
|
||||
performance can be very important, and there is no real need for the dynamism.
|
||||
|
||||
The SpEL compiler is intended to address this need. During evaluation, the compiler
|
||||
generates a Java class that embodies the expression behavior at runtime and uses that
|
||||
@@ -410,17 +402,16 @@ information can cause trouble later if the types of the various expression eleme
|
||||
change over time. For this reason, compilation is best suited to expressions whose
|
||||
type information is not going to change on repeated evaluations.
|
||||
|
||||
Consider the following basic expression.
|
||||
Consider the following basic expression:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
someArray[0].someProperty.someOtherProperty < 0.1
|
||||
someArray[0].someProperty.someOtherProperty < 0.1
|
||||
----
|
||||
|
||||
Because the preceding expression involves array access, some property de-referencing, and
|
||||
numeric operations, the performance gain can be very noticeable. In an example micro
|
||||
benchmark run of 50,000 iterations, it took 75ms to evaluate by using the interpreter and
|
||||
only 3ms using the compiled version of the expression.
|
||||
Because the preceding expression involves array access, some property de-referencing,
|
||||
and numeric operations, the performance gain can be very noticeable. In an example
|
||||
micro benchmark run of 50000 iterations, it took 75ms to evaluate by using the
|
||||
interpreter and only 3ms using the compiled version of the expression.
|
||||
|
||||
|
||||
[[expressions-compiler-configuration]]
|
||||
@@ -428,34 +419,33 @@ only 3ms using the compiled version of the expression.
|
||||
|
||||
The compiler is not turned on by default, but you can turn it on in either of two
|
||||
different ways. You can turn it on by using the parser configuration process
|
||||
(xref:core/expressions/evaluation.adoc#expressions-parser-configuration[discussed
|
||||
earlier]) or by using a Spring property when SpEL usage is embedded inside another
|
||||
component. This section discusses both of these options.
|
||||
(xref:core/expressions/evaluation.adoc#expressions-parser-configuration[discussed earlier]) or by using a Spring property
|
||||
when SpEL usage is embedded inside another component. This section discusses both of
|
||||
these options.
|
||||
|
||||
The compiler can operate in one of three modes, which are captured in the
|
||||
`org.springframework.expression.spel.SpelCompilerMode` enum. The modes are as follows.
|
||||
`org.springframework.expression.spel.SpelCompilerMode` enum. The modes are as follows:
|
||||
|
||||
* `OFF` (default): The compiler is switched off.
|
||||
* `IMMEDIATE`: In immediate mode, the expressions are compiled as soon as possible. This
|
||||
is typically after the first interpreted evaluation. If the compiled expression fails
|
||||
(typically due to a type changing, as described earlier), the caller of the expression
|
||||
evaluation receives an exception.
|
||||
* `MIXED`: In mixed mode, the expressions silently switch between interpreted and
|
||||
compiled mode over time. After some number of interpreted runs, they switch to compiled
|
||||
form and, if something goes wrong with the compiled form (such as a type changing, as
|
||||
described earlier), the expression automatically switches back to interpreted form
|
||||
again. Sometime later, it may generate another compiled form and switch to it.
|
||||
Basically, the exception that the user gets in `IMMEDIATE` mode is instead handled
|
||||
internally.
|
||||
is typically after the first interpreted evaluation. If the compiled expression fails
|
||||
(typically due to a type changing, as described earlier), the caller of the expression
|
||||
evaluation receives an exception.
|
||||
* `MIXED`: In mixed mode, the expressions silently switch between interpreted and compiled
|
||||
mode over time. After some number of interpreted runs, they switch to compiled
|
||||
form and, if something goes wrong with the compiled form (such as a type changing, as
|
||||
described earlier), the expression automatically switches back to interpreted form
|
||||
again. Sometime later, it may generate another compiled form and switch to it. Basically,
|
||||
the exception that the user gets in `IMMEDIATE` mode is instead handled internally.
|
||||
|
||||
`IMMEDIATE` mode exists because `MIXED` mode could cause issues for expressions that
|
||||
have side effects. If a compiled expression blows up after partially succeeding, it
|
||||
may have already done something that has affected the state of the system. If this
|
||||
has happened, the caller may not want it to silently re-run in interpreted mode,
|
||||
since part of the expression may be run twice.
|
||||
since part of the expression may be running twice.
|
||||
|
||||
After selecting a mode, use the `SpelParserConfiguration` to configure the parser. The
|
||||
following example shows how to do so.
|
||||
following example shows how to do so:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
@@ -492,16 +482,15 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
When you specify the compiler mode, you can also specify a `ClassLoader` (passing `null`
|
||||
is allowed). Compiled expressions are defined in a child `ClassLoader` created under any
|
||||
that is supplied. It is important to ensure that, if a `ClassLoader` is specified, it can
|
||||
see all the types involved in the expression evaluation process. If you do not specify a
|
||||
`ClassLoader`, a default `ClassLoader` is used (typically the context `ClassLoader` for
|
||||
the thread that is running during expression evaluation).
|
||||
When you specify the compiler mode, you can also specify a classloader (passing null is allowed).
|
||||
Compiled expressions are defined in a child classloader created under any that is supplied.
|
||||
It is important to ensure that, if a classloader is specified, it can see all the types involved in
|
||||
the expression evaluation process. If you do not specify a classloader, a default classloader is used
|
||||
(typically the context classloader for the thread that is running during expression evaluation).
|
||||
|
||||
The second way to configure the compiler is for use when SpEL is embedded inside some
|
||||
other component and it may not be possible to configure it through a configuration
|
||||
object. In such cases, it is possible to set the `spring.expression.compiler.mode`
|
||||
object. In these cases, it is possible to set the `spring.expression.compiler.mode`
|
||||
property via a JVM system property (or via the
|
||||
xref:appendix.adoc#appendix-spring-properties[`SpringProperties`] mechanism) to one of the
|
||||
`SpelCompilerMode` enum values (`off`, `immediate`, or `mixed`).
|
||||
@@ -510,16 +499,18 @@ xref:appendix.adoc#appendix-spring-properties[`SpringProperties`] mechanism) to
|
||||
[[expressions-compiler-limitations]]
|
||||
=== Compiler Limitations
|
||||
|
||||
Spring does not support compiling every kind of expression. The primary focus is on
|
||||
common expressions that are likely to be used in performance-critical contexts. The
|
||||
following kinds of expressions cannot be compiled.
|
||||
Since Spring Framework 4.1, the basic compilation framework is in place. However, the framework
|
||||
does not yet support compiling every kind of expression. The initial focus has been on the
|
||||
common expressions that are likely to be used in performance-critical contexts. The following
|
||||
kinds of expression cannot be compiled at the moment:
|
||||
|
||||
* Expressions involving assignment
|
||||
* Expressions relying on the conversion service
|
||||
* Expressions using custom resolvers
|
||||
* Expressions using overloaded operators
|
||||
* Expressions using array construction syntax
|
||||
* Expressions using custom resolvers or accessors
|
||||
* Expressions using selection or projection
|
||||
|
||||
Compilation of additional kinds of expressions may be supported in the future.
|
||||
More types of expressions will be compilable in the future.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -3,11 +3,9 @@
|
||||
|
||||
This section lists the classes used in the examples throughout this chapter.
|
||||
|
||||
== `Inventor`
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
Inventor.Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
|
||||
----
|
||||
@@ -82,7 +80,7 @@ Java::
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
Inventor.kt::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
|
||||
----
|
||||
@@ -97,11 +95,9 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
== `PlaceOfBirth`
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
PlaceOfBirth.java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
|
||||
----
|
||||
@@ -139,7 +135,7 @@ Java::
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
PlaceOfBirth.kt::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
|
||||
----
|
||||
@@ -149,11 +145,9 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
== `Society`
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
Society.java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
|
||||
----
|
||||
@@ -198,7 +192,7 @@ Java::
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
Society.kt::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
|
||||
----
|
||||
|
||||
@@ -2,4 +2,24 @@
|
||||
= Language Reference
|
||||
:page-section-summary-toc: 1
|
||||
|
||||
This section describes how the Spring Expression Language works.
|
||||
This section describes how the Spring Expression Language works. It covers the following
|
||||
topics:
|
||||
|
||||
* xref:core/expressions/language-ref/literal.adoc[Literal Expressions]
|
||||
* xref:core/expressions/language-ref/properties-arrays.adoc[Properties, Arrays, Lists, Maps, and Indexers]
|
||||
* xref:core/expressions/language-ref/inline-lists.adoc[Inline Lists]
|
||||
* xref:core/expressions/language-ref/inline-maps.adoc[Inline Maps]
|
||||
* xref:core/expressions/language-ref/array-construction.adoc[Array Construction]
|
||||
* xref:core/expressions/language-ref/methods.adoc[Methods]
|
||||
* xref:core/expressions/language-ref/operators.adoc[Operators]
|
||||
* xref:core/expressions/language-ref/types.adoc[Types]
|
||||
* xref:core/expressions/language-ref/constructors.adoc[Constructors]
|
||||
* xref:core/expressions/language-ref/variables.adoc[Variables]
|
||||
* xref:core/expressions/language-ref/functions.adoc[Functions]
|
||||
* xref:core/expressions/language-ref/bean-references.adoc[Bean References]
|
||||
* xref:core/expressions/language-ref/operator-ternary.adoc[Ternary Operator (If-Then-Else)]
|
||||
* xref:core/expressions/language-ref/operator-elvis.adoc[The Elvis Operator]
|
||||
* xref:core/expressions/language-ref/operator-safe-navigation.adoc[Safe Navigation Operator]
|
||||
|
||||
|
||||
|
||||
|
||||
+4
-12
@@ -13,7 +13,7 @@ Java::
|
||||
int[] numbers1 = (int[]) parser.parseExpression("new int[4]").getValue(context);
|
||||
|
||||
// Array with initializer
|
||||
int[] numbers2 = (int[]) parser.parseExpression("new int[] {1, 2, 3}").getValue(context);
|
||||
int[] numbers2 = (int[]) parser.parseExpression("new int[]{1,2,3}").getValue(context);
|
||||
|
||||
// Multi dimensional array
|
||||
int[][] numbers3 = (int[][]) parser.parseExpression("new int[4][5]").getValue(context);
|
||||
@@ -26,22 +26,14 @@ Kotlin::
|
||||
val numbers1 = parser.parseExpression("new int[4]").getValue(context) as IntArray
|
||||
|
||||
// Array with initializer
|
||||
val numbers2 = parser.parseExpression("new int[] {1, 2, 3}").getValue(context) as IntArray
|
||||
val numbers2 = parser.parseExpression("new int[]{1,2,3}").getValue(context) as IntArray
|
||||
|
||||
// Multi dimensional array
|
||||
val numbers3 = parser.parseExpression("new int[4][5]").getValue(context) as Array<IntArray>
|
||||
----
|
||||
======
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
You cannot currently supply an initializer when you construct a multi-dimensional array.
|
||||
====
|
||||
|
||||
[CAUTION]
|
||||
====
|
||||
Any expression that constructs an array – for example, via `new int[4]` or
|
||||
`new int[] {1, 2, 3}` – cannot be compiled. See
|
||||
xref:core/expressions/evaluation.adoc#expressions-compiler-limitations[Compiler Limitations]
|
||||
for details.
|
||||
====
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user