Compare commits

..

1 Commits

Author SHA1 Message Date
Spring Builds 23bb745ea4 Release v5.3.13 2021-11-11 07:31:25 +00:00
4552 changed files with 95987 additions and 141282 deletions
-29
View File
@@ -1,29 +0,0 @@
name: Backport Bot
on:
issues:
types: [labeled]
push:
branches:
- '*.x'
permissions:
contents: read
jobs:
build:
permissions:
contents: read
issues: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-java@v3
with:
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
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_EVENT: ${{ toJSON(github.event) }}
run: java -jar backport-bot-0.0.1-SNAPSHOT.jar --github.accessToken="$GITHUB_TOKEN" --github.event_name "$GITHUB_EVENT_NAME" --github.event "$GITHUB_EVENT"
@@ -1,13 +1,10 @@
name: "Validate Gradle Wrapper"
on: [push, pull_request]
permissions:
contents: read
jobs:
validation:
name: "Validation"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
- uses: gradle/wrapper-validation-action@v1
+12 -18
View File
@@ -1,42 +1,35 @@
# Miscellaneous
*.java.hsp
*.sonarj
*.sw*
.DS_Store
.settings
.springBeans
bin
build.sh
integration-repo
ivy-cache
argfile*
activemq-data/
classes/
# Log files
jxl.log
jmx.log
derby.log
# Gradle artifacts
spring-test/test-output/
.gradle
.gradletasknamecache
argfile*
pom.xml
activemq-data/
classes/
/build
buildSrc/build
/spring-*/build
/spring-core/kotlin-coroutines/build
/framework-bom/build
/framework-docs/build
/integration-tests/build
/src/asciidoc/build
spring-test/test-output/
# Maven artifacts
pom.xml
target/
# Eclipse artifacts, including WTP generated manifests
bin
.classpath
.project
.settings
.springBeans
spring-*/src/main/java/META-INF/MANIFEST.MF
# IDEA artifacts and output dirs
@@ -47,6 +40,7 @@ spring-*/src/main/java/META-INF/MANIFEST.MF
out
test-output
atlassian-ide-plugin.xml
.gradletasknamecache
# VS Code
.vscode/
.vscode/
+22 -33
View File
@@ -1,34 +1,23 @@
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>
Juergen Hoeller <jhoeller@pivotal.io> jhoeller <jhoeller@vmware.com>
<jhoeller@pivotal.io> <jhoeller@vmware.com>
<jhoeller@pivotal.io> <jhoeller@gopivotal.com>
<rstoyanchev@pivotal.io> <rstoyanchev@vmware.com>
<rstoyanchev@pivotal.io> <rstoyanchev@gopivotal.com>
<pwebb@pivotal.io> <pwebb@vmware.com>
<pwebb@pivotal.io> <pwebb@gopivotal.com>
<cbeams@pivotal.io> <cbeams@vmware.com>
<cbeams@pivotal.io> <cbeams@gopivotal.com>
<cbeams@pivotal.io> <cbeams@gmail.com>
<apoutsma@pivotal.io> <apoutsma@vmware.com>
<apoutsma@pivotal.io> <apoutsma@gopivotal.com>
<apoutsma@pivotal.io> <poutsma@mac.com>
<ogierke@pivotal.io> <ogierke@vmware.com>
<ogierke@pivotal.io> <ogierke@gopivotal.com>
<dsyer@pivotal.io> <dsyer@vmware.com>
<dsyer@pivotal.io> <dsyer@gopivotal.com>
<dsyer@pivotal.io> <david_syer@hotmail.com>
<aclement@pivotal.io> <aclement@vmware.com>
<aclement@pivotal.io> <aclement@gopivotal.com>
<aclement@pivotal.io> <andrew.clement@gmail.com>
<dmitry.katsubo@gmail.com> <dmitry.katsubo@gmai.com>
Nick Williams <nicholas@nicholaswilliams.net>
Nick Williams <nicholas@nicholaswilliams.net> Nicholas Williams <nicholas@nicholaswilliams.net>
-3
View File
@@ -1,3 +0,0 @@
# Enable auto-env through the sdkman_auto_env config
# Add key=value pairs of SDKs to use below
java=17.0.5-librca
+12 -16
View File
@@ -6,7 +6,7 @@ First off, thank you for taking the time to contribute! :+1: :tada:
* [Code of Conduct](#code-of-conduct)
* [How to Contribute](#how-to-contribute)
* [Ask questions](#ask-questions)
* [Discuss](#discuss)
* [Create an Issue](#create-an-issue)
* [Issue Lifecycle](#issue-lifecycle)
* [Submit a Pull Request](#submit-a-pull-request)
@@ -22,10 +22,11 @@ Please report unacceptable behavior to spring-code-of-conduct@pivotal.io.
### How to Contribute
#### Ask questions
#### Discuss
If you have a question, check Stack Overflow using
[this list of tags](https://stackoverflow.com/questions/tagged/spring+or+spring-mvc+or+spring-aop+or+spring-jdbc+or+spring-transactions+or+spring-annotations+or+spring-jms+or+spring-el+or+spring-test+or+spring+or+spring-remoting+or+spring-orm+or+spring-jmx+or+spring-cache+or+spring-webflux?tab=Newest). Find an existing discussion, or start a new one if necessary.
[this list of tags](https://stackoverflow.com/questions/tagged/spring+or+spring-mvc+or+spring-aop+or+spring-jdbc+or+spring-transactions+or+spring-annotations+or+spring-jms+or+spring-el+or+spring-test+or+spring+or+spring-remoting+or+spring-orm+or+spring-jmx+or+spring-cache+or+spring-webflux?tab=Newest).
Find an existing discussion, or start a new one if necessary.
If you believe there is an issue, search through
[existing issues](https://github.com/spring-projects/spring-framework/issues) trying a
@@ -38,18 +39,14 @@ decision.
Reporting an issue or making a feature request is a great way to contribute. Your feedback
and the conversations that result from it provide a continuous flow of ideas. However,
before creating a ticket, please take the time to [ask and research](#ask-questions) first.
before creating a ticket, please take the time to [discuss and research](#discuss) first.
If you create an issue after a discussion on Stack Overflow, please provide a description
If creating an issue after a discussion on Stack Overflow, please provide a description
in the issue instead of simply referring to Stack Overflow. The issue tracker is an
important place of record for design discussions and should be self-sufficient.
Once you're ready, create an issue on [GitHub](https://github.com/spring-projects/spring-framework/issues).
Many issues are caused by subtle behavior, typos, and unintended configuration.
Creating a [Minimal Reproducible Example](https://stackoverflow.com/help/minimal-reproducible-example)
(starting with https://start.spring.io for example) of the problem helps the team
quickly triage your issue and get to the core of the problem.
Once you're ready, create an issue on
[GitHub](https://github.com/spring-projects/spring-framework/issues).
#### Issue Lifecycle
@@ -66,7 +63,7 @@ follow-up reports will need to be created as new issues with a fresh description
#### Submit a Pull Request
1. If you have not previously done so, please sign the
[Contributor License Agreement](https://cla.spring.io/sign/spring). You will be reminded
[Contributor License Agreement](https://cla.pivotal.io/sign/spring). You will be reminded
automatically when you submit the PR.
1. Should you create an issue first? No, just create the pull request and use the
@@ -123,13 +120,12 @@ define the source file coding standards we use along with some IDEA editor setti
### Reference Docs
The reference documentation is in the [framework-docs/src/docs/asciidoc](framework-docs/src/docs/asciidoc) directory, in
The reference documentation is in the [src/docs/asciidoc](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 :framework-docs:asciidoctor` and then browse the result under
`framework-docs/build/docs/ref-docs/html5/index.html`.
When making changes locally, execute `./gradlew asciidoctor` and then browse the result under
`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/).
+4 -4
View File
@@ -1,4 +1,4 @@
# <img src="framework-docs/src/docs/spring-framework.png" width="80" height="80"> Spring Framework [![Build Status](https://ci.spring.io/api/v1/teams/spring-framework/pipelines/spring-framework-5.3.x/jobs/build/badge)](https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-5.3.x?groups=Build") [![Revved up by Gradle Enterprise](https://img.shields.io/badge/Revved%20up%20by-Gradle%20Enterprise-06A0CE?logo=Gradle&labelColor=02303A)](https://ge.spring.io/scans?search.rootProjectNames=spring)
# <img src="src/docs/spring-framework.png" width="80" height="80"> Spring Framework [![Build Status](https://ci.spring.io/api/v1/teams/spring-framework/pipelines/spring-framework-5.3.x/jobs/build/badge)](https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-5.3.x?groups=Build") [![Revved up by Gradle Enterprise](https://img.shields.io/badge/Revved%20up%20by-Gradle%20Enterprise-06A0CE?logo=Gradle&labelColor=02303A)](https://ge.spring.io/scans?search.rootProjectNames=spring)
This is the home of the Spring Framework: the foundation for all [Spring projects](https://spring.io/projects). Collectively the Spring Framework and the family of Spring projects are often referred to simply as "Spring".
@@ -14,16 +14,16 @@ 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/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
The Spring Framework maintains reference documentation ([published](https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/) and [source](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
See the [Micro-Benchmarks](https://github.com/spring-projects/spring-framework/wiki/Micro-Benchmarks) wiki page.
See the [Micro-Benchmarks](https://github.com/spring-projects/spring-framework/wiki/Micro-Benchmarks) Wiki page.
## Build from Source
See the [Build from Source](https://github.com/spring-projects/spring-framework/wiki/Build-from-Source) wiki page and the [CONTRIBUTING.md](CONTRIBUTING.md) file.
See the [Build from Source](https://github.com/spring-projects/spring-framework/wiki/Build-from-Source) Wiki page and the [CONTRIBUTING.md](CONTRIBUTING.md) file.
## Continuous Integration Builds
+325 -69
View File
@@ -1,39 +1,297 @@
plugins {
id 'io.spring.dependency-management' version '1.0.11.RELEASE' apply false
id 'io.spring.nohttp' version '0.0.10'
id 'io.freefair.aspectj' version '6.5.0.3' apply false
// kotlinVersion is managed in gradle.properties
id 'org.jetbrains.kotlin.plugin.serialization' version "${kotlinVersion}" apply false
id 'org.jetbrains.dokka' version '1.7.20'
id 'org.asciidoctor.jvm.convert' version '3.3.2' apply false
id 'org.asciidoctor.jvm.pdf' version '3.3.2' apply false
id 'org.unbroken-dome.xjc' version '2.0.0' apply false
id 'com.github.ben-manes.versions' version '0.42.0'
id 'com.github.johnrengelman.shadow' version '7.1.2' apply false
id 'de.undercouch.download' version '5.1.0'
id 'me.champeau.jmh' version '0.6.6' apply false
id "io.freefair.aspectj" version '6.2.0' apply false
id 'org.jetbrains.dokka' version '1.5.0' apply false
id 'org.jetbrains.kotlin.jvm' version '1.5.31' apply false
id "org.jetbrains.kotlin.plugin.serialization" version "1.5.31" apply false
id 'org.asciidoctor.jvm.convert' version '3.3.2'
id 'org.asciidoctor.jvm.pdf' version '3.3.2'
id "org.unbroken-dome.xjc" version '2.0.0' apply false
id "com.github.ben-manes.versions" version '0.39.0'
id "com.github.johnrengelman.shadow" version '7.0.0' apply false
id 'de.undercouch.download' version '4.1.2'
id "me.champeau.jmh" version "0.6.6" apply false
}
ext {
moduleProjects = subprojects.findAll { it.name.startsWith("spring-") }
javaProjects = subprojects - project(":framework-bom") - project(":framework-platform")
javaProjects = subprojects - project(":framework-bom")
withoutJclOverSlf4j = {
exclude group: "org.slf4j", name: "jcl-over-slf4j"
}
}
configure(allprojects) { project ->
repositories {
mavenCentral()
maven {
url "https://repo.spring.io/milestone"
content {
// Netty 5 optional support
includeGroup 'io.projectreactor.netty'
apply plugin: "io.spring.dependency-management"
dependencyManagement {
imports {
mavenBom "com.fasterxml.jackson:jackson-bom:2.12.5"
mavenBom "io.netty:netty-bom:4.1.70.Final"
mavenBom "io.projectreactor:reactor-bom:2020.0.13"
mavenBom "io.r2dbc:r2dbc-bom:Arabba-SR10"
mavenBom "io.rsocket:rsocket-bom:1.1.1"
mavenBom "org.eclipse.jetty:jetty-bom:9.4.44.v20210927"
mavenBom "org.jetbrains.kotlin:kotlin-bom:1.5.31"
mavenBom "org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.5.2"
mavenBom "org.jetbrains.kotlinx:kotlinx-serialization-bom:1.2.2"
mavenBom "org.junit:junit-bom:5.8.1"
}
dependencies {
dependencySet(group: 'org.apache.logging.log4j', version: '2.14.1') {
entry 'log4j-api'
entry 'log4j-core'
entry 'log4j-jul'
entry 'log4j-slf4j-impl'
}
dependency "org.slf4j:slf4j-api:1.7.32"
dependency("com.google.code.findbugs:findbugs:3.0.1") {
exclude group: "dom4j", name: "dom4j"
}
dependency "com.google.code.findbugs:jsr305:3.0.2"
dependencySet(group: 'org.aspectj', version: '1.9.7') {
entry 'aspectjrt'
entry 'aspectjtools'
entry 'aspectjweaver'
}
dependencySet(group: 'org.codehaus.groovy', version: '3.0.9') {
entry 'groovy'
entry 'groovy-jsr223'
entry 'groovy-templates' // requires findbugs for warning-free compilation
entry 'groovy-test'
entry 'groovy-xml'
}
dependency "io.reactivex:rxjava:1.3.8"
dependency "io.reactivex:rxjava-reactive-streams:1.2.1"
dependency "io.reactivex.rxjava2:rxjava:2.2.21"
dependency "io.reactivex.rxjava3:rxjava:3.1.1"
dependency "io.smallrye.reactive:mutiny:1.1.1"
dependency "io.projectreactor.tools:blockhound:1.0.6.RELEASE"
dependency "com.caucho:hessian:4.0.63"
dependency "com.fasterxml:aalto-xml:1.3.0"
dependency("com.fasterxml.woodstox:woodstox-core:6.2.6") {
exclude group: "stax", name: "stax-api"
}
dependency "com.google.code.gson:gson:2.8.8"
dependency "com.google.protobuf:protobuf-java-util:3.18.0"
dependency "com.googlecode.protobuf-java-format:protobuf-java-format:1.4"
dependency("com.thoughtworks.xstream:xstream:1.4.18") {
exclude group: "xpp3", name: "xpp3_min"
exclude group: "xmlpull", name: "xmlpull"
}
dependency "org.apache.johnzon:johnzon-jsonb:1.2.14"
dependency("org.codehaus.jettison:jettison:1.3.8") {
exclude group: "stax", name: "stax-api"
}
dependencySet(group: 'org.jibx', version: '1.3.3') {
entry 'jibx-bind'
entry 'jibx-run'
}
dependency "org.ogce:xpp3:1.1.6"
dependency "org.yaml:snakeyaml:1.29"
dependency "com.h2database:h2:1.4.200"
dependency "com.github.ben-manes.caffeine:caffeine:2.9.2"
dependency "com.github.librepdf:openpdf:1.3.26"
dependency "com.rometools:rome:1.16.0"
dependency "commons-io:commons-io:2.5"
dependency "io.vavr:vavr:0.10.4"
dependency "net.sf.jopt-simple:jopt-simple:5.0.4"
dependencySet(group: 'org.apache.activemq', version: '5.16.2') {
entry 'activemq-broker'
entry('activemq-kahadb-store') {
exclude group: "org.springframework", name: "spring-context"
}
entry 'activemq-stomp'
}
dependency "org.apache.bcel:bcel:6.0"
dependency "org.apache.commons:commons-pool2:2.9.0"
dependencySet(group: 'org.apache.derby', version: '10.14.2.0') {
entry 'derby'
entry 'derbyclient'
}
dependency "org.apache.poi:poi-ooxml:4.1.2"
dependency "org.apache-extras.beanshell:bsh:2.0b6"
dependency "org.freemarker:freemarker:2.3.31"
dependency "org.hsqldb:hsqldb:2.5.2"
dependency "org.quartz-scheduler:quartz:2.3.2"
dependency "org.codehaus.fabric3.api:commonj:1.1.0"
dependency "net.sf.ehcache:ehcache:2.10.6"
dependency "org.ehcache:jcache:1.0.1"
dependency "org.ehcache:ehcache:3.4.0"
dependency "org.hibernate:hibernate-core:5.4.32.Final"
dependency "org.hibernate:hibernate-validator:6.2.0.Final"
dependency "org.webjars:webjars-locator-core:0.48"
dependency "org.webjars:underscorejs:1.8.3"
dependencySet(group: 'org.apache.tomcat', version: '9.0.54') {
entry 'tomcat-util'
entry('tomcat-websocket') {
exclude group: "org.apache.tomcat", name: "tomcat-websocket-api"
exclude group: "org.apache.tomcat", name: "tomcat-servlet-api"
}
}
dependencySet(group: 'org.apache.tomcat.embed', version: '9.0.54') {
entry 'tomcat-embed-core'
entry 'tomcat-embed-websocket'
}
dependencySet(group: 'io.undertow', version: '2.2.12.Final') {
entry 'undertow-core'
entry('undertow-websockets-jsr') {
exclude group: "org.jboss.spec.javax.websocket", name: "jboss-websocket-api_1.1_spec"
}
entry('undertow-servlet') {
exclude group: "org.jboss.spec.javax.servlet", name: "jboss-servlet-api_4.0_spec"
exclude group: "org.jboss.spec.javax.annotation", name: "jboss-annotations-api_1.3_spec"
}
}
dependencySet(group: 'com.squareup.okhttp3', version: '3.14.9') {
entry 'okhttp'
entry 'mockwebserver'
}
dependency("org.apache.httpcomponents:httpclient:4.5.13") {
exclude group: "commons-logging", name: "commons-logging"
}
dependency("org.apache.httpcomponents:httpasyncclient:4.1.4") {
exclude group: "commons-logging", name: "commons-logging"
}
dependency 'org.apache.httpcomponents.client5:httpclient5:5.1'
dependency 'org.apache.httpcomponents.core5:httpcore5-reactive:5.1.1'
dependency "org.eclipse.jetty:jetty-reactive-httpclient:1.1.9"
dependency "org.jruby:jruby:9.2.19.0"
dependency "org.python:jython-standalone:2.7.1"
dependency "org.mozilla:rhino:1.7.11"
dependency "commons-fileupload:commons-fileupload:1.4"
dependency "org.synchronoss.cloud:nio-multipart-parser:1.1.0"
dependency("org.dom4j:dom4j:2.1.3") {
exclude group: "jaxen", name: "jaxen"
exclude group: "net.java.dev.msv", name: "xsdlib"
exclude group: "pull-parser", name: "pull-parser"
exclude group: "xpp3", name: "xpp3"
}
dependency("jaxen:jaxen:1.2.0") {
exclude group: "dom4j", name: "dom4j"
}
dependency("junit:junit:4.13.2") {
exclude group: "org.hamcrest", name: "hamcrest-core"
}
dependency("de.bechte.junit:junit-hierarchicalcontextrunner:4.12.1") {
exclude group: "junit", name: "junit"
}
dependency "org.testng:testng:7.4.0"
dependency "org.junit.support:testng-engine:1.0.1"
dependency "org.hamcrest:hamcrest:2.1"
dependency "org.awaitility:awaitility:3.1.6"
dependency "org.assertj:assertj-core:3.21.0"
dependencySet(group: 'org.xmlunit', version: '2.8.3') {
entry 'xmlunit-assertj'
entry('xmlunit-matchers') {
exclude group: "org.hamcrest", name: "hamcrest-core"
}
}
dependencySet(group: 'org.mockito', version: '4.0.0') {
entry('mockito-core') {
exclude group: "org.hamcrest", name: "hamcrest-core"
}
entry 'mockito-junit-jupiter'
}
dependency "io.mockk:mockk:1.12.0"
dependency("net.sourceforge.htmlunit:htmlunit:2.54.0") {
exclude group: "commons-logging", name: "commons-logging"
}
dependency("org.seleniumhq.selenium:htmlunit-driver:2.54.0") {
exclude group: "commons-logging", name: "commons-logging"
}
dependency("org.seleniumhq.selenium:selenium-java:3.141.59") {
exclude group: "commons-logging", name: "commons-logging"
exclude group: "io.netty", name: "netty"
}
dependency "org.skyscreamer:jsonassert:1.5.0"
dependency "com.jayway.jsonpath:json-path:2.6.0"
dependency "org.bouncycastle:bcpkix-jdk15on:1.66"
dependencySet(group: 'org.apache.tiles', version: '3.0.8') {
entry 'tiles-api'
entry('tiles-core', withoutJclOverSlf4j)
entry('tiles-servlet', withoutJclOverSlf4j)
entry('tiles-jsp', withoutJclOverSlf4j)
entry('tiles-el', withoutJclOverSlf4j)
entry('tiles-extras') {
exclude group: "org.springframework", name: "spring-web"
exclude group: "org.slf4j", name: "jcl-over-slf4j"
}
}
dependency("org.apache.taglibs:taglibs-standard-jstlel:1.2.5") {
exclude group: "org.apache.taglibs", name: "taglibs-standard-spec"
}
dependency "com.ibm.websphere:uow:6.0.2.17"
dependency "com.jamonapi:jamon:2.82"
dependency "joda-time:joda-time:2.10.10"
dependency "org.eclipse.persistence:org.eclipse.persistence.jpa:2.7.9"
dependency "org.javamoney:moneta:1.3"
dependency "com.sun.activation:javax.activation:1.2.0"
dependency "com.sun.mail:javax.mail:1.6.2"
dependencySet(group: 'com.sun.xml.bind', version: '2.3.0.1') {
entry 'jaxb-core'
entry 'jaxb-impl'
entry 'jaxb-xjc'
}
dependency "javax.activation:javax.activation-api:1.2.0"
dependency "javax.annotation:javax.annotation-api:1.3.2"
dependency "javax.cache:cache-api:1.1.0"
dependency "javax.ejb:javax.ejb-api:3.2"
dependency "javax.el:javax.el-api:3.0.1-b04"
dependency "javax.enterprise.concurrent:javax.enterprise.concurrent-api:1.0"
dependency "javax.faces:javax.faces-api:2.2"
dependency "javax.inject:javax.inject:1"
dependency "javax.inject:javax.inject-tck:1"
dependency "javax.interceptor:javax.interceptor-api:1.2.2"
dependency "javax.jms:javax.jms-api:2.0.1"
dependency "javax.json:javax.json-api:1.1.4"
dependency "javax.json.bind:javax.json.bind-api:1.0"
dependency "javax.mail:javax.mail-api:1.6.2"
dependency "javax.money:money-api:1.0.3"
dependency "javax.resource:javax.resource-api:1.7.1"
dependency "javax.servlet:javax.servlet-api:4.0.1"
dependency "javax.servlet.jsp:javax.servlet.jsp-api:2.3.2-b02"
dependency "javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.2.1"
dependency "javax.transaction:javax.transaction-api:1.3"
dependency "javax.validation:validation-api:2.0.1.Final"
dependency "javax.websocket:javax.websocket-api:1.1"
dependency "javax.xml.bind:jaxb-api:2.3.1"
dependency "javax.xml.ws:jaxws-api:2.3.1"
dependency "org.eclipse.persistence:javax.persistence:2.2.0"
// Substitute for "javax.management:jmxremote_optional:1.0.1_04" which
// is not available on Maven Central
dependency "org.glassfish.external:opendmk_jmxremote_optional_jar:1.0-b01-ea"
dependency "org.glassfish:javax.el:3.0.1-b08"
dependency "org.glassfish.main:javax.jws:4.0-b33"
dependency "org.glassfish.tyrus:tyrus-container-servlet:1.13.1"
}
maven { url "https://repo.spring.io/libs-spring-framework-build" }
if (version.contains('-')) {
maven { url "https://repo.spring.io/milestone" }
generatedPomCustomization {
enabled = false
}
if (version.endsWith('-SNAPSHOT')) {
maven { url "https://repo.spring.io/snapshot" }
resolutionStrategy {
cacheChangingModulesFor 0, "seconds"
}
repositories {
mavenCentral()
maven { url "https://repo.spring.io/libs-spring-framework-build" }
}
}
configurations.all {
@@ -50,17 +308,27 @@ configure([rootProject] + javaProjects) { project ->
apply plugin: "java"
apply plugin: "java-test-fixtures"
apply plugin: "checkstyle"
apply plugin: 'org.springframework.build.conventions'
apply plugin: 'org.springframework.build.compile'
apply from: "${rootDir}/gradle/toolchains.gradle"
apply from: "${rootDir}/gradle/ide.gradle"
configurations {
dependencyManagement {
canBeConsumed = false
canBeResolved = false
visible = false
pluginManager.withPlugin("kotlin") {
apply plugin: "org.jetbrains.dokka"
apply from: "${rootDir}/gradle/docs-dokka.gradle"
compileKotlin {
kotlinOptions {
languageVersion = "1.3"
apiVersion = "1.3"
freeCompilerArgs = ["-Xjsr305=strict", "-Xsuppress-version-warnings", "-Xopt-in=kotlin.RequiresOptIn"]
allWarningsAsErrors = true
}
}
compileTestKotlin {
kotlinOptions {
freeCompilerArgs = ["-Xjsr305=strict"]
}
}
matching { it.name.endsWith("Classpath") }.all { it.extendsFrom(dependencyManagement) }
}
test {
@@ -69,29 +337,14 @@ configure([rootProject] + javaProjects) { project ->
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.4"
toolVersion = "9.0"
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")))
testImplementation("org.junit.jupiter:junit-jupiter-api")
testImplementation("org.junit.jupiter:junit-jupiter-params")
testImplementation("org.junit.platform:junit-platform-suite-api")
@@ -100,48 +353,42 @@ configure([rootProject] + javaProjects) { project ->
testImplementation("io.mockk:mockk")
testImplementation("org.assertj:assertj-core")
// Pull in the latest JUnit 5 Launcher API to ensure proper support in IDEs.
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
testRuntimeOnly("org.junit.platform:junit-platform-suite-engine")
testRuntimeOnly("org.apache.logging.log4j:log4j-core")
testRuntimeOnly("org.apache.logging.log4j:log4j-jul")
testRuntimeOnly("org.apache.logging.log4j:log4j-slf4j-impl")
testRuntimeOnly("org.apache.logging.log4j:log4j-jul")
// 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.31")
checkstyle("io.spring.javaformat:spring-javaformat-checkstyle:0.0.29")
}
ext.javadocLinks = [
"https://docs.oracle.com/en/java/javase/17/docs/api/",
"https://jakarta.ee/specifications/platform/9/apidocs/",
"https://docs.oracle.com/javase/8/docs/api/",
"https://docs.oracle.com/javaee/7/api/",
"https://docs.oracle.com/cd/E13222_01/wls/docs90/javadocs/", // CommonJ
"https://www.ibm.com/docs/api/v1/content/SSEQTP_8.5.5/com.ibm.websphere.javadoc.doc/web/apidocs/",
"https://docs.jboss.org/jbossas/javadoc/4.0.5/connector/",
"https://docs.jboss.org/jbossas/javadoc/7.1.2.Final/",
"https://tiles.apache.org/tiles-request/apidocs/",
"https://tiles.apache.org/framework/apidocs/",
"https://www.eclipse.org/aspectj/doc/released/aspectj5rt-api/",
"https://www.ehcache.org/apidocs/2.10.4/",
"https://www.quartz-scheduler.org/api/2.3.0/",
"https://fasterxml.github.io/jackson-core/javadoc/2.10/",
"https://fasterxml.github.io/jackson-databind/javadoc/2.10/",
"https://fasterxml.github.io/jackson-dataformat-xml/javadoc/2.10/",
"https://hc.apache.org/httpcomponents-client-5.2.x/current/httpclient5/apidocs/",
"https://hc.apache.org/httpcomponents-client-5.1.x/current/httpclient5/apidocs/",
"https://projectreactor.io/docs/test/release/api/",
"https://junit.org/junit4/javadoc/4.13.2/",
// TODO Uncomment link to JUnit 5 docs once we have sorted out
// the following warning in the build.
//
// warning: The code being documented uses packages in the unnamed module, but the packages defined in https://junit.org/junit5/docs/5.9.1/api/ are in named modules.
//
// "https://junit.org/junit5/docs/5.9.1/api/",
// Disabling linking to JUnit 5.8.1, since the `package-list` file no longer exists due to
// https://github.com/junit-team/junit5/commit/67ad4e545518b0ce2b0e7c96df31a669866d5003.
// "https://junit.org/junit5/docs/5.8.1/api/",
"https://www.reactive-streams.org/reactive-streams-1.0.3-javadoc/",
"https://javadoc.io/static/io.rsocket/rsocket-core/1.1.1/",
"https://r2dbc.io/spec/1.0.0.RELEASE/api/",
// The external Javadoc link for JSR 305 must come last to ensure that types from
// JSR 250 (such as @PostConstruct) are still supported. This is due to the fact
// that JSR 250 and JSR 305 both define types in javax.annotation, which results
// in a split package, and the javadoc tool does not support split packages
// across multiple external Javadoc sites.
"https://www.javadoc.io/doc/com.google.code.findbugs/jsr305/3.0.2/"
"https://r2dbc.io/spec/0.8.5.RELEASE/api/"
] as String[]
}
@@ -152,8 +399,12 @@ configure(moduleProjects) { project ->
configure(rootProject) {
description = "Spring Framework"
apply plugin: "groovy"
apply plugin: "kotlin"
apply plugin: "io.spring.nohttp"
apply plugin: 'org.springframework.build.api-diff'
apply from: "${rootDir}/gradle/publications.gradle"
apply from: "${rootDir}/gradle/docs.gradle"
nohttp {
source.exclude "**/test-output/**"
@@ -170,8 +421,13 @@ configure(rootProject) {
}
}
tasks.named("checkstyleNohttp").configure {
maxHeapSize = "1g"
publishing {
publications {
mavenJava(MavenPublication) {
artifact docsZip
artifact schemaZip
artifact distZip
}
}
}
}
+2 -42
View File
@@ -5,12 +5,9 @@ They are declared in the `build.gradle` file in this folder.
## Build Conventions
The `org.springframework.build.conventions` plugin applies all conventions to the Framework build:
* Configuring the Java compiler, see `JavaConventions`
* Configuring the Kotlin compiler, see `KotlinConventions`
* Configuring testing in the build with `TestConventions`
### Compiler conventions
The `org.springframework.build.compile` plugin applies the Java compiler conventions to the build.
## Build Plugins
@@ -36,40 +33,3 @@ current working version with. You can generate the reports for all modules or a
```
The reports are located under `build/reports/api-diff/$OLDVERSION_to_$NEWVERSION/`.
### RuntimeHints Java Agent
The `spring-core-test` project module contributes the `RuntimeHintsAgent` Java agent.
The `RuntimeHintsAgentPlugin` Gradle plugin creates a dedicated `"runtimeHintsTest"` test task for each project.
This task will detect and execute [tests tagged](https://junit.org/junit5/docs/current/user-guide/#running-tests-build-gradle)
with the `"RuntimeHintsTests"` [JUnit tag](https://junit.org/junit5/docs/current/user-guide/#running-tests-tags).
In the Spring Framework test suite, those are usually annotated with the `@EnabledIfRuntimeHintsAgent` annotation.
By default, the agent will instrument all classes located in the `"org.springframework"` package, as they are loaded.
The `RuntimeHintsAgentExtension` allows to customize this using a DSL:
```groovy
// this applies the `RuntimeHintsAgentPlugin` to the project
plugins {
id 'org.springframework.build.runtimehints-agent'
}
// You can configure the agent to include and exclude packages from the instrumentation process.
runtimeHintsAgent {
includedPackages = ["org.springframework", "io.spring"]
excludedPackages = ["org.example"]
}
dependencies {
// to use the test infrastructure, the project should also depend on the "spring-core-test" module
testImplementation(project(":spring-core-test"))
}
```
With this configuration, `./gradlew runtimeHintsTest` will run all tests instrumented by this java agent.
The global `./gradlew check` task depends on `runtimeHintsTest`.
NOTE: the "spring-core-test" module doesn't shade "spring-core" by design, so the agent should never instrument
code that doesn't have "spring-core" on its classpath.
+3 -19
View File
@@ -7,20 +7,8 @@ repositories {
gradlePluginPortal()
}
ext {
def propertiesFile = new File(new File("$projectDir").parentFile, "gradle.properties")
propertiesFile.withInputStream {
def properties = new Properties()
properties.load(it)
set("kotlinVersion", properties["kotlinVersion"])
}
}
dependencies {
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 {
@@ -29,17 +17,13 @@ gradlePlugin {
id = "org.springframework.build.api-diff"
implementationClass = "org.springframework.build.api.ApiDiffPlugin"
}
conventionsPlugin {
id = "org.springframework.build.conventions"
implementationClass = "org.springframework.build.ConventionsPlugin"
compileConventionsPlugin {
id = "org.springframework.build.compile"
implementationClass = "org.springframework.build.compile.CompilerConventionsPlugin"
}
optionalDependenciesPlugin {
id = "org.springframework.build.optional-dependencies"
implementationClass = "org.springframework.build.optional.OptionalDependenciesPlugin"
}
runtimeHintsAgentPlugin {
id = "org.springframework.build.runtimehints-agent"
implementationClass = "org.springframework.build.hint.RuntimeHintsAgentPlugin"
}
}
}
@@ -1,45 +0,0 @@
/*
* 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.
* 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 org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.plugins.JavaBasePlugin;
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.
*
* 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.
*
* @author Brian Clozel
*/
public class ConventionsPlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
new JavaConventions().apply(project);
new KotlinConventions().apply(project);
new TestConventions().apply(project);
}
}
@@ -1,47 +0,0 @@
/*
* 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.
* 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.util.ArrayList;
import java.util.List;
import org.gradle.api.Project;
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions;
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile;
/**
* @author Brian Clozel
*/
public class KotlinConventions {
void apply(Project project) {
project.getPlugins().withId("org.jetbrains.kotlin.jvm",
(plugin) -> project.getTasks().withType(KotlinCompile.class, this::configure));
}
private void configure(KotlinCompile compile) {
KotlinJvmOptions kotlinOptions = compile.getKotlinOptions();
kotlinOptions.setApiVersion("1.7");
kotlinOptions.setLanguageVersion("1.7");
kotlinOptions.setJvmTarget("17");
kotlinOptions.setAllWarningsAsErrors(true);
List<String> freeCompilerArgs = new ArrayList<>(compile.getKotlinOptions().getFreeCompilerArgs());
freeCompilerArgs.addAll(List.of("-Xsuppress-version-warnings", "-Xjsr305=strict", "-opt-in=kotlin.RequiresOptIn"));
compile.getKotlinOptions().setFreeCompilerArgs(freeCompilerArgs);
}
}
@@ -1,56 +0,0 @@
/*
* 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.
* 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 org.gradle.api.Project;
import org.gradle.api.plugins.JavaBasePlugin;
import org.gradle.api.tasks.testing.Test;
import org.gradle.testretry.TestRetryPlugin;
import org.gradle.testretry.TestRetryTaskExtension;
/**
* Conventions that are applied in the presence of the {@link JavaBasePlugin}. When the
* plugin is applied:
* <ul>
* <li>The {@link TestRetryPlugin Test Retry} plugins is applied so that flaky tests
* are retried 3 times when running on the CI.
* </ul>
*
* @author Brian Clozel
* @author Andy Wilkinson
*/
class TestConventions {
void apply(Project project) {
project.getPlugins().withType(JavaBasePlugin.class, (java) -> configureTestConventions(project));
}
private void configureTestConventions(Project project) {
project.getPlugins().apply(TestRetryPlugin.class);
project.getTasks().withType(Test.class,
(test) -> project.getPlugins().withType(TestRetryPlugin.class, (testRetryPlugin) -> {
TestRetryTaskExtension testRetry = test.getExtensions().getByType(TestRetryTaskExtension.class);
testRetry.getFailOnPassedAfterRetry().set(true);
testRetry.getMaxRetries().set(isCi() ? 3 : 0);
}));
}
private boolean isCi() {
return Boolean.parseBoolean(System.getenv("CI"));
}
}
@@ -16,7 +16,6 @@
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;
@@ -24,7 +23,6 @@ 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;
@@ -34,12 +32,10 @@ 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 create tasks for all subprojects, 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.
@@ -50,16 +46,12 @@ import org.slf4j.LoggerFactory;
*/
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())) {
@@ -71,24 +63,16 @@ public class ApiDiffPlugin implements Plugin<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);
}
});
project.subprojects(subProject -> 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));
apiDiff.setOldClasspath(project.files(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));
@@ -114,16 +98,7 @@ public class ApiDiffPlugin implements Plugin<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();
return project.getRootProject().getConfigurations().detachedConfiguration(baselineDependency);
}
private Configuration getRuntimeClassPath(Project project) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.build;
package org.springframework.build.compile;
import java.util.ArrayList;
import java.util.Arrays;
@@ -22,8 +22,9 @@ import java.util.List;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.plugins.JavaBasePlugin;
import org.gradle.api.plugins.JavaLibraryPlugin;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.tasks.compile.JavaCompile;
/**
@@ -33,7 +34,7 @@ import org.gradle.api.tasks.compile.JavaCompile;
* @author Sam Brannen
* @author Sebastien Deleuze
*/
public class JavaConventions {
public class CompilerConventionsPlugin implements Plugin<Project> {
private static final List<String> COMPILER_ARGS;
@@ -58,8 +59,9 @@ public class JavaConventions {
"-Xlint:-deprecation", "-Xlint:-unchecked"));
}
@Override
public void apply(Project project) {
project.getPlugins().withType(JavaBasePlugin.class, javaPlugin -> applyJavaCompileConventions(project));
project.getPlugins().withType(JavaLibraryPlugin.class, javaPlugin -> applyJavaCompileConventions(project));
}
/**
@@ -68,6 +70,7 @@ public class JavaConventions {
* @param project the current project
*/
private void applyJavaCompileConventions(Project project) {
JavaPluginConvention java = project.getConvention().getPlugin(JavaPluginConvention.class);
project.getTasks().withType(JavaCompile.class)
.matching(compileTask -> compileTask.getName().equals(JavaPlugin.COMPILE_JAVA_TASK_NAME))
.forEach(compileTask -> {
@@ -1,53 +0,0 @@
/*
* 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.
* 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.model.ObjectFactory;
import org.gradle.api.provider.SetProperty;
/**
* Entry point to the DSL extension for the {@link RuntimeHintsAgentPlugin} Gradle plugin.
* @author Brian Clozel
*/
public class RuntimeHintsAgentExtension {
private final SetProperty<String> includedPackages;
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,56 +0,0 @@
/*
* 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.
* 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 org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.tasks.bundling.Jar;
import org.gradle.api.tasks.testing.Test;
/**
* {@link Plugin} that configures the {@code RuntimeHints} Java agent to test tasks.
*
* @author Brian Clozel
*/
public class RuntimeHintsAgentPlugin implements Plugin<Project> {
public static final String RUNTIMEHINTS_TEST_TASK = "runtimeHintsTest";
private static final String EXTENSION_NAME = "runtimeHintsAgent";
@Override
public void apply(Project project) {
project.getPlugins().withType(JavaPlugin.class, javaPlugin -> {
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");
});
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));
});
}
}
@@ -19,9 +19,12 @@ 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.attributes.Usage;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.tasks.SourceSetContainer;
import org.gradle.plugins.ide.eclipse.EclipsePlugin;
import org.gradle.plugins.ide.eclipse.model.EclipseModel;
/**
* A {@code Plugin} that adds support for Maven-style optional dependencies. Creates a new
@@ -44,7 +47,7 @@ public class OptionalDependenciesPlugin implements Plugin<Project> {
optional.setCanBeConsumed(false);
optional.setCanBeResolved(false);
project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> {
SourceSetContainer sourceSets = project.getExtensions().getByType(JavaPluginExtension.class)
SourceSetContainer sourceSets = project.getConvention().getPlugin(JavaPluginConvention.class)
.getSourceSets();
sourceSets.all((sourceSet) -> {
project.getConfigurations().getByName(sourceSet.getCompileClasspathConfigurationName()).extendsFrom(optional);
@@ -1,175 +0,0 @@
package org.springframework.build.shadow;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.gradle.api.DefaultTask;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.component.ModuleComponentSelector;
import org.gradle.api.artifacts.query.ArtifactResolutionQuery;
import org.gradle.api.artifacts.result.ArtifactResolutionResult;
import org.gradle.api.artifacts.result.ComponentArtifactsResult;
import org.gradle.api.artifacts.result.DependencyResult;
import org.gradle.api.artifacts.result.ResolutionResult;
import org.gradle.api.artifacts.result.ResolvedArtifactResult;
import org.gradle.api.file.DirectoryProperty;
import org.gradle.api.file.FileCopyDetails;
import org.gradle.api.file.FileTree;
import org.gradle.api.tasks.Classpath;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.Nested;
import org.gradle.api.tasks.Optional;
import org.gradle.api.tasks.OutputDirectory;
import org.gradle.api.tasks.TaskAction;
import org.gradle.jvm.JvmLibrary;
import org.gradle.language.base.artifact.SourcesArtifact;
/**
* Gradle task to add source from shadowed jars into our own source jars.
*
* @author Phillip Webb
* @author Andy Wilkinson
*/
public class ShadowSource extends DefaultTask {
private final DirectoryProperty outputDirectory = getProject().getObjects().directoryProperty();
private List<Configuration> configurations = new ArrayList<>();
private final List<Relocation> relocations = new ArrayList<>();
@Classpath
@Optional
public List<Configuration> getConfigurations() {
return this.configurations;
}
public void setConfigurations(List<Configuration> configurations) {
this.configurations = configurations;
}
@Nested
public List<Relocation> getRelocations() {
return this.relocations;
}
public void relocate(String pattern, String destination) {
this.relocations.add(new Relocation(pattern, destination));
}
@OutputDirectory
DirectoryProperty getOutputDirectory() {
return this.outputDirectory;
}
@TaskAction
void syncSourceJarFiles() {
sync(getSourceJarFiles());
}
private List<File> getSourceJarFiles() {
List<File> sourceJarFiles = new ArrayList<>();
for (Configuration configuration : this.configurations) {
ResolutionResult resolutionResult = configuration.getIncoming().getResolutionResult();
resolutionResult.getRootComponent().get().getDependencies().forEach(dependency -> {
Set<ComponentArtifactsResult> artifactsResults = resolveSourceArtifacts(dependency);
for (ComponentArtifactsResult artifactResult : artifactsResults) {
artifactResult.getArtifacts(SourcesArtifact.class).forEach(sourceArtifact -> {
sourceJarFiles.add(((ResolvedArtifactResult) sourceArtifact).getFile());
});
}
});
}
return Collections.unmodifiableList(sourceJarFiles);
}
private Set<ComponentArtifactsResult> resolveSourceArtifacts(DependencyResult dependency) {
ModuleComponentSelector componentSelector = (ModuleComponentSelector) dependency.getRequested();
ArtifactResolutionQuery query = getProject().getDependencies().createArtifactResolutionQuery()
.forModule(componentSelector.getGroup(), componentSelector.getModule(), componentSelector.getVersion());
return executeQuery(query).getResolvedComponents();
}
@SuppressWarnings("unchecked")
private ArtifactResolutionResult executeQuery(ArtifactResolutionQuery query) {
return query.withArtifacts(JvmLibrary.class, SourcesArtifact.class).execute();
}
private void sync(List<File> sourceJarFiles) {
getProject().sync(spec -> {
spec.into(this.outputDirectory);
spec.eachFile(this::relocateFile);
spec.filter(this::transformContent);
spec.exclude("META-INF/**");
spec.setIncludeEmptyDirs(false);
sourceJarFiles.forEach(sourceJar -> spec.from(zipTree(sourceJar)));
});
}
private void relocateFile(FileCopyDetails details) {
String path = details.getPath();
for (Relocation relocation : this.relocations) {
path = relocation.relocatePath(path);
}
details.setPath(path);
}
private String transformContent(String content) {
for (Relocation relocation : this.relocations) {
content = relocation.transformContent(content);
}
return content;
}
private FileTree zipTree(File sourceJar) {
return getProject().zipTree(sourceJar);
}
/**
* A single relocation.
*/
static class Relocation {
private final String pattern;
private final String pathPattern;
private final String destination;
private final String pathDestination;
Relocation(String pattern, String destination) {
this.pattern = pattern;
this.pathPattern = pattern.replace('.', '/');
this.destination = destination;
this.pathDestination = destination.replace('.', '/');
}
@Input
public String getPattern() {
return this.pattern;
}
@Input
public String getDestination() {
return this.destination;
}
String relocatePath(String path) {
return path.replace(this.pathPattern, this.pathDestination);
}
public String transformContent(String content) {
return content.replaceAll("\\b" + this.pattern, this.destination);
}
}
}
+2 -2
View File
@@ -2,7 +2,7 @@
The Spring Framework uses https://concourse-ci.org/[Concourse] for its CI build and other automated tasks.
The Spring team has a dedicated Concourse instance available at https://ci.spring.io with a build pipeline
for https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-6.0.x[Spring Framework 6.0.x].
for https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-5.3.x[Spring Framework 5.3.x].
=== Setting up your development environment
@@ -51,7 +51,7 @@ The pipeline can be deployed using the following command:
[source]
----
$ fly -t spring set-pipeline -p spring-framework-6.0.x -c ci/pipeline.yml -l ci/parameters.yml
$ fly -t spring set-pipeline -p spring-framework-5.3.x -c ci/pipeline.yml -l ci/parameters.yml
----
NOTE: This assumes that you have credhub integration configured with the appropriate secrets.
-3
View File
@@ -15,6 +15,3 @@ changelog:
sort: "title"
labels:
- "type: dependency-upgrade"
contributors:
exclude:
names: ["bclozel", "jhoeller", "poutsma", "rstoyanchev", "sbrannen", "sdeleuze", "snicoll"]
+5 -6
View File
@@ -1,12 +1,11 @@
FROM ubuntu:focal-20220922
FROM ubuntu:focal-20210827
ADD setup.sh /setup.sh
ADD get-jdk-url.sh /get-jdk-url.sh
RUN ./setup.sh
RUN ./setup.sh java8
ENV JAVA_HOME /opt/openjdk/java17
ENV JDK17 /opt/openjdk/java17
ENV JDK18 /opt/openjdk/java18
ENV JDK19 /opt/openjdk/java19
ENV JAVA_HOME /opt/openjdk/java8
ENV JDK11 /opt/openjdk/java11
ENV JDK16 /opt/openjdk/java16
ENV PATH $JAVA_HOME/bin:$PATH
+8 -8
View File
@@ -2,16 +2,16 @@
set -e
case "$1" in
java17)
echo "https://github.com/bell-sw/Liberica/releases/download/17.0.5+8/bellsoft-jdk17.0.5+8-linux-amd64.tar.gz"
java8)
echo "https://github.com/adoptium/temurin8-binaries/releases/download/jdk8u302-b08/OpenJDK8U-jdk_x64_linux_hotspot_8u302b08.tar.gz"
;;
java18)
echo "https://github.com/bell-sw/Liberica/releases/download/18.0.2.1%2B1/bellsoft-jdk18.0.2.1+1-linux-amd64.tar.gz"
java11)
echo "https://github.com/adoptium/temurin11-binaries/releases/download/jdk-11.0.12%2B7/OpenJDK11U-jdk_x64_linux_hotspot_11.0.12_7.tar.gz"
;;
java19)
echo "https://github.com/bell-sw/Liberica/releases/download/19.0.1%2B11/bellsoft-jdk19.0.1+11-linux-amd64.tar.gz"
;;
*)
java16)
echo "https://github.com/adoptium/temurin16-binaries/releases/download/jdk-16.0.2%2B7/OpenJDK16U-jdk_x64_linux_hotspot_16.0.2_7.tar.gz"
;;
*)
echo $"Unknown java version"
exit 1
esac
+3 -1
View File
@@ -14,13 +14,15 @@ rm -rf /var/lib/apt/lists/*
curl https://raw.githubusercontent.com/spring-io/concourse-java-scripts/v0.0.4/concourse-java.sh > /opt/concourse-java.sh
curl --output /opt/concourse-release-scripts.jar https://repo.spring.io/release/io/spring/concourse/releasescripts/concourse-release-scripts/0.3.2/concourse-release-scripts-0.3.2.jar
###########################################################
# JAVA
###########################################################
mkdir -p /opt/openjdk
pushd /opt/openjdk > /dev/null
for jdk in java17 java18 java19
for jdk in java8 java11 java16
do
JDK_URL=$( /get-jdk-url.sh $jdk )
mkdir $jdk
+5 -6
View File
@@ -1,14 +1,13 @@
email-server: "smtp.svc.pivotal.io"
email-from: "ci@spring.io"
email-to: ["spring-framework-dev@pivotal.io"]
github-repo: "https://github.com/spring-projects/spring-framework.git"
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.0.x"
branch: "5.3.x"
milestone: "5.3.x"
build-name: "spring-framework"
pipeline-name: "spring-framework"
concourse-url: "https://ci.spring.io"
registry-mirror-host: docker.repo.spring.io
registry-mirror-username: ((artifactory-username))
registry-mirror-password: ((artifactory-password))
task-timeout: 1h00m
+66 -79
View File
@@ -12,7 +12,7 @@ anchors:
SONATYPE_USERNAME: ((sonatype-username))
SONATYPE_PASSWORD: ((sonatype-password))
SONATYPE_URL: ((sonatype-url))
SONATYPE_STAGING_PROFILE: ((sonatype-staging-profile))
SONATYPE_STAGING_PROFILE_ID: ((sonatype-staging-profile-id))
artifactory-task-params: &artifactory-task-params
ARTIFACTORY_SERVER: ((artifactory-server))
ARTIFACTORY_USERNAME: ((artifactory-username))
@@ -24,10 +24,6 @@ anchors:
username: ((docker-hub-username))
password: ((docker-hub-password))
tag: ((milestone))
registry-mirror-vars: &registry-mirror-vars
registry-mirror-host: ((registry-mirror-host))
registry-mirror-username: ((registry-mirror-username))
registry-mirror-password: ((registry-mirror-password))
slack-fail-params: &slack-fail-params
text: >
:concourse-failed: <https://ci.spring.io/teams/${BUILD_TEAM_NAME}/pipelines/${BUILD_PIPELINE_NAME}/jobs/${BUILD_JOB_NAME}/builds/${BUILD_NAME}|${BUILD_PIPELINE_NAME} ${BUILD_JOB_NAME} failed!>
@@ -45,11 +41,6 @@ anchors:
GITHUB_TOKEN: ((github-ci-release-token))
resource_types:
- name: registry-image
type: registry-image
source:
repository: concourse/registry-image-resource
tag: 1.5.0
- name: artifactory-resource
type: registry-image
source:
@@ -81,6 +72,13 @@ resources:
icon: github
source:
<<: *git-repo-resource-source
- name: every-morning
type: time
icon: alarm
source:
start: 8:00 AM
stop: 9:00 AM
location: Europe/Vienna
- name: ci-images-git-repo
type: git
icon: github
@@ -89,18 +87,11 @@ resources:
branch: ((branch))
paths: ["ci/images/*"]
- name: ci-image
type: registry-image
type: docker-image
icon: docker
source:
<<: *docker-resource-source
repository: ((docker-hub-organization))/spring-framework-ci
- 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
@@ -125,22 +116,22 @@ resources:
access_token: ((github-ci-status-token))
branch: ((branch))
context: build
- name: repo-status-jdk18-build
- name: repo-status-jdk11-build
type: github-status-resource
icon: eye-check-outline
source:
repository: ((github-repo-name))
access_token: ((github-ci-status-token))
branch: ((branch))
context: jdk18-build
- name: repo-status-jdk19-build
context: jdk11-build
- name: repo-status-jdk16-build
type: github-status-resource
icon: eye-check-outline
source:
repository: ((github-repo-name))
access_token: ((github-ci-status-token))
branch: ((branch))
context: jdk19-build
context: jdk16-build
- name: slack-alert
type: slack-notification
icon: slack
@@ -166,20 +157,13 @@ resources:
jobs:
- name: build-ci-images
plan:
- get: git-repo
- get: ci-images-git-repo
trigger: true
- task: build-ci-image
privileged: true
file: git-repo/ci/tasks/build-ci-image.yml
output_mapping:
image: ci-image
vars:
ci-image-name: ci-image
<<: *registry-mirror-vars
- get: ci-images-git-repo
trigger: true
- in_parallel:
- put: ci-image
params:
image: ci-image/image.tar
build: ci-images-git-repo/ci/images
dockerfile: ci-images-git-repo/ci/images/ci-image/Dockerfile
- name: build
serial: true
public: true
@@ -218,26 +202,54 @@ jobs:
threads: 8
artifact_set:
- include:
- "/**/framework-docs-*.zip"
- "/**/spring-*.zip"
properties:
"zip.name": "spring-framework"
"zip.displayname": "Spring Framework"
"zip.deployed": "false"
- include:
- "/**/framework-docs-*-docs.zip"
- "/**/spring-*-docs.zip"
properties:
"zip.type": "docs"
- include:
- "/**/framework-docs-*-dist.zip"
- "/**/spring-*-dist.zip"
properties:
"zip.type": "dist"
- include:
- "/**/framework-docs-*-schema.zip"
- "/**/spring-*-schema.zip"
properties:
"zip.type": "schema"
get_params:
threads: 8
- name: jdk18-build
- name: jdk11-build
serial: true
public: true
plan:
- get: ci-image
- get: git-repo
- get: every-morning
trigger: true
- put: repo-status-jdk11-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: 11
<<: *build-project-task-params
on_failure:
do:
- put: repo-status-jdk11-build
params: { state: "failure", commit: "git-repo" }
- put: slack-alert
params:
<<: *slack-fail-params
- put: repo-status-jdk11-build
params: { state: "success", commit: "git-repo" }
- name: jdk16-build
serial: true
public: true
plan:
@@ -245,53 +257,25 @@ jobs:
- get: git-repo
- get: every-morning
trigger: true
- put: repo-status-jdk18-build
- put: repo-status-jdk16-build
params: { state: "pending", commit: "git-repo" }
- do:
- task: check-project
image: ci-image
file: git-repo/ci/tasks/check-project.yml
privileged: true
timeout: ((task-timeout))
params:
TEST_TOOLCHAIN: 18
<<: *build-project-task-params
- task: check-project
image: ci-image
file: git-repo/ci/tasks/check-project.yml
privileged: true
timeout: ((task-timeout))
params:
TEST_TOOLCHAIN: 15
<<: *build-project-task-params
on_failure:
do:
- put: repo-status-jdk18-build
- put: repo-status-jdk16-build
params: { state: "failure", commit: "git-repo" }
- put: slack-alert
params:
<<: *slack-fail-params
- put: repo-status-jdk18-build
params: { state: "success", commit: "git-repo" }
- name: jdk19-build
serial: true
public: true
plan:
- get: ci-image
- get: git-repo
- get: every-morning
trigger: true
- put: repo-status-jdk19-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: 19
<<: *build-project-task-params
on_failure:
do:
- put: repo-status-jdk19-build
params: { state: "failure", commit: "git-repo" }
- put: slack-alert
params:
<<: *slack-fail-params
- put: repo-status-jdk19-build
- put: repo-status-jdk16-build
params: { state: "success", commit: "git-repo" }
- name: build-pull-requests
serial: true
@@ -356,6 +340,7 @@ jobs:
download_artifacts: false
save_build_info: true
- task: promote
image: ci-image
file: git-repo/ci/tasks/promote-version.yml
params:
RELEASE_TYPE: M
@@ -400,6 +385,7 @@ jobs:
download_artifacts: false
save_build_info: true
- task: promote
image: ci-image
file: git-repo/ci/tasks/promote-version.yml
params:
RELEASE_TYPE: RC
@@ -444,6 +430,7 @@ jobs:
download_artifacts: true
save_build_info: true
- task: promote
image: ci-image
file: git-repo/ci/tasks/promote-version.yml
params:
RELEASE_TYPE: RELEASE
@@ -471,7 +458,7 @@ jobs:
groups:
- name: "builds"
jobs: ["build", "jdk18-build", "jdk19-build"]
jobs: ["build", "jdk11-build", "jdk16-build"]
- name: "releases"
jobs: ["stage-milestone", "stage-rc", "stage-release", "promote-milestone", "promote-rc", "promote-release", "create-github-release"]
- name: "ci-images"
+1 -1
View File
@@ -4,6 +4,6 @@ set -e
source $(dirname $0)/common.sh
pushd git-repo > /dev/null
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false -Porg.gradle.java.installations.fromEnv=JDK17,JDK18 \
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false -Porg.gradle.java.installations.fromEnv=JDK11,JDK15 \
-PmainToolchain=${MAIN_TOOLCHAIN} -PtestToolchain=${TEST_TOOLCHAIN} --no-daemon --max-workers=4 check
popd > /dev/null
+3 -2
View File
@@ -1,15 +1,16 @@
#!/bin/bash
source $(dirname $0)/common.sh
CONFIG_DIR=git-repo/ci/config
version=$( cat artifactory-repo/build-info.json | jq -r '.buildInfo.modules[0].id' | sed 's/.*:.*:\(.*\)/\1/' )
export BUILD_INFO_LOCATION=$(pwd)/artifactory-repo/build-info.json
java -jar /concourse-release-scripts.jar \
java -jar /opt/concourse-release-scripts.jar \
--spring.config.location=${CONFIG_DIR}/release-scripts.yml \
publishToCentral $RELEASE_TYPE $BUILD_INFO_LOCATION artifactory-repo || { exit 1; }
java -jar /concourse-release-scripts.jar \
java -jar /opt/concourse-release-scripts.jar \
--spring.config.location=${CONFIG_DIR}/release-scripts.yml \
promote $RELEASE_TYPE $BUILD_INFO_LOCATION || { exit 1; }
+8
View File
@@ -0,0 +1,8 @@
#!/bin/bash
export BUILD_INFO_LOCATION=$(pwd)/artifactory-repo/build-info.json
version=$( cat artifactory-repo/build-info.json | jq -r '.buildInfo.modules[0].id' | sed 's/.*:.*:\(.*\)/\1/' )
java -jar /opt/concourse-release-scripts.jar syncToCentral "RELEASE" $BUILD_INFO_LOCATION || { exit 1; }
echo "Sync complete"
echo $version > version/version
-32
View File
@@ -1,32 +0,0 @@
---
platform: linux
image_resource:
type: registry-image
source:
repository: concourse/oci-build-task
tag: 0.10.0
registry_mirror:
host: ((registry-mirror-host))
username: ((registry-mirror-username))
password: ((registry-mirror-password))
inputs:
- name: ci-images-git-repo
outputs:
- name: image
caches:
- path: ci-image-cache
params:
CONTEXT: ci-images-git-repo/ci/images
DOCKERFILE: ci-images-git-repo/ci/images/ci-image/Dockerfile
DOCKER_HUB_AUTH: ((docker-hub-auth))
run:
path: /bin/sh
args:
- "-c"
- |
mkdir -p /root/.docker
cat > /root/.docker/config.json <<EOF
{ "auths": { "https://index.docker.io/v1/": { "auth": "$DOCKER_HUB_AUTH" }}}
EOF
build
+1 -1
View File
@@ -4,7 +4,7 @@ image_resource:
type: registry-image
source:
repository: springio/github-changelog-generator
tag: '0.0.8'
tag: '0.0.7'
inputs:
- name: git-repo
- name: artifactory-repo
+1 -6
View File
@@ -1,10 +1,5 @@
---
platform: linux
image_resource:
type: registry-image
source:
repository: springio/concourse-release-scripts
tag: '0.3.4'
inputs:
- name: git-repo
- name: artifactory-repo
@@ -18,6 +13,6 @@ params:
SONATYPE_USER:
SONATYPE_PASSWORD:
SONATYPE_URL:
SONATYPE_STAGING_PROFILE:
SONATYPE_STAGING_PROFILE_ID:
run:
path: git-repo/ci/scripts/promote-version.sh
@@ -1,292 +0,0 @@
[[aot]]
= Ahead of Time Optimizations
This chapter covers Spring's Ahead of Time (AOT) optimizations.
For AOT support specific to integration tests, see <<testing.adoc#testcontext-aot, Ahead of Time Support for Tests>>.
[[aot-introduction]]
== Introduction to Ahead of Time Optimizations
Spring's support for AOT optimizations is meant to inspect an `ApplicationContext` at build time and apply decisions and discovery logic that usually happens at runtime.
Doing so allows building an application startup arrangement that is more straightforward and focused on a fixed set of features based mainly on the classpath and the `Environment`.
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.
** Environment properties that impact the presence of a bean (`@Conditional`) are only considered at build time.
When these restrictions are in place, it becomes possible to perform ahead-of-time processing at build time and generate additional assets.
A Spring AOT processed application typically generates:
* Java source code
* Bytecode (usually for dynamic 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 offer more JVM-based use cases in future generations.
[[aot-basics]]
== AOT engine overview
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`.
For instance, a core implementation iterates over all candidate bean definitions and generates the necessary code to restore the state of the `BeanFactory`.
Once this process completes, the `GenerationContext` has been updated with the generated code, resources, and classes that are necessary for the application to run.
The `RuntimeHints` instance can also be used to generate the relevant GraalVM native image configuration files.
`ApplicationContextAotGenerator#processAheadOfTime` returns the class name of the `ApplicationContextInitializer` entry point that allows the context to be started with AOT optimizations.
Those steps are covered in more details in the sections below.
[[aot-refresh]]
== Refresh for AOT Processing
Refresh for AOT processing is supported on all `GenericApplicationContext` implementations.
An application context is created with any number of entry points, usually in the form of `@Configuration`-annotated classes.
Let's look at a basic example:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration(proxyBeanMethods=false)
@ComponentScan
@Import({DataSourceConfiguration.class, ContainerConfiguration.class})
public class MyApplication {
}
----
Starting this application with the regular runtime involves a number of steps including classpath scanning, configuration class parsing, bean instantiation, and lifecycle callback handling.
Refresh for AOT processing only applies a subset of what happens with a <<beans-introduction,regular `refresh`>>.
AOT processing can be triggered as follows:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
GenericApplicationContext applicationContext = new AnnotatedConfigApplicationContext();
context.register(MyApplication.class);
context.refreshForAotProcessing();
----
In this mode, <<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 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:
* `MergedBeanDefinitionPostProcessor` implementations post-process bean definitions to extract additional settings, such as `init` and `destroy` methods.
* `SmartInstantiationAwareBeanPostProcessor` implementations determine a more precise bean type if necessary.
This makes sure to create any proxy that is required at runtime.
One this part completes, the `BeanFactory` contains the bean definitions that are necessary for the application to run. It does not trigger bean instantiation but allows the AOT engine to inspect the beans that would be created at runtime.
[[aot-bean-factory-initialization-contributions]]
== Bean Factory Initialization AOT Contributions
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 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` 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.
[NOTE]
====
If a bean implements the `BeanFactoryInitializationAotProcessor` interface, the bean and **all** of its dependencies will be initialized during AOT processing.
We generally recommend that this interface is only implemented by infrastructure beans such as `BeanFactoryPostProcessor` which have limited dependencies and are already initialized early in the bean factory lifecycle.
If such a bean is registered using an `@Bean` factory method, ensure the method is `static` so that its enclosing `@Configuration` class does not have to be initialized.
====
[[aot-bean-registration-contributions]]
=== Bean Registration AOT Contributions
A core `BeanFactoryInitializationAotProcessor` implementation is responsible for collecting the necessary contributions for each candidate `BeanDefinition`.
It does so using a dedicated `BeanRegistrationAotProcessor`.
This interface is used as follows:
* Implemented by a `BeanPostProcessor` bean, to replace its runtime behavior.
For instance <<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.
Typically used when the bean definition needs to be tuned for specific features of the core framework.
[NOTE]
====
If a bean implements the `BeanRegistrationAotProcessor` interface, the bean and **all** of its dependencies will be initialized during AOT processing.
We generally recommend that this interface is only implemented by infrastructure beans such as `BeanFactoryPostProcessor` which have limited dependencies and are already initialized early in the bean factory lifecycle.
If such a bean is registered using an `@Bean` factory method, ensure the method is `static` so that its enclosing `@Configuration` class does not have to be initialized.
====
If no `BeanRegistrationAotProcessor` handles a particular registered bean, a default implementation processes it.
This is the default behavior, since tuning the generated code for a bean definition should be restricted to corner cases.
Taking our previous example, let's assume that `DataSourceConfiguration` is as follows:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration(proxyBeanMethods = false)
public class DataSourceConfiguration {
@Bean
public SimpleDataSource dataSource() {
return new SimpleDataSource();
}
}
----
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:
[source,java,indent=0,role="primary"]
.Java
----
/**
* Bean definitions for {@link DataSourceConfiguration}
*/
public class DataSourceConfiguration__BeanDefinitions {
/**
* Get the bean definition for 'dataSourceConfiguration'
*/
public static BeanDefinition getDataSourceConfigurationBeanDefinition() {
Class<?> beanType = DataSourceConfiguration.class;
RootBeanDefinition beanDefinition = new RootBeanDefinition(beanType);
beanDefinition.setInstanceSupplier(DataSourceConfiguration::new);
return beanDefinition;
}
/**
* Get the bean instance supplier for 'dataSource'.
*/
private static BeanInstanceSupplier<SimpleDataSource> getDataSourceInstanceSupplier() {
return BeanInstanceSupplier.<SimpleDataSource>forFactoryMethod(DataSourceConfiguration.class, "dataSource")
.withGenerator((registeredBean) -> registeredBean.getBeanFactory().getBean(DataSourceConfiguration.class).dataSource());
}
/**
* Get the bean definition for 'dataSource'
*/
public static BeanDefinition getDataSourceBeanDefinition() {
Class<?> beanType = SimpleDataSource.class;
RootBeanDefinition beanDefinition = new RootBeanDefinition(beanType);
beanDefinition.setInstanceSupplier(getDataSourceInstanceSupplier());
return beanDefinition;
}
}
----
NOTE: The exact code generated may differ depending on the exact nature of your bean definitions.
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-hints]]
== Runtime Hints
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 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 {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:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
runtimeHints.resources().registerPattern("config/app.properties");
----
A number of contracts are handled automatically during AOT processing.
For instance, the return type of a `@Controller` method is inspected, and relevant reflection hints are added if Spring detects that the type should be serialized (typically to JSON).
For cases that the core container cannot infer, you can register such hints programmatically.
A number of convenient annotations are also provided for common use cases.
[[aot-hints-import-runtime-hints]]
=== `@ImportRuntimeHints`
`RuntimeHintsRegistrar` implementations allow you to get a callback to the `RuntimeHints` instance managed by the AOT engine.
Implementations of this interface can be registered using `@ImportRuntimeHints` on any Spring bean or `@Bean` factory method.
`RuntimeHintsRegistrar` implementations are detected and invoked at build time.
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Component
@ImportRuntimeHints(MyComponentRuntimeHints.class)
public class MyComponent {
// ...
private static class MyComponentRuntimeHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
// ...
}
}
}
----
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.
[[aot-hints-reflective]]
=== `@Reflective`
{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.
This can be tuned by specifying a custom `ReflectiveProcessor` implementation via the
`@Reflective` annotation.
Library authors can reuse this annotation for their own purposes.
If components other than Spring beans need to be processed, a `BeanFactoryInitializationAotProcessor` can detect the relevant types and use `ReflectiveRuntimeHintsRegistrar` to process them.
[[aot-hints-register-reflection-for-binding]]
=== `@RegisterReflectionForBinding`
{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.
The following example registers `Account` for serialization.
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Component
public class OrderService {
@RegisterReflectionForBinding(Account.class)
public void process(Order order) {
// ...
}
}
----
@@ -1,25 +0,0 @@
:noheader:
= Spring Framework Documentation
include::overview.adoc[leveloffset=+1]
include::core.adoc[leveloffset=+1]
include::testing.adoc[leveloffset=+1]
include::data-access.adoc[leveloffset=+1]
include::web.adoc[leveloffset=+1]
include::web-reactive.adoc[leveloffset=+1]
include::integration.adoc[leveloffset=+1]
include::languages.adoc[leveloffset=+1]
include::appendix.adoc[leveloffset=+1]
Rod Johnson, Juergen Hoeller, Keith Donald, Colin Sampaleanu, Rob Harrop, Thomas Risberg,
Alef Arendsen, Darren Davison, Dmitriy Kopylenko, Mark Pollack, Thierry Templier, Erwin
Vervaet, Portia Tung, Ben Hale, Adrian Colyer, John Lewis, Costin Leau, Mark Fisher, Sam
Brannen, Ramnivas Laddad, Arjen Poutsma, Chris Beams, Tareq Abedrabbo, Andy Clement, Dave
Syer, Oliver Gierke, Rossen Stoyanchev, Phillip Webb, Rob Winch, Brian Clozel, Stephane
Nicoll, Sebastien Deleuze, Jay Bryant, Mark Paluch
Copyright © 2002 - 2022 VMware, Inc. All Rights Reserved.
Copies of this document may be made for your own use and for distribution to others,
provided that you do not charge any fee for such copies and further provided that each
copy contains this Copyright Notice, whether distributed in print or electronically.
@@ -1,95 +0,0 @@
In the context of web applications, _data binding_ involves the binding of HTTP request
parameters (that is, form data or query parameters) to properties in a model object and
its nested objects.
Only `public` properties following the
https://www.oracle.com/java/technologies/javase/javabeans-spec.html[JavaBeans naming conventions]
are exposed for data binding — for example, `public String getFirstName()` and
`public void setFirstName(String)` methods for a `firstName` property.
TIP: The model object, and its nested object graph, is also sometimes referred to as a
_command object_, _form-backing object_, or _POJO_ (Plain Old Java Object).
By default, Spring permits binding to all public properties in the model object graph.
This means you need to carefully consider what public properties the model has, since a
client could target any public property path, even some that are not expected to be
targeted for a given use case.
For example, given an HTTP form data endpoint, a malicious client could supply values for
properties that exist in the model object graph but are not part of the HTML form
presented in the browser. This could lead to data being set on the model object and any
of its nested objects, that is not expected to be updated.
The recommended approach is to use a _dedicated model object_ that exposes only
properties that are relevant for the form submission. For example, on a form for changing
a user's email address, the model object should declare a minimum set of properties such
as in the following `ChangeEmailForm`.
[source,java,indent=0,subs="verbatim,quotes"]
----
public class ChangeEmailForm {
private String oldEmailAddress;
private String newEmailAddress;
public void setOldEmailAddress(String oldEmailAddress) {
this.oldEmailAddress = oldEmailAddress;
}
public String getOldEmailAddress() {
return this.oldEmailAddress;
}
public void setNewEmailAddress(String newEmailAddress) {
this.newEmailAddress = newEmailAddress;
}
public String getNewEmailAddress() {
return this.newEmailAddress;
}
}
----
If you cannot or do not want to use a _dedicated model object_ for each data
binding use case, you **must** limit the properties that are allowed for data binding.
Ideally, you can achieve this by registering _allowed field patterns_ via the
`setAllowedFields()` method on `WebDataBinder`.
For example, to register allowed field patterns in your application, you can implement an
`@InitBinder` method in a `@Controller` or `@ControllerAdvice` component as shown below:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Controller
public class ChangeEmailController {
@InitBinder
void initBinder(WebDataBinder binder) {
binder.setAllowedFields("oldEmailAddress", "newEmailAddress");
}
// @RequestMapping methods, etc.
}
----
In addition to registering allowed patterns, it is also possible to register _disallowed
field patterns_ via the `setDisallowedFields()` method in `DataBinder` and its subclasses.
Please note, however, that an "allow list" is safer than a "deny list". Consequently,
`setAllowedFields()` should be favored over `setDisallowedFields()`.
Note that matching against allowed field patterns is case-sensitive; whereas, matching
against disallowed field patterns is case-insensitive. In addition, a field matching a
disallowed pattern will not be accepted even if it also happens to match a pattern in the
allowed list.
[WARNING]
====
It is extremely important to properly configure allowed and disallowed field patterns
when exposing your domain model directly for data binding purposes. Otherwise, it is a
big security risk.
Furthermore, it is strongly recommended that you do **not** use types from your domain
model such as JPA or Hibernate entities as the model object in data binding scenarios.
====
@@ -1,148 +0,0 @@
plugins {
id 'java-platform'
}
javaPlatform {
allowDependencies()
}
dependencies {
api(platform("com.fasterxml.jackson:jackson-bom:2.14.0"))
api(platform("io.micrometer:micrometer-bom:1.10.0"))
api(platform("io.netty:netty-bom:4.1.85.Final"))
api(platform("io.netty:netty5-bom:5.0.0.Alpha5"))
api(platform("io.projectreactor:reactor-bom:2022.0.0"))
api(platform("io.rsocket:rsocket-bom:1.1.3"))
api(platform("org.apache.groovy:groovy-bom:4.0.5"))
api(platform("org.apache.logging.log4j:log4j-bom:2.19.0"))
api(platform("org.eclipse.jetty:jetty-bom:11.0.12"))
api(platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.6.4"))
api(platform("org.jetbrains.kotlinx:kotlinx-serialization-bom:1.4.0"))
api(platform("org.junit:junit-bom:5.9.1"))
api(platform("org.mockito:mockito-bom:4.8.1"))
constraints {
api("com.fasterxml.woodstox:woodstox-core:6.4.0")
api("com.fasterxml:aalto-xml:1.3.1")
api("com.github.ben-manes.caffeine:caffeine:3.1.1")
api("com.github.librepdf:openpdf:1.3.30")
api("com.google.code.findbugs:findbugs:3.0.1")
api("com.google.code.findbugs:jsr305:3.0.2")
api("com.google.code.gson:gson:2.9.1")
api("com.google.protobuf:protobuf-java-util:3.21.5")
api("com.googlecode.protobuf-java-format:protobuf-java-format:1.4")
api("com.h2database:h2:2.1.214")
api("com.jayway.jsonpath:json-path:2.7.0")
api("com.rometools:rome:1.18.0")
api("com.squareup.okhttp3:mockwebserver:3.14.9")
api("com.squareup.okhttp3:okhttp:3.14.9")
api("com.sun.activation:jakarta.activation:2.0.1")
api("com.sun.mail:jakarta.mail:2.0.1")
api("com.sun.xml.bind:jaxb-core:3.0.2")
api("com.sun.xml.bind:jaxb-impl:3.0.2")
api("com.sun.xml.bind:jaxb-xjc:3.0.2")
api("com.thoughtworks.qdox:qdox:2.0.2")
api("com.thoughtworks.xstream:xstream:1.4.19")
api("commons-io:commons-io:2.11.0")
api("de.bechte.junit:junit-hierarchicalcontextrunner:4.12.1")
api("info.picocli:picocli:4.6.3")
api("io.micrometer:context-propagation:1.0.0")
api("io.mockk:mockk:1.12.1")
api("io.projectreactor.netty:reactor-netty5-http:2.0.0-M3")
api("io.projectreactor.tools:blockhound:1.0.6.RELEASE")
api("io.r2dbc:r2dbc-h2:1.0.0.RELEASE")
api("io.r2dbc:r2dbc-spi-test:1.0.0.RELEASE")
api("io.r2dbc:r2dbc-spi:1.0.0.RELEASE")
api("io.reactivex.rxjava3:rxjava:3.1.5")
api("io.smallrye.reactive:mutiny:1.8.0")
api("io.undertow:undertow-core:2.3.0.Final")
api("io.undertow:undertow-servlet:2.3.0.Final")
api("io.undertow:undertow-websockets-jsr:2.3.0.Final")
api("io.vavr:vavr:0.10.4")
api("jakarta.activation:jakarta.activation-api:2.0.1")
api("jakarta.annotation:jakarta.annotation-api:2.0.0")
api("jakarta.ejb:jakarta.ejb-api:4.0.0")
api("jakarta.el:jakarta.el-api:4.0.0")
api("jakarta.enterprise.concurrent:jakarta.enterprise.concurrent-api:2.0.0")
api("jakarta.faces:jakarta.faces-api:3.0.0")
api("jakarta.inject:jakarta.inject-api:2.0.0")
api("jakarta.inject:jakarta.inject-tck:2.0.1")
api("jakarta.interceptor:jakarta.interceptor-api:2.0.0")
api("jakarta.jms:jakarta.jms-api:3.0.0")
api("jakarta.json.bind:jakarta.json.bind-api:2.0.0")
api("jakarta.json:jakarta.json-api:2.0.1")
api("jakarta.mail:jakarta.mail-api:2.0.1")
api("jakarta.persistence:jakarta.persistence-api:3.0.0")
api("jakarta.resource:jakarta.resource-api:2.0.0")
api("jakarta.servlet.jsp.jstl:jakarta.servlet.jsp.jstl-api:3.0.0")
api("jakarta.servlet.jsp:jakarta.servlet.jsp-api:3.1.0")
api("jakarta.servlet:jakarta.servlet-api:6.0.0")
api("jakarta.transaction:jakarta.transaction-api:2.0.1")
api("jakarta.validation:jakarta.validation-api:3.0.2")
api("jakarta.websocket:jakarta.websocket-api:2.1.0")
api("jakarta.websocket:jakarta.websocket-client-api:2.1.0")
api("jakarta.xml.bind:jakarta.xml.bind-api:3.0.1")
api("javax.cache:cache-api:1.1.1")
api("javax.money:money-api:1.1")
api("jaxen:jaxen:1.2.0")
api("junit:junit:4.13.2")
api("net.sf.jopt-simple:jopt-simple:5.0.4")
api("net.sourceforge.htmlunit:htmlunit:2.66.0")
api("org.apache-extras.beanshell:bsh:2.0b6")
api("org.apache.activemq:activemq-broker:5.16.2")
api("org.apache.activemq:activemq-kahadb-store:5.16.2")
api("org.apache.activemq:activemq-stomp:5.16.2")
api("org.apache.commons:commons-pool2:2.9.0")
api("org.apache.derby:derby:10.16.1.1")
api("org.apache.derby:derbyclient:10.16.1.1")
api("org.apache.derby:derbytools:10.16.1.1")
api("org.apache.httpcomponents.client5:httpclient5:5.1.3")
api("org.apache.httpcomponents.core5:httpcore5-reactive:5.1.3")
api("org.apache.poi:poi-ooxml:5.2.2")
api("org.apache.tomcat.embed:tomcat-embed-core:10.1.1")
api("org.apache.tomcat.embed:tomcat-embed-websocket:10.1.1")
api("org.apache.tomcat:tomcat-util:10.1.1")
api("org.apache.tomcat:tomcat-websocket:10.1.1")
api("org.aspectj:aspectjrt:1.9.9.1")
api("org.aspectj:aspectjtools:1.9.9.1")
api("org.aspectj:aspectjweaver:1.9.9.1")
api("org.assertj:assertj-core:3.23.1")
api("org.awaitility:awaitility:3.1.6")
api("org.bouncycastle:bcpkix-jdk18on:1.71")
api("org.codehaus.jettison:jettison:1.3.8")
api("org.dom4j:dom4j:2.1.3")
api("org.eclipse.jetty:jetty-reactive-httpclient:3.0.7")
api("org.eclipse.persistence:org.eclipse.persistence.jpa:3.0.3")
api("org.eclipse:yasson:2.0.4")
api("org.ehcache:ehcache:3.4.0")
api("org.ehcache:jcache:1.0.1")
api("org.freemarker:freemarker:2.3.31")
// Substitute for "javax.management:jmxremote_optional:1.0.1_04" which
// is not available on Maven Central
api("org.glassfish.external:opendmk_jmxremote_optional_jar:1.0-b01-ea")
api("org.glassfish.tyrus:tyrus-container-servlet:2.0.1")
api("org.glassfish:jakarta.el:4.0.2")
api("org.graalvm.sdk:graal-sdk:22.3.0")
api("org.hamcrest:hamcrest:2.2")
api("org.hibernate:hibernate-core-jakarta:5.6.12.Final")
api("org.hibernate:hibernate-validator:7.0.5.Final")
api("org.hsqldb:hsqldb:2.7.0")
api("org.javamoney:moneta:1.4.2")
api("org.jruby:jruby:9.3.8.0")
api("org.junit.support:testng-engine:1.0.4")
api("org.mozilla:rhino:1.7.11")
api("org.ogce:xpp3:1.1.6")
api("org.python:jython-standalone:2.7.1")
api("org.quartz-scheduler:quartz:2.3.2")
api("org.seleniumhq.selenium:htmlunit-driver:2.66.0")
api("org.seleniumhq.selenium:selenium-java:3.141.59")
api("org.skyscreamer:jsonassert:1.5.0")
api("org.slf4j:slf4j-api:2.0.3")
api("org.testng:testng:7.6.1")
api("org.webjars:underscorejs:1.8.3")
api("org.webjars:webjars-locator-core:0.48")
api("org.xmlunit:xmlunit-assertj:2.9.0")
api("org.xmlunit:xmlunit-matchers:2.9.0")
api("org.yaml:snakeyaml:1.30")
}
}
+3 -7
View File
@@ -1,9 +1,5 @@
version=6.0.0
version=5.3.13
org.gradle.jvmargs=-Xmx1536M
org.gradle.caching=true
org.gradle.jvmargs=-Xmx2048m
org.gradle.parallel=true
kotlinVersion=1.7.20
kotlin.stdlib.default.dependency=false
kotlin.stdlib.default.dependency=false
+1 -1
View File
@@ -20,7 +20,7 @@ tasks.findByName("dokkaHtmlPartial")?.configure {
url.set(new URL("https://javadoc.io/doc/org.hamcrest/hamcrest/2.1/"))
}
externalDocumentationLink {
url.set(new URL("https://javadoc.io/doc/jakarta.servlet/jakarta.servlet-api/latest/"))
url.set(new URL("https://javadoc.io/doc/javax.servlet/javax.servlet-api/latest/"))
}
externalDocumentationLink {
url.set(new URL("https://javadoc.io/static/io.rsocket/rsocket-core/1.1.1/"))
@@ -1,30 +1,17 @@
description = "Spring Framework Docs"
apply plugin: 'kotlin'
apply plugin: 'org.asciidoctor.jvm.convert'
apply plugin: 'org.asciidoctor.jvm.pdf'
apply from: "${rootDir}/gradle/publications.gradle"
configurations {
asciidoctorExtensions
}
jar {
enabled = false
}
javadoc {
enabled = false
asciidoctorExt
}
dependencies {
asciidoctorExtensions "io.spring.asciidoctor.backends:spring-asciidoctor-backends:0.0.3"
asciidoctorExt("io.spring.asciidoctor:spring-asciidoctor-extensions-block-switch:0.5.0")
}
repositories {
maven {
url "https://repo.spring.io/release"
mavenContent {
includeGroup "io.spring.asciidoctor"
}
}
}
@@ -55,11 +42,15 @@ task api(type: Javadoc) {
author = true
header = rootProject.description
use = true
overview = "framework-docs/src/docs/api/overview.html"
overview = "src/docs/api/overview.html"
stylesheetFile = file("src/docs/api/stylesheet.css")
splitIndex = true
links(project.ext.javadocLinks)
addBooleanOption('Xdoclint:syntax', true) // only check syntax with doclint
addBooleanOption('Werror', true) // fail build on Javadoc warnings
addBooleanOption('Xwerror', true) // fail build on Javadoc warnings
if (JavaVersion.current().isJava9Compatible()) {
addBooleanOption('html5', true)
}
}
source moduleProjects.collect { project ->
project.sourceSets.main.allJava
@@ -71,27 +62,49 @@ task api(type: Javadoc) {
/**
* Produce KDoc for all Spring Framework modules in "build/docs/kdoc"
*/
rootProject.tasks.dokkaHtmlMultiModule.configure {
dependsOn {
tasks.getByName("api")
pluginManager.withPlugin("kotlin") {
tasks.dokkaHtmlMultiModule.configure {
dependsOn {
tasks.getByName("api")
}
moduleName.set("spring-framework")
outputDirectory.set(project.file("$buildDir/docs/kdoc"))
}
moduleName.set("spring-framework")
outputDirectory.set(project.file("$buildDir/docs/kdoc"))
}
task downloadResources(type: Download) {
def version = "0.2.5"
src "https://repo.spring.io/release/io/spring/docresources/" +
"spring-doc-resources/$version/spring-doc-resources-${version}.zip"
dest project.file("$buildDir/docs/spring-doc-resources.zip")
onlyIfModified true
useETag "all"
}
task extractDocResources(type: Copy, dependsOn: downloadResources) {
from project.zipTree(downloadResources.dest);
into "$buildDir/docs/spring-docs-resources/"
}
asciidoctorj {
def docRoot = 'https://docs.spring.io'
def docsSpringFramework = "${docRoot}/spring-framework/docs/${project.version}"
version = '2.4.3'
version = '2.4.1'
fatalWarnings ".*"
options doctype: 'book', eruby: 'erubis'
attributes([
icons: 'font',
idprefix: '',
idseparator: '-',
docinfo: 'shared,private-header', // https://docs.asciidoctor.org/asciidoctor/latest/docinfo/
revnumber: project.version,
sectanchors: '',
sectnums: '',
'source-highlighter': 'highlight.js',
highlightjsdir: 'js/highlight',
'highlightjs-theme': 'googlecode',
stylesdir: 'css/',
stylesheet: 'stylesheet.css',
'spring-version': project.version,
'spring-framework-main-code': 'https://github.com/spring-projects/spring-framework/tree/main',
'doc-root': docRoot,
@@ -101,24 +114,22 @@ asciidoctorj {
}
/**
* Generate the Spring Framework Reference documentation from
* "src/docs/asciidoc" in "build/docs/ref-docs/html5".
* Generate the Spring Framework Reference documentation from "src/docs/asciidoc"
* in "build/docs/ref-docs/html5".
*/
asciidoctor {
baseDirFollowsSourceDir()
configurations "asciidoctorExtensions"
configurations 'asciidoctorExt'
sources {
include '*.adoc'
}
outputDir "$buildDir/docs/ref-docs/html5"
outputOptions {
backends "spring-html"
}
logDocuments = true
resources {
from(sourceDir) {
include 'images/*.png'
include 'images/*.png', 'css/**', 'js/**'
}
from extractDocResources
}
}
@@ -128,24 +139,21 @@ asciidoctor {
*/
asciidoctorPdf {
baseDirFollowsSourceDir()
configurations 'asciidoctorExtensions'
configurations 'asciidoctorExt'
sources {
include 'spring-framework.adocbook'
include '*.adoc'
}
outputDir "$buildDir/docs/ref-docs/pdf"
forkOptions {
jvmArgs += ["--add-opens", "java.base/sun.nio.ch=ALL-UNNAMED", "--add-opens", "java.base/java.io=ALL-UNNAMED"]
}
logDocuments = true
}
/**
* Zip all docs (API and reference) into a single archive
*/
task docsZip(type: Zip, dependsOn: ['api', 'asciidoctor', 'asciidoctorPdf', rootProject.tasks.dokkaHtmlMultiModule]) {
task docsZip(type: Zip, dependsOn: ['api', 'asciidoctor', 'asciidoctorPdf', 'dokkaHtmlMultiModule']) {
group = "Distribution"
description = "Builds -${archiveClassifier} archive containing api and reference " +
"for deployment at https://docs.spring.io/spring-framework/docs/."
"for deployment at https://docs.spring.io/spring-framework/docs."
archiveBaseName.set("spring-framework")
archiveClassifier.set("docs")
@@ -161,7 +169,7 @@ task docsZip(type: Zip, dependsOn: ['api', 'asciidoctor', 'asciidoctorPdf', root
from ("$asciidoctorPdf.outputDir") {
into "reference/pdf"
}
from (rootProject.tasks.dokkaHtmlMultiModule.outputDirectory) {
from (dokkaHtmlMultiModule.outputDirectory) {
into "kdoc-api"
}
}
@@ -240,14 +248,3 @@ task distZip(type: Zip, dependsOn: [docsZip, schemaZip]) {
}
distZip.mustRunAfter moduleProjects.check
publishing {
publications {
mavenJava(MavenPublication) {
artifact docsZip
artifact schemaZip
artifact distZip
}
}
}
+4 -6
View File
@@ -4,8 +4,8 @@ import org.gradle.plugins.ide.eclipse.model.SourceFolder
apply plugin: 'eclipse'
eclipse.jdt {
sourceCompatibility = 17
targetCompatibility = 17
sourceCompatibility = 1.8
targetCompatibility = 1.8
}
// Replace classpath entries with project dependencies (GRADLE-1116)
@@ -30,7 +30,7 @@ eclipse.classpath.file.whenMerged { classpath ->
}
// Remove any remaining direct depencencies on JARs in the build/libs folder
// except the repack JARs.
// except Spring's spring-cglib-repack and spring-objenesis-repack JARs.
classpath.entries.removeAll { entry -> (entry.path =~ /(?!.*?repack.*\.jar).*?\/([^\/]+)\/build\/libs\/[^\/]+\.jar/) }
}
@@ -64,7 +64,7 @@ eclipse.classpath.file.whenMerged {
// so that they can see test fixtures.
// https://github.com/melix/jmh-gradle-plugin/issues/157
eclipse.classpath.file.whenMerged {
entries.findAll { it.path =~ /src\/jmh\/(java|kotlin|resources)/ }.each {
entries.findAll { it.path =~ /src\/jmh\/(java|resources)/ }.each {
it.entryAttributes['test'] = 'true'
}
}
@@ -72,7 +72,6 @@ eclipse.classpath.file.whenMerged {
// Include project specific settings
task eclipseSettings(type: Copy) {
from rootProject.files(
'src/eclipse/org.eclipse.core.resources.prefs',
'src/eclipse/org.eclipse.jdt.core.prefs',
'src/eclipse/org.eclipse.jdt.ui.prefs')
into project.file('.settings/')
@@ -80,7 +79,6 @@ task eclipseSettings(type: Copy) {
}
task cleanEclipseSettings(type: Delete) {
delete project.file('.settings/org.eclipse.core.resources.prefs')
delete project.file('.settings/org.eclipse.jdt.core.prefs')
delete project.file('.settings/org.eclipse.jdt.ui.prefs')
}
+2 -7
View File
@@ -1,5 +1,5 @@
apply plugin: 'java-library'
apply plugin: 'org.springframework.build.conventions'
apply plugin: 'org.springframework.build.compile'
apply plugin: 'org.springframework.build.optional-dependencies'
// Uncomment the following for Shadow support in the jmhJar block.
// Currently commented out due to ZipException: archive is not a ZIP archive
@@ -13,11 +13,6 @@ dependencies {
jmh 'net.sf.jopt-simple:jopt-simple'
}
pluginManager.withPlugin("kotlin") {
apply plugin: "org.jetbrains.dokka"
apply from: "${rootDir}/gradle/docs-dokka.gradle"
}
jmh {
duplicateClassesStrategy = DuplicatesStrategy.EXCLUDE
}
@@ -49,7 +44,7 @@ jar {
manifest.attributes["Created-By"] =
"${System.getProperty("java.version")} (${System.getProperty("java.specification.vendor")})"
from("${rootDir}/framework-docs/src/docs/dist") {
from("${rootDir}/src/docs/dist") {
include "license.txt"
include "notice.txt"
into "META-INF"
+60 -21
View File
@@ -5,13 +5,13 @@
* One can choose the toolchain to use for compiling the MAIN sources and/or compiling
* and running the TEST sources. These options apply to Java, Kotlin and Groovy sources
* when available.
* {@code "./gradlew check -PmainToolchain=17 -PtestToolchain=18"} will use:
* {@code "./gradlew check -PmainToolchain=8 -PtestToolchain=11"} will use:
* <ul>
* <li>a JDK17 toolchain for compiling the main SourceSet
* <li>a JDK18 toolchain for compiling and running the test SourceSet
* <li>a JDK8 toolchain for compiling the main SourceSet
* <li>a JDK11 toolchain for compiling and running the test SourceSet
* </ul>
*
* By default, the build will fall back to using the current JDK and 17 language level for all sourceSets.
* By default, the build will fall back to using the current JDK and 1.8 language level for all sourceSets.
*
* Gradle will automatically detect JDK distributions in well-known locations.
* The following command will list the detected JDKs on the host.
@@ -21,11 +21,11 @@
*
* We can also configure ENV variables and let Gradle know about them:
* {@code
* $ echo JDK17
* /opt/openjdk/java17
* $ echo JDK18
* /opt/openjdk/java18
* $ ./gradlew -Porg.gradle.java.installations.fromEnv=JDK17,JDK18 check
* $ echo JDK11
* /opt/openjdk/java11
* $ echo JDK15
* /opt/openjdk/java15
* $ ./gradlew -Porg.gradle.java.installations.fromEnv=JDK11,JDK15 check
* }
*
* @author Brian Clozel
@@ -44,7 +44,7 @@ def mainToolchainLanguageVersion() {
if (mainToolchainConfigured()) {
return JavaLanguageVersion.of(project.mainToolchain.toString())
}
return JavaLanguageVersion.of(17)
return JavaLanguageVersion.of(8)
}
def testToolchainLanguageVersion() {
@@ -64,9 +64,9 @@ plugins.withType(JavaPlugin) {
}
}
else {
// Fallback to JDK17
// Fallback to JDK8
java {
sourceCompatibility = JavaVersion.VERSION_17
sourceCompatibility = JavaVersion.VERSION_1_8
}
}
// Configure a specific Java Toolchain for compiling and running tests if the 'testToolchain' property is defined
@@ -86,24 +86,63 @@ plugins.withType(JavaPlugin) {
}
plugins.withType(GroovyPlugin) {
// Fallback to JDK17
// Fallback to JDK8
if (!mainToolchainConfigured()) {
compileGroovy {
sourceCompatibility = JavaVersion.VERSION_17
sourceCompatibility = JavaVersion.VERSION_1_8
}
}
}
pluginManager.withPlugin("kotlin") {
// Fallback to JDK17
compileKotlin {
kotlinOptions {
jvmTarget = '17'
// Configure the Kotlin compiler if the 'mainToolchain' property is defined
if (mainToolchainConfigured()) {
def mainLanguageVersion = mainToolchainLanguageVersion()
def compiler = javaToolchains.compilerFor {
languageVersion = mainLanguageVersion
}
// See https://kotlinlang.org/docs/gradle.html#attributes-specific-for-jvm
def javaVersion = mainLanguageVersion.toString() == '8' ? '1.8' : mainLanguageVersion.toString()
compileKotlin {
kotlinOptions {
jvmTarget = javaVersion
jdkHome = compiler.get().metadata.installationPath.asFile.absolutePath
}
}
// Compile the test classes with the same version, 'testToolchain' will override if defined
compileTestKotlin {
kotlinOptions {
jvmTarget = javaVersion
jdkHome = compiler.get().metadata.installationPath.asFile.absolutePath
}
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = '17'
else {
// Fallback to JDK8
compileKotlin {
kotlinOptions {
jvmTarget = '1.8'
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = '1.8'
}
}
}
if (testToolchainConfigured()) {
def testLanguageVersion = testToolchainLanguageVersion()
def compiler = javaToolchains.compilerFor {
languageVersion = testLanguageVersion
}
// See https://kotlinlang.org/docs/gradle.html#attributes-specific-for-jvm
def javaVersion = testLanguageVersion.toString() == '8' ? '1.8' : testLanguageVersion.toString()
compileTestKotlin {
kotlinOptions {
jvmTarget = javaVersion
jdkHome = compiler.get().metadata.installationPath.asFile.absolutePath
}
}
}
}
Binary file not shown.
+1 -1
View File
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
+4 -5
View File
@@ -3,7 +3,7 @@
This document will guide you through the process of importing the Spring Framework
projects into Eclipse or the Spring Tool Suite (_STS_). It is recommended that you
have a recent version of Eclipse. As a bare minimum you will need Eclipse with full Java
17 support and Eclipse Buildship.
8 support, Eclipse Buildship, and the Groovy plugin.
The following instructions have been tested against [STS](https://spring.io/tools) 4.12.0
([download](https://github.com/spring-projects/sts4/wiki/Previous-Versions#spring-tools-4120-changelog))
@@ -12,14 +12,13 @@ The instructions should work with the latest Eclipse distribution as long as you
[Buildship](https://marketplace.eclipse.org/content/buildship-gradle-integration). Note
that STS 4 comes with Buildship preinstalled.
If you are using Eclipse 4.21, you will need to install
[Java 17 Support for Eclipse 2021-09 (4.21)](https://marketplace.eclipse.org/content/java-17-support-eclipse-2021-09-421)
from the Eclipse Marketplace.
## Steps
_When instructed to execute `./gradlew` from the command line, be sure to execute it within your locally cloned `spring-framework` working directory._
1. Install the [Groovy Development Tools](https://marketplace.eclipse.org/content/groovy-development-tools).
1. Switch to Groovy 3.0 in Eclipse (Preferences &#8594; Groovy &#8594; Compiler &#8594; Switch to 3.0...).
- If you encounter build errors stating something similar to _"Groovy: compiler mismatch: project level is 2.5, workspace level is 3.0"_, change the Groovy compiler version to 3.0 for each affected project.
1. Ensure that the _Forbidden reference (access rule)_ in Eclipse is set to `Info`
(Preferences &#8594; Java &#8594; Compiler &#8594; Errors/Warnings &#8594; Deprecated and restricted API &#8594; Forbidden reference (access rule)).
1. Optionally install the [Kotlin Plugin for Eclipse](https://marketplace.eclipse.org/content/kotlin-plugin-eclipse) if you need to execute Kotlin-based tests or develop Kotlin extensions.
+1 -1
View File
@@ -31,6 +31,6 @@ You'll notice these files are already intentionally in .gitignore. The same poli
## FAQ
Q. What about IntelliJ IDEA's own [Gradle support](https://www.jetbrains.com/help/idea/gradle.html)?
Q. What about IntelliJ IDEA's own [Gradle support](https://confluence.jetbrains.net/display/IDEADEV/Gradle+integration)?
A. Keep an eye on https://youtrack.jetbrains.com/issue/IDEA-53476
+4 -9
View File
@@ -1,7 +1,3 @@
plugins {
id 'org.springframework.build.runtimehints-agent'
}
description = "Spring Integration Tests"
dependencies {
@@ -9,7 +5,6 @@ dependencies {
testImplementation(project(":spring-beans"))
testImplementation(project(":spring-context"))
testImplementation(project(":spring-core"))
testImplementation(project(":spring-core-test"))
testImplementation(testFixtures(project(":spring-aop")))
testImplementation(testFixtures(project(":spring-beans")))
testImplementation(testFixtures(project(":spring-core")))
@@ -20,12 +15,12 @@ dependencies {
testImplementation(project(":spring-test"))
testImplementation(project(":spring-tx"))
testImplementation(project(":spring-web"))
testImplementation("jakarta.inject:jakarta.inject-api")
testImplementation("jakarta.resource:jakarta.resource-api")
testImplementation("jakarta.servlet:jakarta.servlet-api")
testImplementation("javax.inject:javax.inject")
testImplementation("javax.resource:javax.resource-api")
testImplementation("javax.servlet:javax.servlet-api")
testImplementation("org.aspectj:aspectjweaver")
testImplementation("org.hsqldb:hsqldb")
testImplementation("org.hibernate:hibernate-core-jakarta")
testImplementation("org.hibernate:hibernate-core")
}
normalization {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2020 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.
@@ -28,7 +28,7 @@ import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatException;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
* Integration tests for advice invocation order for advice configured via the
@@ -52,7 +52,7 @@ class AopNamespaceHandlerAdviceOrderIntegrationTests {
assertThat(aspect.invocations).containsExactly("around - start", "before", "around - end", "after", "after returning");
aspect.invocations.clear();
assertThatException().isThrownBy(() -> echo.echo(new Exception()));
assertThatExceptionOfType(Exception.class).isThrownBy(() -> echo.echo(new Exception()));
assertThat(aspect.invocations).containsExactly("around - start", "before", "around - end", "after", "after throwing");
}
}
@@ -69,7 +69,7 @@ class AopNamespaceHandlerAdviceOrderIntegrationTests {
assertThat(aspect.invocations).containsExactly("around - start", "before", "around - end", "after returning", "after");
aspect.invocations.clear();
assertThatException().isThrownBy(() -> echo.echo(new Exception()));
assertThatExceptionOfType(Exception.class).isThrownBy(() -> echo.echo(new Exception()));
assertThat(aspect.invocations).containsExactly("around - start", "before", "around - end", "after throwing", "after");
}
}
@@ -20,7 +20,8 @@ import java.io.IOException;
import java.lang.reflect.Method;
import java.util.List;
import jakarta.servlet.ServletException;
import javax.servlet.ServletException;
import org.junit.jupiter.api.Test;
import org.springframework.aop.support.AopUtils;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2020 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.
@@ -38,7 +38,7 @@ import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatException;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
* Integration tests for advice invocation order for advice configured via
@@ -65,7 +65,8 @@ class AspectJAutoProxyAdviceOrderIntegrationTests {
assertThat(aspect.invocations).containsExactly("around - start", "before", "after returning", "after", "around - end");
aspect.invocations.clear();
assertThatException().isThrownBy(() -> echo.echo(new Exception()));
assertThatExceptionOfType(Exception.class).isThrownBy(
() -> echo.echo(new Exception()));
assertThat(aspect.invocations).containsExactly("around - start", "before", "after throwing", "after", "around - end");
}
}
@@ -94,7 +95,8 @@ class AspectJAutoProxyAdviceOrderIntegrationTests {
assertThat(aspect.invocations).containsExactly("around - start", "before", "after returning", "after", "around - end");
aspect.invocations.clear();
assertThatException().isThrownBy(() -> echo.echo(new Exception()));
assertThatExceptionOfType(Exception.class).isThrownBy(
() -> echo.echo(new Exception()));
assertThat(aspect.invocations).containsExactly("around - start", "before", "after throwing", "after", "around - end");
}
}
@@ -1,297 +0,0 @@
/*
* 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.
* 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.aot;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.ResourceBundle;
import java.util.stream.Stream;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.aot.agent.HintType;
import org.springframework.aot.agent.MethodReference;
import org.springframework.aot.agent.RecordedInvocation;
import org.springframework.aot.agent.RecordedInvocationsListener;
import org.springframework.aot.agent.RecordedInvocationsPublisher;
import org.springframework.aot.agent.RuntimeHintsAgent;
import org.springframework.aot.test.agent.EnabledIfRuntimeHintsAgent;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for {@link RuntimeHintsAgent}.
*
* @author Brian Clozel
*/
@EnabledIfRuntimeHintsAgent
public class RuntimeHintsAgentTests {
private static final ClassLoader classLoader = ClassLoader.getSystemClassLoader();
private static Constructor<String> defaultConstructor;
private static Method toStringMethod;
private static Method privateGreetMethod;
@BeforeAll
public static void classSetup() throws NoSuchMethodException {
defaultConstructor = String.class.getConstructor();
toStringMethod = String.class.getMethod("toString");
privateGreetMethod = PrivateClass.class.getDeclaredMethod("greet");
}
@ParameterizedTest
@MethodSource("instrumentedReflectionMethods")
void shouldInstrumentReflectionMethods(Runnable runnable, MethodReference methodReference) {
RecordingSession session = RecordingSession.record(runnable);
assertThat(session.recordedInvocations()).hasSize(1);
RecordedInvocation invocation = session.recordedInvocations().findFirst().get();
assertThat(invocation.getMethodReference()).isEqualTo(methodReference);
assertThat(invocation.getStackFrames()).first().matches(frame -> frame.getClassName().equals(RuntimeHintsAgentTests.class.getName()));
}
private static Stream<Arguments> instrumentedReflectionMethods() {
return Stream.of(
Arguments.of((Runnable) () -> {
try {
Class.forName("java.lang.String");
}
catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}, MethodReference.of(Class.class, "forName")),
Arguments.of((Runnable) () -> String.class.getClasses(), MethodReference.of(Class.class, "getClasses")),
Arguments.of((Runnable) () -> {
try {
String.class.getConstructor();
}
catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}, MethodReference.of(Class.class, "getConstructor")),
Arguments.of((Runnable) () -> String.class.getConstructors(), MethodReference.of(Class.class, "getConstructors")),
Arguments.of((Runnable) () -> String.class.getDeclaredClasses(), MethodReference.of(Class.class, "getDeclaredClasses")),
Arguments.of((Runnable) () -> {
try {
String.class.getDeclaredConstructor();
}
catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}, MethodReference.of(Class.class, "getDeclaredConstructor")),
Arguments.of((Runnable) () -> String.class.getDeclaredConstructors(), MethodReference.of(Class.class, "getDeclaredConstructors")),
Arguments.of((Runnable) () -> {
try {
String.class.getDeclaredField("value");
}
catch (NoSuchFieldException e) {
throw new RuntimeException(e);
}
}, MethodReference.of(Class.class, "getDeclaredField")),
Arguments.of((Runnable) () -> String.class.getDeclaredFields(), MethodReference.of(Class.class, "getDeclaredFields")),
Arguments.of((Runnable) () -> {
try {
String.class.getDeclaredMethod("toString");
}
catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}, MethodReference.of(Class.class, "getDeclaredMethod")),
Arguments.of((Runnable) () -> String.class.getDeclaredMethods(), MethodReference.of(Class.class, "getDeclaredMethods")),
Arguments.of((Runnable) () -> {
try {
String.class.getField("value");
}
catch (NoSuchFieldException e) {
}
}, MethodReference.of(Class.class, "getField")),
Arguments.of((Runnable) () -> String.class.getFields(), MethodReference.of(Class.class, "getFields")),
Arguments.of((Runnable) () -> {
try {
String.class.getMethod("toString");
}
catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}, MethodReference.of(Class.class, "getMethod")),
Arguments.of((Runnable) () -> String.class.getMethods(), MethodReference.of(Class.class, "getMethods")),
Arguments.of((Runnable) () -> {
try {
classLoader.loadClass("java.lang.String");
}
catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}, MethodReference.of(ClassLoader.class, "loadClass")),
Arguments.of((Runnable) () -> {
try {
defaultConstructor.newInstance();
}
catch (Exception e) {
throw new RuntimeException(e);
}
}, MethodReference.of(Constructor.class, "newInstance")),
Arguments.of((Runnable) () -> {
try {
toStringMethod.invoke("");
}
catch (Exception e) {
throw new RuntimeException(e);
}
}, MethodReference.of(Method.class, "invoke")),
Arguments.of((Runnable) () -> {
try {
privateGreetMethod.invoke(new PrivateClass());
}
catch (Exception e) {
throw new RuntimeException(e);
}
}, MethodReference.of(Method.class, "invoke"))
);
}
@ParameterizedTest
@MethodSource("instrumentedResourceBundleMethods")
void shouldInstrumentResourceBundleMethods(Runnable runnable, MethodReference methodReference) {
RecordingSession session = RecordingSession.record(runnable);
assertThat(session.recordedInvocations(HintType.RESOURCE_BUNDLE)).hasSize(1);
RecordedInvocation resolution = session.recordedInvocations(HintType.RESOURCE_BUNDLE).findFirst().get();
assertThat(resolution.getMethodReference()).isEqualTo(methodReference);
assertThat(resolution.getStackFrames()).first().matches(frame -> frame.getClassName().equals(RuntimeHintsAgentTests.class.getName()));
}
private static Stream<Arguments> instrumentedResourceBundleMethods() {
return Stream.of(
Arguments.of((Runnable) () -> {
try {
ResourceBundle.getBundle("testBundle");
}
catch (Throwable exc) {
}
},
MethodReference.of(ResourceBundle.class, "getBundle"))
);
}
@ParameterizedTest
@MethodSource("instrumentedResourcePatternMethods")
void shouldInstrumentResourcePatternMethods(Runnable runnable, MethodReference methodReference) {
RecordingSession session = RecordingSession.record(runnable);
assertThat(session.recordedInvocations(HintType.RESOURCE_PATTERN)).hasSize(1);
RecordedInvocation resolution = session.recordedInvocations(HintType.RESOURCE_PATTERN).findFirst().get();
assertThat(resolution.getMethodReference()).isEqualTo(methodReference);
assertThat(resolution.getStackFrames()).first().matches(frame -> frame.getClassName().equals(RuntimeHintsAgentTests.class.getName()));
}
private static Stream<Arguments> instrumentedResourcePatternMethods() {
return Stream.of(
Arguments.of((Runnable) () -> RuntimeHintsAgentTests.class.getResource("sample.txt"),
MethodReference.of(Class.class, "getResource")),
Arguments.of((Runnable) () -> RuntimeHintsAgentTests.class.getResourceAsStream("sample.txt"),
MethodReference.of(Class.class, "getResourceAsStream")),
Arguments.of((Runnable) () -> classLoader.getResource("sample.txt"),
MethodReference.of(ClassLoader.class, "getResource")),
Arguments.of((Runnable) () -> classLoader.getResourceAsStream("sample.txt"),
MethodReference.of(ClassLoader.class, "getResourceAsStream")),
Arguments.of((Runnable) () -> {
try {
classLoader.getResources("sample.txt");
}
catch (IOException e) {
}
},
MethodReference.of(ClassLoader.class, "getResources")),
Arguments.of((Runnable) () -> {
try {
RuntimeHintsAgentTests.class.getModule().getResourceAsStream("sample.txt");
}
catch (IOException e) {
}
},
MethodReference.of(Module.class, "getResourceAsStream")),
Arguments.of((Runnable) () -> classLoader.resources("sample.txt"),
MethodReference.of(ClassLoader.class, "resources"))
);
}
@Test
void shouldInstrumentStaticMethodHandle() {
RecordingSession session = RecordingSession.record(ClassLoader.class::getClasses);
assertThat(session.recordedInvocations(HintType.REFLECTION)).hasSize(1);
RecordedInvocation resolution = session.recordedInvocations(HintType.REFLECTION).findFirst().get();
assertThat(resolution.getMethodReference()).isEqualTo(MethodReference.of(Class.class, "getClasses"));
assertThat(resolution.getStackFrames()).first().extracting(StackWalker.StackFrame::getClassName)
.isEqualTo(RuntimeHintsAgentTests.class.getName() + "$RecordingSession");
}
static class RecordingSession implements RecordedInvocationsListener {
final Deque<RecordedInvocation> recordedInvocations = new ArrayDeque<>();
static RecordingSession record(Runnable action) {
RecordingSession session = new RecordingSession();
RecordedInvocationsPublisher.addListener(session);
try {
action.run();
}
finally {
RecordedInvocationsPublisher.removeListener(session);
}
return session;
}
@Override
public void onInvocation(RecordedInvocation invocation) {
this.recordedInvocations.addLast(invocation);
}
Stream<RecordedInvocation> recordedInvocations() {
return this.recordedInvocations.stream();
}
Stream<RecordedInvocation> recordedInvocations(HintType hintType) {
return recordedInvocations().filter(invocation -> invocation.getHintType() == hintType);
}
}
private static class PrivateClass {
@SuppressWarnings("unused")
private String greet() {
return "hello";
}
}
}
@@ -1,57 +0,0 @@
/*
* 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.
* 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.aot.test;
import org.junit.jupiter.api.Test;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.test.agent.EnabledIfRuntimeHintsAgent;
import org.springframework.aot.test.agent.RuntimeHintsInvocations;
import org.springframework.aot.test.agent.RuntimeHintsRecorder;
import static org.assertj.core.api.Assertions.assertThat;
@EnabledIfRuntimeHintsAgent
class ReflectionInvocationsTests {
@Test
void sampleTest() {
RuntimeHints hints = new RuntimeHints();
hints.reflection().registerType(String.class, MemberCategory.INTROSPECT_PUBLIC_METHODS);
RuntimeHintsInvocations invocations = RuntimeHintsRecorder.record(() -> {
SampleReflection sample = new SampleReflection();
sample.sample(); // does Method[] methods = String.class.getMethods();
});
assertThat(invocations).match(hints);
}
@Test
void multipleCallsTest() {
RuntimeHints hints = new RuntimeHints();
hints.reflection().registerType(String.class, MemberCategory.INTROSPECT_PUBLIC_METHODS);
hints.reflection().registerType(Integer.class,MemberCategory.INTROSPECT_PUBLIC_METHODS);
RuntimeHintsInvocations invocations = RuntimeHintsRecorder.record(() -> {
SampleReflection sample = new SampleReflection();
sample.multipleCalls(); // does Method[] methods = String.class.getMethods(); methods = Integer.class.getMethods();
});
assertThat(invocations).match(hints);
}
}
@@ -1,40 +0,0 @@
/*
* 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.
* 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.aot.test;
import java.lang.reflect.Method;
/**
* @author Brian Clozel
* @since 6.0
*/
public class SampleReflection {
@SuppressWarnings("unused")
public void sample() {
String value = "Sample";
Method[] methods = String.class.getMethods();
}
@SuppressWarnings("unused")
public void multipleCalls() {
String value = "Sample";
Method[] methods = String.class.getMethods();
methods = Integer.class.getMethods();
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,7 +34,7 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Repository;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatException;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
* Integration tests for the @EnableCaching annotation.
@@ -62,7 +62,8 @@ class EnableCachingIntegrationTests {
// this test is a bit fragile, but gets the job done, proving that an
// attempt was made to look up the AJ aspect. It's due to classpath issues
// in .integration-tests that it's not found.
assertThatException().isThrownBy(ctx::refresh)
assertThatExceptionOfType(Exception.class).isThrownBy(
ctx::refresh)
.withMessageContaining("AspectJCachingConfiguration");
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,8 +21,9 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import jakarta.inject.Named;
import jakarta.inject.Singleton;
import javax.inject.Named;
import javax.inject.Singleton;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -33,6 +34,7 @@ import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.annotation.ScopeMetadata;
import org.springframework.context.annotation.ScopeMetadataResolver;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpSession;
@@ -305,25 +307,29 @@ class ClassPathBeanDefinitionScannerJsr330ScopeIntegrationTests {
GenericWebApplicationContext context = new GenericWebApplicationContext();
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
scanner.setIncludeAnnotationConfig(false);
scanner.setScopeMetadataResolver(definition -> {
ScopeMetadata metadata = new ScopeMetadata();
if (definition instanceof AnnotatedBeanDefinition annDef) {
for (String type : annDef.getMetadata().getAnnotationTypes()) {
if (type.equals(jakarta.inject.Singleton.class.getName())) {
metadata.setScopeName(BeanDefinition.SCOPE_SINGLETON);
break;
}
else if (annDef.getMetadata().getMetaAnnotationTypes(type).contains(jakarta.inject.Scope.class.getName())) {
metadata.setScopeName(type.substring(type.length() - 13, type.length() - 6).toLowerCase());
metadata.setScopedProxyMode(scopedProxyMode);
break;
}
else if (type.startsWith("jakarta.inject")) {
metadata.setScopeName(BeanDefinition.SCOPE_PROTOTYPE);
scanner.setScopeMetadataResolver(new ScopeMetadataResolver() {
@Override
public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
ScopeMetadata metadata = new ScopeMetadata();
if (definition instanceof AnnotatedBeanDefinition) {
AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
for (String type : annDef.getMetadata().getAnnotationTypes()) {
if (type.equals(javax.inject.Singleton.class.getName())) {
metadata.setScopeName(BeanDefinition.SCOPE_SINGLETON);
break;
}
else if (annDef.getMetadata().getMetaAnnotationTypes(type).contains(javax.inject.Scope.class.getName())) {
metadata.setScopeName(type.substring(type.length() - 13, type.length() - 6).toLowerCase());
metadata.setScopedProxyMode(scopedProxyMode);
break;
}
else if (type.startsWith("javax.inject")) {
metadata.setScopeName(BeanDefinition.SCOPE_PROTOTYPE);
}
}
}
return metadata;
}
return metadata;
});
// Scan twice in order to find errors in the bean definition compatibility check.
@@ -385,14 +391,14 @@ class ClassPathBeanDefinitionScannerJsr330ScopeIntegrationTests {
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@jakarta.inject.Scope
@javax.inject.Scope
public @interface RequestScoped {
}
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@jakarta.inject.Scope
@javax.inject.Scope
public @interface SessionScoped {
}
@@ -41,6 +41,9 @@ import org.springframework.context.support.GenericApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jca.context.ResourceAdapterApplicationContext;
import org.springframework.jca.support.SimpleBootstrapContext;
import org.springframework.jca.work.SimpleTaskWorkManager;
import org.springframework.mock.env.MockEnvironment;
import org.springframework.mock.env.MockPropertySource;
import org.springframework.mock.web.MockServletConfig;
@@ -532,6 +535,22 @@ public class EnvironmentSystemIntegrationTests {
assertThat(environment.getProperty("pSysProps1")).isEqualTo("pSysProps1Value");
}
@Test
void resourceAdapterApplicationContext() {
ResourceAdapterApplicationContext ctx = new ResourceAdapterApplicationContext(new SimpleBootstrapContext(new SimpleTaskWorkManager()));
assertHasStandardEnvironment(ctx);
registerEnvironmentBeanDefinition(ctx);
ctx.setEnvironment(prodEnv);
ctx.refresh();
assertHasEnvironment(ctx, prodEnv);
assertEnvironmentBeanRegistered(ctx);
assertEnvironmentAwareInvoked(ctx, prodEnv);
}
@Test
void abstractApplicationContextValidatesRequiredPropertiesOnRefresh() {
{
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -74,7 +74,7 @@ class ScheduledAndTransactionalAnnotationIntegrationTests {
MyRepository repository = ctx.getBean(MyRepository.class);
CallCountingTransactionManager txManager = ctx.getBean(CallCountingTransactionManager.class);
assertThat(AopUtils.isCglibProxy(repository)).isTrue();
assertThat(AopUtils.isCglibProxy(repository)).isEqualTo(true);
assertThat(repository.getInvocationCount()).isGreaterThan(0);
assertThat(txManager.commits).isGreaterThan(0);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -45,7 +45,7 @@ import org.springframework.transaction.interceptor.BeanFactoryTransactionAttribu
import org.springframework.transaction.testfixture.CallCountingTransactionManager;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatException;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
* Integration tests for the @EnableTransactionManagement annotation.
@@ -98,7 +98,7 @@ class EnableTransactionManagementIntegrationTests {
// this test is a bit fragile, but gets the job done, proving that an
// attempt was made to look up the AJ aspect. It's due to classpath issues
// in .integration-tests that it's not found.
assertThatException()
assertThatExceptionOfType(Exception.class)
.isThrownBy(ctx::refresh)
.withMessageContaining("AspectJJtaTransactionManagementConfiguration");
}
@@ -41,7 +41,7 @@
<props>
<prop key="setA*">PROPAGATION_REQUIRED</prop>
<prop key="rollbackOnly">PROPAGATION_REQUIRED</prop>
<prop key="echoException">PROPAGATION_REQUIRED,+jakarta.servlet.ServletException,-java.lang.Exception</prop>
<prop key="echoException">PROPAGATION_REQUIRED,+javax.servlet.ServletException,-java.lang.Exception</prop>
</props>
</property>
</bean>
+3 -7
View File
@@ -1,14 +1,13 @@
pluginManagement {
repositories {
mavenCentral()
gradlePluginPortal()
maven { url "https://repo.spring.io/release" }
maven { url "https://repo.spring.io/plugins-release" }
}
}
plugins {
id "com.gradle.enterprise" version "3.11.1"
id "io.spring.ge.conventions" version "0.0.11"
id "com.gradle.enterprise" version "3.6.1"
id "io.spring.ge.conventions" version "0.0.7"
}
include "spring-aop"
@@ -18,7 +17,6 @@ include "spring-context"
include "spring-context-indexer"
include "spring-context-support"
include "spring-core"
include "spring-core-test"
include "spring-expression"
include "spring-instrument"
include "spring-jcl"
@@ -35,8 +33,6 @@ include "spring-webflux"
include "spring-webmvc"
include "spring-websocket"
include "framework-bom"
include "framework-docs"
include "framework-platform"
include "integration-tests"
rootProject.name = "spring"
+1 -1
View File
@@ -5,7 +5,7 @@ dependencies {
api(project(":spring-core"))
optional("org.aspectj:aspectjweaver")
optional("org.apache.commons:commons-pool2")
testImplementation(project(":spring-core-test"))
optional("com.jamonapi:jamon")
testImplementation(testFixtures(project(":spring-beans")))
testImplementation(testFixtures(project(":spring-core")))
testFixturesImplementation(testFixtures(project(":spring-beans")))
@@ -63,7 +63,7 @@ public interface Joinpoint {
/**
* Return the static part of this joinpoint.
* <p>The static part is an accessible object on which a chain of
* interceptors is installed.
* interceptors are installed.
*/
@Nonnull
AccessibleObject getStaticPart();
@@ -36,7 +36,7 @@ public interface IntroductionAwareMethodMatcher extends MethodMatcher {
* @param targetClass the target class
* @param hasIntroductions {@code true} if the object on whose behalf we are
* asking is the subject on one or more introductions; {@code false} otherwise
* @return whether this method matches statically
* @return whether or not this method matches statically
*/
boolean matches(Method method, Class<?> targetClass, boolean hasIntroductions);
@@ -60,7 +60,7 @@ public interface MethodMatcher {
* will be made.
* @param method the candidate method
* @param targetClass the target class
* @return whether this method matches statically
* @return whether or not this method matches statically
*/
boolean matches(Method method, Class<?> targetClass);
@@ -70,7 +70,7 @@ public interface MethodMatcher {
* runtime even if the 2-arg matches method returns {@code true}?
* <p>Can be invoked when an AOP proxy is created, and need not be invoked
* again before each method invocation,
* @return whether a runtime match via the 3-arg
* @return whether or not a runtime match via the 3-arg
* {@link #matches(java.lang.reflect.Method, Class, Object[])} method
* is required if static matching passed
*/
@@ -18,7 +18,7 @@ package org.springframework.aop;
/**
* Marker interface implemented by all AOP proxies. Used to detect
* whether objects are Spring-generated proxies.
* whether or not objects are Spring-generated proxies.
*
* @author Rob Harrop
* @since 2.0.1
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2020 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.
@@ -78,9 +78,10 @@ public abstract class AbstractAspectJAdvice implements Advice, AspectJPrecedence
*/
public static JoinPoint currentJoinPoint() {
MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation();
if (!(mi instanceof ProxyMethodInvocation pmi)) {
if (!(mi instanceof ProxyMethodInvocation)) {
throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
}
ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
JoinPoint jp = (JoinPoint) pmi.getUserAttribute(JOIN_POINT_KEY);
if (jp == null) {
jp = new MethodInvocationProceedingJoinPoint(pmi);
@@ -261,7 +262,7 @@ public abstract class AbstractAspectJAdvice implements Advice, AspectJPrecedence
public void setArgumentNamesFromStringArray(String... args) {
this.argumentNames = new String[args.length];
for (int i = 0; i < args.length; i++) {
this.argumentNames[i] = args[i].strip();
this.argumentNames[i] = StringUtils.trimWhitespace(args[i]);
if (!isVariableName(this.argumentNames[i])) {
throw new IllegalArgumentException(
"'argumentNames' property of AbstractAspectJAdvice contains an argument name '" +
@@ -367,7 +368,7 @@ public abstract class AbstractAspectJAdvice implements Advice, AspectJPrecedence
* to which argument name. There are multiple strategies for determining
* this binding, which are arranged in a ChainOfResponsibility.
*/
public final void calculateArgumentBindings() {
public final synchronized void calculateArgumentBindings() {
// The simple case... nothing to bind.
if (this.argumentsIntrospected || this.parameterTypes.length == 0) {
return;
@@ -713,9 +714,10 @@ public abstract class AbstractAspectJAdvice implements Advice, AspectJPrecedence
if (this == other) {
return true;
}
if (!(other instanceof AdviceExcludingMethodMatcher otherMm)) {
if (!(other instanceof AdviceExcludingMethodMatcher)) {
return false;
}
AdviceExcludingMethodMatcher otherMm = (AdviceExcludingMethodMatcher) other;
return this.adviceMethod.equals(otherMm.adviceMethod);
}
@@ -158,7 +158,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
/** The pointcut expression associated with the advice, as a simple String. */
@Nullable
private final String pointcutExpression;
private String pointcutExpression;
private boolean raiseExceptions;
@@ -486,7 +486,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
}
String[] tokens = StringUtils.tokenizeToStringArray(argsSpec, ",");
for (int i = 0; i < tokens.length; i++) {
tokens[i] = tokens[i].strip();
tokens[i] = StringUtils.trimWhitespace(tokens[i]);
String varName = maybeExtractVariableName(tokens[i]);
if (varName != null) {
varNames.add(varName);
@@ -759,10 +759,10 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
*/
private static class PointcutBody {
private final int numTokensConsumed;
private int numTokensConsumed;
@Nullable
private final String text;
private String text;
public PointcutBody(int tokens, @Nullable String text) {
this.numTokensConsumed = tokens;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2020 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.
@@ -63,9 +63,10 @@ public class AspectJAroundAdvice extends AbstractAspectJAdvice implements Method
@Override
@Nullable
public Object invoke(MethodInvocation mi) throws Throwable {
if (!(mi instanceof ProxyMethodInvocation pmi)) {
if (!(mi instanceof ProxyMethodInvocation)) {
throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
}
ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
JoinPointMatch jpm = getJoinPointMatch(pmi);
return invokeAdviceMethod(pjp, jpm, null, null);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 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.
@@ -378,7 +378,7 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
}
catch (Throwable ex) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to evaluate join point for arguments " + Arrays.toString(args) +
logger.debug("Failed to evaluate join point for arguments " + Arrays.asList(args) +
" - falling back to non-match", ex);
}
return false;
@@ -523,9 +523,10 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
if (this == other) {
return true;
}
if (!(other instanceof AspectJExpressionPointcut otherPc)) {
if (!(other instanceof AspectJExpressionPointcut)) {
return false;
}
AspectJExpressionPointcut otherPc = (AspectJExpressionPointcut) other;
return ObjectUtils.nullSafeEquals(this.getExpression(), otherPc.getExpression()) &&
ObjectUtils.nullSafeEquals(this.pointcutDeclarationScope, otherPc.pointcutDeclarationScope) &&
ObjectUtils.nullSafeEquals(this.pointcutParameterNames, otherPc.pointcutParameterNames) &&
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -97,9 +97,10 @@ public class AspectJPointcutAdvisor implements PointcutAdvisor, Ordered {
if (this == other) {
return true;
}
if (!(other instanceof AspectJPointcutAdvisor otherAdvisor)) {
if (!(other instanceof AspectJPointcutAdvisor)) {
return false;
}
AspectJPointcutAdvisor otherAdvisor = (AspectJPointcutAdvisor) other;
return this.advice.equals(otherAdvisor.advice);
}
@@ -214,7 +214,8 @@ public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFac
private String resolveExpression(A annotation) {
for (String attributeName : EXPRESSION_ATTRIBUTES) {
Object val = AnnotationUtils.getValue(annotation, attributeName);
if (val instanceof String str) {
if (val instanceof String) {
String str = (String) val;
if (!str.isEmpty()) {
return str;
}
@@ -39,7 +39,7 @@ import org.springframework.lang.Nullable;
public interface AspectJAdvisorFactory {
/**
* Determine whether the given class is an aspect, as reported
* Determine whether or not the given class is an aspect, as reported
* by AspectJ's {@link org.aspectj.lang.reflect.AjTypeSystem}.
* <p>Will simply return {@code false} if the supposed aspect is
* invalid (such as an extension of a concrete aspect class).
@@ -47,7 +47,7 @@ public interface AspectJAdvisorFactory {
* such as those with unsupported instantiation models.
* Use the {@link #validate} method to handle these cases if necessary.
* @param clazz the supposed annotation-style AspectJ class
* @return whether this class is recognized by AspectJ as an aspect class
* @return whether or not this class is recognized by AspectJ as an aspect class
*/
boolean isAspect(Class<?> clazz);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2020 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.
@@ -34,7 +34,7 @@ import org.springframework.util.ClassUtils;
/**
* AspectJ-based proxy factory, allowing for programmatic building
* of proxies which include AspectJ aspects (code style as well
* annotation style).
* Java 5 annotation style).
*
* @author Rob Harrop
* @author Juergen Hoeller
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -60,14 +60,14 @@ public class AspectMetadata implements Serializable {
private final Class<?> aspectClass;
/**
* AspectJ reflection information.
* <p>Re-resolved on deserialization since it isn't serializable itself.
* AspectJ reflection information (AspectJ 5 / Java 5 specific).
* Re-resolved on deserialization since it isn't serializable itself.
*/
private transient AjType<?> ajType;
/**
* Spring AOP pointcut corresponding to the per clause of the
* aspect. Will be the {@code Pointcut.TRUE} canonical instance in the
* aspect. Will be the Pointcut.TRUE canonical instance in the
* case of a singleton, otherwise an AspectJExpressionPointcut.
*/
private final Pointcut perClausePointcut;
@@ -101,22 +101,24 @@ public class AspectMetadata implements Serializable {
this.ajType = ajType;
switch (this.ajType.getPerClause().getKind()) {
case SINGLETON -> {
case SINGLETON:
this.perClausePointcut = Pointcut.TRUE;
}
case PERTARGET, PERTHIS -> {
return;
case PERTARGET:
case PERTHIS:
AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut();
ajexp.setLocation(aspectClass.getName());
ajexp.setExpression(findPerClause(aspectClass));
ajexp.setPointcutDeclarationScope(aspectClass);
this.perClausePointcut = ajexp;
}
case PERTYPEWITHIN -> {
return;
case PERTYPEWITHIN:
// Works with a type pattern
this.perClausePointcut = new ComposablePointcut(new TypePatternClassFilter(findPerClause(aspectClass)));
}
default -> throw new AopConfigException(
"PerClause " + ajType.getPerClause().getKind() + " not supported by Spring AOP for " + aspectClass);
return;
default:
throw new AopConfigException(
"PerClause " + ajType.getPerClause().getKind() + " not supported by Spring AOP for " + aspectClass);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -220,18 +220,21 @@ final class InstantiationModelAwarePointcutAdvisorImpl
}
else {
switch (aspectJAnnotation.getAnnotationType()) {
case AtPointcut, AtAround -> {
case AtPointcut:
case AtAround:
this.isBeforeAdvice = false;
this.isAfterAdvice = false;
}
case AtBefore -> {
break;
case AtBefore:
this.isBeforeAdvice = true;
this.isAfterAdvice = false;
}
case AtAfter, AtAfterReturning, AtAfterThrowing -> {
break;
case AtAfter:
case AtAfterReturning:
case AtAfterThrowing:
this.isBeforeAdvice = false;
this.isAfterAdvice = true;
}
break;
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2020 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.
@@ -261,36 +261,42 @@ public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFacto
AbstractAspectJAdvice springAdvice;
switch (aspectJAnnotation.getAnnotationType()) {
case AtPointcut -> {
case AtPointcut:
if (logger.isDebugEnabled()) {
logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
}
return null;
}
case AtAround -> springAdvice = new AspectJAroundAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
case AtBefore -> springAdvice = new AspectJMethodBeforeAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
case AtAfter -> springAdvice = new AspectJAfterAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
case AtAfterReturning -> {
case AtAround:
springAdvice = new AspectJAroundAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtBefore:
springAdvice = new AspectJMethodBeforeAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtAfter:
springAdvice = new AspectJAfterAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtAfterReturning:
springAdvice = new AspectJAfterReturningAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterReturningAnnotation.returning())) {
springAdvice.setReturningName(afterReturningAnnotation.returning());
}
}
case AtAfterThrowing -> {
break;
case AtAfterThrowing:
springAdvice = new AspectJAfterThrowingAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
}
}
default -> throw new UnsupportedOperationException(
"Unsupported advice type on method: " + candidateAdviceMethod);
break;
default:
throw new UnsupportedOperationException(
"Unsupported advice type on method: " + candidateAdviceMethod);
}
// Now to configure the advice...
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2020 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.
@@ -60,8 +60,8 @@ public class AspectJAwareAdvisorAutoProxyCreator extends AbstractAdvisorAutoProx
* <li>Otherwise the advice declared first gets highest precedence (i.e., runs
* first).</li>
* </ul>
* <p><b>Important:</b> Advisors are sorted in precedence order, from the highest
* precedence to the lowest. "On the way in" to a join point, the highest precedence
* <p><b>Important:</b> Advisors are sorted in precedence order, from highest
* precedence to lowest. "On the way in" to a join point, the highest precedence
* advisor should run first. "On the way out" of a join point, the highest
* precedence advisor should run last.
*/
@@ -100,8 +100,8 @@ public class AspectJAwareAdvisorAutoProxyCreator extends AbstractAdvisorAutoProx
// TODO: Consider optimization by caching the list of the aspect names
List<Advisor> candidateAdvisors = findCandidateAdvisors();
for (Advisor advisor : candidateAdvisors) {
if (advisor instanceof AspectJPointcutAdvisor pointcutAdvisor &&
pointcutAdvisor.getAspectName().equals(beanName)) {
if (advisor instanceof AspectJPointcutAdvisor &&
((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {
return true;
}
}
@@ -143,12 +143,13 @@ public class AspectJAwareAdvisorAutoProxyCreator extends AbstractAdvisorAutoProx
Advice advice = this.advisor.getAdvice();
StringBuilder sb = new StringBuilder(ClassUtils.getShortName(advice.getClass()));
boolean appended = false;
if (this.advisor instanceof Ordered ordered) {
sb.append(": order = ").append(ordered.getOrder());
if (this.advisor instanceof Ordered) {
sb.append(": order = ").append(((Ordered) this.advisor).getOrder());
appended = true;
}
if (advice instanceof AbstractAspectJAdvice ajAdvice) {
if (advice instanceof AbstractAspectJAdvice) {
sb.append(!appended ? ": " : ", ");
AbstractAspectJAdvice ajAdvice = (AbstractAspectJAdvice) advice;
sb.append("aspect name = ");
sb.append(ajAdvice.getAspectName());
sb.append(", declaration order = ");
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2016 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.
@@ -59,7 +59,8 @@ class AspectJAutoProxyBeanDefinitionParser implements BeanDefinitionParser {
NodeList childNodes = element.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
Node node = childNodes.item(i);
if (node instanceof Element includeElement) {
if (node instanceof Element) {
Element includeElement = (Element) node;
TypedStringValue valueHolder = new TypedStringValue(includeElement.getAttribute("name"));
valueHolder.setSource(parserContext.extractSource(includeElement));
includePatterns.add(valueHolder);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -93,7 +93,7 @@ class ConfigBeanDefinitionParser implements BeanDefinitionParser {
private static final int POINTCUT_INDEX = 1;
private static final int ASPECT_INSTANCE_FACTORY_INDEX = 2;
private final ParseState parseState = new ParseState();
private ParseState parseState = new ParseState();
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2012 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.
@@ -42,7 +42,8 @@ class ScopedProxyBeanDefinitionDecorator implements BeanDefinitionDecorator {
@Override
public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext) {
boolean proxyTargetClass = true;
if (node instanceof Element ele) {
if (node instanceof Element) {
Element ele = (Element) node;
if (ele.hasAttribute(PROXY_TARGET_CLASS)) {
proxyTargetClass = Boolean.parseBoolean(ele.getAttribute(PROXY_TARGET_CLASS));
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -58,7 +58,7 @@ public class SimpleBeanFactoryAwareAspectInstanceFactory implements AspectInstan
/**
* Look up the aspect bean from the {@link BeanFactory} and return it.
* Look up the aspect bean from the {@link BeanFactory} and returns it.
* @see #setAspectBeanName
*/
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 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.
@@ -22,7 +22,6 @@ import java.util.concurrent.ConcurrentHashMap;
import org.springframework.aop.Advisor;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
import org.springframework.core.SmartClassLoader;
import org.springframework.lang.Nullable;
@@ -34,8 +33,7 @@ import org.springframework.lang.Nullable;
* @since 3.2
*/
@SuppressWarnings("serial")
public abstract class AbstractAdvisingBeanPostProcessor extends ProxyProcessorSupport
implements SmartInstantiationAwareBeanPostProcessor {
public abstract class AbstractAdvisingBeanPostProcessor extends ProxyProcessorSupport implements BeanPostProcessor {
@Nullable
protected Advisor advisor;
@@ -60,27 +58,8 @@ public abstract class AbstractAdvisingBeanPostProcessor extends ProxyProcessorSu
@Override
public Class<?> determineBeanType(Class<?> beanClass, String beanName) {
if (this.advisor != null && isEligible(beanClass)) {
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
proxyFactory.setTargetClass(beanClass);
if (!proxyFactory.isProxyTargetClass()) {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
proxyFactory.addAdvisor(this.advisor);
customizeProxyFactory(proxyFactory);
// Use original ClassLoader if bean class not locally loaded in overriding class loader
ClassLoader classLoader = getProxyClassLoader();
if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) {
classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
}
return proxyFactory.getProxyClass(classLoader);
}
return beanClass;
public Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean;
}
@Override
@@ -90,7 +69,8 @@ public abstract class AbstractAdvisingBeanPostProcessor extends ProxyProcessorSu
return bean;
}
if (bean instanceof Advised advised) {
if (bean instanceof Advised) {
Advised advised = (Advised) bean;
if (!advised.isFrozen() && isEligible(AopUtils.getTargetClass(bean))) {
// Add our local Advisor to the existing proxy's Advisor chain...
if (this.beforeExistingAdvisors) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2020 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.
@@ -72,14 +72,12 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
/** Package-protected to allow direct access for efficiency. */
@SuppressWarnings("serial")
TargetSource targetSource = EMPTY_TARGET_SOURCE;
/** Whether the Advisors are already filtered for the specific target class. */
private boolean preFiltered = false;
/** The AdvisorChainFactory to use. */
@SuppressWarnings("serial")
AdvisorChainFactory advisorChainFactory = new DefaultAdvisorChainFactory();
/** Cache with Method as key and advisor chain List as value. */
@@ -89,14 +87,12 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
* Interfaces to be implemented by the proxy. Held in List to keep the order
* of registration, to create JDK proxy with specified order of interfaces.
*/
@SuppressWarnings("serial")
private List<Class<?>> interfaces = new ArrayList<>();
/**
* List of Advisors. If an Advice is added, it will be wrapped
* in an Advisor before being added to this List.
*/
@SuppressWarnings("serial")
private List<Advisor> advisors = new ArrayList<>();
@@ -287,7 +283,8 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
}
Advisor advisor = this.advisors.remove(index);
if (advisor instanceof IntroductionAdvisor ia) {
if (advisor instanceof IntroductionAdvisor) {
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
// We need to remove introduction interfaces.
for (Class<?> ifc : ia.getInterfaces()) {
removeInterface(ifc);
@@ -317,7 +314,7 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
}
/**
* Add all the given advisors to this proxy configuration.
* Add all of the given advisors to this proxy configuration.
* @param advisors the advisors to register
*/
public void addAdvisors(Advisor... advisors) {
@@ -325,7 +322,7 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
}
/**
* Add all the given advisors to this proxy configuration.
* Add all of the given advisors to this proxy configuration.
* @param advisors the advisors to register
*/
public void addAdvisors(Collection<Advisor> advisors) {
@@ -334,8 +331,8 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
}
if (!CollectionUtils.isEmpty(advisors)) {
for (Advisor advisor : advisors) {
if (advisor instanceof IntroductionAdvisor introductionAdvisor) {
validateIntroductionAdvisor(introductionAdvisor);
if (advisor instanceof IntroductionAdvisor) {
validateIntroductionAdvisor((IntroductionAdvisor) advisor);
}
Assert.notNull(advisor, "Advisor must not be null");
this.advisors.add(advisor);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2012 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.
@@ -52,13 +52,4 @@ public interface AopProxy {
*/
Object getProxy(@Nullable ClassLoader classLoader);
/**
* Determine the proxy class.
* @param classLoader the class loader to create the proxy class with
* (or {@code null} for the low-level proxy facility's default)
* @return the proxy class
* @since 6.0
*/
Class<?> getProxyClass(@Nullable ClassLoader classLoader);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 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.
@@ -33,22 +33,26 @@ import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
/**
* Utility methods for AOP proxy factories.
*
* <p>Mainly for internal use within the AOP framework.
* Mainly for internal use within the AOP framework.
*
* <p>See {@link org.springframework.aop.support.AopUtils} for a collection of
* generic AOP utility methods which do not depend on AOP framework internals.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Sam Brannen
* @see org.springframework.aop.support.AopUtils
*/
public abstract class AopProxyUtils {
// JDK 17 Class.isSealed() method available?
@Nullable
private static final Method isSealedMethod = ClassUtils.getMethodIfAvailable(Class.class, "isSealed");
/**
* Obtain the singleton target object behind the given proxy, if any.
* @param candidate the (potential) proxy to check
@@ -60,10 +64,10 @@ public abstract class AopProxyUtils {
*/
@Nullable
public static Object getSingletonTarget(Object candidate) {
if (candidate instanceof Advised advised) {
TargetSource targetSource = advised.getTargetSource();
if (targetSource instanceof SingletonTargetSource singleTargetSource) {
return singleTargetSource.getTarget();
if (candidate instanceof Advised) {
TargetSource targetSource = ((Advised) candidate).getTargetSource();
if (targetSource instanceof SingletonTargetSource) {
return ((SingletonTargetSource) targetSource).getTarget();
}
}
return null;
@@ -83,8 +87,8 @@ public abstract class AopProxyUtils {
Assert.notNull(candidate, "Candidate object must not be null");
Object current = candidate;
Class<?> result = null;
while (current instanceof TargetClassAware targetClassAware) {
result = targetClassAware.getTargetClass();
while (current instanceof TargetClassAware) {
result = ((TargetClassAware) current).getTargetClass();
current = getSingletonTarget(current);
}
if (result == null) {
@@ -93,46 +97,6 @@ public abstract class AopProxyUtils {
return result;
}
/**
* Complete the set of interfaces that are typically required in a JDK dynamic
* proxy generated by Spring AOP.
* <p>Specifically, {@link SpringProxy}, {@link Advised}, and {@link DecoratingProxy}
* will be appended to the set of user-specified interfaces.
* <p>This method can be useful when registering
* {@linkplain org.springframework.aot.hint.ProxyHints proxy hints} for Spring's
* AOT support, as demonstrated in the following example which uses this method
* via a {@code static} import.
* <pre class="code">
* RuntimeHints hints = ...
* hints.proxies().registerJdkProxy(completeJdkProxyInterfaces(MyInterface.class));
* </pre>
* @param userInterfaces the set of user-specified interfaces implemented by
* the component to be proxied
* @return the complete set of interfaces that the proxy should implement
* @throws IllegalArgumentException if a supplied {@code Class} is {@code null},
* is not an {@linkplain Class#isInterface() interface}, or is a
* {@linkplain Class#isSealed() sealed} interface
* @since 6.0
* @see SpringProxy
* @see Advised
* @see DecoratingProxy
* @see org.springframework.aot.hint.RuntimeHints#proxies()
* @see org.springframework.aot.hint.ProxyHints#registerJdkProxy(Class...)
*/
public static Class<?>[] completeJdkProxyInterfaces(Class<?>... userInterfaces) {
List<Class<?>> completedInterfaces = new ArrayList<>(userInterfaces.length + 3);
for (Class<?> ifc : userInterfaces) {
Assert.notNull(ifc, "'userInterfaces' must not contain null values");
Assert.isTrue(ifc.isInterface() && !ifc.isSealed(),
() -> ifc.getName() + " must be a non-sealed interface");
completedInterfaces.add(ifc);
}
completedInterfaces.add(SpringProxy.class);
completedInterfaces.add(Advised.class);
completedInterfaces.add(DecoratingProxy.class);
return completedInterfaces.toArray(Class<?>[]::new);
}
/**
* Determine the complete set of interfaces to proxy for the given AOP configuration.
* <p>This will always add the {@link Advised} interface unless the AdvisedSupport's
@@ -169,7 +133,7 @@ public abstract class AopProxyUtils {
if (targetClass.isInterface()) {
advised.setInterfaces(targetClass);
}
else if (Proxy.isProxyClass(targetClass) || ClassUtils.isLambdaClass(targetClass)) {
else if (Proxy.isProxyClass(targetClass)) {
advised.setInterfaces(targetClass.getInterfaces());
}
specifiedInterfaces = advised.getProxiedInterfaces();
@@ -178,7 +142,7 @@ public abstract class AopProxyUtils {
List<Class<?>> proxiedInterfaces = new ArrayList<>(specifiedInterfaces.length + 3);
for (Class<?> ifc : specifiedInterfaces) {
// Only non-sealed interfaces are actually eligible for JDK proxying (on JDK 17)
if (!ifc.isSealed()) {
if (isSealedMethod == null || Boolean.FALSE.equals(ReflectionUtils.invokeMethod(isSealedMethod, ifc))) {
proxiedInterfaces.add(ifc);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 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.
@@ -126,6 +126,9 @@ class CglibAopProxy implements AopProxy, Serializable {
*/
public CglibAopProxy(AdvisedSupport config) throws AopConfigException {
Assert.notNull(config, "AdvisedSupport must not be null");
if (config.getAdvisorCount() == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
throw new AopConfigException("No advisors and no TargetSource specified");
}
this.advised = config;
this.advisedDispatcher = new AdvisedDispatcher(this.advised);
}
@@ -150,20 +153,11 @@ class CglibAopProxy implements AopProxy, Serializable {
@Override
public Object getProxy() {
return buildProxy(null, false);
return getProxy(null);
}
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
return buildProxy(classLoader, false);
}
@Override
public Class<?> getProxyClass(@Nullable ClassLoader classLoader) {
return (Class<?>) buildProxy(classLoader, true);
}
private Object buildProxy(@Nullable ClassLoader classLoader, boolean classOnly) {
if (logger.isTraceEnabled()) {
logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource());
}
@@ -196,7 +190,6 @@ class CglibAopProxy implements AopProxy, Serializable {
enhancer.setSuperclass(proxySuperClass);
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setAttemptLoad(true);
enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));
Callback[] callbacks = getCallbacks(rootClass);
@@ -210,7 +203,7 @@ class CglibAopProxy implements AopProxy, Serializable {
enhancer.setCallbackTypes(types);
// Generate the proxy class and create a proxy instance.
return (classOnly ? createProxyClass(enhancer) : createProxyClassAndInstance(enhancer, callbacks));
return createProxyClassAndInstance(enhancer, callbacks);
}
catch (CodeGenerationException | IllegalArgumentException ex) {
throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
@@ -223,11 +216,6 @@ class CglibAopProxy implements AopProxy, Serializable {
}
}
protected Class<?> createProxyClass(Enhancer enhancer) {
enhancer.setInterceptDuringConstruction(false);
return enhancer.createClass();
}
protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
enhancer.setInterceptDuringConstruction(false);
enhancer.setCallbacks(callbacks);
@@ -420,9 +408,10 @@ class CglibAopProxy implements AopProxy, Serializable {
/**
* Method interceptor used for static targets with no advice chain. The call is
* passed directly back to the target. Used when the proxy needs to be exposed
* and it can't be determined that the method won't return {@code this}.
* Method interceptor used for static targets with no advice chain. The call
* is passed directly back to the target. Used when the proxy needs to be
* exposed and it can't be determined that the method won't return
* {@code this}.
*/
private static class StaticUnadvisedInterceptor implements MethodInterceptor, Serializable {
@@ -436,7 +425,7 @@ class CglibAopProxy implements AopProxy, Serializable {
@Override
@Nullable
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object retVal = AopUtils.invokeJoinpointUsingReflection(this.target, method, args);
Object retVal = methodProxy.invoke(this.target, args);
return processReturnType(proxy, this.target, method, retVal);
}
}
@@ -461,7 +450,7 @@ class CglibAopProxy implements AopProxy, Serializable {
Object oldProxy = null;
try {
oldProxy = AopContext.setCurrentProxy(proxy);
Object retVal = AopUtils.invokeJoinpointUsingReflection(this.target, method, args);
Object retVal = methodProxy.invoke(this.target, args);
return processReturnType(proxy, this.target, method, retVal);
}
finally {
@@ -489,7 +478,7 @@ class CglibAopProxy implements AopProxy, Serializable {
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object target = this.targetSource.getTarget();
try {
Object retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
Object retVal = methodProxy.invoke(target, args);
return processReturnType(proxy, target, method, retVal);
}
finally {
@@ -519,7 +508,7 @@ class CglibAopProxy implements AopProxy, Serializable {
Object target = this.targetSource.getTarget();
try {
oldProxy = AopContext.setCurrentProxy(proxy);
Object retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
Object retVal = methodProxy.invoke(target, args);
return processReturnType(proxy, target, method, retVal);
}
finally {
@@ -690,13 +679,19 @@ class CglibAopProxy implements AopProxy, Serializable {
Object retVal;
// Check whether we only have one InvokerInterceptor: that is,
// no real advice, but just reflective invocation of the target.
if (chain.isEmpty()) {
if (chain.isEmpty() && CglibMethodInvocation.isMethodProxyCompatible(method)) {
// We can skip creating a MethodInvocation: just invoke the target directly.
// Note that the final invoker must be an InvokerInterceptor, so we know
// it does nothing but a reflective operation on the target, and no hot
// swapping or fancy proxying.
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
try {
retVal = methodProxy.invoke(target, argsToUse);
}
catch (CodeGenerationException ex) {
CglibMethodInvocation.logFastClassGenerationFailure(method);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
}
else {
// We need to create a method invocation...
@@ -738,11 +733,17 @@ class CglibAopProxy implements AopProxy, Serializable {
*/
private static class CglibMethodInvocation extends ReflectiveMethodInvocation {
@Nullable
private final MethodProxy methodProxy;
public CglibMethodInvocation(Object proxy, @Nullable Object target, Method method,
Object[] arguments, @Nullable Class<?> targetClass,
List<Object> interceptorsAndDynamicMethodMatchers, MethodProxy methodProxy) {
super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers);
// Only use method proxy for public methods not derived from java.lang.Object
this.methodProxy = (isMethodProxyCompatible(method) ? methodProxy : null);
}
@Override
@@ -770,6 +771,35 @@ class CglibAopProxy implements AopProxy, Serializable {
}
}
}
/**
* Gives a marginal performance improvement versus using reflection to
* invoke the target when invoking public methods.
*/
@Override
protected Object invokeJoinpoint() throws Throwable {
if (this.methodProxy != null) {
try {
return this.methodProxy.invoke(this.target, this.arguments);
}
catch (CodeGenerationException ex) {
logFastClassGenerationFailure(this.method);
}
}
return super.invokeJoinpoint();
}
static boolean isMethodProxyCompatible(Method method) {
return (Modifier.isPublic(method.getModifiers()) &&
method.getDeclaringClass() != Object.class && !AopUtils.isEqualsMethod(method) &&
!AopUtils.isHashCodeMethod(method) && !AopUtils.isToStringMethod(method));
}
static void logFastClassGenerationFailure(Method method) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to generate CGLIB fast class for method: " + method);
}
}
}
@@ -919,9 +949,10 @@ class CglibAopProxy implements AopProxy, Serializable {
if (this == other) {
return true;
}
if (!(other instanceof ProxyCallbackFilter otherCallbackFilter)) {
if (!(other instanceof ProxyCallbackFilter)) {
return false;
}
ProxyCallbackFilter otherCallbackFilter = (ProxyCallbackFilter) other;
AdvisedSupport otherAdvised = otherCallbackFilter.advised;
if (this.advised.isFrozen() != otherAdvised.isFrozen()) {
return false;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -60,8 +60,9 @@ public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializ
Boolean hasIntroductions = null;
for (Advisor advisor : advisors) {
if (advisor instanceof PointcutAdvisor pointcutAdvisor) {
if (advisor instanceof PointcutAdvisor) {
// Add it conditionally.
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
boolean match;
@@ -89,7 +90,8 @@ public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializ
}
}
}
else if (advisor instanceof IntroductionAdvisor ia) {
else if (advisor instanceof IntroductionAdvisor) {
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
@@ -109,7 +111,8 @@ public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializ
*/
private static boolean hasMatchingIntroductions(Advisor[] advisors, Class<?> actualClass) {
for (Advisor advisor : advisors) {
if (advisor instanceof IntroductionAdvisor ia) {
if (advisor instanceof IntroductionAdvisor) {
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
if (ia.getClassFilter().matches(actualClass)) {
return true;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 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.
@@ -20,7 +20,7 @@ import java.io.Serializable;
import java.lang.reflect.Proxy;
import org.springframework.aop.SpringProxy;
import org.springframework.util.ClassUtils;
import org.springframework.core.NativeDetector;
/**
* Default {@link AopProxyFactory} implementation, creating either a CGLIB proxy
@@ -40,26 +40,25 @@ import org.springframework.util.ClassUtils;
* @author Rod Johnson
* @author Juergen Hoeller
* @author Sebastien Deleuze
* @author Sam Brannen
* @since 12.03.2004
* @see AdvisedSupport#setOptimize
* @see AdvisedSupport#setProxyTargetClass
* @see AdvisedSupport#setInterfaces
*/
@SuppressWarnings("serial")
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
private static final long serialVersionUID = 7930414337282325166L;
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
if (!NativeDetector.inNativeImage() &&
(config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass) || ClassUtils.isLambdaClass(targetClass)) {
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2020 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.
@@ -72,8 +72,8 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa
* NOTE: We could avoid the code duplication between this class and the CGLIB
* proxies by refactoring "invoke" into a template method. However, this approach
* adds at least 10% performance overhead versus a copy-paste solution, so we sacrifice
* elegance for performance (we have a good test suite to ensure that the different
* proxies behave the same :-)).
* elegance for performance. (We have a good test suite to ensure that the different
* proxies behave the same :-)
* This way, we can also more easily take advantage of minor optimizations in each class.
*/
@@ -104,6 +104,9 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa
*/
public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
Assert.notNull(config, "AdvisedSupport must not be null");
if (config.getAdvisorCount() == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
throw new AopConfigException("No advisors and no TargetSource specified");
}
this.advised = config;
this.proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(this.proxiedInterfaces);
@@ -123,12 +126,6 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa
return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
}
@SuppressWarnings("deprecation")
@Override
public Class<?> getProxyClass(@Nullable ClassLoader classLoader) {
return Proxy.getProxyClass(classLoader, this.proxiedInterfaces);
}
/**
* Finds any {@link #equals} or {@link #hashCode} method that may be defined
* on the supplied set of interfaces.
@@ -201,7 +198,7 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa
// Get the interception chain for this method.
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// Check whether we have any advice. If we don't, we can fall back on direct
// Check whether we have any advice. If we don't, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
if (chain.isEmpty()) {
// We can skip creating a MethodInvocation: just invoke the target directly
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -52,11 +52,6 @@ class ObjenesisCglibAopProxy extends CglibAopProxy {
}
@Override
protected Class<?> createProxyClass(Enhancer enhancer) {
return enhancer.createClass();
}
@Override
protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
Class<?> proxyClass = enhancer.createClass();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2020 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.
@@ -86,7 +86,7 @@ public class ProxyCreatorSupport extends AdvisedSupport {
/**
* Remove the given AdvisedSupportListener from this proxy configuration.
* @param listener the listener to remove
* @param listener the listener to deregister
*/
public void removeListener(AdvisedSupportListener listener) {
Assert.notNull(listener, "AdvisedSupportListener must not be null");
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2016 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.
@@ -110,17 +110,6 @@ public class ProxyFactory extends ProxyCreatorSupport {
return createAopProxy().getProxy(classLoader);
}
/**
* Determine the proxy class according to the settings in this factory.
* @param classLoader the class loader to create the proxy class with
* (or {@code null} for the low-level proxy facility's default)
* @return the proxy class
* @since 6.0
*/
public Class<?> getProxyClass(@Nullable ClassLoader classLoader) {
return createAopProxy().getProxyClass(classLoader);
}
/**
* Create a new proxy for the given interface and interceptor.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -50,7 +50,7 @@ import org.springframework.util.ObjectUtils;
/**
* {@link org.springframework.beans.factory.FactoryBean} implementation that builds an
* AOP proxy based on beans in a Spring {@link org.springframework.beans.factory.BeanFactory}.
* AOP proxy based on beans in Spring {@link org.springframework.beans.factory.BeanFactory}.
*
* <p>{@link org.aopalliance.intercept.MethodInterceptor MethodInterceptors} and
* {@link org.springframework.aop.Advisor Advisors} are identified by a list of bean
@@ -61,11 +61,10 @@ import org.springframework.util.ObjectUtils;
*
* <p>Global interceptors and advisors can be added at the factory level. The specified
* ones are expanded in an interceptor list where an "xxx*" entry is included in the
* list, matching the given prefix with the bean names &mdash; for example, "global*"
* would match both "globalBean1" and "globalBean2"; whereas, "*" would match all
* defined interceptors. The matching interceptors get applied according to their
* returned order value, if they implement the {@link org.springframework.core.Ordered}
* interface.
* list, matching the given prefix with the bean names (e.g. "global*" would match
* both "globalBean1" and "globalBean2", "*" all defined interceptors). The matching
* interceptors get applied according to their returned order value, if they implement
* the {@link org.springframework.core.Ordered} interface.
*
* <p>Creates a JDK proxy when proxy interfaces are given, and a CGLIB proxy for the
* actual target class if not. Note that the latter will only work if the target class
@@ -76,7 +75,7 @@ import org.springframework.util.ObjectUtils;
* This won't work for existing prototype references, which are independent. However,
* it will work for prototypes subsequently obtained from the factory. Changes to
* interception will work immediately on singletons (including existing references).
* However, to change interfaces or a target it's necessary to obtain a new instance
* However, to change interfaces or target it's necessary to obtain a new instance
* from the factory. This means that singleton instances obtained from the factory
* do not have the same object identity. However, they do have the same interceptors
* and target, and changing any reference will change all objects.
@@ -274,9 +273,19 @@ public class ProxyFactoryBean extends ProxyCreatorSupport
return this.singletonInstance.getClass();
}
}
// This might be incomplete since it potentially misses introduced interfaces
// from Advisors that will be lazily retrieved via setInterceptorNames.
return createAopProxy().getProxyClass(this.proxyClassLoader);
Class<?>[] ifcs = getProxiedInterfaces();
if (ifcs.length == 1) {
return ifcs[0];
}
else if (ifcs.length > 1) {
return createCompositeInterface(ifcs);
}
else if (this.targetName != null && this.beanFactory != null) {
return this.beanFactory.getType(this.targetName);
}
else {
return getTargetClass();
}
}
@Override
@@ -285,6 +294,19 @@ public class ProxyFactoryBean extends ProxyCreatorSupport
}
/**
* Create a composite interface Class for the given interfaces,
* implementing the given interfaces in one single Class.
* <p>The default implementation builds a JDK proxy class for the
* given interfaces.
* @param interfaces the interfaces to merge
* @return the merged interface as Class
* @see java.lang.reflect.Proxy#getProxyClass
*/
protected Class<?> createCompositeInterface(Class<?>[] interfaces) {
return ClassUtils.createCompositeInterface(interfaces, this.proxyClassLoader);
}
/**
* Return the singleton instance of this class's proxy object,
* lazily creating it if it hasn't been created already.
@@ -384,7 +406,7 @@ public class ProxyFactoryBean extends ProxyCreatorSupport
if (namedBeanClass != null) {
return (Advisor.class.isAssignableFrom(namedBeanClass) || Advice.class.isAssignableFrom(namedBeanClass));
}
// Treat it as a target bean if we can't tell.
// Treat it as an target bean if we can't tell.
if (logger.isDebugEnabled()) {
logger.debug("Could not determine type of bean with name '" + beanName +
"' - assuming it is neither an Advisor nor an Advice");
@@ -399,10 +421,14 @@ public class ProxyFactoryBean extends ProxyCreatorSupport
* are unaffected by such changes.
*/
private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {
if (!this.advisorChainInitialized && !ObjectUtils.isEmpty(this.interceptorNames)) {
if (this.advisorChainInitialized) {
return;
}
if (!ObjectUtils.isEmpty(this.interceptorNames)) {
if (this.beanFactory == null) {
throw new IllegalStateException("No BeanFactory available anymore (probably due to serialization) " +
"- cannot resolve interceptor names " + Arrays.toString(this.interceptorNames));
"- cannot resolve interceptor names " + Arrays.asList(this.interceptorNames));
}
// Globals can't be last unless we specified a targetSource using the property...
@@ -414,11 +440,12 @@ public class ProxyFactoryBean extends ProxyCreatorSupport
// Materialize interceptor chain from bean names.
for (String name : this.interceptorNames) {
if (name.endsWith(GLOBAL_SUFFIX)) {
if (!(this.beanFactory instanceof ListableBeanFactory lbf)) {
if (!(this.beanFactory instanceof ListableBeanFactory)) {
throw new AopConfigException(
"Can only use global advisors or interceptors with a ListableBeanFactory");
}
addGlobalAdvisors(lbf, name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
addGlobalAdvisors((ListableBeanFactory) this.beanFactory,
name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
}
else {
@@ -437,9 +464,9 @@ public class ProxyFactoryBean extends ProxyCreatorSupport
addAdvisorOnChainCreation(advice);
}
}
this.advisorChainInitialized = true;
}
this.advisorChainInitialized = true;
}
@@ -452,16 +479,17 @@ public class ProxyFactoryBean extends ProxyCreatorSupport
Advisor[] advisors = getAdvisors();
List<Advisor> freshAdvisors = new ArrayList<>(advisors.length);
for (Advisor advisor : advisors) {
if (advisor instanceof PrototypePlaceholderAdvisor ppa) {
if (advisor instanceof PrototypePlaceholderAdvisor) {
PrototypePlaceholderAdvisor pa = (PrototypePlaceholderAdvisor) advisor;
if (logger.isDebugEnabled()) {
logger.debug("Refreshing bean named '" + ppa.getBeanName() + "'");
logger.debug("Refreshing bean named '" + pa.getBeanName() + "'");
}
// Replace the placeholder with a fresh prototype instance resulting from a getBean lookup
if (this.beanFactory == null) {
throw new IllegalStateException("No BeanFactory available anymore (probably due to " +
"serialization) - cannot resolve prototype advisor '" + ppa.getBeanName() + "'");
"serialization) - cannot resolve prototype advisor '" + pa.getBeanName() + "'");
}
Object bean = this.beanFactory.getBean(ppa.getBeanName());
Object bean = this.beanFactory.getBean(pa.getBeanName());
Advisor refreshedAdvisor = namedBeanToAdvisor(bean);
freshAdvisors.add(refreshedAdvisor);
}
@@ -533,7 +561,7 @@ public class ProxyFactoryBean extends ProxyCreatorSupport
logger.debug("Refreshing target with name '" + this.targetName + "'");
}
Object target = this.beanFactory.getBean(this.targetName);
return (target instanceof TargetSource targetSource ? targetSource : new SingletonTargetSource(target));
return (target instanceof TargetSource ? (TargetSource) target : new SingletonTargetSource(target));
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -165,9 +165,11 @@ public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Clonea
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher dm) {
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -58,9 +58,10 @@ public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Se
if (adviceObject instanceof Advisor) {
return (Advisor) adviceObject;
}
if (!(adviceObject instanceof Advice advice)) {
if (!(adviceObject instanceof Advice)) {
throw new UnknownAdviceTypeException(adviceObject);
}
Advice advice = (Advice) adviceObject;
if (advice instanceof MethodInterceptor) {
// So well-known it doesn't even need an adapter.
return new DefaultPointcutAdvisor(advice);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2012 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,8 @@ import org.springframework.aop.Advisor;
import org.springframework.aop.ThrowsAdvice;
/**
* Adapter to enable {@link org.springframework.aop.ThrowsAdvice} to be used
* in the Spring AOP framework.
* Adapter to enable {@link org.springframework.aop.MethodBeforeAdvice}
* to be used in the Spring AOP framework.
*
* @author Rod Johnson
* @author Juergen Hoeller
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 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.
@@ -38,7 +38,6 @@ import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.framework.ProxyProcessorSupport;
import org.springframework.aop.framework.adapter.AdvisorAdapterRegistry;
import org.springframework.aop.framework.adapter.GlobalAdvisorAdapterRegistry;
import org.springframework.aop.target.EmptyTargetSource;
import org.springframework.aop.target.SingletonTargetSource;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
@@ -51,7 +50,6 @@ import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostP
import org.springframework.core.SmartClassLoader;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/**
@@ -87,7 +85,6 @@ import org.springframework.util.StringUtils;
* @author Juergen Hoeller
* @author Rod Johnson
* @author Rob Harrop
* @author Sam Brannen
* @since 13.10.2003
* @see #setInterceptorNames
* @see #getAdvicesAndAdvisorsForBean
@@ -120,7 +117,7 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
private AdvisorAdapterRegistry advisorAdapterRegistry = GlobalAdvisorAdapterRegistry.getInstance();
/**
* Indicates whether the proxy should be frozen. Overridden from super
* Indicates whether or not the proxy should be frozen. Overridden from super
* to prevent the configuration from becoming frozen too early.
*/
private boolean freezeProxy = false;
@@ -146,9 +143,9 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
/**
* Set whether the proxy should be frozen, preventing advice
* Set whether or not the proxy should be frozen, preventing advice
* from being added to it once it is created.
* <p>Overridden from the superclass to prevent the proxy configuration
* <p>Overridden from the super class to prevent the proxy configuration
* from being frozen before the proxy is created.
*/
@Override
@@ -232,30 +229,6 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
return this.proxyTypes.get(cacheKey);
}
@Override
public Class<?> determineBeanType(Class<?> beanClass, String beanName) {
Object cacheKey = getCacheKey(beanClass, beanName);
Class<?> proxyType = this.proxyTypes.get(cacheKey);
if (proxyType == null) {
TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
if (targetSource != null) {
if (StringUtils.hasLength(beanName)) {
this.targetSourcedBeans.add(beanName);
}
}
else {
targetSource = EmptyTargetSource.forClass(beanClass);
}
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
proxyType = createProxyClass(beanClass, beanName, specificInterceptors, targetSource);
this.proxyTypes.put(cacheKey, proxyType);
}
}
return (proxyType != null ? proxyType : beanClass);
}
@Override
@Nullable
public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) {
@@ -461,18 +434,6 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
return buildProxy(beanClass, beanName, specificInterceptors, targetSource, false);
}
private Class<?> createProxyClass(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
return (Class<?>) buildProxy(beanClass, beanName, specificInterceptors, targetSource, true);
}
private Object buildProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource, boolean classOnly) {
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}
@@ -481,8 +442,8 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
proxyFactory.copyFrom(this);
if (proxyFactory.isProxyTargetClass()) {
// Explicit handling of JDK proxy targets and lambdas (for introduction advice scenarios)
if (Proxy.isProxyClass(beanClass) || ClassUtils.isLambdaClass(beanClass)) {
// Explicit handling of JDK proxy targets (for introduction advice scenarios)
if (Proxy.isProxyClass(beanClass)) {
// Must allow for introductions; can't just set interfaces to the proxy's interfaces only.
for (Class<?> ifc : beanClass.getInterfaces()) {
proxyFactory.addInterface(ifc);
@@ -514,7 +475,7 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) {
classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
}
return (classOnly ? proxyFactory.getProxyClass(classLoader) : proxyFactory.getProxy(classLoader));
return proxyFactory.getProxy(classLoader);
}
/**
@@ -25,6 +25,7 @@ import org.springframework.beans.factory.FactoryBean;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.PatternMatchUtils;
import org.springframework.util.StringUtils;
/**
* Auto proxy creator that identifies beans to proxy via a list of names.
@@ -68,7 +69,7 @@ public class BeanNameAutoProxyCreator extends AbstractAutoProxyCreator {
Assert.notEmpty(beanNames, "'beanNames' must not be empty");
this.beanNames = new ArrayList<>(beanNames.length);
for (String mappedName : beanNames) {
this.beanNames.add(mappedName.strip());
this.beanNames.add(StringUtils.trimWhitespace(mappedName));
}
}
@@ -56,7 +56,7 @@ public abstract class AbstractTraceInterceptor implements MethodInterceptor, Ser
protected transient Log defaultLogger = LogFactory.getLog(getClass());
/**
* Indicates whether proxy class names should be hidden when using dynamic loggers.
* Indicates whether or not proxy class names should be hidden when using dynamic loggers.
* @see #setUseDynamicLogger
*/
private boolean hideProxyClassNames = false;
@@ -119,7 +119,7 @@ public abstract class AbstractTraceInterceptor implements MethodInterceptor, Ser
/**
* Determines whether logging is enabled for the particular {@code MethodInvocation}.
* Determines whether or not logging is enabled for the particular {@code MethodInvocation}.
* If not, the method invocation proceeds as normal, otherwise the method invocation is passed
* to the {@code invokeUnderTrace} method for handling.
* @see #invokeUnderTrace(org.aopalliance.intercept.MethodInvocation, org.apache.commons.logging.Log)
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@ import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
@@ -33,15 +34,14 @@ import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.config.EmbeddedValueResolver;
import org.springframework.core.task.AsyncListenableTaskExecutor;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.core.task.TaskExecutor;
import org.springframework.core.task.support.TaskExecutorAdapter;
import org.springframework.lang.Nullable;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.StringValueResolver;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.function.SingletonSupplier;
/**
@@ -57,7 +57,6 @@ import org.springframework.util.function.SingletonSupplier;
* @author Chris Beams
* @author Juergen Hoeller
* @author Stephane Nicoll
* @author He Bo
* @since 3.1.2
*/
public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
@@ -82,8 +81,6 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
@Nullable
private BeanFactory beanFactory;
@Nullable
private StringValueResolver embeddedValueResolver;
/**
* Create a new instance with a default {@link AsyncUncaughtExceptionHandler}.
@@ -154,14 +151,12 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
@Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
if (beanFactory instanceof ConfigurableBeanFactory configurableBeanFactory) {
this.embeddedValueResolver = new EmbeddedValueResolver(configurableBeanFactory);
}
}
/**
* Determine the specific executor to use when executing the given method.
* Should preferably return an {@link AsyncListenableTaskExecutor} implementation.
* @return the executor to use (or {@code null}, but just if no default executor is available)
*/
@Nullable
@@ -170,9 +165,6 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
if (executor == null) {
Executor targetExecutor;
String qualifier = getExecutorQualifier(method);
if (this.embeddedValueResolver != null && StringUtils.hasLength(qualifier)) {
qualifier = this.embeddedValueResolver.resolveStringValue(qualifier);
}
if (StringUtils.hasLength(qualifier)) {
targetExecutor = findQualifiedExecutor(this.beanFactory, qualifier);
}
@@ -182,8 +174,8 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
if (targetExecutor == null) {
return null;
}
executor = (targetExecutor instanceof AsyncTaskExecutor ?
(AsyncTaskExecutor) targetExecutor : new TaskExecutorAdapter(targetExecutor));
executor = (targetExecutor instanceof AsyncListenableTaskExecutor ?
(AsyncListenableTaskExecutor) targetExecutor : new TaskExecutorAdapter(targetExecutor));
this.executors.put(method, executor);
}
return executor;
@@ -192,7 +184,7 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
/**
* Return the qualifier or bean name of the executor to be used when executing the
* given async method, typically specified in the form of an annotation attribute.
* <p>Returning an empty string or {@code null} indicates that no specific executor has
* Returning an empty string or {@code null} indicates that no specific executor has
* been specified and that the {@linkplain #setExecutor(Executor) default executor}
* should be used.
* @param method the method to inspect for executor qualifier metadata
@@ -221,7 +213,7 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
/**
* Retrieve or build a default executor for this advice instance.
* <p>An executor returned from here will be cached for further use.
* An executor returned from here will be cached for further use.
* <p>The default implementation searches for a unique {@link TaskExecutor} bean
* in the context, or for an {@link Executor} bean named "taskExecutor" otherwise.
* If neither of the two is resolvable, this implementation will return {@code null}.
@@ -241,8 +233,7 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
return beanFactory.getBean(TaskExecutor.class);
}
catch (NoUniqueBeanDefinitionException ex) {
logger.debug("Could not find unique TaskExecutor bean. " +
"Continuing search for an Executor bean named 'taskExecutor'", ex);
logger.debug("Could not find unique TaskExecutor bean", ex);
try {
return beanFactory.getBean(DEFAULT_TASK_EXECUTOR_BEAN_NAME, Executor.class);
}
@@ -255,8 +246,7 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
}
}
catch (NoSuchBeanDefinitionException ex) {
logger.debug("Could not find default TaskExecutor bean. " +
"Continuing search for an Executor bean named 'taskExecutor'", ex);
logger.debug("Could not find default TaskExecutor bean", ex);
try {
return beanFactory.getBean(DEFAULT_TASK_EXECUTOR_BEAN_NAME, Executor.class);
}
@@ -279,25 +269,27 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
* @return the execution result (potentially a corresponding {@link Future} handle)
*/
@Nullable
@SuppressWarnings("deprecation")
protected Object doSubmit(Callable<Object> task, AsyncTaskExecutor executor, Class<?> returnType) {
if (CompletableFuture.class.isAssignableFrom(returnType)) {
return executor.submitCompletable(task);
return CompletableFuture.supplyAsync(() -> {
try {
return task.call();
}
catch (Throwable ex) {
throw new CompletionException(ex);
}
}, executor);
}
else if (org.springframework.util.concurrent.ListenableFuture.class.isAssignableFrom(returnType)) {
return ((org.springframework.core.task.AsyncListenableTaskExecutor) executor).submitListenable(task);
else if (ListenableFuture.class.isAssignableFrom(returnType)) {
return ((AsyncListenableTaskExecutor) executor).submitListenable(task);
}
else if (Future.class.isAssignableFrom(returnType)) {
return executor.submit(task);
}
else if (void.class == returnType) {
else {
executor.submit(task);
return null;
}
else {
throw new IllegalArgumentException(
"Invalid return type for async method (only Future and void supported): " + returnType);
}
}
/**

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