mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 91cf5ebd2d |
@@ -35,7 +35,9 @@ jobs:
|
||||
env:
|
||||
CI: 'true'
|
||||
GRADLE_ENTERPRISE_URL: 'https://ge.spring.io'
|
||||
DEVELOCITY_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GRADLE_ENTERPRISE_CACHE_USER }}
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GRADLE_ENTERPRISE_CACHE_PASSWORD }}
|
||||
run: ./gradlew -PdeploymentRepository=$(pwd)/deployment-repository build publishAllPublicationsToDeploymentRepository
|
||||
- name: Deploy
|
||||
uses: spring-io/artifactory-deploy-action@v0.0.1
|
||||
@@ -49,10 +51,9 @@ jobs:
|
||||
signing-key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
signing-passphrase: ${{ secrets.GPG_PASSPHRASE }}
|
||||
artifact-properties: |
|
||||
/**/framework-docs-*.zip::zip.name=spring-framework,zip.deployed=false
|
||||
/**/framework-docs-*-docs.zip::zip.type=docs
|
||||
/**/framework-docs-*-dist.zip::zip.type=dist
|
||||
/**/framework-docs-*-schema.zip::zip.type=schema
|
||||
/**/framework-api-*.zip::zip.name=spring-framework,zip.deployed=false
|
||||
/**/framework-api-*-docs.zip::zip.type=docs
|
||||
/**/framework-api-*-schema.zip::zip.type=schema
|
||||
- name: Send notification
|
||||
uses: ./.github/actions/send-notification
|
||||
if: always()
|
||||
|
||||
@@ -66,7 +66,9 @@ jobs:
|
||||
env:
|
||||
CI: 'true'
|
||||
GRADLE_ENTERPRISE_URL: 'https://ge.spring.io'
|
||||
DEVELOCITY_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GRADLE_ENTERPRISE_CACHE_USER }}
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GRADLE_ENTERPRISE_CACHE_PASSWORD }}
|
||||
run: ./gradlew check antora
|
||||
- name: Send notification
|
||||
uses: ./.github/actions/send-notification
|
||||
|
||||
@@ -51,5 +51,3 @@ atlassian-ide-plugin.xml
|
||||
.vscode/
|
||||
|
||||
cached-antora-playbook.yml
|
||||
|
||||
node_modules
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# Enable auto-env through the sdkman_auto_env config
|
||||
# Add key=value pairs of SDKs to use below
|
||||
java=17.0.11-librca
|
||||
java=17.0.8.1-librca
|
||||
|
||||
+7
-5
@@ -3,12 +3,12 @@ plugins {
|
||||
id 'io.freefair.aspectj' version '8.0.1' apply false
|
||||
// kotlinVersion is managed in gradle.properties
|
||||
id 'org.jetbrains.kotlin.plugin.serialization' version "${kotlinVersion}" apply false
|
||||
id 'org.jetbrains.dokka' version '1.8.20'
|
||||
id 'org.jetbrains.dokka' version '1.8.10'
|
||||
id 'org.unbroken-dome.xjc' version '2.0.0' apply false
|
||||
id 'com.github.ben-manes.versions' version '0.51.0'
|
||||
id 'com.github.ben-manes.versions' version '0.49.0'
|
||||
id 'com.github.johnrengelman.shadow' version '8.1.1' apply false
|
||||
id 'de.undercouch.download' version '5.4.0'
|
||||
id 'me.champeau.jmh' version '0.7.2' apply false
|
||||
id 'me.champeau.jmh' version '0.7.1' apply false
|
||||
}
|
||||
|
||||
ext {
|
||||
@@ -106,12 +106,14 @@ configure([rootProject] + javaProjects) { project ->
|
||||
// JSR-305 only used for non-required meta-annotations
|
||||
compileOnly("com.google.code.findbugs:jsr305")
|
||||
testCompileOnly("com.google.code.findbugs:jsr305")
|
||||
checkstyle("io.spring.javaformat:spring-javaformat-checkstyle:0.0.42")
|
||||
checkstyle("io.spring.javaformat:spring-javaformat-checkstyle:0.0.41")
|
||||
}
|
||||
|
||||
ext.javadocLinks = [
|
||||
"https://docs.oracle.com/en/java/javase/17/docs/api/",
|
||||
"https://jakarta.ee/specifications/platform/9/apidocs/",
|
||||
"https://docs.oracle.com/cd/E13222_01/wls/docs90/javadocs/", // CommonJ and weblogic.* packages
|
||||
"https://docs.jboss.org/jbossas/javadoc/4.0.5/connector/", // org.jboss.resource.*
|
||||
"https://docs.jboss.org/hibernate/orm/5.6/javadocs/",
|
||||
"https://eclipse.dev/aspectj/doc/released/aspectj5rt-api",
|
||||
"https://www.quartz-scheduler.org/api/2.3.0/",
|
||||
@@ -151,7 +153,7 @@ configure(rootProject) {
|
||||
def rootPath = file(rootDir).toPath()
|
||||
def projectDirs = allprojects.collect { it.projectDir } + "${rootDir}/buildSrc"
|
||||
projectDirs.forEach { dir ->
|
||||
[ 'bin', 'build', 'out', '.settings', 'node_modules' ]
|
||||
[ 'bin', 'build', 'out', '.settings' ]
|
||||
.collect { rootPath.relativize(new File(dir, it).toPath()) }
|
||||
.forEach { source.exclude "$it/**" }
|
||||
[ '.classpath', '.project' ]
|
||||
|
||||
+3
-1
@@ -5,7 +5,9 @@ anchors:
|
||||
password: ((github-ci-release-token))
|
||||
branch: ((branch))
|
||||
gradle-enterprise-task-params: &gradle-enterprise-task-params
|
||||
DEVELOCITY_ACCESS_KEY: ((gradle_enterprise_secret_access_key))
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY: ((gradle_enterprise_secret_access_key))
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME: ((gradle_enterprise_cache_user.username))
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD: ((gradle_enterprise_cache_user.password))
|
||||
sonatype-task-params: &sonatype-task-params
|
||||
SONATYPE_USERNAME: ((sonatype-username))
|
||||
SONATYPE_PASSWORD: ((sonatype-password))
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
antora:
|
||||
extensions:
|
||||
- require: '@springio/antora-extensions'
|
||||
root_component_name: 'framework'
|
||||
site:
|
||||
title: Spring Framework
|
||||
url: https://docs.spring.io/spring-framework/reference
|
||||
robots: allow
|
||||
git:
|
||||
ensure_git_suffix: false
|
||||
content:
|
||||
sources:
|
||||
- url: https://github.com/spring-projects/spring-framework
|
||||
# Refname matching:
|
||||
# https://docs.antora.org/antora/latest/playbook/content-refname-matching/
|
||||
branches: ['main', '{6..9}.+({0..9}).x']
|
||||
tags: ['v{6..9}.+({0..9}).+({0..9})?(-{RC,M}*)', '!(v6.0.{0..8})', '!(v6.0.0-{RC,M}{0..9})']
|
||||
start_path: framework-docs
|
||||
asciidoc:
|
||||
extensions:
|
||||
- '@asciidoctor/tabs'
|
||||
- '@springio/asciidoctor-extensions'
|
||||
- '@springio/asciidoctor-extensions/include-code-extension'
|
||||
attributes:
|
||||
page-stackoverflow-url: https://stackoverflow.com/questions/tagged/spring
|
||||
page-pagination: ''
|
||||
hide-uri-scheme: '@'
|
||||
tabs-sync-option: '@'
|
||||
include-java: 'example$docs-src/main/java/org/springframework/docs'
|
||||
urls:
|
||||
latest_version_segment_strategy: redirect:to
|
||||
latest_version_segment: ''
|
||||
redirect_facility: httpd
|
||||
runtime:
|
||||
log:
|
||||
failure_level: warn
|
||||
ui:
|
||||
bundle:
|
||||
url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.15/ui-bundle.zip
|
||||
@@ -10,10 +10,27 @@ apply from: "${rootDir}/gradle/publications.gradle"
|
||||
|
||||
|
||||
antora {
|
||||
options = [clean: true, fetch: !project.gradle.startParameter.offline, stacktrace: true]
|
||||
version = '3.2.0-alpha.2'
|
||||
playbook = 'cached-antora-playbook.yml'
|
||||
playbookProvider {
|
||||
repository = 'spring-projects/spring-framework'
|
||||
branch = 'docs-build'
|
||||
path = 'lib/antora/templates/per-branch-antora-playbook.yml'
|
||||
checkLocalBranch = true
|
||||
}
|
||||
options = ['--clean', '--stacktrace']
|
||||
environment = [
|
||||
'BUILD_REFNAME': 'HEAD',
|
||||
'BUILD_VERSION': project.version,
|
||||
'ALGOLIA_API_KEY': '82c7ead946afbac3cf98c32446154691',
|
||||
'ALGOLIA_APP_ID': '244V8V9FGG',
|
||||
'ALGOLIA_INDEX_NAME': 'framework-docs'
|
||||
]
|
||||
dependencies = [
|
||||
'@antora/atlas-extension': '1.0.0-alpha.1',
|
||||
'@antora/collector-extension': '1.0.0-alpha.3',
|
||||
'@asciidoctor/tabs': '1.0.0-beta.3',
|
||||
'@opendevise/antora-release-line-extension': '1.0.0-alpha.2',
|
||||
'@springio/antora-extensions': '1.3.0',
|
||||
'@springio/asciidoctor-extensions': '1.0.0-alpha.9'
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
+7
-8
@@ -153,16 +153,15 @@ Letting qualifier values select against target bean names, within the type-match
|
||||
candidates, does not require a `@Qualifier` annotation at the injection point.
|
||||
If there is no other resolution indicator (such as a qualifier or a primary marker),
|
||||
for a non-unique dependency situation, Spring matches the injection point name
|
||||
(that is, the field name or parameter name) against the target bean names and chooses
|
||||
the same-named candidate, if any (either by bean name or by associated alias).
|
||||
|
||||
This requires the `-parameters` Java compiler flag to be present. As a fallback,
|
||||
version 6.0 still supports parameter names from debug symbols via `-debug` as well.
|
||||
(that is, the field name or parameter name) against the target bean names and chooses the
|
||||
same-named candidate, if any.
|
||||
====
|
||||
|
||||
As an alternative for injection by name, consider the JSR-250 `@Resource` annotation
|
||||
which is semantically defined to identify a specific target component by its unique name,
|
||||
with the declared type being irrelevant for the matching process. `@Autowired` has rather
|
||||
That said, if you intend to express annotation-driven injection by name, do not
|
||||
primarily use `@Autowired`, even if it is capable of selecting by bean name among
|
||||
type-matching candidates. Instead, use the JSR-250 `@Resource` annotation, which is
|
||||
semantically defined to identify a specific target component by its unique name, with
|
||||
the declared type being irrelevant for the matching process. `@Autowired` has rather
|
||||
different semantics: After selecting candidate beans by type, the specified `String`
|
||||
qualifier value is considered within those type-selected candidates only (for example,
|
||||
matching an `account` qualifier against beans marked with the same qualifier label).
|
||||
|
||||
@@ -416,8 +416,8 @@ Kotlin::
|
||||
======
|
||||
|
||||
This approach shows that the factory bean itself can be managed and configured through
|
||||
dependency injection (DI).
|
||||
See xref:core/beans/dependencies/factory-properties-detailed.adoc[Dependencies and Configuration in Detail].
|
||||
dependency injection (DI). See xref:core/beans/dependencies/factory-properties-detailed.adoc[Dependencies and Configuration in Detail]
|
||||
.
|
||||
|
||||
NOTE: In Spring documentation, "factory bean" refers to a bean that is configured in the
|
||||
Spring container and that creates objects through an
|
||||
@@ -444,3 +444,5 @@ cases into account and returns the type of object that a `BeanFactory.getBean` c
|
||||
going to return for the same bean name.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -29,6 +29,8 @@ Brannen, Ramnivas Laddad, Arjen Poutsma, Chris Beams, Tareq Abedrabbo, Andy Clem
|
||||
Syer, Oliver Gierke, Rossen Stoyanchev, Phillip Webb, Rob Winch, Brian Clozel, Stephane
|
||||
Nicoll, Sebastien Deleuze, Jay Bryant, Mark Paluch
|
||||
|
||||
Copyright © 2002 - 2023 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Copies of this document may be made for your own use and for distribution to others,
|
||||
provided that you do not charge any fee for such copies and further provided that each
|
||||
copy contains the Copyright Notice, whether distributed in print or electronically.
|
||||
copy contains this Copyright Notice, whether distributed in print or electronically.
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
[[spring-mvc-test-vs-end-to-end-integration-tests]]
|
||||
= MockMvc vs End-to-End Tests
|
||||
|
||||
MockMvc is built on Servlet API mock implementations from the
|
||||
MockMVc is built on Servlet API mock implementations from the
|
||||
`spring-test` module and does not rely on a running container. Therefore, there are
|
||||
some differences when compared to full end-to-end integration tests with an actual
|
||||
client and a live server running.
|
||||
|
||||
+33
-10
@@ -1,15 +1,38 @@
|
||||
[[spring-mvc-test-vs-streaming-response]]
|
||||
= Streaming Responses
|
||||
|
||||
You can use `WebTestClient` to test xref:testing/webtestclient.adoc#webtestclient-stream[streaming responses]
|
||||
such as Server-Sent Events. However, `MockMvcWebTestClient` doesn't support infinite
|
||||
streams because there is no way to cancel the server stream from the client side.
|
||||
To test infinite streams, you'll need to
|
||||
xref:testing/webtestclient.adoc#webtestclient-server-config[bind to] a running server,
|
||||
or when using Spring Boot,
|
||||
{docs-spring-boot}/spring-boot-features.html#boot-features-testing-spring-boot-applications-testing-with-running-server[test with a running server].
|
||||
The best way to test streaming responses such as Server-Sent Events is through the
|
||||
<<WebTestClient>> which can be used as a test client to connect to a `MockMvc` instance
|
||||
to perform tests on Spring MVC controllers without a running server. For example:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
WebTestClient client = MockMvcWebTestClient.bindToController(new SseController()).build();
|
||||
|
||||
FluxExchangeResult<Person> exchangeResult = client.get()
|
||||
.uri("/persons")
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectHeader().contentType("text/event-stream")
|
||||
.returnResult(Person.class);
|
||||
|
||||
// Use StepVerifier from Project Reactor to test the streaming response
|
||||
|
||||
StepVerifier.create(exchangeResult.getResponseBody())
|
||||
.expectNext(new Person("N0"), new Person("N1"), new Person("N2"))
|
||||
.expectNextCount(4)
|
||||
.consumeNextWith(person -> assertThat(person.getName()).endsWith("7"))
|
||||
.thenCancel()
|
||||
.verify();
|
||||
----
|
||||
======
|
||||
|
||||
`WebTestClient` can also connect to a live server and perform full end-to-end integration
|
||||
tests. This is also supported in Spring Boot where you can
|
||||
{docs-spring-boot}/html/spring-boot-features.html#boot-features-testing-spring-boot-applications-testing-with-running-server[test a running server].
|
||||
|
||||
`MockMvcWebTestClient` does support asynchronous responses, and even streaming responses.
|
||||
The limitation is that it can't influence the server to stop, and therefore the server
|
||||
must finish writing the response on its own.
|
||||
|
||||
|
||||
@@ -52,10 +52,14 @@ The following example shows how to achieve the same configuration in XML:
|
||||
</mvc:interceptors>
|
||||
----
|
||||
|
||||
WARNING: Interceptors are not ideally suited as a security layer due to the potential for
|
||||
a mismatch with annotated controller path matching. Generally, we recommend using Spring
|
||||
Security, or alternatively a similar approach integrated with the Servlet filter chain,
|
||||
and applied as early as possible.
|
||||
NOTE: Interceptors are not ideally suited as a security layer due to the potential
|
||||
for a mismatch with annotated controller path matching, which can also match trailing
|
||||
slashes and path extensions transparently, along with other path matching options. Many
|
||||
of these options have been deprecated but the potential for a mismatch remains.
|
||||
Generally, we recommend using Spring Security which includes a dedicated
|
||||
https://docs.spring.io/spring-security/reference/servlet/integrations/mvc.html#mvc-requestmatcher[MvcRequestMatcher]
|
||||
to align with Spring MVC path matching and also has a security firewall that blocks many
|
||||
unwanted characters in URL paths.
|
||||
|
||||
NOTE: The XML config declares interceptors as `MappedInterceptor` beans, and those are in
|
||||
turn detected by any `HandlerMapping` bean, including those from other frameworks.
|
||||
|
||||
+23
-18
@@ -1,29 +1,34 @@
|
||||
[[mvc-handlermapping-interceptor]]
|
||||
= Interception
|
||||
|
||||
All `HandlerMapping` implementations support handler interception which is useful when
|
||||
you want to apply functionality across requests. A `HandlerInterceptor` can implement the
|
||||
following:
|
||||
All `HandlerMapping` implementations support handler interceptors that are useful when
|
||||
you want to apply specific functionality to certain requests -- for example, checking for
|
||||
a principal. Interceptors must implement `HandlerInterceptor` from the
|
||||
`org.springframework.web.servlet` package with three methods that should provide enough
|
||||
flexibility to do all kinds of pre-processing and post-processing:
|
||||
|
||||
* `preHandle(..)` -- callback before the actual handler is run that returns a boolean.
|
||||
If the method returns `true`, execution continues; if it returns `false`, the rest of the
|
||||
execution chain is bypassed and the handler is not called.
|
||||
* `postHandle(..)` -- callback after the handler is run.
|
||||
* `afterCompletion(..)` -- callback after the complete request has finished.
|
||||
* `preHandle(..)`: Before the actual handler is run
|
||||
* `postHandle(..)`: After the handler is run
|
||||
* `afterCompletion(..)`: After the complete request has finished
|
||||
|
||||
NOTE: For `@ResponseBody` and `ResponseEntity` controller methods, the response is written
|
||||
and committed within the `HandlerAdapter`, before `postHandle` is called. That means it is
|
||||
too late to change the response, such as to add an extra header. You can implement
|
||||
`ResponseBodyAdvice` and declare it as an
|
||||
xref:web/webmvc/mvc-controller/ann-advice.adoc[Controller Advice] bean or configure it
|
||||
directly on `RequestMappingHandlerAdapter`.
|
||||
The `preHandle(..)` method returns a boolean value. You can use this method to break or
|
||||
continue the processing of the execution chain. When this method returns `true`, the
|
||||
handler execution chain continues. When it returns false, the `DispatcherServlet`
|
||||
assumes the interceptor itself has taken care of requests (and, for example, rendered an
|
||||
appropriate view) and does not continue executing the other interceptors and the actual
|
||||
handler in the execution chain.
|
||||
|
||||
See xref:web/webmvc/mvc-config/interceptors.adoc[Interceptors] in the section on MVC configuration for examples of how to
|
||||
configure interceptors. You can also register them directly by using setters on individual
|
||||
`HandlerMapping` implementations.
|
||||
|
||||
WARNING: Interceptors are not ideally suited as a security layer due to the potential for
|
||||
a mismatch with annotated controller path matching. Generally, we recommend using Spring
|
||||
Security, or alternatively a similar approach integrated with the Servlet filter chain,
|
||||
and applied as early as possible.
|
||||
`postHandle` method is less useful with `@ResponseBody` and `ResponseEntity` methods for
|
||||
which the response is written and committed within the `HandlerAdapter` and before
|
||||
`postHandle`. That means it is too late to make any changes to the response, such as adding
|
||||
an extra header. For such scenarios, you can implement `ResponseBodyAdvice` and either
|
||||
declare it as an xref:web/webmvc/mvc-controller/ann-advice.adoc[Controller Advice] bean or configure it directly on
|
||||
`RequestMappingHandlerAdapter`.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"antora": "3.2.0-alpha.4",
|
||||
"@antora/atlas-extension": "1.0.0-alpha.2",
|
||||
"@antora/collector-extension": "1.0.0-alpha.3",
|
||||
"@asciidoctor/tabs": "1.0.0-beta.6",
|
||||
"@springio/antora-extensions": "1.11.1",
|
||||
"@springio/asciidoctor-extensions": "1.0.0-alpha.10"
|
||||
}
|
||||
}
|
||||
@@ -9,17 +9,17 @@ javaPlatform {
|
||||
dependencies {
|
||||
api(platform("com.fasterxml.jackson:jackson-bom:2.14.3"))
|
||||
api(platform("io.micrometer:micrometer-bom:1.10.13"))
|
||||
api(platform("io.netty:netty-bom:4.1.111.Final"))
|
||||
api(platform("io.netty:netty-bom:4.1.108.Final"))
|
||||
api(platform("io.netty:netty5-bom:5.0.0.Alpha5"))
|
||||
api(platform("io.projectreactor:reactor-bom:2022.0.20"))
|
||||
api(platform("io.projectreactor:reactor-bom:2022.0.18"))
|
||||
api(platform("io.rsocket:rsocket-bom:1.1.3"))
|
||||
api(platform("org.apache.groovy:groovy-bom:4.0.21"))
|
||||
api(platform("org.apache.groovy:groovy-bom:4.0.20"))
|
||||
api(platform("org.apache.logging.log4j:log4j-bom:2.21.1"))
|
||||
api(platform("org.eclipse.jetty:jetty-bom:11.0.20"))
|
||||
api(platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.6.4"))
|
||||
api(platform("org.jetbrains.kotlinx:kotlinx-serialization-bom:1.4.0"))
|
||||
api(platform("org.junit:junit-bom:5.9.3"))
|
||||
api(platform("org.mockito:mockito-bom:5.12.0"))
|
||||
api(platform("org.mockito:mockito-bom:5.11.0"))
|
||||
|
||||
constraints {
|
||||
api("com.fasterxml:aalto-xml:1.3.2")
|
||||
@@ -54,9 +54,9 @@ dependencies {
|
||||
api("io.r2dbc:r2dbc-spi:1.0.0.RELEASE")
|
||||
api("io.reactivex.rxjava3:rxjava:3.1.8")
|
||||
api("io.smallrye.reactive:mutiny:1.10.0")
|
||||
api("io.undertow:undertow-core:2.3.13.Final")
|
||||
api("io.undertow:undertow-servlet:2.3.13.Final")
|
||||
api("io.undertow:undertow-websockets-jsr:2.3.13.Final")
|
||||
api("io.undertow:undertow-core:2.3.12.Final")
|
||||
api("io.undertow:undertow-servlet:2.3.12.Final")
|
||||
api("io.undertow:undertow-websockets-jsr:2.3.12.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")
|
||||
@@ -103,11 +103,11 @@ dependencies {
|
||||
api("org.apache.tomcat.embed:tomcat-embed-websocket:10.1.15")
|
||||
api("org.apache.tomcat:tomcat-util:10.1.15")
|
||||
api("org.apache.tomcat:tomcat-websocket:10.1.15")
|
||||
api("org.aspectj:aspectjrt:1.9.22.1")
|
||||
api("org.aspectj:aspectjtools:1.9.22.1")
|
||||
api("org.aspectj:aspectjweaver:1.9.22.1")
|
||||
api("org.aspectj:aspectjrt:1.9.20.1")
|
||||
api("org.aspectj:aspectjtools:1.9.20.1")
|
||||
api("org.aspectj:aspectjweaver:1.9.20.1")
|
||||
api("org.assertj:assertj-core:3.24.2")
|
||||
api("org.awaitility:awaitility:4.2.1")
|
||||
api("org.awaitility:awaitility:4.2.0")
|
||||
api("org.bouncycastle:bcpkix-jdk18on:1.72")
|
||||
api("org.codehaus.jettison:jettison:1.5.4")
|
||||
api("org.dom4j:dom4j:2.1.4")
|
||||
@@ -126,7 +126,7 @@ dependencies {
|
||||
api("org.hibernate:hibernate-validator:7.0.5.Final")
|
||||
api("org.hsqldb:hsqldb:2.7.2")
|
||||
api("org.javamoney:moneta:1.4.2")
|
||||
api("org.jruby:jruby:9.4.7.0")
|
||||
api("org.jruby:jruby:9.4.6.0")
|
||||
api("org.junit.support:testng-engine:1.0.4")
|
||||
api("org.mozilla:rhino:1.7.14")
|
||||
api("org.ogce:xpp3:1.1.6")
|
||||
@@ -135,7 +135,7 @@ dependencies {
|
||||
api("org.seleniumhq.selenium:htmlunit-driver:2.70.0")
|
||||
api("org.seleniumhq.selenium:selenium-java:3.141.59")
|
||||
api("org.skyscreamer:jsonassert:1.5.1")
|
||||
api("org.slf4j:slf4j-api:2.0.13")
|
||||
api("org.slf4j:slf4j-api:2.0.12")
|
||||
api("org.testng:testng:7.8.0")
|
||||
api("org.webjars:underscorejs:1.8.3")
|
||||
api("org.webjars:webjars-locator-core:0.55")
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
version=6.0.22
|
||||
version=6.0.19
|
||||
|
||||
org.gradle.caching=true
|
||||
org.gradle.jvmargs=-Xmx2048m
|
||||
|
||||
@@ -6,7 +6,6 @@ tasks.findByName("dokkaHtmlPartial")?.configure {
|
||||
classpath.from(sourceSets["main"].runtimeClasspath)
|
||||
externalDocumentationLink {
|
||||
url.set(new URL("https://docs.spring.io/spring-framework/docs/current/javadoc-api/"))
|
||||
packageListUrl.set(new URL("https://docs.spring.io/spring-framework/docs/current/javadoc-api/element-list"))
|
||||
}
|
||||
externalDocumentationLink {
|
||||
url.set(new URL("https://projectreactor.io/docs/core/release/api/"))
|
||||
@@ -22,7 +21,6 @@ tasks.findByName("dokkaHtmlPartial")?.configure {
|
||||
}
|
||||
externalDocumentationLink {
|
||||
url.set(new URL("https://javadoc.io/doc/jakarta.servlet/jakarta.servlet-api/latest/"))
|
||||
packageListUrl.set(new URL("https://javadoc.io/doc/jakarta.servlet/jakarta.servlet-api/latest/element-list"))
|
||||
}
|
||||
externalDocumentationLink {
|
||||
url.set(new URL("https://javadoc.io/static/io.rsocket/rsocket-core/1.1.1/"))
|
||||
|
||||
+1
-1
@@ -63,7 +63,7 @@ class EnableCachingIntegrationTests {
|
||||
// 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)
|
||||
.withMessageContaining("AspectJCachingConfiguration");
|
||||
.withMessageContaining("AspectJCachingConfiguration");
|
||||
}
|
||||
|
||||
|
||||
|
||||
Vendored
+2
-1
@@ -542,7 +542,8 @@ public class EnvironmentSystemIntegrationTests {
|
||||
{
|
||||
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.getEnvironment().setRequiredProperties("foo", "bar");
|
||||
assertThatExceptionOfType(MissingRequiredPropertiesException.class).isThrownBy(ctx::refresh);
|
||||
assertThatExceptionOfType(MissingRequiredPropertiesException.class).isThrownBy(
|
||||
ctx::refresh);
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
+3
-2
@@ -98,8 +98,9 @@ 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().isThrownBy(ctx::refresh)
|
||||
.withMessageContaining("AspectJJtaTransactionManagementConfiguration");
|
||||
assertThatException()
|
||||
.isThrownBy(ctx::refresh)
|
||||
.withMessageContaining("AspectJJtaTransactionManagementConfiguration");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+3
-3
@@ -7,8 +7,8 @@ pluginManagement {
|
||||
}
|
||||
|
||||
plugins {
|
||||
id "com.gradle.develocity" version "3.17.2"
|
||||
id "io.spring.ge.conventions" version "0.0.17"
|
||||
id "com.gradle.enterprise" version "3.12.6"
|
||||
id "io.spring.ge.conventions" version "0.0.13"
|
||||
}
|
||||
|
||||
include "spring-aop"
|
||||
@@ -45,7 +45,7 @@ rootProject.children.each {project ->
|
||||
}
|
||||
|
||||
settings.gradle.projectsLoaded {
|
||||
develocity {
|
||||
gradleEnterprise {
|
||||
buildScan {
|
||||
File buildDir = settings.gradle.rootProject
|
||||
.getLayout().getBuildDirectory().getAsFile().get()
|
||||
|
||||
+10
-45
@@ -18,7 +18,6 @@ package org.springframework.aop.aspectj;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.Arrays;
|
||||
@@ -42,7 +41,6 @@ import org.aspectj.weaver.tools.PointcutParameter;
|
||||
import org.aspectj.weaver.tools.PointcutParser;
|
||||
import org.aspectj.weaver.tools.PointcutPrimitive;
|
||||
import org.aspectj.weaver.tools.ShadowMatch;
|
||||
import org.aspectj.weaver.tools.UnsupportedPointcutPrimitiveException;
|
||||
|
||||
import org.springframework.aop.ClassFilter;
|
||||
import org.springframework.aop.IntroductionAwareMethodMatcher;
|
||||
@@ -86,8 +84,6 @@ import org.springframework.util.StringUtils;
|
||||
public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
implements ClassFilter, IntroductionAwareMethodMatcher, BeanFactoryAware {
|
||||
|
||||
private static final String AJC_MAGIC = "ajc$";
|
||||
|
||||
private static final Set<PointcutPrimitive> SUPPORTED_PRIMITIVES = Set.of(
|
||||
PointcutPrimitive.EXECUTION,
|
||||
PointcutPrimitive.ARGS,
|
||||
@@ -105,8 +101,6 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
@Nullable
|
||||
private Class<?> pointcutDeclarationScope;
|
||||
|
||||
private boolean aspectCompiledByAjc;
|
||||
|
||||
private String[] pointcutParameterNames = new String[0];
|
||||
|
||||
private Class<?>[] pointcutParameterTypes = new Class<?>[0];
|
||||
@@ -120,8 +114,6 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
@Nullable
|
||||
private transient PointcutExpression pointcutExpression;
|
||||
|
||||
private transient boolean pointcutParsingFailed = false;
|
||||
|
||||
private transient Map<Method, ShadowMatch> shadowMatchCache = new ConcurrentHashMap<>(32);
|
||||
|
||||
|
||||
@@ -138,7 +130,7 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
* @param paramTypes the parameter types for the pointcut
|
||||
*/
|
||||
public AspectJExpressionPointcut(Class<?> declarationScope, String[] paramNames, Class<?>[] paramTypes) {
|
||||
setPointcutDeclarationScope(declarationScope);
|
||||
this.pointcutDeclarationScope = declarationScope;
|
||||
if (paramNames.length != paramTypes.length) {
|
||||
throw new IllegalStateException(
|
||||
"Number of pointcut parameter names must match number of pointcut parameter types");
|
||||
@@ -153,7 +145,6 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
*/
|
||||
public void setPointcutDeclarationScope(Class<?> pointcutDeclarationScope) {
|
||||
this.pointcutDeclarationScope = pointcutDeclarationScope;
|
||||
this.aspectCompiledByAjc = compiledByAjc(pointcutDeclarationScope);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,30 +169,25 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
|
||||
@Override
|
||||
public ClassFilter getClassFilter() {
|
||||
checkExpression();
|
||||
obtainPointcutExpression();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodMatcher getMethodMatcher() {
|
||||
checkExpression();
|
||||
obtainPointcutExpression();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check whether this pointcut is ready to match.
|
||||
* Check whether this pointcut is ready to match,
|
||||
* lazily building the underlying AspectJ pointcut expression.
|
||||
*/
|
||||
private void checkExpression() {
|
||||
private PointcutExpression obtainPointcutExpression() {
|
||||
if (getExpression() == null) {
|
||||
throw new IllegalStateException("Must set property 'expression' before attempting to match");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazily build the underlying AspectJ pointcut expression.
|
||||
*/
|
||||
private PointcutExpression obtainPointcutExpression() {
|
||||
if (this.pointcutExpression == null) {
|
||||
this.pointcutClassLoader = determinePointcutClassLoader();
|
||||
this.pointcutExpression = buildPointcutExpression(this.pointcutClassLoader);
|
||||
@@ -278,18 +264,10 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
|
||||
@Override
|
||||
public boolean matches(Class<?> targetClass) {
|
||||
if (this.pointcutParsingFailed) {
|
||||
// Pointcut parsing failed before below -> avoid trying again.
|
||||
return false;
|
||||
}
|
||||
if (this.aspectCompiledByAjc && compiledByAjc(targetClass)) {
|
||||
// ajc-compiled aspect class for ajc-compiled target class -> already weaved.
|
||||
return false;
|
||||
}
|
||||
|
||||
PointcutExpression pointcutExpression = obtainPointcutExpression();
|
||||
try {
|
||||
try {
|
||||
return obtainPointcutExpression().couldMatchJoinPointsInType(targetClass);
|
||||
return pointcutExpression.couldMatchJoinPointsInType(targetClass);
|
||||
}
|
||||
catch (ReflectionWorldException ex) {
|
||||
logger.debug("PointcutExpression matching rejected target class - trying fallback expression", ex);
|
||||
@@ -300,12 +278,6 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IllegalArgumentException | IllegalStateException | UnsupportedPointcutPrimitiveException ex) {
|
||||
this.pointcutParsingFailed = true;
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Pointcut parser rejected expression [" + getExpression() + "]: " + ex);
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
logger.debug("PointcutExpression matching rejected target class", ex);
|
||||
}
|
||||
@@ -314,6 +286,7 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
|
||||
@Override
|
||||
public boolean matches(Method method, Class<?> targetClass, boolean hasIntroductions) {
|
||||
obtainPointcutExpression();
|
||||
ShadowMatch shadowMatch = getTargetShadowMatch(method, targetClass);
|
||||
|
||||
// Special handling for this, target, @this, @target, @annotation
|
||||
@@ -351,6 +324,7 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
|
||||
@Override
|
||||
public boolean matches(Method method, Class<?> targetClass, Object... args) {
|
||||
obtainPointcutExpression();
|
||||
ShadowMatch shadowMatch = getTargetShadowMatch(method, targetClass);
|
||||
|
||||
// Bind Spring AOP proxy to AspectJ "this" and Spring AOP target to AspectJ target,
|
||||
@@ -539,15 +513,6 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
return shadowMatch;
|
||||
}
|
||||
|
||||
private static boolean compiledByAjc(Class<?> clazz) {
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
if (field.getName().startsWith(AJC_MAGIC)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object other) {
|
||||
|
||||
+30
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,6 +18,7 @@ package org.springframework.aop.aspectj.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
import java.util.StringTokenizer;
|
||||
@@ -55,6 +56,8 @@ import org.springframework.lang.Nullable;
|
||||
*/
|
||||
public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFactory {
|
||||
|
||||
private static final String AJC_MAGIC = "ajc$";
|
||||
|
||||
private static final Class<?>[] ASPECTJ_ANNOTATION_CLASSES = new Class<?>[] {
|
||||
Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class};
|
||||
|
||||
@@ -65,11 +68,37 @@ public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFac
|
||||
protected final ParameterNameDiscoverer parameterNameDiscoverer = new AspectJAnnotationParameterNameDiscoverer();
|
||||
|
||||
|
||||
/**
|
||||
* We consider something to be an AspectJ aspect suitable for use by the Spring AOP system
|
||||
* if it has the @Aspect annotation, and was not compiled by ajc. The reason for this latter test
|
||||
* is that aspects written in the code-style (AspectJ language) also have the annotation present
|
||||
* when compiled by ajc with the -1.5 flag, yet they cannot be consumed by Spring AOP.
|
||||
*/
|
||||
@Override
|
||||
public boolean isAspect(Class<?> clazz) {
|
||||
return (hasAspectAnnotation(clazz) && !compiledByAjc(clazz));
|
||||
}
|
||||
|
||||
private boolean hasAspectAnnotation(Class<?> clazz) {
|
||||
return (AnnotationUtils.findAnnotation(clazz, Aspect.class) != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* We need to detect this as "code-style" AspectJ aspects should not be
|
||||
* interpreted by Spring AOP.
|
||||
*/
|
||||
private boolean compiledByAjc(Class<?> clazz) {
|
||||
// The AJTypeSystem goes to great lengths to provide a uniform appearance between code-style and
|
||||
// annotation-style aspects. Therefore there is no 'clean' way to tell them apart. Here we rely on
|
||||
// an implementation detail of the AspectJ compiler.
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
if (field.getName().startsWith(AJC_MAGIC)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(Class<?> aspectClass) throws AopConfigException {
|
||||
AjType<?> ajType = AjTypeSystem.getAjType(aspectClass);
|
||||
@@ -86,7 +115,6 @@ public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFac
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find and return the first AspectJ annotation on the given method
|
||||
* (there <i>should</i> only be one anyway...).
|
||||
|
||||
+5
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -124,16 +124,10 @@ public class AspectMetadata implements Serializable {
|
||||
* Extract contents from String of form {@code pertarget(contents)}.
|
||||
*/
|
||||
private String findPerClause(Class<?> aspectClass) {
|
||||
Aspect ann = aspectClass.getAnnotation(Aspect.class);
|
||||
if (ann == null) {
|
||||
return "";
|
||||
}
|
||||
String value = ann.value();
|
||||
int beginIndex = value.indexOf('(');
|
||||
if (beginIndex < 0) {
|
||||
return "";
|
||||
}
|
||||
return value.substring(beginIndex + 1, value.length() - 1);
|
||||
String str = aspectClass.getAnnotation(Aspect.class).value();
|
||||
int beginIndex = str.indexOf('(') + 1;
|
||||
int endIndex = str.length() - 1;
|
||||
return str.substring(beginIndex, endIndex);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+19
-31
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -22,12 +22,9 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.aspectj.lang.reflect.PerClauseKind;
|
||||
|
||||
import org.springframework.aop.Advisor;
|
||||
import org.springframework.aop.framework.AopConfigException;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -43,8 +40,6 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public class BeanFactoryAspectJAdvisorsBuilder {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(BeanFactoryAspectJAdvisorsBuilder.class);
|
||||
|
||||
private final ListableBeanFactory beanFactory;
|
||||
|
||||
private final AspectJAdvisorFactory advisorFactory;
|
||||
@@ -107,37 +102,30 @@ public class BeanFactoryAspectJAdvisorsBuilder {
|
||||
continue;
|
||||
}
|
||||
if (this.advisorFactory.isAspect(beanType)) {
|
||||
try {
|
||||
AspectMetadata amd = new AspectMetadata(beanType, beanName);
|
||||
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
|
||||
MetadataAwareAspectInstanceFactory factory =
|
||||
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
|
||||
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
|
||||
if (this.beanFactory.isSingleton(beanName)) {
|
||||
this.advisorsCache.put(beanName, classAdvisors);
|
||||
}
|
||||
else {
|
||||
this.aspectFactoryCache.put(beanName, factory);
|
||||
}
|
||||
advisors.addAll(classAdvisors);
|
||||
aspectNames.add(beanName);
|
||||
AspectMetadata amd = new AspectMetadata(beanType, beanName);
|
||||
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
|
||||
MetadataAwareAspectInstanceFactory factory =
|
||||
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
|
||||
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
|
||||
if (this.beanFactory.isSingleton(beanName)) {
|
||||
this.advisorsCache.put(beanName, classAdvisors);
|
||||
}
|
||||
else {
|
||||
// Per target or per this.
|
||||
if (this.beanFactory.isSingleton(beanName)) {
|
||||
throw new IllegalArgumentException("Bean with name '" + beanName +
|
||||
"' is a singleton, but aspect instantiation model is not singleton");
|
||||
}
|
||||
MetadataAwareAspectInstanceFactory factory =
|
||||
new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
|
||||
this.aspectFactoryCache.put(beanName, factory);
|
||||
advisors.addAll(this.advisorFactory.getAdvisors(factory));
|
||||
}
|
||||
aspectNames.add(beanName);
|
||||
advisors.addAll(classAdvisors);
|
||||
}
|
||||
catch (IllegalArgumentException | IllegalStateException | AopConfigException ex) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Ignoring incompatible aspect [" + beanType.getName() + "]: " + ex);
|
||||
else {
|
||||
// Per target or per this.
|
||||
if (this.beanFactory.isSingleton(beanName)) {
|
||||
throw new IllegalArgumentException("Bean with name '" + beanName +
|
||||
"' is a singleton, but aspect instantiation model is not singleton");
|
||||
}
|
||||
MetadataAwareAspectInstanceFactory factory =
|
||||
new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
|
||||
this.aspectFactoryCache.put(beanName, factory);
|
||||
advisors.addAll(this.advisorFactory.getAdvisors(factory));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+14
-25
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -50,7 +50,6 @@ import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.converter.ConvertingComparator;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.ReflectionUtils.MethodFilter;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -134,19 +133,17 @@ public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFacto
|
||||
|
||||
List<Advisor> advisors = new ArrayList<>();
|
||||
for (Method method : getAdvisorMethods(aspectClass)) {
|
||||
if (method.equals(ClassUtils.getMostSpecificMethod(method, aspectClass))) {
|
||||
// Prior to Spring Framework 5.2.7, advisors.size() was supplied as the declarationOrderInAspect
|
||||
// to getAdvisor(...) to represent the "current position" in the declared methods list.
|
||||
// However, since Java 7 the "current position" is not valid since the JDK no longer
|
||||
// returns declared methods in the order in which they are declared in the source code.
|
||||
// Thus, we now hard code the declarationOrderInAspect to 0 for all advice methods
|
||||
// discovered via reflection in order to support reliable advice ordering across JVM launches.
|
||||
// Specifically, a value of 0 aligns with the default value used in
|
||||
// AspectJPrecedenceComparator.getAspectDeclarationOrder(Advisor).
|
||||
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);
|
||||
if (advisor != null) {
|
||||
advisors.add(advisor);
|
||||
}
|
||||
// Prior to Spring Framework 5.2.7, advisors.size() was supplied as the declarationOrderInAspect
|
||||
// to getAdvisor(...) to represent the "current position" in the declared methods list.
|
||||
// However, since Java 7 the "current position" is not valid since the JDK no longer
|
||||
// returns declared methods in the order in which they are declared in the source code.
|
||||
// Thus, we now hard code the declarationOrderInAspect to 0 for all advice methods
|
||||
// discovered via reflection in order to support reliable advice ordering across JVM launches.
|
||||
// Specifically, a value of 0 aligns with the default value used in
|
||||
// AspectJPrecedenceComparator.getAspectDeclarationOrder(Advisor).
|
||||
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);
|
||||
if (advisor != null) {
|
||||
advisors.add(advisor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,16 +210,8 @@ public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFacto
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
|
||||
this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
|
||||
}
|
||||
catch (IllegalArgumentException | IllegalStateException ex) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Ignoring incompatible advice method: " + candidateAdviceMethod, ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
|
||||
this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -368,7 +368,8 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
private void validateIntroductionAdvisor(IntroductionAdvisor advisor) {
|
||||
advisor.validateInterfaces();
|
||||
// If the advisor passed validation, we can make the change.
|
||||
for (Class<?> ifc : advisor.getInterfaces()) {
|
||||
Class<?>[] ifcs = advisor.getInterfaces();
|
||||
for (Class<?> ifc : ifcs) {
|
||||
addInterface(ifc);
|
||||
}
|
||||
}
|
||||
|
||||
+19
-16
@@ -23,6 +23,8 @@ import java.util.Map;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.aspectj.weaver.tools.PointcutPrimitive;
|
||||
import org.aspectj.weaver.tools.UnsupportedPointcutPrimitiveException;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import test.annotation.EmptySpringAnnotation;
|
||||
@@ -39,13 +41,14 @@ import org.springframework.beans.testfixture.beans.TestBean;
|
||||
import org.springframework.beans.testfixture.beans.subpkg.DeepBean;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
|
||||
/**
|
||||
* @author Rob Harrop
|
||||
* @author Rod Johnson
|
||||
* @author Chris Beams
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public class AspectJExpressionPointcutTests {
|
||||
|
||||
@@ -171,25 +174,25 @@ public class AspectJExpressionPointcutTests {
|
||||
@Test
|
||||
public void testFriendlyErrorOnNoLocationClassMatching() {
|
||||
AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> pc.getClassFilter().matches(ITestBean.class))
|
||||
.withMessageContaining("expression");
|
||||
assertThatIllegalStateException().isThrownBy(() ->
|
||||
pc.matches(ITestBean.class))
|
||||
.withMessageContaining("expression");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFriendlyErrorOnNoLocation2ArgMatching() {
|
||||
AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> pc.getMethodMatcher().matches(getAge, ITestBean.class))
|
||||
.withMessageContaining("expression");
|
||||
assertThatIllegalStateException().isThrownBy(() ->
|
||||
pc.matches(getAge, ITestBean.class))
|
||||
.withMessageContaining("expression");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFriendlyErrorOnNoLocation3ArgMatching() {
|
||||
AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> pc.getMethodMatcher().matches(getAge, ITestBean.class, (Object[]) null))
|
||||
.withMessageContaining("expression");
|
||||
assertThatIllegalStateException().isThrownBy(() ->
|
||||
pc.matches(getAge, ITestBean.class, (Object[]) null))
|
||||
.withMessageContaining("expression");
|
||||
}
|
||||
|
||||
|
||||
@@ -206,10 +209,8 @@ public class AspectJExpressionPointcutTests {
|
||||
// not currently testable in a reliable fashion
|
||||
//assertDoesNotMatchStringClass(classFilter);
|
||||
|
||||
assertThat(methodMatcher.matches(setSomeNumber, TestBean.class, 12D))
|
||||
.as("Should match with setSomeNumber with Double input").isTrue();
|
||||
assertThat(methodMatcher.matches(setSomeNumber, TestBean.class, 11))
|
||||
.as("Should not match setSomeNumber with Integer input").isFalse();
|
||||
assertThat(methodMatcher.matches(setSomeNumber, TestBean.class, 12D)).as("Should match with setSomeNumber with Double input").isTrue();
|
||||
assertThat(methodMatcher.matches(setSomeNumber, TestBean.class, 11)).as("Should not match setSomeNumber with Integer input").isFalse();
|
||||
assertThat(methodMatcher.matches(getAge, TestBean.class)).as("Should not match getAge").isFalse();
|
||||
assertThat(methodMatcher.isRuntime()).as("Should be a runtime match").isTrue();
|
||||
}
|
||||
@@ -244,7 +245,7 @@ public class AspectJExpressionPointcutTests {
|
||||
@Test
|
||||
public void testInvalidExpression() {
|
||||
String expression = "execution(void org.springframework.beans.testfixture.beans.TestBean.setSomeNumber(Number) && args(Double)";
|
||||
assertThat(getPointcut(expression).getClassFilter().matches(Object.class)).isFalse();
|
||||
assertThatIllegalArgumentException().isThrownBy(getPointcut(expression)::getClassFilter); // call to getClassFilter forces resolution
|
||||
}
|
||||
|
||||
private TestBean getAdvisedProxy(String pointcutExpression, CallCountingInterceptor interceptor) {
|
||||
@@ -274,7 +275,9 @@ public class AspectJExpressionPointcutTests {
|
||||
@Test
|
||||
public void testWithUnsupportedPointcutPrimitive() {
|
||||
String expression = "call(int org.springframework.beans.testfixture.beans.TestBean.getAge())";
|
||||
assertThat(getPointcut(expression).getClassFilter().matches(Object.class)).isFalse();
|
||||
assertThatExceptionOfType(UnsupportedPointcutPrimitiveException.class)
|
||||
.isThrownBy(() -> getPointcut(expression).getClassFilter()) // call to getClassFilter forces resolution...
|
||||
.satisfies(ex -> assertThat(ex.getUnsupportedPrimitive()).isEqualTo(PointcutPrimitive.CALL));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+14
-16
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -38,6 +38,7 @@ import org.aspectj.lang.annotation.DeclareParents;
|
||||
import org.aspectj.lang.annotation.DeclarePrecedence;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aop.Advisor;
|
||||
@@ -83,15 +84,15 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
@Test
|
||||
void rejectsPerCflowAspect() {
|
||||
assertThatExceptionOfType(AopConfigException.class)
|
||||
.isThrownBy(() -> getAdvisorFactory().getAdvisors(aspectInstanceFactory(new PerCflowAspect(), "someBean")))
|
||||
.withMessageContaining("PERCFLOW");
|
||||
.isThrownBy(() -> getAdvisorFactory().getAdvisors(aspectInstanceFactory(new PerCflowAspect(), "someBean")))
|
||||
.withMessageContaining("PERCFLOW");
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsPerCflowBelowAspect() {
|
||||
assertThatExceptionOfType(AopConfigException.class)
|
||||
.isThrownBy(() -> getAdvisorFactory().getAdvisors(aspectInstanceFactory(new PerCflowBelowAspect(), "someBean")))
|
||||
.withMessageContaining("PERCFLOWBELOW");
|
||||
.isThrownBy(() -> getAdvisorFactory().getAdvisors(aspectInstanceFactory(new PerCflowBelowAspect(), "someBean")))
|
||||
.withMessageContaining("PERCFLOWBELOW");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -362,7 +363,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
assertThat(lockable.locked()).as("Already locked").isTrue();
|
||||
lockable.lock();
|
||||
assertThat(lockable.locked()).as("Real target ignores locking").isTrue();
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(lockable::unlock);
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> lockable.unlock());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -388,7 +389,9 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
assertThat(lockable.locked()).isTrue();
|
||||
}
|
||||
|
||||
// TODO: Why does this test fail? It hasn't been run before, so it maybe never actually passed...
|
||||
@Test
|
||||
@Disabled
|
||||
void introductionWithArgumentBinding() {
|
||||
TestBean target = new TestBean();
|
||||
|
||||
@@ -645,7 +648,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
static class NamedPointcutAspectWithFQN {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final ITestBean fieldThatShouldBeIgnoredBySpringAtAspectJProcessing = new TestBean();
|
||||
private ITestBean fieldThatShouldBeIgnoredBySpringAtAspectJProcessing = new TestBean();
|
||||
|
||||
@Around("org.springframework.aop.aspectj.annotation.AbstractAspectJAdvisorFactoryTests.CommonPointcuts.getAge()()")
|
||||
int changeReturnValue(ProceedingJoinPoint pjp) {
|
||||
@@ -762,7 +765,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
|
||||
|
||||
@Aspect
|
||||
static class DoublingAspect {
|
||||
class DoublingAspect {
|
||||
|
||||
@Around("execution(* getAge())")
|
||||
public Object doubleAge(ProceedingJoinPoint pjp) throws Throwable {
|
||||
@@ -770,14 +773,8 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Aspect
|
||||
static class IncrementingAspect extends DoublingAspect {
|
||||
|
||||
@Override
|
||||
public Object doubleAge(ProceedingJoinPoint pjp) throws Throwable {
|
||||
return ((int) pjp.proceed()) * 2;
|
||||
}
|
||||
class IncrementingAspect extends DoublingAspect {
|
||||
|
||||
@Around("execution(* getAge())")
|
||||
public int incrementAge(ProceedingJoinPoint pjp) throws Throwable {
|
||||
@@ -786,6 +783,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Aspect
|
||||
private static class InvocationTrackingAspect {
|
||||
|
||||
@@ -1085,7 +1083,7 @@ class PerThisAspect {
|
||||
|
||||
// Just to check that this doesn't cause problems with introduction processing
|
||||
@SuppressWarnings("unused")
|
||||
private final ITestBean fieldThatShouldBeIgnoredBySpringAtAspectJProcessing = new TestBean();
|
||||
private ITestBean fieldThatShouldBeIgnoredBySpringAtAspectJProcessing = new TestBean();
|
||||
|
||||
@Around("execution(int *.getAge())")
|
||||
int returnCountAsAge() {
|
||||
|
||||
+2
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2015 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,13 +22,12 @@ import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
|
||||
/**
|
||||
* @author Adrian Colyer
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public class AutoProxyWithCodeStyleAspectsTests {
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("resource")
|
||||
public void noAutoProxyingOfAjcCompiledAspects() {
|
||||
public void noAutoproxyingOfAjcCompiledAspects() {
|
||||
new ClassPathXmlApplicationContext("org/springframework/aop/aspectj/autoproxy/ajcAutoproxyTests.xml");
|
||||
}
|
||||
|
||||
|
||||
+1
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2015 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,10 +20,6 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
|
||||
/**
|
||||
* @author Ramnivas Laddad
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public class SpringConfiguredWithAutoProxyingTests {
|
||||
|
||||
@Test
|
||||
|
||||
+4
-17
@@ -2,29 +2,16 @@
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:aop="http://www.springframework.org/schema/aop"
|
||||
xmlns:cache="http://www.springframework.org/schema/cache"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-2.0.xsd
|
||||
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-2.0.xsd
|
||||
http://www.springframework.org/schema/cache https://www.springframework.org/schema/cache/spring-cache-3.1.xsd
|
||||
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context-2.5.xsd">
|
||||
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
|
||||
|
||||
<aop:aspectj-autoproxy/>
|
||||
|
||||
<context:spring-configured/>
|
||||
|
||||
<cache:annotation-driven mode="aspectj"/>
|
||||
|
||||
<bean id="cacheManager" class="org.springframework.cache.support.NoOpCacheManager"/>
|
||||
|
||||
<bean id="myAspect" class="org.springframework.aop.aspectj.autoproxy.CodeStyleAspect" factory-method="aspectOf">
|
||||
<bean id="myAspect" class="org.springframework.aop.aspectj.autoproxy.CodeStyleAspect"
|
||||
factory-method="aspectOf">
|
||||
<property name="foo" value="bar"/>
|
||||
</bean>
|
||||
|
||||
<bean id="otherBean" class="org.springframework.beans.factory.aspectj.ShouldBeConfiguredBySpring"/>
|
||||
|
||||
<bean id="yetAnotherBean" class="org.springframework.beans.factory.aspectj.ShouldBeConfiguredBySpring"/>
|
||||
|
||||
<bean id="configuredBean" class="org.springframework.beans.factory.aspectj.ShouldBeConfiguredBySpring" lazy-init="true"/>
|
||||
<bean id="otherBean" class="java.lang.Object"/>
|
||||
|
||||
</beans>
|
||||
|
||||
Vendored
+2
-1
@@ -24,7 +24,8 @@
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="defaultCache" class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
|
||||
<bean id="defaultCache"
|
||||
class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
|
||||
<property name="name" value="default"/>
|
||||
</bean>
|
||||
|
||||
|
||||
+4
-2
@@ -7,10 +7,12 @@
|
||||
http://www.springframework.org/schema/task
|
||||
https://www.springframework.org/schema/task/spring-task.xsd">
|
||||
|
||||
<task:annotation-driven mode="aspectj" executor="testExecutor" exception-handler="testExceptionHandler"/>
|
||||
<task:annotation-driven mode="aspectj" executor="testExecutor"
|
||||
exception-handler="testExceptionHandler"/>
|
||||
|
||||
<task:executor id="testExecutor"/>
|
||||
|
||||
<bean id="testExceptionHandler" class="org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler"/>
|
||||
<bean id="testExceptionHandler"
|
||||
class="org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler"/>
|
||||
|
||||
</beans>
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -818,11 +818,11 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
||||
/**
|
||||
* This implementation attempts to query the FactoryBean's generic parameter metadata
|
||||
* if present to determine the object type. If not present, i.e. the FactoryBean is
|
||||
* declared as a raw type, it checks the FactoryBean's {@code getObjectType} method
|
||||
* declared as a raw type, checks the FactoryBean's {@code getObjectType} method
|
||||
* on a plain instance of the FactoryBean, without bean properties applied yet.
|
||||
* If this doesn't return a type yet and {@code allowInit} is {@code true}, full
|
||||
* creation of the FactoryBean is attempted as fallback (through delegation to the
|
||||
* superclass implementation).
|
||||
* If this doesn't return a type yet, and {@code allowInit} is {@code true} a
|
||||
* full creation of the FactoryBean is used as fallback (through delegation to the
|
||||
* superclass's implementation).
|
||||
* <p>The shortcut check for a FactoryBean is only applied in case of a singleton
|
||||
* FactoryBean. If the FactoryBean instance itself is not kept as singleton,
|
||||
* it will be fully created to check the type of its exposed object.
|
||||
|
||||
+2
-2
@@ -1053,7 +1053,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
|
||||
@Override
|
||||
public void setApplicationStartup(ApplicationStartup applicationStartup) {
|
||||
Assert.notNull(applicationStartup, "ApplicationStartup must not be null");
|
||||
Assert.notNull(applicationStartup, "applicationStartup must not be null");
|
||||
this.applicationStartup = applicationStartup;
|
||||
}
|
||||
|
||||
@@ -1649,7 +1649,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
* already. The implementation is allowed to instantiate the target factory bean if
|
||||
* {@code allowInit} is {@code true} and the type cannot be determined another way;
|
||||
* otherwise it is restricted to introspecting signatures and related metadata.
|
||||
* <p>If no {@link FactoryBean#OBJECT_TYPE_ATTRIBUTE} is set on the bean definition
|
||||
* <p>If no {@link FactoryBean#OBJECT_TYPE_ATTRIBUTE} if set on the bean definition
|
||||
* and {@code allowInit} is {@code true}, the default implementation will create
|
||||
* the FactoryBean via {@code getBean} to call its {@code getObjectType} method.
|
||||
* Subclasses are encouraged to optimize this, typically by inspecting the generic
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -41,8 +41,6 @@ import org.springframework.beans.propertyeditors.CustomDateEditor;
|
||||
import org.springframework.beans.testfixture.beans.DerivedTestBean;
|
||||
import org.springframework.beans.testfixture.beans.ITestBean;
|
||||
import org.springframework.beans.testfixture.beans.TestBean;
|
||||
import org.springframework.cglib.proxy.Enhancer;
|
||||
import org.springframework.cglib.proxy.MethodInterceptor;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceEditor;
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -53,7 +51,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
import static org.assertj.core.api.SoftAssertions.assertSoftly;
|
||||
|
||||
/**
|
||||
* Tests for {@link BeanUtils}.
|
||||
* Unit tests for {@link BeanUtils}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Rob Harrop
|
||||
@@ -323,13 +321,12 @@ class BeanUtilsTests {
|
||||
Order original = new Order("test", List.of("foo", "bar"));
|
||||
|
||||
// Create a Proxy that loses the generic type information for the getLineItems() method.
|
||||
OrderSummary proxy = (OrderSummary) Proxy.newProxyInstance(getClass().getClassLoader(),
|
||||
new Class<?>[] {OrderSummary.class}, new OrderInvocationHandler(original));
|
||||
OrderSummary proxy = proxyOrder(original);
|
||||
assertThat(OrderSummary.class.getDeclaredMethod("getLineItems").toGenericString())
|
||||
.contains("java.util.List<java.lang.String>");
|
||||
.contains("java.util.List<java.lang.String>");
|
||||
assertThat(proxy.getClass().getDeclaredMethod("getLineItems").toGenericString())
|
||||
.contains("java.util.List")
|
||||
.doesNotContain("<java.lang.String>");
|
||||
.contains("java.util.List")
|
||||
.doesNotContain("<java.lang.String>");
|
||||
|
||||
// Ensure that our custom Proxy works as expected.
|
||||
assertThat(proxy.getId()).isEqualTo("test");
|
||||
@@ -342,23 +339,6 @@ class BeanUtilsTests {
|
||||
assertThat(target.getLineItems()).containsExactly("foo", "bar");
|
||||
}
|
||||
|
||||
@Test // gh-32888
|
||||
public void copyPropertiesWithGenericCglibClass() {
|
||||
Enhancer enhancer = new Enhancer();
|
||||
enhancer.setSuperclass(User.class);
|
||||
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> proxy.invokeSuper(obj, args));
|
||||
User user = (User) enhancer.create();
|
||||
user.setId(1);
|
||||
user.setName("proxy");
|
||||
user.setAddress("addr");
|
||||
|
||||
User target = new User();
|
||||
BeanUtils.copyProperties(user, target);
|
||||
assertThat(target.getId()).isEqualTo(user.getId());
|
||||
assertThat(target.getName()).isEqualTo(user.getName());
|
||||
assertThat(target.getAddress()).isEqualTo(user.getAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
void copyPropertiesWithEditable() throws Exception {
|
||||
TestBean tb = new TestBean();
|
||||
@@ -538,7 +518,6 @@ class BeanUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class IntegerHolder {
|
||||
|
||||
@@ -553,7 +532,6 @@ class BeanUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class WildcardListHolder1 {
|
||||
|
||||
@@ -568,7 +546,6 @@ class BeanUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class WildcardListHolder2 {
|
||||
|
||||
@@ -583,7 +560,6 @@ class BeanUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class NumberUpperBoundedWildcardListHolder {
|
||||
|
||||
@@ -598,7 +574,6 @@ class BeanUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class NumberListHolder {
|
||||
|
||||
@@ -613,7 +588,6 @@ class BeanUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class IntegerListHolder1 {
|
||||
|
||||
@@ -628,7 +602,6 @@ class BeanUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class IntegerListHolder2 {
|
||||
|
||||
@@ -643,7 +616,6 @@ class BeanUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class LongListHolder {
|
||||
|
||||
@@ -824,7 +796,6 @@ class BeanUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class BeanWithNullableTypes {
|
||||
|
||||
private Integer counter;
|
||||
@@ -855,7 +826,6 @@ class BeanUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class BeanWithPrimitiveTypes {
|
||||
|
||||
private boolean flag;
|
||||
@@ -868,6 +838,7 @@ class BeanUtilsTests {
|
||||
private char character;
|
||||
private String text;
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public BeanWithPrimitiveTypes(boolean flag, byte byteCount, short shortCount, int intCount, long longCount,
|
||||
float floatCount, double doubleCount, char character, String text) {
|
||||
@@ -918,8 +889,8 @@ class BeanUtilsTests {
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class PrivateBeanWithPrivateConstructor {
|
||||
|
||||
@@ -927,14 +898,13 @@ class BeanUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class Order {
|
||||
|
||||
private String id;
|
||||
|
||||
private List<String> lineItems;
|
||||
|
||||
|
||||
Order() {
|
||||
}
|
||||
|
||||
@@ -965,7 +935,6 @@ class BeanUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private interface OrderSummary {
|
||||
|
||||
String getId();
|
||||
@@ -974,10 +943,17 @@ class BeanUtilsTests {
|
||||
}
|
||||
|
||||
|
||||
private OrderSummary proxyOrder(Order order) {
|
||||
return (OrderSummary) Proxy.newProxyInstance(getClass().getClassLoader(),
|
||||
new Class<?>[] { OrderSummary.class }, new OrderInvocationHandler(order));
|
||||
}
|
||||
|
||||
|
||||
private static class OrderInvocationHandler implements InvocationHandler {
|
||||
|
||||
private final Order order;
|
||||
|
||||
|
||||
OrderInvocationHandler(Order order) {
|
||||
this.order = order;
|
||||
}
|
||||
@@ -995,46 +971,4 @@ class BeanUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class GenericBaseModel<T> {
|
||||
|
||||
private T id;
|
||||
|
||||
private String name;
|
||||
|
||||
public T getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(T id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class User extends GenericBaseModel<Integer> {
|
||||
|
||||
private String address;
|
||||
|
||||
public User() {
|
||||
super();
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddress(String address) {
|
||||
this.address = address;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+5
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,12 +22,11 @@ import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.aot.hint.TypeHint.Builder;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
import org.springframework.aot.hint.annotation.ReflectiveRuntimeHintsRegistrar;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* {@link RuntimeHintsRegistrar} implementation that makes sure {@link SchedulerFactoryBean}
|
||||
* reflection hints are registered.
|
||||
* reflection entries are registered.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Stephane Nicoll
|
||||
@@ -37,11 +36,11 @@ class SchedulerFactoryBeanRuntimeHints implements RuntimeHintsRegistrar {
|
||||
|
||||
private static final String SCHEDULER_FACTORY_CLASS_NAME = "org.quartz.impl.StdSchedulerFactory";
|
||||
|
||||
private static final ReflectiveRuntimeHintsRegistrar registrar = new ReflectiveRuntimeHintsRegistrar();
|
||||
private final ReflectiveRuntimeHintsRegistrar reflectiveRegistrar = new ReflectiveRuntimeHintsRegistrar();
|
||||
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
|
||||
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
|
||||
if (!ClassUtils.isPresent(SCHEDULER_FACTORY_CLASS_NAME, classLoader)) {
|
||||
return;
|
||||
}
|
||||
@@ -49,7 +48,7 @@ class SchedulerFactoryBeanRuntimeHints implements RuntimeHintsRegistrar {
|
||||
.registerType(TypeReference.of(SCHEDULER_FACTORY_CLASS_NAME), this::typeHint)
|
||||
.registerTypes(TypeReference.listOf(ResourceLoaderClassLoadHelper.class,
|
||||
LocalTaskExecutorThreadPool.class, LocalDataSourceJobStore.class), this::typeHint);
|
||||
registrar.registerRuntimeHints(hints, LocalTaskExecutorThreadPool.class);
|
||||
this.reflectiveRegistrar.registerRuntimeHints(hints, LocalTaskExecutorThreadPool.class);
|
||||
}
|
||||
|
||||
private void typeHint(Builder typeHint) {
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -225,6 +225,7 @@ class ConfigurationClassEnhancer {
|
||||
};
|
||||
return new TransformingClassGenerator(cg, transformer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -333,7 +334,6 @@ class ConfigurationClassEnhancer {
|
||||
return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Object resolveBeanReference(Method beanMethod, Object[] beanMethodArgs,
|
||||
ConfigurableBeanFactory beanFactory, String beanName) {
|
||||
|
||||
|
||||
+12
-13
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -386,11 +386,11 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
});
|
||||
|
||||
// Detect any custom bean name generation strategy supplied through the enclosing application context
|
||||
SingletonBeanRegistry singletonRegistry = null;
|
||||
if (registry instanceof SingletonBeanRegistry sbr) {
|
||||
singletonRegistry = sbr;
|
||||
SingletonBeanRegistry sbr = null;
|
||||
if (registry instanceof SingletonBeanRegistry _sbr) {
|
||||
sbr = _sbr;
|
||||
if (!this.localBeanNameGeneratorSet) {
|
||||
BeanNameGenerator generator = (BeanNameGenerator) singletonRegistry.getSingleton(
|
||||
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(
|
||||
AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
|
||||
if (generator != null) {
|
||||
this.componentScanBeanNameGenerator = generator;
|
||||
@@ -451,8 +451,8 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
while (!candidates.isEmpty());
|
||||
|
||||
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
|
||||
if (singletonRegistry != null && !singletonRegistry.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
|
||||
singletonRegistry.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
|
||||
if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
|
||||
sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
|
||||
}
|
||||
|
||||
// Store the PropertySourceDescriptors to contribute them Ahead-of-time if necessary
|
||||
@@ -550,7 +550,6 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public PropertyValues postProcessProperties(@Nullable PropertyValues pvs, Object bean, String beanName) {
|
||||
// Inject the BeanFactory before AutowiredAnnotationBeanPostProcessor's
|
||||
// postProcessProperties method attempts to autowire other configuration beans.
|
||||
@@ -646,8 +645,8 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
}
|
||||
return mappings;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class PropertySourcesAotContribution implements BeanFactoryInitializationAotContribution {
|
||||
|
||||
@@ -744,14 +743,15 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
return nonNull.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class ConfigurationClassProxyBeanRegistrationCodeFragments extends BeanRegistrationCodeFragmentsDecorator {
|
||||
|
||||
private final Class<?> proxyClass;
|
||||
|
||||
public ConfigurationClassProxyBeanRegistrationCodeFragments(BeanRegistrationCodeFragments codeFragments, Class<?> proxyClass) {
|
||||
public ConfigurationClassProxyBeanRegistrationCodeFragments(BeanRegistrationCodeFragments codeFragments,
|
||||
Class<?> proxyClass) {
|
||||
super(codeFragments);
|
||||
this.proxyClass = proxyClass;
|
||||
}
|
||||
@@ -759,7 +759,6 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
@Override
|
||||
public CodeBlock generateSetBeanDefinitionPropertiesCode(GenerationContext generationContext,
|
||||
BeanRegistrationCode beanRegistrationCode, RootBeanDefinition beanDefinition, Predicate<String> attributeFilter) {
|
||||
|
||||
CodeBlock.Builder code = CodeBlock.builder();
|
||||
code.add(super.generateSetBeanDefinitionPropertiesCode(generationContext,
|
||||
beanRegistrationCode, beanDefinition, attributeFilter));
|
||||
@@ -772,7 +771,6 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
public CodeBlock generateInstanceSupplierCode(GenerationContext generationContext,
|
||||
BeanRegistrationCode beanRegistrationCode, Executable constructorOrFactoryMethod,
|
||||
boolean allowDirectSupplierShortcut) {
|
||||
|
||||
Executable executableToUse = proxyExecutable(generationContext.getRuntimeHints(), constructorOrFactoryMethod);
|
||||
return super.generateInstanceSupplierCode(generationContext, beanRegistrationCode,
|
||||
executableToUse, allowDirectSupplierShortcut);
|
||||
@@ -790,6 +788,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
}
|
||||
return userExecutable;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+5
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -39,8 +39,7 @@ import org.springframework.beans.factory.support.RegisteredBean;
|
||||
*/
|
||||
class ReflectiveProcessorBeanFactoryInitializationAotProcessor implements BeanFactoryInitializationAotProcessor {
|
||||
|
||||
private static final ReflectiveRuntimeHintsRegistrar registrar = new ReflectiveRuntimeHintsRegistrar();
|
||||
|
||||
private static final ReflectiveRuntimeHintsRegistrar REGISTRAR = new ReflectiveRuntimeHintsRegistrar();
|
||||
|
||||
@Override
|
||||
public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) {
|
||||
@@ -50,9 +49,7 @@ class ReflectiveProcessorBeanFactoryInitializationAotProcessor implements BeanFa
|
||||
return new ReflectiveProcessorBeanFactoryInitializationAotContribution(beanTypes);
|
||||
}
|
||||
|
||||
|
||||
private static class ReflectiveProcessorBeanFactoryInitializationAotContribution
|
||||
implements BeanFactoryInitializationAotContribution {
|
||||
private static class ReflectiveProcessorBeanFactoryInitializationAotContribution implements BeanFactoryInitializationAotContribution {
|
||||
|
||||
private final Class<?>[] types;
|
||||
|
||||
@@ -63,8 +60,9 @@ class ReflectiveProcessorBeanFactoryInitializationAotProcessor implements BeanFa
|
||||
@Override
|
||||
public void applyTo(GenerationContext generationContext, BeanFactoryInitializationCode beanFactoryInitializationCode) {
|
||||
RuntimeHints runtimeHints = generationContext.getRuntimeHints();
|
||||
registrar.registerRuntimeHints(runtimeHints, this.types);
|
||||
REGISTRAR.registerRuntimeHints(runtimeHints, this.types);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -126,7 +126,6 @@ public abstract class AbstractRefreshableApplicationContext extends AbstractAppl
|
||||
try {
|
||||
DefaultListableBeanFactory beanFactory = createBeanFactory();
|
||||
beanFactory.setSerializationId(getId());
|
||||
beanFactory.setApplicationStartup(getApplicationStartup());
|
||||
customizeBeanFactory(beanFactory);
|
||||
loadBeanDefinitions(beanFactory);
|
||||
this.beanFactory = beanFactory;
|
||||
|
||||
+4
-6
@@ -149,13 +149,13 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
|
||||
|
||||
lifecycleBeans.forEach((beanName, bean) -> {
|
||||
if (!autoStartupOnly || (bean instanceof SmartLifecycle smartLifecycle && smartLifecycle.isAutoStartup())) {
|
||||
int startupPhase = getPhase(bean);
|
||||
phases.computeIfAbsent(startupPhase,
|
||||
phase -> new LifecycleGroup(phase, this.timeoutPerShutdownPhase, lifecycleBeans, autoStartupOnly)
|
||||
int phase = getPhase(bean);
|
||||
phases.computeIfAbsent(
|
||||
phase,
|
||||
p -> new LifecycleGroup(phase, this.timeoutPerShutdownPhase, lifecycleBeans, autoStartupOnly)
|
||||
).add(beanName, bean);
|
||||
}
|
||||
});
|
||||
|
||||
if (!phases.isEmpty()) {
|
||||
phases.values().forEach(LifecycleGroup::start);
|
||||
}
|
||||
@@ -195,7 +195,6 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
|
||||
private void stopBeans() {
|
||||
Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
|
||||
Map<Integer, LifecycleGroup> phases = new HashMap<>();
|
||||
|
||||
lifecycleBeans.forEach((beanName, bean) -> {
|
||||
int shutdownPhase = getPhase(bean);
|
||||
LifecycleGroup group = phases.get(shutdownPhase);
|
||||
@@ -205,7 +204,6 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
|
||||
}
|
||||
group.add(beanName, bean);
|
||||
});
|
||||
|
||||
if (!phases.isEmpty()) {
|
||||
List<Integer> keys = new ArrayList<>(phases.keySet());
|
||||
keys.sort(Collections.reverseOrder());
|
||||
|
||||
+10
-32
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,7 +18,6 @@ package org.springframework.context.support;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Supplier;
|
||||
@@ -424,37 +423,16 @@ public class GenericApplicationContext extends AbstractApplicationContext implem
|
||||
PostProcessorRegistrationDelegate.loadBeanPostProcessors(
|
||||
this.beanFactory, SmartInstantiationAwareBeanPostProcessor.class);
|
||||
|
||||
List<String> lazyBeans = new ArrayList<>();
|
||||
|
||||
// First round: non-lazy singleton beans in definition order,
|
||||
// matching preInstantiateSingletons.
|
||||
for (String beanName : this.beanFactory.getBeanDefinitionNames()) {
|
||||
BeanDefinition bd = getBeanDefinition(beanName);
|
||||
if (bd.isSingleton() && !bd.isLazyInit()) {
|
||||
preDetermineBeanType(beanName, bpps, runtimeHints);
|
||||
}
|
||||
else {
|
||||
lazyBeans.add(beanName);
|
||||
}
|
||||
}
|
||||
|
||||
// Second round: lazy singleton beans and scoped beans.
|
||||
for (String beanName : lazyBeans) {
|
||||
preDetermineBeanType(beanName, bpps, runtimeHints);
|
||||
}
|
||||
}
|
||||
|
||||
private void preDetermineBeanType(String beanName, List<SmartInstantiationAwareBeanPostProcessor> bpps,
|
||||
RuntimeHints runtimeHints) {
|
||||
|
||||
Class<?> beanType = this.beanFactory.getType(beanName);
|
||||
if (beanType != null) {
|
||||
ClassHintUtils.registerProxyIfNecessary(beanType, runtimeHints);
|
||||
for (SmartInstantiationAwareBeanPostProcessor bpp : bpps) {
|
||||
Class<?> newBeanType = bpp.determineBeanType(beanType, beanName);
|
||||
if (newBeanType != beanType) {
|
||||
ClassHintUtils.registerProxyIfNecessary(newBeanType, runtimeHints);
|
||||
beanType = newBeanType;
|
||||
Class<?> beanType = this.beanFactory.getType(beanName);
|
||||
if (beanType != null) {
|
||||
ClassHintUtils.registerProxyIfNecessary(beanType, runtimeHints);
|
||||
for (SmartInstantiationAwareBeanPostProcessor bpp : bpps) {
|
||||
Class<?> newBeanType = bpp.determineBeanType(beanType, beanName);
|
||||
if (newBeanType != beanType) {
|
||||
ClassHintUtils.registerProxyIfNecessary(newBeanType, runtimeHints);
|
||||
beanType = newBeanType;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-28
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,10 +22,8 @@ import java.text.SimpleDateFormat;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.EnumMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.springframework.format.Formatter;
|
||||
@@ -37,14 +35,9 @@ import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A formatter for {@link java.util.Date} types.
|
||||
*
|
||||
* <p>Supports the configuration of an explicit date time pattern, timezone,
|
||||
* locale, and fallback date time patterns for lenient parsing.
|
||||
*
|
||||
* <p>Common ISO patterns for UTC instants are applied at millisecond precision.
|
||||
* Note that {@link org.springframework.format.datetime.standard.InstantFormatter}
|
||||
* is recommended for flexible UTC parsing into a {@link java.time.Instant} instead.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Juergen Hoeller
|
||||
* @author Phillip Webb
|
||||
@@ -56,23 +49,15 @@ public class DateFormatter implements Formatter<Date> {
|
||||
|
||||
private static final TimeZone UTC = TimeZone.getTimeZone("UTC");
|
||||
|
||||
// We use an EnumMap instead of Map.of(...) since the former provides better performance.
|
||||
private static final Map<ISO, String> ISO_PATTERNS;
|
||||
|
||||
private static final Map<ISO, String> ISO_FALLBACK_PATTERNS;
|
||||
|
||||
static {
|
||||
// We use an EnumMap instead of Map.of(...) since the former provides better performance.
|
||||
Map<ISO, String> formats = new EnumMap<>(ISO.class);
|
||||
formats.put(ISO.DATE, "yyyy-MM-dd");
|
||||
formats.put(ISO.TIME, "HH:mm:ss.SSSXXX");
|
||||
formats.put(ISO.DATE_TIME, "yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
|
||||
ISO_PATTERNS = Collections.unmodifiableMap(formats);
|
||||
|
||||
// Fallback format for the time part without milliseconds.
|
||||
Map<ISO, String> fallbackFormats = new EnumMap<>(ISO.class);
|
||||
fallbackFormats.put(ISO.TIME, "HH:mm:ssXXX");
|
||||
fallbackFormats.put(ISO.DATE_TIME, "yyyy-MM-dd'T'HH:mm:ssXXX");
|
||||
ISO_FALLBACK_PATTERNS = Collections.unmodifiableMap(fallbackFormats);
|
||||
}
|
||||
|
||||
|
||||
@@ -217,16 +202,8 @@ public class DateFormatter implements Formatter<Date> {
|
||||
return getDateFormat(locale).parse(text);
|
||||
}
|
||||
catch (ParseException ex) {
|
||||
Set<String> fallbackPatterns = new LinkedHashSet<>();
|
||||
String isoPattern = ISO_FALLBACK_PATTERNS.get(this.iso);
|
||||
if (isoPattern != null) {
|
||||
fallbackPatterns.add(isoPattern);
|
||||
}
|
||||
if (!ObjectUtils.isEmpty(this.fallbackPatterns)) {
|
||||
Collections.addAll(fallbackPatterns, this.fallbackPatterns);
|
||||
}
|
||||
if (!fallbackPatterns.isEmpty()) {
|
||||
for (String pattern : fallbackPatterns) {
|
||||
for (String pattern : this.fallbackPatterns) {
|
||||
try {
|
||||
DateFormat dateFormat = configureDateFormat(new SimpleDateFormat(pattern, locale));
|
||||
// Align timezone for parsing format with printing format if ISO is set.
|
||||
@@ -244,8 +221,8 @@ public class DateFormatter implements Formatter<Date> {
|
||||
}
|
||||
if (this.source != null) {
|
||||
ParseException parseException = new ParseException(
|
||||
String.format("Unable to parse date time value \"%s\" using configuration from %s", text, this.source),
|
||||
ex.getErrorOffset());
|
||||
String.format("Unable to parse date time value \"%s\" using configuration from %s", text, this.source),
|
||||
ex.getErrorOffset());
|
||||
parseException.initCause(ex);
|
||||
throw parseException;
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -41,12 +41,12 @@ public class InstantFormatter implements Formatter<Instant> {
|
||||
|
||||
@Override
|
||||
public Instant parse(String text, Locale locale) throws ParseException {
|
||||
if (!text.isEmpty() && Character.isAlphabetic(text.charAt(0))) {
|
||||
if (text.length() > 0 && Character.isAlphabetic(text.charAt(0))) {
|
||||
// assuming RFC-1123 value a la "Tue, 3 Jun 2008 11:05:30 GMT"
|
||||
return Instant.from(DateTimeFormatter.RFC_1123_DATE_TIME.parse(text));
|
||||
}
|
||||
else {
|
||||
// assuming UTC instant a la "2007-12-03T10:15:30.000Z"
|
||||
// assuming UTC instant a la "2007-12-03T10:15:30.00Z"
|
||||
return Instant.parse(text);
|
||||
}
|
||||
}
|
||||
|
||||
+6
-3
@@ -28,14 +28,17 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
*
|
||||
* @author Adrian Colyer
|
||||
* @author Chris Beams
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
class OverloadedAdviceTests {
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("resource")
|
||||
void testConfigParsingWithMismatchedAdviceMethod() {
|
||||
new ClassPathXmlApplicationContext(getClass().getSimpleName() + ".xml", getClass());
|
||||
void testExceptionOnConfigParsingWithMismatchedAdviceMethod() {
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(() -> new ClassPathXmlApplicationContext(getClass().getSimpleName() + ".xml", getClass()))
|
||||
.havingRootCause()
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.as("invalidAbsoluteTypeName should be detected by AJ").withMessageContaining("invalidAbsoluteTypeName");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+10
-10
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -99,8 +99,8 @@ class ConfigurationClassPostProcessorAotContributionTests {
|
||||
initializer.accept(freshBeanFactory);
|
||||
freshContext.refresh();
|
||||
assertThat(freshBeanFactory.getBeanPostProcessors()).filteredOn(ImportAwareAotBeanPostProcessor.class::isInstance)
|
||||
.singleElement().satisfies(postProcessor ->
|
||||
assertPostProcessorEntry(postProcessor, ImportAwareConfiguration.class, ImportConfiguration.class));
|
||||
.singleElement().satisfies(postProcessor -> assertPostProcessorEntry(postProcessor, ImportAwareConfiguration.class,
|
||||
ImportConfiguration.class));
|
||||
freshContext.close();
|
||||
});
|
||||
}
|
||||
@@ -117,8 +117,8 @@ class ConfigurationClassPostProcessorAotContributionTests {
|
||||
freshContext.refresh();
|
||||
TestAwareCallbackBean bean = freshContext.getBean(TestAwareCallbackBean.class);
|
||||
assertThat(bean.instances).hasSize(2);
|
||||
assertThat(bean.instances).element(0).isEqualTo(freshContext);
|
||||
assertThat(bean.instances).element(1).isInstanceOfSatisfying(AnnotationMetadata.class, metadata ->
|
||||
assertThat(bean.instances.get(0)).isEqualTo(freshContext);
|
||||
assertThat(bean.instances.get(1)).isInstanceOfSatisfying(AnnotationMetadata.class, metadata ->
|
||||
assertThat(metadata.getClassName()).isEqualTo(TestAwareCallbackConfiguration.class.getName()));
|
||||
freshContext.close();
|
||||
});
|
||||
@@ -236,14 +236,13 @@ class ConfigurationClassPostProcessorAotContributionTests {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(this.metadata, "Metadata was not injected");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Nested
|
||||
class PropertySourceTests {
|
||||
|
||||
@@ -363,8 +362,8 @@ class ConfigurationClassPostProcessorAotContributionTests {
|
||||
static class PropertySourceWithCustomFactoryConfiguration {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
class ConfigurationClassProxyTests {
|
||||
@@ -385,13 +384,14 @@ class ConfigurationClassPostProcessorAotContributionTests {
|
||||
getRegisteredBean(CglibConfiguration.class))).isNotNull();
|
||||
}
|
||||
|
||||
|
||||
private RegisteredBean getRegisteredBean(Class<?> bean) {
|
||||
this.beanFactory.registerBeanDefinition("test", new RootBeanDefinition(bean));
|
||||
this.processor.postProcessBeanFactory(this.beanFactory);
|
||||
return RegisteredBean.of(this.beanFactory, "test");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private BeanFactoryInitializationAotContribution getContribution(Class<?>... types) {
|
||||
@@ -410,8 +410,8 @@ class ConfigurationClassPostProcessorAotContributionTests {
|
||||
.containsExactly(entry(key.getName(), value.getName()));
|
||||
}
|
||||
|
||||
|
||||
static class CustomPropertySourcesFactory extends DefaultPropertySourceFactory {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+40
-58
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -29,9 +29,7 @@ import org.springframework.beans.factory.support.AbstractBeanFactory;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.GenericBeanDefinition;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -41,62 +39,51 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* {@link FactoryBean FactoryBeans} defined in the configuration.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
class ConfigurationWithFactoryBeanEarlyDeductionTests {
|
||||
public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests {
|
||||
|
||||
@Test
|
||||
void preFreezeDirect() {
|
||||
public void preFreezeDirect() {
|
||||
assertPreFreeze(DirectConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void postFreezeDirect() {
|
||||
public void postFreezeDirect() {
|
||||
assertPostFreeze(DirectConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void preFreezeGenericMethod() {
|
||||
public void preFreezeGenericMethod() {
|
||||
assertPreFreeze(GenericMethodConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void postFreezeGenericMethod() {
|
||||
public void postFreezeGenericMethod() {
|
||||
assertPostFreeze(GenericMethodConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void preFreezeGenericClass() {
|
||||
public void preFreezeGenericClass() {
|
||||
assertPreFreeze(GenericClassConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void postFreezeGenericClass() {
|
||||
public void postFreezeGenericClass() {
|
||||
assertPostFreeze(GenericClassConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void preFreezeAttribute() {
|
||||
public void preFreezeAttribute() {
|
||||
assertPreFreeze(AttributeClassConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void postFreezeAttribute() {
|
||||
public void postFreezeAttribute() {
|
||||
assertPostFreeze(AttributeClassConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void preFreezeTargetType() {
|
||||
assertPreFreeze(TargetTypeConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void postFreezeTargetType() {
|
||||
assertPostFreeze(TargetTypeConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void preFreezeUnresolvedGenericFactoryBean() {
|
||||
public void preFreezeUnresolvedGenericFactoryBean() {
|
||||
// Covers the case where a @Configuration is picked up via component scanning
|
||||
// and its bean definition only has a String bean class. In such cases
|
||||
// beanDefinition.hasBeanClass() returns false so we need to actually
|
||||
@@ -118,13 +105,14 @@ class ConfigurationWithFactoryBeanEarlyDeductionTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void assertPostFreeze(Class<?> configurationClass) {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(configurationClass);
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
|
||||
configurationClass);
|
||||
assertContainsMyBeanName(context);
|
||||
}
|
||||
|
||||
private void assertPreFreeze(Class<?> configurationClass, BeanFactoryPostProcessor... postProcessors) {
|
||||
private void assertPreFreeze(Class<?> configurationClass,
|
||||
BeanFactoryPostProcessor... postProcessors) {
|
||||
NameCollectingBeanFactoryPostProcessor postProcessor = new NameCollectingBeanFactoryPostProcessor();
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
try (context) {
|
||||
@@ -144,38 +132,41 @@ class ConfigurationWithFactoryBeanEarlyDeductionTests {
|
||||
assertThat(names).containsExactly("myBean");
|
||||
}
|
||||
|
||||
|
||||
private static class NameCollectingBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
|
||||
private static class NameCollectingBeanFactoryPostProcessor
|
||||
implements BeanFactoryPostProcessor {
|
||||
|
||||
private String[] names;
|
||||
|
||||
@Override
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
|
||||
ResolvableType typeToMatch = ResolvableType.forClassWithGenerics(MyBean.class, String.class);
|
||||
this.names = beanFactory.getBeanNamesForType(typeToMatch, true, false);
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
|
||||
throws BeansException {
|
||||
this.names = beanFactory.getBeanNamesForType(MyBean.class, true, false);
|
||||
}
|
||||
|
||||
public String[] getNames() {
|
||||
return this.names;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class DirectConfiguration {
|
||||
|
||||
@Bean
|
||||
MyBean<String> myBean() {
|
||||
return new MyBean<>();
|
||||
MyBean myBean() {
|
||||
return new MyBean();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class GenericMethodConfiguration {
|
||||
|
||||
@Bean
|
||||
FactoryBean<MyBean<String>> myBean() {
|
||||
return new TestFactoryBean<>(new MyBean<>());
|
||||
FactoryBean<MyBean> myBean() {
|
||||
return new TestFactoryBean<>(new MyBean());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@@ -185,11 +176,13 @@ class ConfigurationWithFactoryBeanEarlyDeductionTests {
|
||||
MyFactoryBean myBean() {
|
||||
return new MyFactoryBean();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(AttributeClassRegistrar.class)
|
||||
static class AttributeClassConfiguration {
|
||||
|
||||
}
|
||||
|
||||
static class AttributeClassRegistrar implements ImportBeanDefinitionRegistrar {
|
||||
@@ -198,32 +191,16 @@ class ConfigurationWithFactoryBeanEarlyDeductionTests {
|
||||
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
|
||||
BeanDefinition definition = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
RawWithAbstractObjectTypeFactoryBean.class).getBeanDefinition();
|
||||
definition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE,
|
||||
ResolvableType.forClassWithGenerics(MyBean.class, String.class));
|
||||
definition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, MyBean.class);
|
||||
registry.registerBeanDefinition("myBean", definition);
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(TargetTypeRegistrar.class)
|
||||
static class TargetTypeConfiguration {
|
||||
}
|
||||
|
||||
static class TargetTypeRegistrar implements ImportBeanDefinitionRegistrar {
|
||||
|
||||
@Override
|
||||
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
|
||||
RootBeanDefinition definition = new RootBeanDefinition(RawWithAbstractObjectTypeFactoryBean.class);
|
||||
definition.setTargetType(ResolvableType.forClassWithGenerics(FactoryBean.class,
|
||||
ResolvableType.forClassWithGenerics(MyBean.class, String.class)));
|
||||
registry.registerBeanDefinition("myBean", definition);
|
||||
}
|
||||
}
|
||||
|
||||
abstract static class AbstractMyBean {
|
||||
}
|
||||
|
||||
static class MyBean<T> extends AbstractMyBean {
|
||||
static class MyBean extends AbstractMyBean {
|
||||
}
|
||||
|
||||
static class TestFactoryBean<T> implements FactoryBean<T> {
|
||||
@@ -235,7 +212,7 @@ class ConfigurationWithFactoryBeanEarlyDeductionTests {
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getObject() {
|
||||
public T getObject() throws Exception {
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
@@ -243,26 +220,31 @@ class ConfigurationWithFactoryBeanEarlyDeductionTests {
|
||||
public Class<?> getObjectType() {
|
||||
return this.instance.getClass();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class MyFactoryBean extends TestFactoryBean<MyBean<String>> {
|
||||
static class MyFactoryBean extends TestFactoryBean<MyBean> {
|
||||
|
||||
public MyFactoryBean() {
|
||||
super(new MyBean<>());
|
||||
super(new MyBean());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class RawWithAbstractObjectTypeFactoryBean implements FactoryBean<Object> {
|
||||
|
||||
private final Object object = new MyBean();
|
||||
|
||||
@Override
|
||||
public Object getObject() throws Exception {
|
||||
throw new IllegalStateException();
|
||||
return object;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return MyBean.class;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+17
-36
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -35,83 +35,75 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Phillip Webb
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
class DateFormatterTests {
|
||||
public class DateFormatterTests {
|
||||
|
||||
private static final TimeZone UTC = TimeZone.getTimeZone("UTC");
|
||||
|
||||
|
||||
@Test
|
||||
void shouldPrintAndParseDefault() throws Exception {
|
||||
public void shouldPrintAndParseDefault() throws Exception {
|
||||
DateFormatter formatter = new DateFormatter();
|
||||
formatter.setTimeZone(UTC);
|
||||
|
||||
Date date = getDate(2009, Calendar.JUNE, 1);
|
||||
assertThat(formatter.print(date, Locale.US)).isEqualTo("Jun 1, 2009");
|
||||
assertThat(formatter.parse("Jun 1, 2009", Locale.US)).isEqualTo(date);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldPrintAndParseFromPattern() throws ParseException {
|
||||
public void shouldPrintAndParseFromPattern() throws ParseException {
|
||||
DateFormatter formatter = new DateFormatter("yyyy-MM-dd");
|
||||
formatter.setTimeZone(UTC);
|
||||
|
||||
Date date = getDate(2009, Calendar.JUNE, 1);
|
||||
assertThat(formatter.print(date, Locale.US)).isEqualTo("2009-06-01");
|
||||
assertThat(formatter.parse("2009-06-01", Locale.US)).isEqualTo(date);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldPrintAndParseShort() throws Exception {
|
||||
public void shouldPrintAndParseShort() throws Exception {
|
||||
DateFormatter formatter = new DateFormatter();
|
||||
formatter.setTimeZone(UTC);
|
||||
formatter.setStyle(DateFormat.SHORT);
|
||||
|
||||
Date date = getDate(2009, Calendar.JUNE, 1);
|
||||
assertThat(formatter.print(date, Locale.US)).isEqualTo("6/1/09");
|
||||
assertThat(formatter.parse("6/1/09", Locale.US)).isEqualTo(date);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldPrintAndParseMedium() throws Exception {
|
||||
public void shouldPrintAndParseMedium() throws Exception {
|
||||
DateFormatter formatter = new DateFormatter();
|
||||
formatter.setTimeZone(UTC);
|
||||
formatter.setStyle(DateFormat.MEDIUM);
|
||||
|
||||
Date date = getDate(2009, Calendar.JUNE, 1);
|
||||
assertThat(formatter.print(date, Locale.US)).isEqualTo("Jun 1, 2009");
|
||||
assertThat(formatter.parse("Jun 1, 2009", Locale.US)).isEqualTo(date);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldPrintAndParseLong() throws Exception {
|
||||
public void shouldPrintAndParseLong() throws Exception {
|
||||
DateFormatter formatter = new DateFormatter();
|
||||
formatter.setTimeZone(UTC);
|
||||
formatter.setStyle(DateFormat.LONG);
|
||||
|
||||
Date date = getDate(2009, Calendar.JUNE, 1);
|
||||
assertThat(formatter.print(date, Locale.US)).isEqualTo("June 1, 2009");
|
||||
assertThat(formatter.parse("June 1, 2009", Locale.US)).isEqualTo(date);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldPrintAndParseFull() throws Exception {
|
||||
public void shouldPrintAndParseFull() throws Exception {
|
||||
DateFormatter formatter = new DateFormatter();
|
||||
formatter.setTimeZone(UTC);
|
||||
formatter.setStyle(DateFormat.FULL);
|
||||
|
||||
Date date = getDate(2009, Calendar.JUNE, 1);
|
||||
assertThat(formatter.print(date, Locale.US)).isEqualTo("Monday, June 1, 2009");
|
||||
assertThat(formatter.parse("Monday, June 1, 2009", Locale.US)).isEqualTo(date);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldPrintAndParseIsoDate() throws Exception {
|
||||
public void shouldPrintAndParseISODate() throws Exception {
|
||||
DateFormatter formatter = new DateFormatter();
|
||||
formatter.setTimeZone(UTC);
|
||||
formatter.setIso(ISO.DATE);
|
||||
|
||||
Date date = getDate(2009, Calendar.JUNE, 1, 14, 23, 5, 3);
|
||||
assertThat(formatter.print(date, Locale.US)).isEqualTo("2009-06-01");
|
||||
assertThat(formatter.parse("2009-6-01", Locale.US))
|
||||
@@ -119,56 +111,45 @@ class DateFormatterTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldPrintAndParseIsoTime() throws Exception {
|
||||
public void shouldPrintAndParseISOTime() throws Exception {
|
||||
DateFormatter formatter = new DateFormatter();
|
||||
formatter.setTimeZone(UTC);
|
||||
formatter.setIso(ISO.TIME);
|
||||
|
||||
Date date = getDate(2009, Calendar.JANUARY, 1, 14, 23, 5, 3);
|
||||
assertThat(formatter.print(date, Locale.US)).isEqualTo("14:23:05.003Z");
|
||||
assertThat(formatter.parse("14:23:05.003Z", Locale.US))
|
||||
.isEqualTo(getDate(1970, Calendar.JANUARY, 1, 14, 23, 5, 3));
|
||||
|
||||
date = getDate(2009, Calendar.JANUARY, 1, 14, 23, 5, 0);
|
||||
assertThat(formatter.print(date, Locale.US)).isEqualTo("14:23:05.000Z");
|
||||
assertThat(formatter.parse("14:23:05Z", Locale.US))
|
||||
.isEqualTo(getDate(1970, Calendar.JANUARY, 1, 14, 23, 5, 0).toInstant());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldPrintAndParseIsoDateTime() throws Exception {
|
||||
public void shouldPrintAndParseISODateTime() throws Exception {
|
||||
DateFormatter formatter = new DateFormatter();
|
||||
formatter.setTimeZone(UTC);
|
||||
formatter.setIso(ISO.DATE_TIME);
|
||||
|
||||
Date date = getDate(2009, Calendar.JUNE, 1, 14, 23, 5, 3);
|
||||
assertThat(formatter.print(date, Locale.US)).isEqualTo("2009-06-01T14:23:05.003Z");
|
||||
assertThat(formatter.parse("2009-06-01T14:23:05.003Z", Locale.US)).isEqualTo(date);
|
||||
|
||||
date = getDate(2009, Calendar.JUNE, 1, 14, 23, 5, 0);
|
||||
assertThat(formatter.print(date, Locale.US)).isEqualTo("2009-06-01T14:23:05.000Z");
|
||||
assertThat(formatter.parse("2009-06-01T14:23:05Z", Locale.US)).isEqualTo(date.toInstant());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldThrowOnUnsupportedStylePattern() {
|
||||
public void shouldThrowOnUnsupportedStylePattern() throws Exception {
|
||||
DateFormatter formatter = new DateFormatter();
|
||||
formatter.setStylePattern("OO");
|
||||
|
||||
assertThatIllegalStateException().isThrownBy(() -> formatter.parse("2009", Locale.US))
|
||||
.withMessageContaining("Unsupported style pattern 'OO'");
|
||||
assertThatIllegalStateException().isThrownBy(() ->
|
||||
formatter.parse("2009", Locale.US))
|
||||
.withMessageContaining("Unsupported style pattern 'OO'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUseCorrectOrder() {
|
||||
public void shouldUseCorrectOrder() throws Exception {
|
||||
DateFormatter formatter = new DateFormatter();
|
||||
formatter.setTimeZone(UTC);
|
||||
formatter.setStyle(DateFormat.SHORT);
|
||||
formatter.setStylePattern("L-");
|
||||
formatter.setIso(ISO.DATE_TIME);
|
||||
formatter.setPattern("yyyy");
|
||||
|
||||
Date date = getDate(2009, Calendar.JUNE, 1, 14, 23, 5, 3);
|
||||
|
||||
assertThat(formatter.print(date, Locale.US)).as("uses pattern").isEqualTo("2009");
|
||||
|
||||
formatter.setPattern("");
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -52,7 +52,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
class DateFormattingTests {
|
||||
public class DateFormattingTests {
|
||||
|
||||
private final FormattingConversionService conversionService = new FormattingConversionService();
|
||||
|
||||
|
||||
+23
-13
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -274,7 +274,9 @@ class DateTimeFormattingTests {
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
String value = binder.getBindingResult().getFieldValue("localDateTime").toString();
|
||||
assertThat(value).startsWith("10/31/09").endsWith("12:00 PM");
|
||||
assertThat(value)
|
||||
.startsWith("10/31/09")
|
||||
.endsWith("12:00 PM");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -284,7 +286,9 @@ class DateTimeFormattingTests {
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
String value = binder.getBindingResult().getFieldValue("localDateTime").toString();
|
||||
assertThat(value).startsWith("10/31/09").endsWith("12:00 PM");
|
||||
assertThat(value)
|
||||
.startsWith("10/31/09")
|
||||
.endsWith("12:00 PM");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -294,7 +298,9 @@ class DateTimeFormattingTests {
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
String value = binder.getBindingResult().getFieldValue("styleLocalDateTime").toString();
|
||||
assertThat(value).startsWith("Oct 31, 2009").endsWith("12:00:00 PM");
|
||||
assertThat(value)
|
||||
.startsWith("Oct 31, 2009")
|
||||
.endsWith("12:00:00 PM");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -304,7 +310,9 @@ class DateTimeFormattingTests {
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
String value = binder.getBindingResult().getFieldValue("localDateTime").toString();
|
||||
assertThat(value).startsWith("10/31/09").endsWith("12:00 PM");
|
||||
assertThat(value)
|
||||
.startsWith("10/31/09")
|
||||
.endsWith("12:00 PM");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -317,7 +325,9 @@ class DateTimeFormattingTests {
|
||||
binder.bind(propertyValues);
|
||||
assertThat(binder.getBindingResult().getErrorCount()).isZero();
|
||||
String value = binder.getBindingResult().getFieldValue("localDateTime").toString();
|
||||
assertThat(value).startsWith("Oct 31, 2009").endsWith("12:00:00 PM");
|
||||
assertThat(value)
|
||||
.startsWith("Oct 31, 2009")
|
||||
.endsWith("12:00:00 PM");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -502,7 +512,7 @@ class DateTimeFormattingTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBindYearMonthAnnotatedPattern() {
|
||||
public void testBindYearMonthAnnotatedPattern() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("yearMonthAnnotatedPattern", "12/2007");
|
||||
binder.bind(propertyValues);
|
||||
@@ -521,7 +531,7 @@ class DateTimeFormattingTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBindMonthDayAnnotatedPattern() {
|
||||
public void testBindMonthDayAnnotatedPattern() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("monthDayAnnotatedPattern", "1/3");
|
||||
binder.bind(propertyValues);
|
||||
@@ -530,7 +540,6 @@ class DateTimeFormattingTests {
|
||||
assertThat(binder.getBindingResult().getRawFieldValue("monthDayAnnotatedPattern")).isEqualTo(MonthDay.parse("--01-03"));
|
||||
}
|
||||
|
||||
|
||||
@Nested
|
||||
class FallbackPatternTests {
|
||||
|
||||
@@ -622,10 +631,10 @@ class DateTimeFormattingTests {
|
||||
@DateTimeFormat(style = "M-")
|
||||
private LocalDate styleLocalDate;
|
||||
|
||||
@DateTimeFormat(style = "S-", fallbackPatterns = {"yyyy-MM-dd", "yyyyMMdd", "yyyy.MM.dd"})
|
||||
@DateTimeFormat(style = "S-", fallbackPatterns = { "yyyy-MM-dd", "yyyyMMdd", "yyyy.MM.dd" })
|
||||
private LocalDate styleLocalDateWithFallbackPatterns;
|
||||
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd", fallbackPatterns = {"M/d/yy", "yyyyMMdd", "yyyy.MM.dd"})
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd", fallbackPatterns = { "M/d/yy", "yyyyMMdd", "yyyy.MM.dd" })
|
||||
private LocalDate patternLocalDateWithFallbackPatterns;
|
||||
|
||||
private LocalTime localTime;
|
||||
@@ -633,7 +642,7 @@ class DateTimeFormattingTests {
|
||||
@DateTimeFormat(style = "-M")
|
||||
private LocalTime styleLocalTime;
|
||||
|
||||
@DateTimeFormat(style = "-M", fallbackPatterns = {"HH:mm:ss", "HH:mm"})
|
||||
@DateTimeFormat(style = "-M", fallbackPatterns = { "HH:mm:ss", "HH:mm"})
|
||||
private LocalTime styleLocalTimeWithFallbackPatterns;
|
||||
|
||||
private LocalDateTime localDateTime;
|
||||
@@ -653,7 +662,7 @@ class DateTimeFormattingTests {
|
||||
@DateTimeFormat(iso = ISO.DATE_TIME)
|
||||
private LocalDateTime isoLocalDateTime;
|
||||
|
||||
@DateTimeFormat(iso = ISO.DATE_TIME, fallbackPatterns = {"yyyy-MM-dd HH:mm:ss", "M/d/yy HH:mm"})
|
||||
@DateTimeFormat(iso = ISO.DATE_TIME, fallbackPatterns = { "yyyy-MM-dd HH:mm:ss", "M/d/yy HH:mm"})
|
||||
private LocalDateTime isoLocalDateTimeWithFallbackPatterns;
|
||||
|
||||
private Instant instant;
|
||||
@@ -681,6 +690,7 @@ class DateTimeFormattingTests {
|
||||
|
||||
private final List<DateTimeBean> children = new ArrayList<>();
|
||||
|
||||
|
||||
public LocalDate getLocalDate() {
|
||||
return this.localDate;
|
||||
}
|
||||
|
||||
+10
-9
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -19,7 +19,6 @@ package org.springframework.format.datetime.standard;
|
||||
import java.text.ParseException;
|
||||
import java.time.Instant;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Locale;
|
||||
import java.util.Random;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -50,12 +49,13 @@ class InstantFormatterTests {
|
||||
|
||||
private final InstantFormatter instantFormatter = new InstantFormatter();
|
||||
|
||||
|
||||
@ParameterizedTest
|
||||
@ArgumentsSource(ISOSerializedInstantProvider.class)
|
||||
void should_parse_an_ISO_formatted_string_representation_of_an_Instant(String input) throws ParseException {
|
||||
Instant expected = DateTimeFormatter.ISO_INSTANT.parse(input, Instant::from);
|
||||
Instant actual = instantFormatter.parse(input, Locale.US);
|
||||
|
||||
Instant actual = instantFormatter.parse(input, null);
|
||||
|
||||
assertThat(actual).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@@ -63,7 +63,9 @@ class InstantFormatterTests {
|
||||
@ArgumentsSource(RFC1123SerializedInstantProvider.class)
|
||||
void should_parse_an_RFC1123_formatted_string_representation_of_an_Instant(String input) throws ParseException {
|
||||
Instant expected = DateTimeFormatter.RFC_1123_DATE_TIME.parse(input, Instant::from);
|
||||
Instant actual = instantFormatter.parse(input, Locale.US);
|
||||
|
||||
Instant actual = instantFormatter.parse(input, null);
|
||||
|
||||
assertThat(actual).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@@ -71,11 +73,12 @@ class InstantFormatterTests {
|
||||
@ArgumentsSource(RandomInstantProvider.class)
|
||||
void should_serialize_an_Instant_using_ISO_format_and_ignoring_Locale(Instant input) {
|
||||
String expected = DateTimeFormatter.ISO_INSTANT.format(input);
|
||||
String actual = instantFormatter.print(input, Locale.US);
|
||||
|
||||
String actual = instantFormatter.print(input, null);
|
||||
|
||||
assertThat(actual).isEqualTo(expected);
|
||||
}
|
||||
|
||||
|
||||
private static class RandomInstantProvider implements ArgumentsProvider {
|
||||
|
||||
private static final long DATA_SET_SIZE = 10;
|
||||
@@ -97,7 +100,6 @@ class InstantFormatterTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class ISOSerializedInstantProvider extends RandomInstantProvider {
|
||||
|
||||
@Override
|
||||
@@ -106,7 +108,6 @@ class InstantFormatterTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class RFC1123SerializedInstantProvider extends RandomInstantProvider {
|
||||
|
||||
// RFC-1123 supports only 4-digit years
|
||||
|
||||
-2
@@ -18,6 +18,4 @@
|
||||
|
||||
<bean id="testBean" class="org.springframework.beans.testfixture.beans.TestBean"/>
|
||||
|
||||
<bean id="testBean2" class="org.springframework.beans.testfixture.beans.TestBean"/>
|
||||
|
||||
</beans>
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -39,7 +39,8 @@ import org.springframework.core.annotation.AliasFor;
|
||||
* @see ReflectiveRuntimeHintsRegistrar
|
||||
* @see RegisterReflectionForBinding @RegisterReflectionForBinding
|
||||
*/
|
||||
@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD})
|
||||
@Target({ ElementType.ANNOTATION_TYPE, ElementType.TYPE, ElementType.CONSTRUCTOR,
|
||||
ElementType.FIELD, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface Reflective {
|
||||
|
||||
+5
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -36,7 +36,7 @@ import org.springframework.core.annotation.AliasFor;
|
||||
*
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* @RegisterReflectionForBinding({Foo.class, Bar.class})
|
||||
* @RegisterReflectionForBinding({ Foo.class, Bar.class })
|
||||
* public class MyConfig {
|
||||
* // ...
|
||||
* }</pre>
|
||||
@@ -63,7 +63,7 @@ import org.springframework.core.annotation.AliasFor;
|
||||
* @see org.springframework.aot.hint.BindingReflectionHintsRegistrar
|
||||
* @see Reflective @Reflective
|
||||
*/
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Reflective(RegisterReflectionForBindingProcessor.class)
|
||||
@@ -77,7 +77,8 @@ public @interface RegisterReflectionForBinding {
|
||||
|
||||
/**
|
||||
* Classes for which reflection hints should be registered.
|
||||
* <p>At least one class must be specified either via {@link #value} or {@code classes}.
|
||||
* <p>At least one class must be specified either via {@link #value} or
|
||||
* {@link #classes}.
|
||||
* @see #value()
|
||||
*/
|
||||
@AliasFor("value")
|
||||
|
||||
+4
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -47,11 +47,9 @@ class SpringFactoriesLoaderRuntimeHints implements RuntimeHintsRegistrar {
|
||||
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
|
||||
ClassLoader classLoaderToUse = (classLoader != null ? classLoader :
|
||||
SpringFactoriesLoaderRuntimeHints.class.getClassLoader());
|
||||
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
|
||||
for (String resourceLocation : RESOURCE_LOCATIONS) {
|
||||
registerHints(hints, classLoaderToUse, resourceLocation);
|
||||
registerHints(hints, classLoader, resourceLocation);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +63,6 @@ class SpringFactoriesLoaderRuntimeHints implements RuntimeHintsRegistrar {
|
||||
|
||||
private void registerHints(RuntimeHints hints, ClassLoader classLoader,
|
||||
String factoryClassName, List<String> implementationClassNames) {
|
||||
|
||||
Class<?> factoryClass = resolveClassName(classLoader, factoryClassName);
|
||||
if (factoryClass == null) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
@@ -103,7 +100,6 @@ class SpringFactoriesLoaderRuntimeHints implements RuntimeHintsRegistrar {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class ExtendedSpringFactoriesLoader extends SpringFactoriesLoader {
|
||||
|
||||
ExtendedSpringFactoriesLoader(@Nullable ClassLoader classLoader, Map<String, List<String>> factories) {
|
||||
@@ -113,6 +109,7 @@ class SpringFactoriesLoaderRuntimeHints implements RuntimeHintsRegistrar {
|
||||
static Map<String, List<String>> accessLoadFactoriesResource(ClassLoader classLoader, String resourceLocation) {
|
||||
return SpringFactoriesLoader.loadFactoriesResource(classLoader, resourceLocation);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ public class ReflectUtils {
|
||||
Throwable throwable = null;
|
||||
try {
|
||||
classLoaderDefineClass = ClassLoader.class.getDeclaredMethod("defineClass",
|
||||
String.class, byte[].class, Integer.TYPE, Integer.TYPE, ProtectionDomain.class);
|
||||
String.class, byte[].class, Integer.TYPE, Integer.TYPE, ProtectionDomain.class);
|
||||
}
|
||||
catch (Throwable t) {
|
||||
classLoaderDefineClass = null;
|
||||
|
||||
@@ -57,7 +57,7 @@ public class MethodProxy {
|
||||
proxy.createInfo = new CreateInfo(c1, c2);
|
||||
|
||||
// SPRING PATCH BEGIN
|
||||
if (c1 != Object.class && c1.isAssignableFrom(c2.getSuperclass()) && !Factory.class.isAssignableFrom(c2)) {
|
||||
if (!c1.isInterface() && c1 != Object.class && !Factory.class.isAssignableFrom(c2)) {
|
||||
// Try early initialization for overridden methods on specifically purposed subclasses
|
||||
try {
|
||||
proxy.init();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -57,11 +57,11 @@ public final class BridgeMethodResolver {
|
||||
|
||||
|
||||
/**
|
||||
* Find the local original method for the supplied {@link Method bridge Method}.
|
||||
* Find the original method for the supplied {@link Method bridge Method}.
|
||||
* <p>It is safe to call this method passing in a non-bridge {@link Method} instance.
|
||||
* In such a case, the supplied {@link Method} instance is returned directly to the caller.
|
||||
* Callers are <strong>not</strong> required to check for bridging before calling this method.
|
||||
* @param bridgeMethod the method to introspect against its declaring class
|
||||
* @param bridgeMethod the method to introspect
|
||||
* @return the original method (either the bridged method or the passed-in method
|
||||
* if no more specific one could be found)
|
||||
*/
|
||||
@@ -73,7 +73,8 @@ public final class BridgeMethodResolver {
|
||||
if (bridgedMethod == null) {
|
||||
// Gather all methods with matching name and parameter size.
|
||||
List<Method> candidateMethods = new ArrayList<>();
|
||||
MethodFilter filter = (candidateMethod -> isBridgedCandidateFor(candidateMethod, bridgeMethod));
|
||||
MethodFilter filter = candidateMethod ->
|
||||
isBridgedCandidateFor(candidateMethod, bridgeMethod);
|
||||
ReflectionUtils.doWithMethods(bridgeMethod.getDeclaringClass(), candidateMethods::add, filter);
|
||||
if (!candidateMethods.isEmpty()) {
|
||||
bridgedMethod = candidateMethods.size() == 1 ?
|
||||
@@ -120,8 +121,8 @@ public final class BridgeMethodResolver {
|
||||
return candidateMethod;
|
||||
}
|
||||
else if (previousMethod != null) {
|
||||
sameSig = sameSig && Arrays.equals(
|
||||
candidateMethod.getGenericParameterTypes(), previousMethod.getGenericParameterTypes());
|
||||
sameSig = sameSig &&
|
||||
Arrays.equals(candidateMethod.getGenericParameterTypes(), previousMethod.getGenericParameterTypes());
|
||||
}
|
||||
previousMethod = candidateMethod;
|
||||
}
|
||||
@@ -162,8 +163,7 @@ public final class BridgeMethodResolver {
|
||||
}
|
||||
}
|
||||
// A non-array type: compare the type itself.
|
||||
if (!ClassUtils.resolvePrimitiveIfNecessary(candidateParameter).equals(
|
||||
ClassUtils.resolvePrimitiveIfNecessary(genericParameter.toClass()))) {
|
||||
if (!ClassUtils.resolvePrimitiveIfNecessary(candidateParameter).equals(ClassUtils.resolvePrimitiveIfNecessary(genericParameter.toClass()))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -226,8 +226,8 @@ public final class BridgeMethodResolver {
|
||||
/**
|
||||
* Compare the signatures of the bridge method and the method which it bridges. If
|
||||
* the parameter and return types are the same, it is a 'visibility' bridge method
|
||||
* introduced in Java 6 to fix <a href="https://bugs.openjdk.org/browse/JDK-6342411">
|
||||
* JDK-6342411</a>.
|
||||
* introduced in Java 6 to fix https://bugs.openjdk.org/browse/JDK-6342411.
|
||||
* See also https://stas-blogspot.blogspot.com/2010/03/java-bridge-methods-explained.html
|
||||
* @return whether signatures match as described
|
||||
*/
|
||||
public static boolean isVisibilityBridgeMethodPair(Method bridgeMethod, Method bridgedMethod) {
|
||||
|
||||
@@ -1049,16 +1049,16 @@ public abstract class AnnotationUtils {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
for (Method method : annotation.annotationType().getDeclaredMethods()) {
|
||||
if (method.getName().equals(attributeName) && method.getParameterCount() == 0) {
|
||||
return invokeAnnotationMethod(method, annotation);
|
||||
}
|
||||
}
|
||||
Method method = annotation.annotationType().getDeclaredMethod(attributeName);
|
||||
return invokeAnnotationMethod(method, annotation);
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
return null;
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
handleValueRetrievalFailure(annotation, ex);
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+2
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -418,10 +418,7 @@ final class TypeMappedAnnotations implements MergedAnnotations {
|
||||
|
||||
Annotation[] repeatedAnnotations = repeatableContainers.findRepeatedAnnotations(annotation);
|
||||
if (repeatedAnnotations != null) {
|
||||
MergedAnnotation<A> result = doWithAnnotations(type, aggregateIndex, source, repeatedAnnotations);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
return doWithAnnotations(type, aggregateIndex, source, repeatedAnnotations);
|
||||
}
|
||||
AnnotationTypeMappings mappings = AnnotationTypeMappings.forAnnotationType(
|
||||
annotation.annotationType(), repeatableContainers, annotationFilter);
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -48,7 +48,7 @@ public class PropertiesPropertySource extends MapPropertySource {
|
||||
@Override
|
||||
public String[] getPropertyNames() {
|
||||
synchronized (this.source) {
|
||||
return ((Map<?, ?>) this.source).keySet().stream().filter(k -> k instanceof String).toArray(String[]::new);
|
||||
return super.getPropertyNames();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+102
-124
@@ -71,18 +71,18 @@ import org.springframework.util.StringUtils;
|
||||
/**
|
||||
* A {@link ResourcePatternResolver} implementation that is able to resolve a
|
||||
* specified resource location path into one or more matching Resources.
|
||||
* The source path may be a simple path which has a one-to-one mapping to a
|
||||
* target {@link org.springframework.core.io.Resource}, or alternatively
|
||||
* may contain the special "{@code classpath*:}" prefix and/or
|
||||
* internal Ant-style regular expressions (matched using Spring's
|
||||
* {@link org.springframework.util.AntPathMatcher} utility).
|
||||
* Both of the latter are effectively wildcards.
|
||||
*
|
||||
* <p>The source path may be a simple path which has a one-to-one mapping to a
|
||||
* target {@link org.springframework.core.io.Resource}, or alternatively may
|
||||
* contain the special "{@code classpath*:}" prefix and/or internal Ant-style
|
||||
* path patterns (matched using Spring's {@link AntPathMatcher} utility). Both
|
||||
* of the latter are effectively wildcards.
|
||||
*
|
||||
* <h3>No Wildcards</h3>
|
||||
* <p><b>No Wildcards:</b>
|
||||
*
|
||||
* <p>In the simple case, if the specified location path does not start with the
|
||||
* {@code "classpath*:}" prefix and does not contain a {@link PathMatcher}
|
||||
* pattern, this resolver will simply return a single resource via a
|
||||
* {@code "classpath*:}" prefix, and does not contain a PathMatcher pattern,
|
||||
* this resolver will simply return a single resource via a
|
||||
* {@code getResource()} call on the underlying {@code ResourceLoader}.
|
||||
* Examples are real URLs such as "{@code file:C:/context.xml}", pseudo-URLs
|
||||
* such as "{@code classpath:/context.xml}", and simple unprefixed paths
|
||||
@@ -90,14 +90,14 @@ import org.springframework.util.StringUtils;
|
||||
* fashion specific to the underlying {@code ResourceLoader} (e.g.
|
||||
* {@code ServletContextResource} for a {@code WebApplicationContext}).
|
||||
*
|
||||
* <h3>Ant-style Patterns</h3>
|
||||
* <p><b>Ant-style Patterns:</b>
|
||||
*
|
||||
* <p>When the path location contains an Ant-style pattern, for example:
|
||||
* <p>When the path location contains an Ant-style pattern, e.g.:
|
||||
* <pre class="code">
|
||||
* /WEB-INF/*-context.xml
|
||||
* com/example/**/applicationContext.xml
|
||||
* com/mycompany/**/applicationContext.xml
|
||||
* file:C:/some/path/*-context.xml
|
||||
* classpath:com/example/**/applicationContext.xml</pre>
|
||||
* classpath:com/mycompany/**/applicationContext.xml</pre>
|
||||
* the resolver follows a more complex but defined procedure to try to resolve
|
||||
* the wildcard. It produces a {@code Resource} for the path up to the last
|
||||
* non-wildcard segment and obtains a {@code URL} from it. If this URL is not a
|
||||
@@ -108,31 +108,31 @@ import org.springframework.util.StringUtils;
|
||||
* {@code java.net.JarURLConnection} from it, or manually parses the jar URL, and
|
||||
* then traverses the contents of the jar file, to resolve the wildcards.
|
||||
*
|
||||
* <h3>Implications on Portability</h3>
|
||||
* <p><b>Implications on portability:</b>
|
||||
*
|
||||
* <p>If the specified path is already a file URL (either explicitly, or
|
||||
* implicitly because the base {@code ResourceLoader} is a filesystem one),
|
||||
* then wildcarding is guaranteed to work in a completely portable fashion.
|
||||
*
|
||||
* <p>If the specified path is a class path location, then the resolver must
|
||||
* <p>If the specified path is a classpath location, then the resolver must
|
||||
* obtain the last non-wildcard path segment URL via a
|
||||
* {@code Classloader.getResource()} call. Since this is just a
|
||||
* node of the path (not the file at the end) it is actually undefined
|
||||
* (in the ClassLoader Javadocs) exactly what sort of URL is returned in
|
||||
* this case. In practice, it is usually a {@code java.io.File} representing
|
||||
* the directory, where the class path resource resolves to a filesystem
|
||||
* location, or a jar URL of some sort, where the class path resource resolves
|
||||
* the directory, where the classpath resource resolves to a filesystem
|
||||
* location, or a jar URL of some sort, where the classpath resource resolves
|
||||
* to a jar location. Still, there is a portability concern on this operation.
|
||||
*
|
||||
* <p>If a jar URL is obtained for the last non-wildcard segment, the resolver
|
||||
* must be able to get a {@code java.net.JarURLConnection} from it, or
|
||||
* manually parse the jar URL, to be able to walk the contents of the jar
|
||||
* and resolve the wildcard. This will work in most environments but will
|
||||
* manually parse the jar URL, to be able to walk the contents of the jar,
|
||||
* and resolve the wildcard. This will work in most environments, but will
|
||||
* fail in others, and it is strongly recommended that the wildcard
|
||||
* resolution of resources coming from jars be thoroughly tested in your
|
||||
* specific environment before you rely on it.
|
||||
*
|
||||
* <h3>{@code classpath*:} Prefix</h3>
|
||||
* <p><b>{@code classpath*:} Prefix:</b>
|
||||
*
|
||||
* <p>There is special support for retrieving multiple class path resources with
|
||||
* the same name, via the "{@code classpath*:}" prefix. For example,
|
||||
@@ -142,22 +142,22 @@ import org.springframework.util.StringUtils;
|
||||
* at the same location within each jar file. Internally, this happens via a
|
||||
* {@code ClassLoader.getResources()} call, and is completely portable.
|
||||
*
|
||||
* <p>The "{@code classpath*:}" prefix can also be combined with a {@code PathMatcher}
|
||||
* pattern in the rest of the location path — for example,
|
||||
* "{@code classpath*:META-INF/*-beans.xml"}. In this case, the resolution strategy
|
||||
* is fairly simple: a {@code ClassLoader.getResources()} call is used on the last
|
||||
* non-wildcard path segment to get all the matching resources in the class loader
|
||||
* hierarchy, and then off each resource the same {@code PathMatcher} resolution
|
||||
* strategy described above is used for the wildcard sub pattern.
|
||||
* <p>The "classpath*:" prefix can also be combined with a PathMatcher pattern in
|
||||
* the rest of the location path, for example "classpath*:META-INF/*-beans.xml".
|
||||
* In this case, the resolution strategy is fairly simple: a
|
||||
* {@code ClassLoader.getResources()} call is used on the last non-wildcard
|
||||
* path segment to get all the matching resources in the class loader hierarchy,
|
||||
* and then off each resource the same PathMatcher resolution strategy described
|
||||
* above is used for the wildcard sub pattern.
|
||||
*
|
||||
* <h3>Other Notes</h3>
|
||||
* <p><b>Other notes:</b>
|
||||
*
|
||||
* <p>As of Spring Framework 6.0, if {@link #getResources(String)} is invoked with
|
||||
* a location pattern using the "{@code classpath*:}" prefix it will first search
|
||||
* <p>As of Spring Framework 6.0, if {@link #getResources(String)} is invoked
|
||||
* with a location pattern using the "classpath*:" prefix it will first search
|
||||
* all modules in the {@linkplain ModuleLayer#boot() boot layer}, excluding
|
||||
* {@linkplain ModuleFinder#ofSystem() system modules}. It will then search the
|
||||
* class path using {@link ClassLoader} APIs as described previously and return the
|
||||
* combined results. Consequently, some of the limitations of class path searches
|
||||
* classpath using {@link ClassLoader} APIs as described previously and return the
|
||||
* combined results. Consequently, some of the limitations of classpath searches
|
||||
* may not apply when applications are deployed as modules.
|
||||
*
|
||||
* <p><b>WARNING:</b> Note that "{@code classpath*:}" when combined with
|
||||
@@ -168,26 +168,26 @@ import org.springframework.util.StringUtils;
|
||||
* root of expanded directories. This originates from a limitation in the JDK's
|
||||
* {@code ClassLoader.getResources()} method which only returns file system
|
||||
* locations for a passed-in empty String (indicating potential roots to search).
|
||||
* This {@code ResourcePatternResolver} implementation tries to mitigate the
|
||||
* This {@code ResourcePatternResolver} implementation is trying to mitigate the
|
||||
* jar root lookup limitation through {@link URLClassLoader} introspection and
|
||||
* "{@code java.class.path}" manifest evaluation; however, without portability
|
||||
* guarantees.
|
||||
* "java.class.path" manifest evaluation; however, without portability guarantees.
|
||||
*
|
||||
* <p><b>WARNING:</b> Ant-style patterns with "{@code classpath:}" resources are not
|
||||
* guaranteed to find matching resources if the base package to search is available
|
||||
* <p><b>WARNING:</b> Ant-style patterns with "classpath:" resources are not
|
||||
* guaranteed to find matching resources if the root package to search is available
|
||||
* in multiple class path locations. This is because a resource such as
|
||||
* <pre class="code">
|
||||
* com/example/package1/service-context.xml</pre>
|
||||
* may exist in only one class path location, but when a location pattern such as
|
||||
* com/mycompany/package1/service-context.xml
|
||||
* </pre>
|
||||
* may be in only one location, but when a path such as
|
||||
* <pre class="code">
|
||||
* classpath:com/example/**/service-context.xml</pre>
|
||||
* classpath:com/mycompany/**/service-context.xml
|
||||
* </pre>
|
||||
* is used to try to resolve it, the resolver will work off the (first) URL
|
||||
* returned by {@code getResource("com/example")}. If the {@code com/example} base
|
||||
* package node exists in multiple class path locations, the actual desired resource
|
||||
* may not be present under the {@code com/example} base package in the first URL.
|
||||
* Therefore, preferably, use "{@code classpath*:}" with the same Ant-style pattern
|
||||
* in such a case, which will search <i>all</i> class path locations that contain
|
||||
* the base package.
|
||||
* returned by {@code getResource("com/mycompany");}. If this base package node
|
||||
* exists in multiple classloader locations, the actual end resource may not be
|
||||
* underneath. Therefore, preferably, use "{@code classpath*:}" with the same
|
||||
* Ant-style pattern in such a case, which will search <i>all</i> class path
|
||||
* locations that contain the root package.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Colin Sampaleanu
|
||||
@@ -249,21 +249,19 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
|
||||
|
||||
/**
|
||||
* Create a {@code PathMatchingResourcePatternResolver} with a
|
||||
* {@link DefaultResourceLoader}.
|
||||
* Create a new PathMatchingResourcePatternResolver with a DefaultResourceLoader.
|
||||
* <p>ClassLoader access will happen via the thread context class loader.
|
||||
* @see DefaultResourceLoader
|
||||
* @see org.springframework.core.io.DefaultResourceLoader
|
||||
*/
|
||||
public PathMatchingResourcePatternResolver() {
|
||||
this.resourceLoader = new DefaultResourceLoader();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@code PathMatchingResourcePatternResolver} with the supplied
|
||||
* {@link ResourceLoader}.
|
||||
* Create a new PathMatchingResourcePatternResolver.
|
||||
* <p>ClassLoader access will happen via the thread context class loader.
|
||||
* @param resourceLoader the {@code ResourceLoader} to load root directories
|
||||
* and actual resources with
|
||||
* @param resourceLoader the ResourceLoader to load root directories and
|
||||
* actual resources with
|
||||
*/
|
||||
public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {
|
||||
Assert.notNull(resourceLoader, "ResourceLoader must not be null");
|
||||
@@ -271,9 +269,8 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@code PathMatchingResourcePatternResolver} with a
|
||||
* {@link DefaultResourceLoader} and the supplied {@link ClassLoader}.
|
||||
* @param classLoader the ClassLoader to load class path resources with,
|
||||
* Create a new PathMatchingResourcePatternResolver with a DefaultResourceLoader.
|
||||
* @param classLoader the ClassLoader to load classpath resources with,
|
||||
* or {@code null} for using the thread context class loader
|
||||
* at the time of actual resource access
|
||||
* @see org.springframework.core.io.DefaultResourceLoader
|
||||
@@ -284,7 +281,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
|
||||
|
||||
/**
|
||||
* Return the {@link ResourceLoader} that this pattern resolver works with.
|
||||
* Return the ResourceLoader that this pattern resolver works with.
|
||||
*/
|
||||
public ResourceLoader getResourceLoader() {
|
||||
return this.resourceLoader;
|
||||
@@ -297,10 +294,9 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link PathMatcher} implementation to use for this
|
||||
* resource pattern resolver.
|
||||
* <p>Default is {@link AntPathMatcher}.
|
||||
* @see AntPathMatcher
|
||||
* Set the PathMatcher implementation to use for this
|
||||
* resource pattern resolver. Default is AntPathMatcher.
|
||||
* @see org.springframework.util.AntPathMatcher
|
||||
*/
|
||||
public void setPathMatcher(PathMatcher pathMatcher) {
|
||||
Assert.notNull(pathMatcher, "PathMatcher must not be null");
|
||||
@@ -308,7 +304,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link PathMatcher} that this resource pattern resolver uses.
|
||||
* Return the PathMatcher that this resource pattern resolver uses.
|
||||
*/
|
||||
public PathMatcher getPathMatcher() {
|
||||
return this.pathMatcher;
|
||||
@@ -357,8 +353,8 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
|
||||
/**
|
||||
* Find all class location resources with the given location via the ClassLoader.
|
||||
* <p>Delegates to {@link #doFindAllClassPathResources(String)}.
|
||||
* @param location the absolute path within the class path
|
||||
* Delegates to {@link #doFindAllClassPathResources(String)}.
|
||||
* @param location the absolute path within the classpath
|
||||
* @return the result as Resource array
|
||||
* @throws IOException in case of I/O errors
|
||||
* @see java.lang.ClassLoader#getResources
|
||||
@@ -368,16 +364,15 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
String path = stripLeadingSlash(location);
|
||||
Set<Resource> result = doFindAllClassPathResources(path);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Resolved class path location [" + path + "] to resources " + result);
|
||||
logger.trace("Resolved classpath location [" + path + "] to resources " + result);
|
||||
}
|
||||
return result.toArray(new Resource[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all class path resources with the given path via the configured
|
||||
* {@link #getClassLoader() ClassLoader}.
|
||||
* <p>Called by {@link #findAllClassPathResources(String)}.
|
||||
* @param path the absolute path within the class path (never a leading slash)
|
||||
* Find all class location resources with the given path via the ClassLoader.
|
||||
* Called by {@link #findAllClassPathResources(String)}.
|
||||
* @param path the absolute path within the classpath (never a leading slash)
|
||||
* @return a mutable Set of matching Resource instances
|
||||
* @since 4.1.1
|
||||
*/
|
||||
@@ -391,21 +386,20 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
}
|
||||
if (!StringUtils.hasLength(path)) {
|
||||
// The above result is likely to be incomplete, i.e. only containing file system references.
|
||||
// We need to have pointers to each of the jar files on the class path as well...
|
||||
// We need to have pointers to each of the jar files on the classpath as well...
|
||||
addAllClassLoaderJarRoots(cl, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given URL as returned from the configured
|
||||
* {@link #getClassLoader() ClassLoader} into a {@link Resource}, applying
|
||||
* to path lookups without a pattern (see {@link #findAllClassPathResources}).
|
||||
* Convert the given URL as returned from the ClassLoader into a {@link Resource},
|
||||
* applying to path lookups without a pattern ({@link #findAllClassPathResources}).
|
||||
* <p>As of 6.0.5, the default implementation creates a {@link FileSystemResource}
|
||||
* in case of the "file" protocol or a {@link UrlResource} otherwise, matching
|
||||
* the outcome of pattern-based class path traversal in the same resource layout,
|
||||
* the outcome of pattern-based classpath traversal in the same resource layout,
|
||||
* as well as matching the outcome of module path searches.
|
||||
* @param url a URL as returned from the configured ClassLoader
|
||||
* @param url a URL as returned from the ClassLoader
|
||||
* @return the corresponding Resource object
|
||||
* @see java.lang.ClassLoader#getResources
|
||||
* @see #doFindAllClassPathResources
|
||||
@@ -428,8 +422,8 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
}
|
||||
|
||||
/**
|
||||
* Search all {@link URLClassLoader} URLs for jar file references and add each to the
|
||||
* given set of resources in the form of a pointer to the root of the jar file content.
|
||||
* Search all {@link URLClassLoader} URLs for jar file references and add them to the
|
||||
* given set of resources in the form of pointers to the root of the jar file content.
|
||||
* @param classLoader the ClassLoader to search (including its ancestors)
|
||||
* @param result the set of resources to add jar roots to
|
||||
* @since 4.1.1
|
||||
@@ -463,7 +457,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
}
|
||||
|
||||
if (classLoader == ClassLoader.getSystemClassLoader()) {
|
||||
// JAR "Class-Path" manifest header evaluation...
|
||||
// "java.class.path" manifest evaluation...
|
||||
addClassPathManifestEntries(result);
|
||||
}
|
||||
|
||||
@@ -482,17 +476,16 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine jar file references from {@code Class-Path} manifest entries (which
|
||||
* are added to the {@code java.class.path} JVM system property by the system
|
||||
* class loader) and add each to the given set of resources in the form of
|
||||
* a pointer to the root of the jar file content.
|
||||
* Determine jar file references from the "java.class.path." manifest property and add them
|
||||
* to the given set of resources in the form of pointers to the root of the jar file content.
|
||||
* @param result the set of resources to add jar roots to
|
||||
* @since 4.3
|
||||
*/
|
||||
protected void addClassPathManifestEntries(Set<Resource> result) {
|
||||
try {
|
||||
String javaClassPathProperty = System.getProperty("java.class.path");
|
||||
for (String path : StringUtils.delimitedListToStringArray(javaClassPathProperty, File.pathSeparator)) {
|
||||
for (String path : StringUtils.delimitedListToStringArray(
|
||||
javaClassPathProperty, System.getProperty("path.separator"))) {
|
||||
try {
|
||||
String filePath = new File(path).getAbsolutePath();
|
||||
int prefixIndex = filePath.indexOf(':');
|
||||
@@ -506,7 +499,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
// Build URL that points to the root of the jar file
|
||||
UrlResource jarResource = new UrlResource(ResourceUtils.JAR_URL_PREFIX +
|
||||
ResourceUtils.FILE_URL_PREFIX + filePath + ResourceUtils.JAR_URL_SEPARATOR);
|
||||
// Potentially overlapping with URLClassLoader.getURLs() result in addAllClassLoaderJarRoots().
|
||||
// Potentially overlapping with URLClassLoader.getURLs() result above!
|
||||
if (!result.contains(jarResource) && !hasDuplicate(filePath, result) && jarResource.exists()) {
|
||||
result.add(jarResource);
|
||||
}
|
||||
@@ -550,18 +543,14 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all resources that match the given location pattern via the Ant-style
|
||||
* {@link #getPathMatcher() PathMatcher}.
|
||||
* <p>Supports resources in OSGi bundles, JBoss VFS, jar files, zip files,
|
||||
* and file systems.
|
||||
* Find all resources that match the given location pattern via the
|
||||
* Ant-style PathMatcher. Supports resources in OSGi bundles, JBoss VFS,
|
||||
* jar files, zip files, and file systems.
|
||||
* @param locationPattern the location pattern to match
|
||||
* @return the result as Resource array
|
||||
* @throws IOException in case of I/O errors
|
||||
* @see #determineRootDir(String)
|
||||
* @see #resolveRootDirResource(Resource)
|
||||
* @see #isJarResource(Resource)
|
||||
* @see #doFindPathMatchingJarResources(Resource, URL, String)
|
||||
* @see #doFindPathMatchingFileResources(Resource, String)
|
||||
* @see #doFindPathMatchingJarResources
|
||||
* @see #doFindPathMatchingFileResources
|
||||
* @see org.springframework.util.PathMatcher
|
||||
*/
|
||||
protected Resource[] findPathMatchingResources(String locationPattern) throws IOException {
|
||||
@@ -618,39 +607,29 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the supplied root directory resource for path matching.
|
||||
* <p>By default, {@link #findPathMatchingResources(String)} resolves Equinox
|
||||
* OSGi "bundleresource:" and "bundleentry:" URLs into standard jar file URLs
|
||||
* that will be traversed using Spring's standard jar file traversal algorithm.
|
||||
* <p>For any custom resolution, override this template method and replace the
|
||||
* supplied resource handle accordingly.
|
||||
* <p>The default implementation of this method returns the supplied resource
|
||||
* unmodified.
|
||||
* Resolve the specified resource for path matching.
|
||||
* <p>By default, Equinox OSGi "bundleresource:" / "bundleentry:" URL will be
|
||||
* resolved into a standard jar file URL that be traversed using Spring's
|
||||
* standard jar file traversal algorithm. For any preceding custom resolution,
|
||||
* override this method and replace the resource handle accordingly.
|
||||
* @param original the resource to resolve
|
||||
* @return the resolved resource (may be identical to the supplied resource)
|
||||
* @return the resolved resource (may be identical to the passed-in resource)
|
||||
* @throws IOException in case of resolution failure
|
||||
* @see #findPathMatchingResources(String)
|
||||
*/
|
||||
protected Resource resolveRootDirResource(Resource original) throws IOException {
|
||||
return original;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given resource handle indicates a jar resource that the
|
||||
* {@link #doFindPathMatchingJarResources} method can handle.
|
||||
* <p>{@link #findPathMatchingResources(String)} delegates to
|
||||
* {@link ResourceUtils#isJarURL(URL)} to determine whether the given URL
|
||||
* points to a resource in a jar file, and only invokes this method as a fallback.
|
||||
* <p>This template method therefore allows for detecting further kinds of
|
||||
* jar-like resources — for example, via {@code instanceof} checks on
|
||||
* the resource handle type.
|
||||
* <p>The default implementation of this method returns {@code false}.
|
||||
* @param resource the resource handle to check (usually the root directory
|
||||
* to start path matching from)
|
||||
* @return {@code true} if the given resource handle indicates a jar resource
|
||||
* @throws IOException in case of I/O errors
|
||||
* @see #findPathMatchingResources(String)
|
||||
* @see #doFindPathMatchingJarResources(Resource, URL, String)
|
||||
* Return whether the given resource handle indicates a jar resource
|
||||
* that the {@link #doFindPathMatchingJarResources} method can handle.
|
||||
* <p>By default, the URL protocols "jar", "zip", "vfszip, and "wsjar"
|
||||
* will be treated as jar resources. This template method allows for
|
||||
* detecting further kinds of jar-like resources, e.g. through
|
||||
* {@code instanceof} checks on the resource handle type.
|
||||
* @param resource the resource handle to check
|
||||
* (usually the root directory to start path matching from)
|
||||
* @see #doFindPathMatchingJarResources
|
||||
* @see org.springframework.util.ResourceUtils#isJarURL
|
||||
*/
|
||||
protected boolean isJarResource(Resource resource) throws IOException {
|
||||
@@ -659,7 +638,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
|
||||
/**
|
||||
* Find all resources in jar files that match the given location pattern
|
||||
* via the Ant-style {@link #getPathMatcher() PathMatcher}.
|
||||
* via the Ant-style PathMatcher.
|
||||
* @param rootDirResource the root directory as Resource
|
||||
* @param rootDirUrl the pre-resolved root directory URL
|
||||
* @param subPattern the sub pattern to match (below the root directory)
|
||||
@@ -712,7 +691,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
}
|
||||
catch (ZipException ex) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Skipping invalid jar class path entry [" + urlFile + "]");
|
||||
logger.debug("Skipping invalid jar classpath entry [" + urlFile + "]");
|
||||
}
|
||||
return Collections.emptySet();
|
||||
}
|
||||
@@ -767,8 +746,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
|
||||
/**
|
||||
* Find all resources in the file system of the supplied root directory that
|
||||
* match the given location sub pattern via the Ant-style {@link #getPathMatcher()
|
||||
* PathMatcher}.
|
||||
* match the given location sub pattern via the Ant-style PathMatcher.
|
||||
* @param rootDirResource the root directory as a Resource
|
||||
* @param subPattern the sub pattern to match (below the root directory)
|
||||
* @return a mutable Set of matching Resource instances
|
||||
@@ -946,8 +924,8 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
}
|
||||
|
||||
/**
|
||||
* If it's a "file:" URI, use {@link FileSystemResource} to avoid duplicates
|
||||
* for the same path discovered via class path scanning.
|
||||
* If it's a "file:" URI, use FileSystemResource to avoid duplicates
|
||||
* for the same path discovered via class-path scanning.
|
||||
*/
|
||||
private Resource convertModuleSystemURI(URI uri) {
|
||||
return (ResourceUtils.URL_PROTOCOL_FILE.equals(uri.getScheme()) ?
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -59,7 +59,7 @@ public class MultiValueMapAdapter<K, V> implements MultiValueMap<K, V>, Serializ
|
||||
@Nullable
|
||||
public V getFirst(K key) {
|
||||
List<V> values = this.targetMap.get(key);
|
||||
return (!CollectionUtils.isEmpty(values) ? values.get(0) : null);
|
||||
return (values != null && !values.isEmpty() ? values.get(0) : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -95,7 +95,7 @@ public class MultiValueMapAdapter<K, V> implements MultiValueMap<K, V>, Serializ
|
||||
public Map<K, V> toSingleValueMap() {
|
||||
Map<K, V> singleValueMap = CollectionUtils.newLinkedHashMap(this.targetMap.size());
|
||||
this.targetMap.forEach((key, values) -> {
|
||||
if (!CollectionUtils.isEmpty(values)) {
|
||||
if (values != null && !values.isEmpty()) {
|
||||
singleValueMap.put(key, values.get(0));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -100,7 +100,6 @@ public abstract class ResourceUtils {
|
||||
* @return whether the location qualifies as a URL
|
||||
* @see #CLASSPATH_URL_PREFIX
|
||||
* @see java.net.URL
|
||||
* @see #toURL(String)
|
||||
*/
|
||||
public static boolean isUrl(@Nullable String resourceLocation) {
|
||||
if (resourceLocation == null) {
|
||||
@@ -126,7 +125,6 @@ public abstract class ResourceUtils {
|
||||
* "classpath:" pseudo URL, a "file:" URL, or a plain file path
|
||||
* @return a corresponding URL object
|
||||
* @throws FileNotFoundException if the resource cannot be resolved to a URL
|
||||
* @see #toURL(String)
|
||||
*/
|
||||
public static URL getURL(String resourceLocation) throws FileNotFoundException {
|
||||
Assert.notNull(resourceLocation, "Resource location must not be null");
|
||||
@@ -167,7 +165,6 @@ public abstract class ResourceUtils {
|
||||
* @return a corresponding File object
|
||||
* @throws FileNotFoundException if the resource cannot be resolved to
|
||||
* a file in the file system
|
||||
* @see #getFile(URL)
|
||||
*/
|
||||
public static File getFile(String resourceLocation) throws FileNotFoundException {
|
||||
Assert.notNull(resourceLocation, "Resource location must not be null");
|
||||
@@ -199,7 +196,6 @@ public abstract class ResourceUtils {
|
||||
* @return a corresponding File object
|
||||
* @throws FileNotFoundException if the URL cannot be resolved to
|
||||
* a file in the file system
|
||||
* @see #getFile(URL, String)
|
||||
*/
|
||||
public static File getFile(URL resourceUrl) throws FileNotFoundException {
|
||||
return getFile(resourceUrl, "URL");
|
||||
@@ -240,7 +236,6 @@ public abstract class ResourceUtils {
|
||||
* @throws FileNotFoundException if the URL cannot be resolved to
|
||||
* a file in the file system
|
||||
* @since 2.5
|
||||
* @see #getFile(URI, String)
|
||||
*/
|
||||
public static File getFile(URI resourceUri) throws FileNotFoundException {
|
||||
return getFile(resourceUri, "URI");
|
||||
@@ -272,7 +267,6 @@ public abstract class ResourceUtils {
|
||||
* i.e. has protocol "file", "vfsfile" or "vfs".
|
||||
* @param url the URL to check
|
||||
* @return whether the URL has been identified as a file system URL
|
||||
* @see #isJarURL(URL)
|
||||
*/
|
||||
public static boolean isFileURL(URL url) {
|
||||
String protocol = url.getProtocol();
|
||||
@@ -281,12 +275,10 @@ public abstract class ResourceUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the given URL points to a resource in a jar file
|
||||
* — for example, whether the URL has protocol "jar", "war, "zip",
|
||||
* "vfszip", or "wsjar".
|
||||
* Determine whether the given URL points to a resource in a jar file.
|
||||
* i.e. has protocol "jar", "war, ""zip", "vfszip" or "wsjar".
|
||||
* @param url the URL to check
|
||||
* @return whether the URL has been identified as a JAR URL
|
||||
* @see #isJarFileURL(URL)
|
||||
*/
|
||||
public static boolean isJarURL(URL url) {
|
||||
String protocol = url.getProtocol();
|
||||
@@ -301,7 +293,6 @@ public abstract class ResourceUtils {
|
||||
* @param url the URL to check
|
||||
* @return whether the URL has been identified as a JAR file URL
|
||||
* @since 4.1
|
||||
* @see #extractJarFileURL(URL)
|
||||
*/
|
||||
public static boolean isJarFileURL(URL url) {
|
||||
return (URL_PROTOCOL_FILE.equals(url.getProtocol()) &&
|
||||
@@ -314,7 +305,6 @@ public abstract class ResourceUtils {
|
||||
* @param jarUrl the original URL
|
||||
* @return the URL for the actual jar file
|
||||
* @throws MalformedURLException if no valid jar file URL could be extracted
|
||||
* @see #extractArchiveURL(URL)
|
||||
*/
|
||||
public static URL extractJarFileURL(URL jarUrl) throws MalformedURLException {
|
||||
String urlFile = jarUrl.getFile();
|
||||
@@ -376,7 +366,6 @@ public abstract class ResourceUtils {
|
||||
* @return the URI instance
|
||||
* @throws URISyntaxException if the URL wasn't a valid URI
|
||||
* @see java.net.URL#toURI()
|
||||
* @see #toURI(String)
|
||||
*/
|
||||
public static URI toURI(URL url) throws URISyntaxException {
|
||||
return toURI(url.toString());
|
||||
@@ -388,7 +377,6 @@ public abstract class ResourceUtils {
|
||||
* @param location the location String to convert into a URI instance
|
||||
* @return the URI instance
|
||||
* @throws URISyntaxException if the location wasn't a valid URI
|
||||
* @see #toURI(URL)
|
||||
*/
|
||||
public static URI toURI(String location) throws URISyntaxException {
|
||||
return new URI(StringUtils.replace(location, " ", "%20"));
|
||||
@@ -401,7 +389,6 @@ public abstract class ResourceUtils {
|
||||
* @return the URL instance
|
||||
* @throws MalformedURLException if the location wasn't a valid URL
|
||||
* @since 6.0
|
||||
* @see java.net.URL#URL(String)
|
||||
*/
|
||||
public static URL toURL(String location) throws MalformedURLException {
|
||||
// Equivalent without java.net.URL constructor - for building on JDK 20+
|
||||
@@ -427,7 +414,6 @@ public abstract class ResourceUtils {
|
||||
* @return the relative URL instance
|
||||
* @throws MalformedURLException if the end result is not a valid URL
|
||||
* @since 6.0
|
||||
* @see java.net.URL#URL(URL, String)
|
||||
*/
|
||||
public static URL toRelativeURL(URL root, String relativePath) throws MalformedURLException {
|
||||
// # can appear in filenames, java.net.URL should not treat it as a fragment
|
||||
@@ -443,10 +429,9 @@ public abstract class ResourceUtils {
|
||||
|
||||
/**
|
||||
* Set the {@link URLConnection#setUseCaches "useCaches"} flag on the
|
||||
* given connection, preferring {@code false} but leaving the flag at
|
||||
* {@code true} for JNLP based resources.
|
||||
* given connection, preferring {@code false} but leaving the
|
||||
* flag at {@code true} for JNLP based resources.
|
||||
* @param con the URLConnection to set the flag on
|
||||
* @see URLConnection#setUseCaches
|
||||
*/
|
||||
public static void useCachesIfNecessary(URLConnection con) {
|
||||
con.setUseCaches(con.getClass().getSimpleName().startsWith("JNLP"));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -123,7 +123,7 @@ public abstract class StringUtils {
|
||||
* @see #hasText(CharSequence)
|
||||
*/
|
||||
public static boolean hasLength(@Nullable CharSequence str) {
|
||||
return (str != null && !str.isEmpty()); // as of JDK 15
|
||||
return (str != null && str.length() > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -791,7 +791,6 @@ public abstract class StringUtils {
|
||||
* and {@code "0"} through {@code "9"} stay the same.</li>
|
||||
* <li>Special characters {@code "-"}, {@code "_"}, {@code "."}, and {@code "*"} stay the same.</li>
|
||||
* <li>A sequence "{@code %<i>xy</i>}" is interpreted as a hexadecimal representation of the character.</li>
|
||||
* <li>For all other characters (including those already decoded), the output is undefined.</li>
|
||||
* </ul>
|
||||
* @param source the encoded String
|
||||
* @param charset the character set
|
||||
@@ -840,7 +839,7 @@ public abstract class StringUtils {
|
||||
* the {@link Locale#toString} format as well as BCP 47 language tags as
|
||||
* specified by {@link Locale#forLanguageTag}.
|
||||
* @param localeValue the locale value: following either {@code Locale's}
|
||||
* {@code toString()} format ("en", "en_UK", etc.), also accepting spaces as
|
||||
* {@code toString()} format ("en", "en_UK", etc), also accepting spaces as
|
||||
* separators (as an alternative to underscores), or BCP 47 (e.g. "en-UK")
|
||||
* @return a corresponding {@code Locale} instance, or {@code null} if none
|
||||
* @throws IllegalArgumentException in case of an invalid locale specification
|
||||
@@ -853,7 +852,7 @@ public abstract class StringUtils {
|
||||
if (!localeValue.contains("_") && !localeValue.contains(" ")) {
|
||||
validateLocalePart(localeValue);
|
||||
Locale resolved = Locale.forLanguageTag(localeValue);
|
||||
if (!resolved.getLanguage().isEmpty()) {
|
||||
if (resolved.getLanguage().length() > 0) {
|
||||
return resolved;
|
||||
}
|
||||
}
|
||||
@@ -869,7 +868,7 @@ public abstract class StringUtils {
|
||||
* <p><b>Note: This delegate does not accept the BCP 47 language tag format.
|
||||
* Please use {@link #parseLocale} for lenient parsing of both formats.</b>
|
||||
* @param localeString the locale {@code String}: following {@code Locale's}
|
||||
* {@code toString()} format ("en", "en_UK", etc.), also accepting spaces as
|
||||
* {@code toString()} format ("en", "en_UK", etc), also accepting spaces as
|
||||
* separators (as an alternative to underscores)
|
||||
* @return a corresponding {@code Locale} instance, or {@code null} if none
|
||||
* @throws IllegalArgumentException in case of an invalid locale specification
|
||||
@@ -877,7 +876,7 @@ public abstract class StringUtils {
|
||||
@SuppressWarnings("deprecation") // for Locale constructors on JDK 19
|
||||
@Nullable
|
||||
public static Locale parseLocaleString(String localeString) {
|
||||
if (localeString.isEmpty()) {
|
||||
if (localeString.equals("")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1182,7 +1181,7 @@ public abstract class StringUtils {
|
||||
if (trimTokens) {
|
||||
token = token.trim();
|
||||
}
|
||||
if (!ignoreEmptyTokens || !token.isEmpty()) {
|
||||
if (!ignoreEmptyTokens || token.length() > 0) {
|
||||
tokens.add(token);
|
||||
}
|
||||
}
|
||||
@@ -1244,7 +1243,7 @@ public abstract class StringUtils {
|
||||
result.add(deleteAny(str.substring(pos, delPos), charsToDelete));
|
||||
pos = delPos + delimiter.length();
|
||||
}
|
||||
if (!str.isEmpty() && pos <= str.length()) {
|
||||
if (str.length() > 0 && pos <= str.length()) {
|
||||
// Add rest of String, but not in case of empty input.
|
||||
result.add(deleteAny(str.substring(pos), charsToDelete));
|
||||
}
|
||||
|
||||
+1
-63
@@ -24,7 +24,6 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -176,7 +175,7 @@ class MergedAnnotationsRepeatableAnnotationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void typeHierarchyWhenOnSuperclassReturnsAnnotations() {
|
||||
void typeHierarchyWhenWhenOnSuperclassReturnsAnnotations() {
|
||||
Set<PeteRepeat> annotations = getAnnotations(null, PeteRepeat.class,
|
||||
SearchStrategy.TYPE_HIERARCHY, SubRepeatableClass.class);
|
||||
assertThat(annotations.stream().map(PeteRepeat::value)).containsExactly("A", "B",
|
||||
@@ -241,44 +240,6 @@ class MergedAnnotationsRepeatableAnnotationTests {
|
||||
assertThat(annotationTypes).containsExactly(WithRepeatedMetaAnnotations.class, Noninherited.class, Noninherited.class);
|
||||
}
|
||||
|
||||
@Test // gh-32731
|
||||
void searchFindsRepeatableContainerAnnotationAndRepeatedAnnotations() {
|
||||
Class<?> clazz = StandardRepeatablesWithContainerWithMultipleAttributesTestCase.class;
|
||||
|
||||
// NO RepeatableContainers
|
||||
MergedAnnotations mergedAnnotations = MergedAnnotations.from(clazz, SearchStrategy.TYPE_HIERARCHY, RepeatableContainers.none());
|
||||
ContainerWithMultipleAttributes container = mergedAnnotations
|
||||
.get(ContainerWithMultipleAttributes.class)
|
||||
.synthesize(MergedAnnotation::isPresent).orElse(null);
|
||||
assertThat(container).as("container").isNotNull();
|
||||
assertThat(container.name()).isEqualTo("enigma");
|
||||
RepeatableWithContainerWithMultipleAttributes[] repeatedAnnotations = container.value();
|
||||
assertThat(Arrays.stream(repeatedAnnotations).map(RepeatableWithContainerWithMultipleAttributes::value))
|
||||
.containsExactly("A", "B");
|
||||
Set<RepeatableWithContainerWithMultipleAttributes> set =
|
||||
mergedAnnotations.stream(RepeatableWithContainerWithMultipleAttributes.class)
|
||||
.collect(MergedAnnotationCollectors.toAnnotationSet());
|
||||
// Only finds the locally declared repeated annotation.
|
||||
assertThat(set.stream().map(RepeatableWithContainerWithMultipleAttributes::value))
|
||||
.containsExactly("C");
|
||||
|
||||
// Standard RepeatableContainers
|
||||
mergedAnnotations = MergedAnnotations.from(clazz, SearchStrategy.TYPE_HIERARCHY, RepeatableContainers.standardRepeatables());
|
||||
container = mergedAnnotations
|
||||
.get(ContainerWithMultipleAttributes.class)
|
||||
.synthesize(MergedAnnotation::isPresent).orElse(null);
|
||||
assertThat(container).as("container").isNotNull();
|
||||
assertThat(container.name()).isEqualTo("enigma");
|
||||
repeatedAnnotations = container.value();
|
||||
assertThat(Arrays.stream(repeatedAnnotations).map(RepeatableWithContainerWithMultipleAttributes::value))
|
||||
.containsExactly("A", "B");
|
||||
set = mergedAnnotations.stream(RepeatableWithContainerWithMultipleAttributes.class)
|
||||
.collect(MergedAnnotationCollectors.toAnnotationSet());
|
||||
// Finds the locally declared repeated annotation plus the 2 in the container.
|
||||
assertThat(set.stream().map(RepeatableWithContainerWithMultipleAttributes::value))
|
||||
.containsExactly("A", "B", "C");
|
||||
}
|
||||
|
||||
private <A extends Annotation> Set<A> getAnnotations(Class<? extends Annotation> container,
|
||||
Class<A> repeatable, SearchStrategy searchStrategy, AnnotatedElement element) {
|
||||
|
||||
@@ -488,27 +449,4 @@ class MergedAnnotationsRepeatableAnnotationTests {
|
||||
static class WithRepeatedMetaAnnotationsClass {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface ContainerWithMultipleAttributes {
|
||||
|
||||
RepeatableWithContainerWithMultipleAttributes[] value();
|
||||
|
||||
String name() default "";
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Repeatable(ContainerWithMultipleAttributes.class)
|
||||
@interface RepeatableWithContainerWithMultipleAttributes {
|
||||
|
||||
String value() default "";
|
||||
}
|
||||
|
||||
@ContainerWithMultipleAttributes(name = "enigma", value = {
|
||||
@RepeatableWithContainerWithMultipleAttributes("A"),
|
||||
@RepeatableWithContainerWithMultipleAttributes("B")
|
||||
})
|
||||
@RepeatableWithContainerWithMultipleAttributes("C")
|
||||
static class StandardRepeatablesWithContainerWithMultipleAttributesTestCase {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-11
@@ -16,9 +16,7 @@
|
||||
|
||||
package org.springframework.core.env;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -302,12 +300,6 @@ class StandardEnvironmentTests {
|
||||
assertThat(systemProperties.get(DISALLOWED_PROPERTY_NAME)).isEqualTo(DISALLOWED_PROPERTY_VALUE);
|
||||
assertThat(systemProperties.get(STRING_PROPERTY_NAME)).isEqualTo(NON_STRING_PROPERTY_VALUE);
|
||||
assertThat(systemProperties.get(NON_STRING_PROPERTY_NAME)).isEqualTo(STRING_PROPERTY_VALUE);
|
||||
|
||||
PropertiesPropertySource systemPropertySource = (PropertiesPropertySource)
|
||||
environment.getPropertySources().get(StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME);
|
||||
Set<String> expectedKeys = new HashSet<>(System.getProperties().stringPropertyNames());
|
||||
expectedKeys.add(STRING_PROPERTY_NAME); // filtered out by stringPropertyNames due to non-String value
|
||||
assertThat(Set.of(systemPropertySource.getPropertyNames())).isEqualTo(expectedKeys);
|
||||
}
|
||||
finally {
|
||||
System.clearProperty(ALLOWED_PROPERTY_NAME);
|
||||
@@ -324,7 +316,6 @@ class StandardEnvironmentTests {
|
||||
assertThat(System.getenv()).isSameAs(systemEnvironment);
|
||||
}
|
||||
|
||||
|
||||
@Nested
|
||||
class GetActiveProfiles {
|
||||
|
||||
@@ -374,7 +365,6 @@ class StandardEnvironmentTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Nested
|
||||
class AcceptsProfilesTests {
|
||||
|
||||
@@ -457,8 +447,8 @@ class StandardEnvironmentTests {
|
||||
environment.addActiveProfile("p2");
|
||||
assertThat(environment.acceptsProfiles(Profiles.of("p1 & p2"))).isTrue();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
class MatchesProfilesTests {
|
||||
@@ -569,6 +559,7 @@ class StandardEnvironmentTests {
|
||||
assertThat(environment.matchesProfiles("p2 & (foo | p1)")).isTrue();
|
||||
assertThat(environment.matchesProfiles("foo", "(p2 & p1)")).isTrue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -45,7 +45,6 @@ class StreamUtilsTests {
|
||||
|
||||
private String string = "";
|
||||
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
new Random().nextBytes(bytes);
|
||||
@@ -54,7 +53,6 @@ class StreamUtilsTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void copyToByteArray() throws Exception {
|
||||
InputStream inputStream = new ByteArrayInputStream(bytes);
|
||||
@@ -129,5 +127,4 @@ class StreamUtilsTests {
|
||||
ordered.verify(source).write(bytes, 1, 2);
|
||||
ordered.verify(source, never()).close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+12
-13
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -225,8 +225,6 @@ public class Indexer extends SpelNodeImpl {
|
||||
cf.loadTarget(mv);
|
||||
}
|
||||
|
||||
SpelNodeImpl index = this.children[0];
|
||||
|
||||
if (this.indexedType == IndexedType.ARRAY) {
|
||||
int insn;
|
||||
if ("D".equals(this.exitTypeDescriptor)) {
|
||||
@@ -263,14 +261,18 @@ public class Indexer extends SpelNodeImpl {
|
||||
//depthPlusOne(exitTypeDescriptor)+"Ljava/lang/Object;");
|
||||
insn = AALOAD;
|
||||
}
|
||||
|
||||
generateIndexCode(mv, cf, index, int.class);
|
||||
SpelNodeImpl index = this.children[0];
|
||||
cf.enterCompilationScope();
|
||||
index.generateCode(mv, cf);
|
||||
cf.exitCompilationScope();
|
||||
mv.visitInsn(insn);
|
||||
}
|
||||
|
||||
else if (this.indexedType == IndexedType.LIST) {
|
||||
mv.visitTypeInsn(CHECKCAST, "java/util/List");
|
||||
generateIndexCode(mv, cf, index, int.class);
|
||||
cf.enterCompilationScope();
|
||||
this.children[0].generateCode(mv, cf);
|
||||
cf.exitCompilationScope();
|
||||
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "get", "(I)Ljava/lang/Object;", true);
|
||||
}
|
||||
|
||||
@@ -278,12 +280,14 @@ public class Indexer extends SpelNodeImpl {
|
||||
mv.visitTypeInsn(CHECKCAST, "java/util/Map");
|
||||
// Special case when the key is an unquoted string literal that will be parsed as
|
||||
// a property/field reference
|
||||
if (index instanceof PropertyOrFieldReference reference) {
|
||||
if ((this.children[0] instanceof PropertyOrFieldReference reference)) {
|
||||
String mapKeyName = reference.getName();
|
||||
mv.visitLdcInsn(mapKeyName);
|
||||
}
|
||||
else {
|
||||
generateIndexCode(mv, cf, index, Object.class);
|
||||
cf.enterCompilationScope();
|
||||
this.children[0].generateCode(mv, cf);
|
||||
cf.exitCompilationScope();
|
||||
}
|
||||
mv.visitMethodInsn(
|
||||
INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;", true);
|
||||
@@ -319,11 +323,6 @@ public class Indexer extends SpelNodeImpl {
|
||||
cf.pushDescriptor(this.exitTypeDescriptor);
|
||||
}
|
||||
|
||||
private void generateIndexCode(MethodVisitor mv, CodeFlow cf, SpelNodeImpl indexNode, Class<?> indexType) {
|
||||
String indexDesc = CodeFlow.toDescriptor(indexType);
|
||||
generateCodeForArgument(mv, cf, indexNode, indexDesc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toStringAST() {
|
||||
StringJoiner sj = new StringJoiner(",", "[", "]");
|
||||
|
||||
+1
-489
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,8 +20,6 @@ import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@@ -29,10 +27,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
@@ -137,489 +132,6 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
|
||||
private SpelNodeImpl ast;
|
||||
|
||||
|
||||
@Nested
|
||||
class VariableReferenceTests {
|
||||
|
||||
@Test
|
||||
void userDefinedVariable() {
|
||||
EvaluationContext ctx = new StandardEvaluationContext();
|
||||
ctx.setVariable("target", "abc");
|
||||
expression = parser.parseExpression("#target");
|
||||
assertThat(expression.getValue(ctx)).isEqualTo("abc");
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(ctx)).isEqualTo("abc");
|
||||
ctx.setVariable("target", "123");
|
||||
assertThat(expression.getValue(ctx)).isEqualTo("123");
|
||||
|
||||
// Changing the variable type from String to Integer results in a
|
||||
// ClassCastException in the compiled code.
|
||||
ctx.setVariable("target", 42);
|
||||
assertThatExceptionOfType(SpelEvaluationException.class)
|
||||
.isThrownBy(() -> expression.getValue(ctx))
|
||||
.withCauseInstanceOf(ClassCastException.class);
|
||||
|
||||
ctx.setVariable("target", "abc");
|
||||
expression = parser.parseExpression("#target.charAt(0)");
|
||||
assertThat(expression.getValue(ctx)).isEqualTo('a');
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(ctx)).isEqualTo('a');
|
||||
ctx.setVariable("target", "1");
|
||||
assertThat(expression.getValue(ctx)).isEqualTo('1');
|
||||
|
||||
// Changing the variable type from String to Integer results in a
|
||||
// ClassCastException in the compiled code.
|
||||
ctx.setVariable("target", 42);
|
||||
assertThatExceptionOfType(SpelEvaluationException.class)
|
||||
.isThrownBy(() -> expression.getValue(ctx))
|
||||
.withCauseInstanceOf(ClassCastException.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
class IndexingTests {
|
||||
|
||||
@Test
|
||||
void indexIntoPrimitiveShortArray() {
|
||||
short[] shorts = { (short) 33, (short) 44, (short) 55 };
|
||||
|
||||
expression = parser.parseExpression("[2]");
|
||||
|
||||
assertThat(expression.getValue(shorts)).isEqualTo((short) 55);
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(shorts)).isEqualTo((short) 55);
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("S");
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexIntoPrimitiveByteArray() {
|
||||
byte[] bytes = { (byte) 2, (byte) 3, (byte) 4 };
|
||||
|
||||
expression = parser.parseExpression("[2]");
|
||||
|
||||
assertThat(expression.getValue(bytes)).isEqualTo((byte) 4);
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(bytes)).isEqualTo((byte) 4);
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("B");
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexIntoPrimitiveIntArray() {
|
||||
int[] ints = { 8, 9, 10 };
|
||||
|
||||
expression = parser.parseExpression("[2]");
|
||||
|
||||
assertThat(expression.getValue(ints)).isEqualTo(10);
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(ints)).isEqualTo(10);
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("I");
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexIntoPrimitiveLongArray() {
|
||||
long[] longs = { 2L, 3L, 4L };
|
||||
|
||||
expression = parser.parseExpression("[0]");
|
||||
|
||||
assertThat(expression.getValue(longs)).isEqualTo(2L);
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(longs)).isEqualTo(2L);
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("J");
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexIntoPrimitiveFloatArray() {
|
||||
float[] floats = { 6.0f, 7.0f, 8.0f };
|
||||
|
||||
expression = parser.parseExpression("[0]");
|
||||
|
||||
assertThat(expression.getValue(floats)).isEqualTo(6.0f);
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(floats)).isEqualTo(6.0f);
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("F");
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexIntoPrimitiveDoubleArray() {
|
||||
double[] doubles = { 3.0d, 4.0d, 5.0d };
|
||||
|
||||
expression = parser.parseExpression("[1]");
|
||||
|
||||
assertThat(expression.getValue(doubles)).isEqualTo(4.0d);
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(doubles)).isEqualTo(4.0d);
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("D");
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexIntoPrimitiveCharArray() {
|
||||
char[] chars = { 'a', 'b', 'c' };
|
||||
|
||||
expression = parser.parseExpression("[1]");
|
||||
|
||||
assertThat(expression.getValue(chars)).isEqualTo('b');
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(chars)).isEqualTo('b');
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("C");
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexIntoStringArray() {
|
||||
String[] strings = { "a", "b", "c" };
|
||||
|
||||
expression = parser.parseExpression("[0]");
|
||||
|
||||
assertThat(expression.getValue(strings)).isEqualTo("a");
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(strings)).isEqualTo("a");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/String");
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexIntoNumberArray() {
|
||||
Number[] numbers = { 2, 8, 9 };
|
||||
|
||||
expression = parser.parseExpression("[1]");
|
||||
|
||||
assertThat(expression.getValue(numbers)).isEqualTo(8);
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(numbers)).isEqualTo(8);
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Number");
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexInto2DPrimitiveIntArray() {
|
||||
int[][] array = new int[][] {
|
||||
{ 1, 2, 3 },
|
||||
{ 4, 5, 6 }
|
||||
};
|
||||
|
||||
expression = parser.parseExpression("[1]");
|
||||
|
||||
assertThat(stringify(expression.getValue(array))).isEqualTo("4 5 6");
|
||||
assertCanCompile(expression);
|
||||
assertThat(stringify(expression.getValue(array))).isEqualTo("4 5 6");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("[I");
|
||||
|
||||
expression = parser.parseExpression("[1][2]");
|
||||
|
||||
assertThat(stringify(expression.getValue(array))).isEqualTo("6");
|
||||
assertCanCompile(expression);
|
||||
assertThat(stringify(expression.getValue(array))).isEqualTo("6");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("I");
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexInto2DStringArray() {
|
||||
String[][] array = new String[][] {
|
||||
{ "a", "b", "c" },
|
||||
{ "d", "e", "f" }
|
||||
};
|
||||
|
||||
expression = parser.parseExpression("[1]");
|
||||
|
||||
assertThat(stringify(expression.getValue(array))).isEqualTo("d e f");
|
||||
assertCanCompile(expression);
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("[Ljava/lang/String");
|
||||
assertThat(stringify(expression.getValue(array))).isEqualTo("d e f");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("[Ljava/lang/String");
|
||||
|
||||
expression = parser.parseExpression("[1][2]");
|
||||
|
||||
assertThat(stringify(expression.getValue(array))).isEqualTo("f");
|
||||
assertCanCompile(expression);
|
||||
assertThat(stringify(expression.getValue(array))).isEqualTo("f");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/String");
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void indexIntoArrayOfListOfString() {
|
||||
List<String>[] array = new List[] {
|
||||
List.of("a", "b", "c"),
|
||||
List.of("d", "e", "f")
|
||||
};
|
||||
|
||||
expression = parser.parseExpression("[1]");
|
||||
|
||||
assertThat(stringify(expression.getValue(array))).isEqualTo("d e f");
|
||||
assertCanCompile(expression);
|
||||
assertThat(stringify(expression.getValue(array))).isEqualTo("d e f");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/util/List");
|
||||
|
||||
expression = parser.parseExpression("[1][2]");
|
||||
|
||||
assertThat(stringify(expression.getValue(array))).isEqualTo("f");
|
||||
assertCanCompile(expression);
|
||||
assertThat(stringify(expression.getValue(array))).isEqualTo("f");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void indexIntoArrayOfMap() {
|
||||
Map<String, String>[] array = new Map[] { Map.of("key", "value1") };
|
||||
|
||||
expression = parser.parseExpression("[0]");
|
||||
|
||||
assertThat(stringify(expression.getValue(array))).isEqualTo("{key=value1}");
|
||||
assertCanCompile(expression);
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/util/Map");
|
||||
assertThat(stringify(expression.getValue(array))).isEqualTo("{key=value1}");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/util/Map");
|
||||
|
||||
expression = parser.parseExpression("[0]['key']");
|
||||
|
||||
assertThat(stringify(expression.getValue(array))).isEqualTo("value1");
|
||||
assertCanCompile(expression);
|
||||
assertThat(stringify(expression.getValue(array))).isEqualTo("value1");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexIntoListOfString() {
|
||||
List<String> list = List.of("aaa", "bbb", "ccc");
|
||||
|
||||
expression = parser.parseExpression("[1]");
|
||||
|
||||
assertThat(expression.getValue(list)).isEqualTo("bbb");
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(list)).isEqualTo("bbb");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexIntoListOfInteger() {
|
||||
List<Integer> list = List.of(123, 456, 789);
|
||||
|
||||
expression = parser.parseExpression("[2]");
|
||||
|
||||
assertThat(expression.getValue(list)).isEqualTo(789);
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(list)).isEqualTo(789);
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexIntoListOfStringArray() {
|
||||
List<String[]> list = List.of(
|
||||
new String[] { "a", "b", "c" },
|
||||
new String[] { "d", "e", "f" }
|
||||
);
|
||||
|
||||
expression = parser.parseExpression("[1]");
|
||||
|
||||
assertThat(stringify(expression.getValue(list))).isEqualTo("d e f");
|
||||
assertCanCompile(expression);
|
||||
assertThat(stringify(expression.getValue(list))).isEqualTo("d e f");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
|
||||
|
||||
expression = parser.parseExpression("[1][0]");
|
||||
|
||||
assertThat(stringify(expression.getValue(list))).isEqualTo("d");
|
||||
assertCanCompile(expression);
|
||||
assertThat(stringify(expression.getValue(list))).isEqualTo("d");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/String");
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexIntoListOfIntegerArray() {
|
||||
List<Integer[]> list = List.of(
|
||||
new Integer[] { 1, 2, 3 },
|
||||
new Integer[] { 4, 5, 6 }
|
||||
);
|
||||
|
||||
expression = parser.parseExpression("[0]");
|
||||
|
||||
assertThat(stringify(expression.getValue(list))).isEqualTo("1 2 3");
|
||||
assertCanCompile(expression);
|
||||
assertThat(stringify(expression.getValue(list))).isEqualTo("1 2 3");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
|
||||
|
||||
expression = parser.parseExpression("[0][1]");
|
||||
|
||||
assertThat(expression.getValue(list)).isEqualTo(2);
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(list)).isEqualTo(2);
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Integer");
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexIntoListOfListOfString() {
|
||||
List<List<String>> list = List.of(
|
||||
List.of("a", "b", "c"),
|
||||
List.of("d", "e", "f")
|
||||
);
|
||||
|
||||
expression = parser.parseExpression("[1]");
|
||||
|
||||
assertThat(stringify(expression.getValue(list))).isEqualTo("d e f");
|
||||
assertCanCompile(expression);
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
|
||||
assertThat(stringify(expression.getValue(list))).isEqualTo("d e f");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
|
||||
|
||||
expression = parser.parseExpression("[1][2]");
|
||||
|
||||
assertThat(stringify(expression.getValue(list))).isEqualTo("f");
|
||||
assertCanCompile(expression);
|
||||
assertThat(stringify(expression.getValue(list))).isEqualTo("f");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexIntoMap() {
|
||||
Map<String, Integer> map = Map.of("aaa", 111);
|
||||
|
||||
expression = parser.parseExpression("['aaa']");
|
||||
|
||||
assertThat(expression.getValue(map)).isEqualTo(111);
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(map)).isEqualTo(111);
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexIntoMapOfListOfString() {
|
||||
Map<String, List<String>> map = Map.of("foo", List.of("a", "b", "c"));
|
||||
|
||||
expression = parser.parseExpression("['foo']");
|
||||
|
||||
assertThat(stringify(expression.getValue(map))).isEqualTo("a b c");
|
||||
assertCanCompile(expression);
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
|
||||
assertThat(stringify(expression.getValue(map))).isEqualTo("a b c");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
|
||||
|
||||
expression = parser.parseExpression("['foo'][2]");
|
||||
|
||||
assertThat(stringify(expression.getValue(map))).isEqualTo("c");
|
||||
assertCanCompile(expression);
|
||||
assertThat(stringify(expression.getValue(map))).isEqualTo("c");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexIntoObject() {
|
||||
TestClass6 tc = new TestClass6();
|
||||
|
||||
// field access
|
||||
expression = parser.parseExpression("['orange']");
|
||||
|
||||
assertThat(expression.getValue(tc)).isEqualTo("value1");
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(tc)).isEqualTo("value1");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/String");
|
||||
|
||||
// field access
|
||||
expression = parser.parseExpression("['peach']");
|
||||
|
||||
assertThat(expression.getValue(tc)).isEqualTo(34L);
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(tc)).isEqualTo(34L);
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("J");
|
||||
|
||||
// property access (getter)
|
||||
expression = parser.parseExpression("['banana']");
|
||||
|
||||
assertThat(expression.getValue(tc)).isEqualTo("value3");
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(tc)).isEqualTo("value3");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/String");
|
||||
}
|
||||
|
||||
@Test // gh-32694, gh-32908
|
||||
void indexIntoArrayUsingIntegerWrapper() {
|
||||
context.setVariable("array", new int[] {1, 2, 3, 4});
|
||||
context.setVariable("index", 2);
|
||||
|
||||
expression = parser.parseExpression("#array[#index]");
|
||||
|
||||
assertThat(expression.getValue(context)).isEqualTo(3);
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(context)).isEqualTo(3);
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("I");
|
||||
}
|
||||
|
||||
@Test // gh-32694, gh-32908
|
||||
void indexIntoListUsingIntegerWrapper() {
|
||||
context.setVariable("list", List.of(1, 2, 3, 4));
|
||||
context.setVariable("index", 2);
|
||||
|
||||
expression = parser.parseExpression("#list[#index]");
|
||||
|
||||
assertThat(expression.getValue(context)).isEqualTo(3);
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(context)).isEqualTo(3);
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
|
||||
}
|
||||
|
||||
@Test // gh-32903
|
||||
void indexIntoMapUsingPrimitiveLiteral() {
|
||||
Map<Object, String> map = Map.of(
|
||||
false, "0", // BooleanLiteral
|
||||
1, "ABC", // IntLiteral
|
||||
2L, "XYZ", // LongLiteral
|
||||
9.99F, "~10", // FloatLiteral
|
||||
3.14159, "PI" // RealLiteral
|
||||
);
|
||||
context.setVariable("map", map);
|
||||
|
||||
// BooleanLiteral
|
||||
expression = parser.parseExpression("#map[false]");
|
||||
assertThat(expression.getValue(context)).isEqualTo("0");
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(context)).isEqualTo("0");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
|
||||
|
||||
// IntLiteral
|
||||
expression = parser.parseExpression("#map[1]");
|
||||
assertThat(expression.getValue(context)).isEqualTo("ABC");
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(context)).isEqualTo("ABC");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
|
||||
|
||||
// LongLiteral
|
||||
expression = parser.parseExpression("#map[2L]");
|
||||
assertThat(expression.getValue(context)).isEqualTo("XYZ");
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(context)).isEqualTo("XYZ");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
|
||||
|
||||
// FloatLiteral
|
||||
expression = parser.parseExpression("#map[9.99F]");
|
||||
assertThat(expression.getValue(context)).isEqualTo("~10");
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(context)).isEqualTo("~10");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
|
||||
|
||||
// RealLiteral
|
||||
expression = parser.parseExpression("#map[3.14159]");
|
||||
assertThat(expression.getValue(context)).isEqualTo("PI");
|
||||
assertCanCompile(expression);
|
||||
assertThat(expression.getValue(context)).isEqualTo("PI");
|
||||
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
|
||||
}
|
||||
|
||||
private String stringify(Object object) {
|
||||
Stream<? extends Object> stream;
|
||||
if (object instanceof Collection<?> collection) {
|
||||
stream = collection.stream();
|
||||
}
|
||||
else if (object instanceof Object[] objects) {
|
||||
stream = Arrays.stream(objects);
|
||||
}
|
||||
else if (object instanceof int[] ints) {
|
||||
stream = Arrays.stream(ints).mapToObj(Integer::valueOf);
|
||||
}
|
||||
else {
|
||||
return String.valueOf(object);
|
||||
}
|
||||
return stream.map(Object::toString).collect(Collectors.joining(" "));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void typeReference() {
|
||||
expression = parse("T(String)");
|
||||
|
||||
+5
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,12 +21,11 @@ import java.util.Collections;
|
||||
import org.springframework.aot.hint.ExecutableMode;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* {@link RuntimeHintsRegistrar} implementation that registers reflection hints
|
||||
* for {@code EmbeddedDataSourceProxy#shutdown} in order to allow it to be used
|
||||
* as a bean destroy method.
|
||||
* {@link RuntimeHintsRegistrar} implementation that registers reflection hints for
|
||||
* {@code EmbeddedDataSourceProxy#shutdown} in order to allow it to be used as a bean
|
||||
* destroy method.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 6.0
|
||||
@@ -34,7 +33,7 @@ import org.springframework.lang.Nullable;
|
||||
class EmbeddedDatabaseFactoryRuntimeHints implements RuntimeHintsRegistrar {
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
|
||||
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
|
||||
hints.reflection().registerTypeIfPresent(classLoader,
|
||||
"org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseFactory$EmbeddedDataSourceProxy",
|
||||
builder -> builder
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -32,7 +32,7 @@ import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* The common implementation of Spring's {@link SqlRowSet} interface, wrapping a
|
||||
* The default implementation of Spring's {@link SqlRowSet} interface, wrapping a
|
||||
* {@link java.sql.ResultSet}, catching any {@link SQLException SQLExceptions} and
|
||||
* translating them to a corresponding Spring {@link InvalidResultSetAccessException}.
|
||||
*
|
||||
|
||||
+29
-30
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -37,168 +37,167 @@ import static org.mockito.Mockito.mock;
|
||||
/**
|
||||
* @author Thomas Risberg
|
||||
*/
|
||||
class ResultSetWrappingRowSetTests {
|
||||
public class ResultSetWrappingRowSetTests {
|
||||
|
||||
private final ResultSet resultSet = mock();
|
||||
private ResultSet resultSet = mock();
|
||||
|
||||
private final ResultSetWrappingSqlRowSet rowSet = new ResultSetWrappingSqlRowSet(resultSet);
|
||||
private ResultSetWrappingSqlRowSet rowSet = new ResultSetWrappingSqlRowSet(resultSet);
|
||||
|
||||
|
||||
@Test
|
||||
void testGetBigDecimalInt() throws Exception {
|
||||
public void testGetBigDecimalInt() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getBigDecimal", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getBigDecimal", int.class);
|
||||
doTest(rset, rowset, 1, BigDecimal.ONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetBigDecimalString() throws Exception {
|
||||
public void testGetBigDecimalString() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getBigDecimal", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getBigDecimal", String.class);
|
||||
doTest(rset, rowset, "test", BigDecimal.ONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetStringInt() throws Exception {
|
||||
public void testGetStringInt() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getString", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getString", int.class);
|
||||
doTest(rset, rowset, 1, "test");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetStringString() throws Exception {
|
||||
public void testGetStringString() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getString", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getString", String.class);
|
||||
doTest(rset, rowset, "test", "test");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetTimestampInt() throws Exception {
|
||||
public void testGetTimestampInt() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getTimestamp", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getTimestamp", int.class);
|
||||
doTest(rset, rowset, 1, new Timestamp(1234L));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetTimestampString() throws Exception {
|
||||
public void testGetTimestampString() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getTimestamp", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getTimestamp", String.class);
|
||||
doTest(rset, rowset, "test", new Timestamp(1234L));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetDateInt() throws Exception {
|
||||
public void testGetDateInt() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getDate", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getDate", int.class);
|
||||
doTest(rset, rowset, 1, new Date(1234L));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetDateString() throws Exception {
|
||||
public void testGetDateString() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getDate", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getDate", String.class);
|
||||
doTest(rset, rowset, "test", new Date(1234L));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetTimeInt() throws Exception {
|
||||
public void testGetTimeInt() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getTime", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getTime", int.class);
|
||||
doTest(rset, rowset, 1, new Time(1234L));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetTimeString() throws Exception {
|
||||
public void testGetTimeString() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getTime", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getTime", String.class);
|
||||
doTest(rset, rowset, "test", new Time(1234L));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetObjectInt() throws Exception {
|
||||
public void testGetObjectInt() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getObject", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getObject", int.class);
|
||||
doTest(rset, rowset, 1, new Object());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetObjectString() throws Exception {
|
||||
public void testGetObjectString() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getObject", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getObject", String.class);
|
||||
doTest(rset, rowset, "test", new Object());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetIntInt() throws Exception {
|
||||
public void testGetIntInt() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getInt", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getInt", int.class);
|
||||
doTest(rset, rowset, 1, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetIntString() throws Exception {
|
||||
public void testGetIntString() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getInt", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getInt", String.class);
|
||||
doTest(rset, rowset, "test", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetFloatInt() throws Exception {
|
||||
public void testGetFloatInt() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getFloat", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getFloat", int.class);
|
||||
doTest(rset, rowset, 1, 1.0f);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetFloatString() throws Exception {
|
||||
public void testGetFloatString() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getFloat", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getFloat", String.class);
|
||||
doTest(rset, rowset, "test", 1.0f);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetDoubleInt() throws Exception {
|
||||
public void testGetDoubleInt() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getDouble", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getDouble", int.class);
|
||||
doTest(rset, rowset, 1, 1.0d);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetDoubleString() throws Exception {
|
||||
public void testGetDoubleString() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getDouble", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getDouble", String.class);
|
||||
doTest(rset, rowset, "test", 1.0d);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetLongInt() throws Exception {
|
||||
public void testGetLongInt() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getLong", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getLong", int.class);
|
||||
doTest(rset, rowset, 1, 1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetLongString() throws Exception {
|
||||
public void testGetLongString() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getLong", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getLong", String.class);
|
||||
doTest(rset, rowset, "test", 1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetBooleanInt() throws Exception {
|
||||
public void testGetBooleanInt() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getBoolean", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getBoolean", int.class);
|
||||
doTest(rset, rowset, 1, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetBooleanString() throws Exception {
|
||||
public void testGetBooleanString() throws Exception {
|
||||
Method rset = ResultSet.class.getDeclaredMethod("getBoolean", int.class);
|
||||
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getBoolean", String.class);
|
||||
doTest(rset, rowset, "test", true);
|
||||
}
|
||||
|
||||
|
||||
private void doTest(Method rsetMethod, Method rowsetMethod, Object arg, Object ret) throws Exception {
|
||||
if (arg instanceof String) {
|
||||
given(resultSet.findColumn((String) arg)).willReturn(1);
|
||||
@@ -208,9 +207,9 @@ class ResultSetWrappingRowSetTests {
|
||||
given(rsetMethod.invoke(resultSet, arg)).willReturn(ret).willThrow(new SQLException("test"));
|
||||
}
|
||||
rowsetMethod.invoke(rowSet, arg);
|
||||
assertThatExceptionOfType(InvocationTargetException.class)
|
||||
.isThrownBy(() -> rowsetMethod.invoke(rowSet, arg))
|
||||
.satisfies(ex -> assertThat(ex.getTargetException()).isExactlyInstanceOf(InvalidResultSetAccessException.class));
|
||||
assertThatExceptionOfType(InvocationTargetException.class).isThrownBy(() ->
|
||||
rowsetMethod.invoke(rowSet, arg)).
|
||||
satisfies(ex -> assertThat(ex.getTargetException()).isExactlyInstanceOf(InvalidResultSetAccessException.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -189,6 +189,11 @@ public class JmsMessagingTemplate extends AbstractMessagingTemplate<Destination>
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void convertAndSend(Object payload) throws MessagingException {
|
||||
convertAndSend(payload, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void convertAndSend(Object payload, @Nullable MessagePostProcessor postProcessor) throws MessagingException {
|
||||
Destination defaultDestination = getDefaultDestination();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -290,7 +290,7 @@ public interface JmsOperations {
|
||||
* <p>This method should be used carefully, since it will block the thread
|
||||
* until the message becomes available or until the timeout value is exceeded.
|
||||
* <p>This will only work with a default destination specified!
|
||||
* @return the message produced for the consumer, or {@code null} if the timeout expires
|
||||
* @return the message produced for the consumer or {@code null} if the timeout expires.
|
||||
* @throws JmsException checked JMSException converted to unchecked
|
||||
*/
|
||||
@Nullable
|
||||
@@ -303,7 +303,7 @@ public interface JmsOperations {
|
||||
* <p>This method should be used carefully, since it will block the thread
|
||||
* until the message becomes available or until the timeout value is exceeded.
|
||||
* @param destination the destination to receive a message from
|
||||
* @return the message produced for the consumer, or {@code null} if the timeout expires
|
||||
* @return the message produced for the consumer or {@code null} if the timeout expires.
|
||||
* @throws JmsException checked JMSException converted to unchecked
|
||||
*/
|
||||
@Nullable
|
||||
@@ -317,7 +317,7 @@ public interface JmsOperations {
|
||||
* until the message becomes available or until the timeout value is exceeded.
|
||||
* @param destinationName the name of the destination to send this message to
|
||||
* (to be resolved to an actual destination by a DestinationResolver)
|
||||
* @return the message produced for the consumer, or {@code null} if the timeout expires
|
||||
* @return the message produced for the consumer or {@code null} if the timeout expires.
|
||||
* @throws JmsException checked JMSException converted to unchecked
|
||||
*/
|
||||
@Nullable
|
||||
@@ -332,7 +332,7 @@ public interface JmsOperations {
|
||||
* <p>This will only work with a default destination specified!
|
||||
* @param messageSelector the JMS message selector expression (or {@code null} if none).
|
||||
* See the JMS specification for a detailed definition of selector expressions.
|
||||
* @return the message produced for the consumer, or {@code null} if the timeout expires
|
||||
* @return the message produced for the consumer or {@code null} if the timeout expires.
|
||||
* @throws JmsException checked JMSException converted to unchecked
|
||||
*/
|
||||
@Nullable
|
||||
@@ -347,7 +347,7 @@ public interface JmsOperations {
|
||||
* @param destination the destination to receive a message from
|
||||
* @param messageSelector the JMS message selector expression (or {@code null} if none).
|
||||
* See the JMS specification for a detailed definition of selector expressions.
|
||||
* @return the message produced for the consumer, or {@code null} if the timeout expires
|
||||
* @return the message produced for the consumer or {@code null} if the timeout expires.
|
||||
* @throws JmsException checked JMSException converted to unchecked
|
||||
*/
|
||||
@Nullable
|
||||
@@ -363,7 +363,7 @@ public interface JmsOperations {
|
||||
* (to be resolved to an actual destination by a DestinationResolver)
|
||||
* @param messageSelector the JMS message selector expression (or {@code null} if none).
|
||||
* See the JMS specification for a detailed definition of selector expressions.
|
||||
* @return the message produced for the consumer, or {@code null} if the timeout expires
|
||||
* @return the message produced for the consumer or {@code null} if the timeout expires.
|
||||
* @throws JmsException checked JMSException converted to unchecked
|
||||
*/
|
||||
@Nullable
|
||||
|
||||
+3
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -168,7 +168,8 @@ public abstract class AbstractMessageSendingTemplate<D> implements MessageSendin
|
||||
|
||||
Map<String, Object> headersToUse = processHeadersToSend(headers);
|
||||
if (headersToUse != null) {
|
||||
messageHeaders = (headersToUse instanceof MessageHeaders mh ? mh : new MessageHeaders(headersToUse));
|
||||
messageHeaders = (headersToUse instanceof MessageHeaders _messageHeaders ?
|
||||
_messageHeaders : new MessageHeaders(headersToUse));
|
||||
}
|
||||
|
||||
MessageConverter converter = getMessageConverter();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,7 +22,6 @@ import org.springframework.aot.hint.ExecutableMode;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
@@ -42,9 +41,8 @@ class EntityManagerRuntimeHints implements RuntimeHintsRegistrar {
|
||||
|
||||
private static final String NATIVE_QUERY_IMPL_CLASS_NAME = "org.hibernate.query.sql.internal.NativeQueryImpl";
|
||||
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
|
||||
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
|
||||
if (ClassUtils.isPresent(HIBERNATE_SESSION_FACTORY_CLASS_NAME, classLoader)) {
|
||||
hints.proxies().registerJdkProxy(TypeReference.of(HIBERNATE_SESSION_FACTORY_CLASS_NAME),
|
||||
TypeReference.of(EntityManagerFactoryInfo.class));
|
||||
@@ -72,5 +70,4 @@ class EntityManagerRuntimeHints implements RuntimeHintsRegistrar {
|
||||
catch (ClassNotFoundException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -385,9 +385,7 @@ public abstract class SharedEntityManagerCreator {
|
||||
else if (targetClass.isInstance(proxy)) {
|
||||
return proxy;
|
||||
}
|
||||
else {
|
||||
return this.target.unwrap(targetClass);
|
||||
}
|
||||
break;
|
||||
case "getOutputParameterValue":
|
||||
if (this.entityManager == null) {
|
||||
Object key = args[0];
|
||||
|
||||
+12
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -154,7 +154,7 @@ final class PersistenceUnitReader {
|
||||
/**
|
||||
* Validate the given stream and return a valid DOM document for parsing.
|
||||
*/
|
||||
Document buildDocument(ErrorHandler handler, InputStream stream)
|
||||
protected Document buildDocument(ErrorHandler handler, InputStream stream)
|
||||
throws ParserConfigurationException, SAXException, IOException {
|
||||
|
||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
||||
@@ -168,7 +168,9 @@ final class PersistenceUnitReader {
|
||||
/**
|
||||
* Parse the validated document and add entries to the given unit info list.
|
||||
*/
|
||||
void parseDocument(Resource resource, Document document, List<SpringPersistenceUnitInfo> infos) throws IOException {
|
||||
protected List<SpringPersistenceUnitInfo> parseDocument(
|
||||
Resource resource, Document document, List<SpringPersistenceUnitInfo> infos) throws IOException {
|
||||
|
||||
Element persistence = document.getDocumentElement();
|
||||
String version = persistence.getAttribute(PERSISTENCE_VERSION);
|
||||
URL rootUrl = determinePersistenceUnitRootUrl(resource);
|
||||
@@ -177,12 +179,14 @@ final class PersistenceUnitReader {
|
||||
for (Element unit : units) {
|
||||
infos.add(parsePersistenceUnitInfo(unit, version, rootUrl));
|
||||
}
|
||||
|
||||
return infos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the unit info DOM element.
|
||||
*/
|
||||
SpringPersistenceUnitInfo parsePersistenceUnitInfo(
|
||||
protected SpringPersistenceUnitInfo parsePersistenceUnitInfo(
|
||||
Element persistenceUnit, String version, @Nullable URL rootUrl) throws IOException {
|
||||
|
||||
SpringPersistenceUnitInfo unitInfo = new SpringPersistenceUnitInfo();
|
||||
@@ -249,7 +253,7 @@ final class PersistenceUnitReader {
|
||||
/**
|
||||
* Parse the {@code property} XML elements.
|
||||
*/
|
||||
void parseProperties(Element persistenceUnit, SpringPersistenceUnitInfo unitInfo) {
|
||||
protected void parseProperties(Element persistenceUnit, SpringPersistenceUnitInfo unitInfo) {
|
||||
Element propRoot = DomUtils.getChildElementByTagName(persistenceUnit, PROPERTIES);
|
||||
if (propRoot == null) {
|
||||
return;
|
||||
@@ -265,7 +269,7 @@ final class PersistenceUnitReader {
|
||||
/**
|
||||
* Parse the {@code class} XML elements.
|
||||
*/
|
||||
void parseManagedClasses(Element persistenceUnit, SpringPersistenceUnitInfo unitInfo) {
|
||||
protected void parseManagedClasses(Element persistenceUnit, SpringPersistenceUnitInfo unitInfo) {
|
||||
List<Element> classes = DomUtils.getChildElementsByTagName(persistenceUnit, MANAGED_CLASS_NAME);
|
||||
for (Element element : classes) {
|
||||
String value = DomUtils.getTextValue(element).trim();
|
||||
@@ -278,7 +282,7 @@ final class PersistenceUnitReader {
|
||||
/**
|
||||
* Parse the {@code mapping-file} XML elements.
|
||||
*/
|
||||
void parseMappingFiles(Element persistenceUnit, SpringPersistenceUnitInfo unitInfo) {
|
||||
protected void parseMappingFiles(Element persistenceUnit, SpringPersistenceUnitInfo unitInfo) {
|
||||
List<Element> files = DomUtils.getChildElementsByTagName(persistenceUnit, MAPPING_FILE_NAME);
|
||||
for (Element element : files) {
|
||||
String value = DomUtils.getTextValue(element).trim();
|
||||
@@ -291,7 +295,7 @@ final class PersistenceUnitReader {
|
||||
/**
|
||||
* Parse the {@code jar-file} XML elements.
|
||||
*/
|
||||
void parseJarFiles(Element persistenceUnit, SpringPersistenceUnitInfo unitInfo) throws IOException {
|
||||
protected void parseJarFiles(Element persistenceUnit, SpringPersistenceUnitInfo unitInfo) throws IOException {
|
||||
List<Element> jars = DomUtils.getChildElementsByTagName(persistenceUnit, JAR_FILE_URL);
|
||||
for (Element element : jars) {
|
||||
String value = DomUtils.getTextValue(element).trim();
|
||||
|
||||
+50
-70
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -37,16 +37,16 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.withSettings;
|
||||
|
||||
/**
|
||||
* Tests for {@link SharedEntityManagerCreator}.
|
||||
* Unit tests for {@link SharedEntityManagerCreator}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class SharedEntityManagerCreatorTests {
|
||||
public class SharedEntityManagerCreatorTests {
|
||||
|
||||
@Test
|
||||
void proxyingWorksIfInfoReturnsNullEntityManagerInterface() {
|
||||
public void proxyingWorksIfInfoReturnsNullEntityManagerInterface() {
|
||||
EntityManagerFactory emf = mock(EntityManagerFactory.class,
|
||||
withSettings().extraInterfaces(EntityManagerFactoryInfo.class));
|
||||
// EntityManagerFactoryInfo.getEntityManagerInterface returns null
|
||||
@@ -54,7 +54,7 @@ class SharedEntityManagerCreatorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void transactionRequiredExceptionOnJoinTransaction() {
|
||||
public void transactionRequiredExceptionOnJoinTransaction() {
|
||||
EntityManagerFactory emf = mock();
|
||||
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
|
||||
assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy(
|
||||
@@ -62,7 +62,7 @@ class SharedEntityManagerCreatorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void transactionRequiredExceptionOnFlush() {
|
||||
public void transactionRequiredExceptionOnFlush() {
|
||||
EntityManagerFactory emf = mock();
|
||||
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
|
||||
assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy(
|
||||
@@ -70,7 +70,7 @@ class SharedEntityManagerCreatorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void transactionRequiredExceptionOnPersist() {
|
||||
public void transactionRequiredExceptionOnPersist() {
|
||||
EntityManagerFactory emf = mock();
|
||||
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
|
||||
assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy(() ->
|
||||
@@ -78,7 +78,7 @@ class SharedEntityManagerCreatorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void transactionRequiredExceptionOnMerge() {
|
||||
public void transactionRequiredExceptionOnMerge() {
|
||||
EntityManagerFactory emf = mock();
|
||||
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
|
||||
assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy(() ->
|
||||
@@ -86,7 +86,7 @@ class SharedEntityManagerCreatorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void transactionRequiredExceptionOnRemove() {
|
||||
public void transactionRequiredExceptionOnRemove() {
|
||||
EntityManagerFactory emf = mock();
|
||||
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
|
||||
assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy(() ->
|
||||
@@ -94,7 +94,7 @@ class SharedEntityManagerCreatorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void transactionRequiredExceptionOnRefresh() {
|
||||
public void transactionRequiredExceptionOnRefresh() {
|
||||
EntityManagerFactory emf = mock();
|
||||
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
|
||||
assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy(() ->
|
||||
@@ -102,98 +102,78 @@ class SharedEntityManagerCreatorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void deferredQueryWithUpdate() {
|
||||
public void deferredQueryWithUpdate() {
|
||||
EntityManagerFactory emf = mock();
|
||||
EntityManager targetEm = mock();
|
||||
Query targetQuery = mock();
|
||||
Query query = mock();
|
||||
given(emf.createEntityManager()).willReturn(targetEm);
|
||||
given(targetEm.createQuery("x")).willReturn(targetQuery);
|
||||
given(targetEm.createQuery("x")).willReturn(query);
|
||||
given(targetEm.isOpen()).willReturn(true);
|
||||
given((Query) targetQuery.unwrap(targetQuery.getClass())).willReturn(targetQuery);
|
||||
|
||||
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
|
||||
Query query = em.createQuery("x");
|
||||
assertThat((Query) query.unwrap(null)).isSameAs(targetQuery);
|
||||
assertThat((Query) query.unwrap(targetQuery.getClass())).isSameAs(targetQuery);
|
||||
assertThat(query.unwrap(Query.class)).isSameAs(query);
|
||||
query.executeUpdate();
|
||||
em.createQuery("x").executeUpdate();
|
||||
|
||||
verify(targetQuery).executeUpdate();
|
||||
verify(query).executeUpdate();
|
||||
verify(targetEm).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void deferredQueryWithSingleResult() {
|
||||
public void deferredQueryWithSingleResult() {
|
||||
EntityManagerFactory emf = mock();
|
||||
EntityManager targetEm = mock();
|
||||
Query targetQuery = mock();
|
||||
Query query = mock();
|
||||
given(emf.createEntityManager()).willReturn(targetEm);
|
||||
given(targetEm.createQuery("x")).willReturn(targetQuery);
|
||||
given(targetEm.createQuery("x")).willReturn(query);
|
||||
given(targetEm.isOpen()).willReturn(true);
|
||||
given((Query) targetQuery.unwrap(targetQuery.getClass())).willReturn(targetQuery);
|
||||
|
||||
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
|
||||
Query query = em.createQuery("x");
|
||||
assertThat((Query) query.unwrap(null)).isSameAs(targetQuery);
|
||||
assertThat((Query) query.unwrap(targetQuery.getClass())).isSameAs(targetQuery);
|
||||
assertThat(query.unwrap(Query.class)).isSameAs(query);
|
||||
query.getSingleResult();
|
||||
em.createQuery("x").getSingleResult();
|
||||
|
||||
verify(targetQuery).getSingleResult();
|
||||
verify(query).getSingleResult();
|
||||
verify(targetEm).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void deferredQueryWithResultList() {
|
||||
public void deferredQueryWithResultList() {
|
||||
EntityManagerFactory emf = mock();
|
||||
EntityManager targetEm = mock();
|
||||
Query targetQuery = mock();
|
||||
Query query = mock();
|
||||
given(emf.createEntityManager()).willReturn(targetEm);
|
||||
given(targetEm.createQuery("x")).willReturn(targetQuery);
|
||||
given(targetEm.createQuery("x")).willReturn(query);
|
||||
given(targetEm.isOpen()).willReturn(true);
|
||||
given((Query) targetQuery.unwrap(targetQuery.getClass())).willReturn(targetQuery);
|
||||
|
||||
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
|
||||
Query query = em.createQuery("x");
|
||||
assertThat((Query) query.unwrap(null)).isSameAs(targetQuery);
|
||||
assertThat((Query) query.unwrap(targetQuery.getClass())).isSameAs(targetQuery);
|
||||
assertThat(query.unwrap(Query.class)).isSameAs(query);
|
||||
query.getResultList();
|
||||
em.createQuery("x").getResultList();
|
||||
|
||||
verify(targetQuery).getResultList();
|
||||
verify(query).getResultList();
|
||||
verify(targetEm).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void deferredQueryWithResultStream() {
|
||||
public void deferredQueryWithResultStream() {
|
||||
EntityManagerFactory emf = mock();
|
||||
EntityManager targetEm = mock();
|
||||
Query targetQuery = mock();
|
||||
Query query = mock();
|
||||
given(emf.createEntityManager()).willReturn(targetEm);
|
||||
given(targetEm.createQuery("x")).willReturn(targetQuery);
|
||||
given(targetEm.createQuery("x")).willReturn(query);
|
||||
given(targetEm.isOpen()).willReturn(true);
|
||||
given((Query) targetQuery.unwrap(targetQuery.getClass())).willReturn(targetQuery);
|
||||
|
||||
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
|
||||
Query query = em.createQuery("x");
|
||||
assertThat((Query) query.unwrap(null)).isSameAs(targetQuery);
|
||||
assertThat((Query) query.unwrap(targetQuery.getClass())).isSameAs(targetQuery);
|
||||
assertThat(query.unwrap(Query.class)).isSameAs(query);
|
||||
query.getResultStream();
|
||||
em.createQuery("x").getResultStream();
|
||||
|
||||
verify(targetQuery).getResultStream();
|
||||
verify(query).getResultStream();
|
||||
verify(targetEm).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void deferredStoredProcedureQueryWithIndexedParameters() {
|
||||
public void deferredStoredProcedureQueryWithIndexedParameters() {
|
||||
EntityManagerFactory emf = mock();
|
||||
EntityManager targetEm = mock();
|
||||
StoredProcedureQuery targetQuery = mock();
|
||||
StoredProcedureQuery query = mock();
|
||||
given(emf.createEntityManager()).willReturn(targetEm);
|
||||
given(targetEm.createStoredProcedureQuery("x")).willReturn(targetQuery);
|
||||
willReturn("y").given(targetQuery).getOutputParameterValue(0);
|
||||
willReturn("z").given(targetQuery).getOutputParameterValue(2);
|
||||
given(targetEm.createStoredProcedureQuery("x")).willReturn(query);
|
||||
willReturn("y").given(query).getOutputParameterValue(0);
|
||||
willReturn("z").given(query).getOutputParameterValue(2);
|
||||
given(targetEm.isOpen()).willReturn(true);
|
||||
|
||||
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
|
||||
@@ -207,24 +187,24 @@ class SharedEntityManagerCreatorTests {
|
||||
spq.getOutputParameterValue(1));
|
||||
assertThat(spq.getOutputParameterValue(2)).isEqualTo("z");
|
||||
|
||||
verify(targetQuery).registerStoredProcedureParameter(0, String.class, ParameterMode.OUT);
|
||||
verify(targetQuery).registerStoredProcedureParameter(1, Number.class, ParameterMode.IN);
|
||||
verify(targetQuery).registerStoredProcedureParameter(2, Object.class, ParameterMode.INOUT);
|
||||
verify(targetQuery).execute();
|
||||
verify(query).registerStoredProcedureParameter(0, String.class, ParameterMode.OUT);
|
||||
verify(query).registerStoredProcedureParameter(1, Number.class, ParameterMode.IN);
|
||||
verify(query).registerStoredProcedureParameter(2, Object.class, ParameterMode.INOUT);
|
||||
verify(query).execute();
|
||||
verify(targetEm).close();
|
||||
verifyNoMoreInteractions(targetQuery);
|
||||
verifyNoMoreInteractions(query);
|
||||
verifyNoMoreInteractions(targetEm);
|
||||
}
|
||||
|
||||
@Test
|
||||
void deferredStoredProcedureQueryWithNamedParameters() {
|
||||
public void deferredStoredProcedureQueryWithNamedParameters() {
|
||||
EntityManagerFactory emf = mock();
|
||||
EntityManager targetEm = mock();
|
||||
StoredProcedureQuery targetQuery = mock();
|
||||
StoredProcedureQuery query = mock();
|
||||
given(emf.createEntityManager()).willReturn(targetEm);
|
||||
given(targetEm.createStoredProcedureQuery("x")).willReturn(targetQuery);
|
||||
willReturn("y").given(targetQuery).getOutputParameterValue("a");
|
||||
willReturn("z").given(targetQuery).getOutputParameterValue("c");
|
||||
given(targetEm.createStoredProcedureQuery("x")).willReturn(query);
|
||||
willReturn("y").given(query).getOutputParameterValue("a");
|
||||
willReturn("z").given(query).getOutputParameterValue("c");
|
||||
given(targetEm.isOpen()).willReturn(true);
|
||||
|
||||
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
|
||||
@@ -238,12 +218,12 @@ class SharedEntityManagerCreatorTests {
|
||||
spq.getOutputParameterValue("b"));
|
||||
assertThat(spq.getOutputParameterValue("c")).isEqualTo("z");
|
||||
|
||||
verify(targetQuery).registerStoredProcedureParameter("a", String.class, ParameterMode.OUT);
|
||||
verify(targetQuery).registerStoredProcedureParameter("b", Number.class, ParameterMode.IN);
|
||||
verify(targetQuery).registerStoredProcedureParameter("c", Object.class, ParameterMode.INOUT);
|
||||
verify(targetQuery).execute();
|
||||
verify(query).registerStoredProcedureParameter("a", String.class, ParameterMode.OUT);
|
||||
verify(query).registerStoredProcedureParameter("b", Number.class, ParameterMode.IN);
|
||||
verify(query).registerStoredProcedureParameter("c", Object.class, ParameterMode.INOUT);
|
||||
verify(query).execute();
|
||||
verify(targetEm).close();
|
||||
verifyNoMoreInteractions(targetQuery);
|
||||
verifyNoMoreInteractions(query);
|
||||
verifyNoMoreInteractions(targetEm);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -37,8 +37,6 @@ import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import javax.xml.XMLConstants;
|
||||
import javax.xml.datatype.Duration;
|
||||
@@ -194,7 +192,7 @@ public class Jaxb2Marshaller implements MimeMarshaller, MimeUnmarshaller, Generi
|
||||
@Nullable
|
||||
private ClassLoader beanClassLoader;
|
||||
|
||||
private final Lock jaxbContextLock = new ReentrantLock();
|
||||
private final Object jaxbContextMonitor = new Object();
|
||||
|
||||
@Nullable
|
||||
private volatile JAXBContext jaxbContext;
|
||||
@@ -206,12 +204,6 @@ public class Jaxb2Marshaller implements MimeMarshaller, MimeUnmarshaller, Generi
|
||||
|
||||
private boolean processExternalEntities = false;
|
||||
|
||||
@Nullable
|
||||
private volatile SAXParserFactory schemaParserFactory;
|
||||
|
||||
@Nullable
|
||||
private volatile SAXParserFactory sourceParserFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Set multiple JAXB context paths. The given array of context paths gets
|
||||
@@ -434,7 +426,6 @@ public class Jaxb2Marshaller implements MimeMarshaller, MimeUnmarshaller, Generi
|
||||
*/
|
||||
public void setSupportDtd(boolean supportDtd) {
|
||||
this.supportDtd = supportDtd;
|
||||
this.sourceParserFactory = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -459,7 +450,6 @@ public class Jaxb2Marshaller implements MimeMarshaller, MimeUnmarshaller, Generi
|
||||
if (processExternalEntities) {
|
||||
this.supportDtd = true;
|
||||
}
|
||||
this.sourceParserFactory = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -507,9 +497,7 @@ public class Jaxb2Marshaller implements MimeMarshaller, MimeUnmarshaller, Generi
|
||||
if (context != null) {
|
||||
return context;
|
||||
}
|
||||
|
||||
this.jaxbContextLock.lock();
|
||||
try {
|
||||
synchronized (this.jaxbContextMonitor) {
|
||||
context = this.jaxbContext;
|
||||
if (context == null) {
|
||||
try {
|
||||
@@ -533,9 +521,6 @@ public class Jaxb2Marshaller implements MimeMarshaller, MimeUnmarshaller, Generi
|
||||
}
|
||||
return context;
|
||||
}
|
||||
finally {
|
||||
this.jaxbContextLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private JAXBContext createJaxbContextFromContextPath(String contextPath) throws JAXBException {
|
||||
@@ -602,24 +587,17 @@ public class Jaxb2Marshaller implements MimeMarshaller, MimeUnmarshaller, Generi
|
||||
Assert.notEmpty(resources, "No resources given");
|
||||
Assert.hasLength(schemaLanguage, "No schema language provided");
|
||||
Source[] schemaSources = new Source[resources.length];
|
||||
|
||||
SAXParserFactory saxParserFactory = this.schemaParserFactory;
|
||||
if (saxParserFactory == null) {
|
||||
saxParserFactory = SAXParserFactory.newInstance();
|
||||
saxParserFactory.setNamespaceAware(true);
|
||||
saxParserFactory.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
|
||||
this.schemaParserFactory = saxParserFactory;
|
||||
}
|
||||
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
|
||||
saxParserFactory.setNamespaceAware(true);
|
||||
saxParserFactory.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
|
||||
SAXParser saxParser = saxParserFactory.newSAXParser();
|
||||
XMLReader xmlReader = saxParser.getXMLReader();
|
||||
|
||||
for (int i = 0; i < resources.length; i++) {
|
||||
Resource resource = resources[i];
|
||||
Assert.isTrue(resource != null && resource.exists(), () -> "Resource does not exist: " + resource);
|
||||
InputSource inputSource = SaxResourceUtils.createInputSource(resource);
|
||||
schemaSources[i] = new SAXSource(xmlReader, inputSource);
|
||||
}
|
||||
|
||||
SchemaFactory schemaFactory = SchemaFactory.newInstance(schemaLanguage);
|
||||
if (this.schemaResourceResolver != null) {
|
||||
schemaFactory.setResourceResolver(this.schemaResourceResolver);
|
||||
@@ -908,16 +886,11 @@ public class Jaxb2Marshaller implements MimeMarshaller, MimeUnmarshaller, Generi
|
||||
|
||||
try {
|
||||
if (xmlReader == null) {
|
||||
SAXParserFactory saxParserFactory = this.sourceParserFactory;
|
||||
if (saxParserFactory == null) {
|
||||
saxParserFactory = SAXParserFactory.newInstance();
|
||||
saxParserFactory.setNamespaceAware(true);
|
||||
saxParserFactory.setFeature(
|
||||
"http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd());
|
||||
saxParserFactory.setFeature(
|
||||
"http://xml.org/sax/features/external-general-entities", isProcessExternalEntities());
|
||||
this.sourceParserFactory = saxParserFactory;
|
||||
}
|
||||
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
|
||||
saxParserFactory.setNamespaceAware(true);
|
||||
saxParserFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd());
|
||||
String name = "http://xml.org/sax/features/external-general-entities";
|
||||
saxParserFactory.setFeature(name, isProcessExternalEntities());
|
||||
SAXParser saxParser = saxParserFactory.newSAXParser();
|
||||
xmlReader = saxParser.getXMLReader();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -83,10 +83,9 @@ public abstract class AbstractMarshaller implements Marshaller, Unmarshaller {
|
||||
private boolean processExternalEntities = false;
|
||||
|
||||
@Nullable
|
||||
private volatile DocumentBuilderFactory documentBuilderFactory;
|
||||
private DocumentBuilderFactory documentBuilderFactory;
|
||||
|
||||
@Nullable
|
||||
private volatile SAXParserFactory saxParserFactory;
|
||||
private final Object documentBuilderFactoryMonitor = new Object();
|
||||
|
||||
|
||||
/**
|
||||
@@ -95,8 +94,6 @@ public abstract class AbstractMarshaller implements Marshaller, Unmarshaller {
|
||||
*/
|
||||
public void setSupportDtd(boolean supportDtd) {
|
||||
this.supportDtd = supportDtd;
|
||||
this.documentBuilderFactory = null;
|
||||
this.saxParserFactory = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -121,8 +118,6 @@ public abstract class AbstractMarshaller implements Marshaller, Unmarshaller {
|
||||
if (processExternalEntities) {
|
||||
this.supportDtd = true;
|
||||
}
|
||||
this.documentBuilderFactory = null;
|
||||
this.saxParserFactory = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -142,13 +137,14 @@ public abstract class AbstractMarshaller implements Marshaller, Unmarshaller {
|
||||
*/
|
||||
protected Document buildDocument() {
|
||||
try {
|
||||
DocumentBuilderFactory builderFactory = this.documentBuilderFactory;
|
||||
if (builderFactory == null) {
|
||||
builderFactory = createDocumentBuilderFactory();
|
||||
this.documentBuilderFactory = builderFactory;
|
||||
DocumentBuilder documentBuilder;
|
||||
synchronized (this.documentBuilderFactoryMonitor) {
|
||||
if (this.documentBuilderFactory == null) {
|
||||
this.documentBuilderFactory = createDocumentBuilderFactory();
|
||||
}
|
||||
documentBuilder = createDocumentBuilder(this.documentBuilderFactory);
|
||||
}
|
||||
DocumentBuilder builder = createDocumentBuilder(builderFactory);
|
||||
return builder.newDocument();
|
||||
return documentBuilder.newDocument();
|
||||
}
|
||||
catch (ParserConfigurationException ex) {
|
||||
throw new UnmarshallingFailureException("Could not create document placeholder: " + ex.getMessage(), ex);
|
||||
@@ -183,11 +179,11 @@ public abstract class AbstractMarshaller implements Marshaller, Unmarshaller {
|
||||
protected DocumentBuilder createDocumentBuilder(DocumentBuilderFactory factory)
|
||||
throws ParserConfigurationException {
|
||||
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
DocumentBuilder documentBuilder = factory.newDocumentBuilder();
|
||||
if (!isProcessExternalEntities()) {
|
||||
builder.setEntityResolver(NO_OP_ENTITY_RESOLVER);
|
||||
documentBuilder.setEntityResolver(NO_OP_ENTITY_RESOLVER);
|
||||
}
|
||||
return builder;
|
||||
return documentBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -197,17 +193,11 @@ public abstract class AbstractMarshaller implements Marshaller, Unmarshaller {
|
||||
* @throws ParserConfigurationException if thrown by JAXP methods
|
||||
*/
|
||||
protected XMLReader createXmlReader() throws SAXException, ParserConfigurationException {
|
||||
SAXParserFactory parserFactory = this.saxParserFactory;
|
||||
if (parserFactory == null) {
|
||||
parserFactory = SAXParserFactory.newInstance();
|
||||
parserFactory.setNamespaceAware(true);
|
||||
parserFactory.setFeature(
|
||||
"http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd());
|
||||
parserFactory.setFeature(
|
||||
"http://xml.org/sax/features/external-general-entities", isProcessExternalEntities());
|
||||
this.saxParserFactory = parserFactory;
|
||||
}
|
||||
SAXParser saxParser = parserFactory.newSAXParser();
|
||||
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
|
||||
saxParserFactory.setNamespaceAware(true);
|
||||
saxParserFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd());
|
||||
saxParserFactory.setFeature("http://xml.org/sax/features/external-general-entities", isProcessExternalEntities());
|
||||
SAXParser saxParser = saxParserFactory.newSAXParser();
|
||||
XMLReader xmlReader = saxParser.getXMLReader();
|
||||
if (!isProcessExternalEntities()) {
|
||||
xmlReader.setEntityResolver(NO_OP_ENTITY_RESOLVER);
|
||||
|
||||
@@ -100,6 +100,9 @@ public class MockHttpServletRequest implements HttpServletRequest {
|
||||
|
||||
private static final TimeZone GMT = TimeZone.getTimeZone("GMT");
|
||||
|
||||
private static final BufferedReader EMPTY_BUFFERED_READER =
|
||||
new BufferedReader(new StringReader(""));
|
||||
|
||||
/**
|
||||
* Date formats as specified in the HTTP RFC.
|
||||
* @see <a href="https://tools.ietf.org/html/rfc7231#section-7.1.1.1">Section 7.1.1.1 of RFC 7231</a>
|
||||
@@ -733,7 +736,7 @@ public class MockHttpServletRequest implements HttpServletRequest {
|
||||
this.reader = new BufferedReader(sourceReader);
|
||||
}
|
||||
else {
|
||||
this.reader = new BufferedReader(new StringReader(""));
|
||||
this.reader = EMPTY_BUFFERED_READER;
|
||||
}
|
||||
return this.reader;
|
||||
}
|
||||
|
||||
-11
@@ -93,17 +93,6 @@ class MockHttpServletRequestTests {
|
||||
secondRequest.getInputStream().close();
|
||||
}
|
||||
|
||||
@Test // gh-32820
|
||||
void readEmptyReaderWorksAcrossRequests() throws IOException {
|
||||
MockHttpServletRequest firstRequest = new MockHttpServletRequest();
|
||||
firstRequest.getReader().read(new char[256]);
|
||||
firstRequest.getReader().close();
|
||||
|
||||
MockHttpServletRequest secondRequest = new MockHttpServletRequest();
|
||||
secondRequest.getReader().read(new char[256]);
|
||||
secondRequest.getReader().close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void setContentAndGetReader() throws IOException {
|
||||
byte[] bytes = "body".getBytes(Charset.defaultCharset());
|
||||
|
||||
+2
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,8 +17,7 @@
|
||||
package org.springframework.dao;
|
||||
|
||||
/**
|
||||
* Exception thrown when an attempt to execute an SQL statement fails to map
|
||||
* the given data, typically but no limited to an insert or update data
|
||||
* Exception thrown when an attempt to insert or update data
|
||||
* results in violation of an integrity constraint. Note that this
|
||||
* is not purely a relational concept; integrity constraints such
|
||||
* as unique primary keys are required by most database types.
|
||||
|
||||
+6
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,12 +21,11 @@ import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.aot.hint.TypeHint;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
|
||||
/**
|
||||
* {@link RuntimeHintsRegistrar} implementation that registers runtime hints
|
||||
* for transaction management.
|
||||
* {@link RuntimeHintsRegistrar} implementation that registers runtime hints for
|
||||
* transaction management.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 6.0
|
||||
@@ -35,9 +34,9 @@ import org.springframework.transaction.TransactionDefinition;
|
||||
class TransactionRuntimeHints implements RuntimeHintsRegistrar {
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
|
||||
hints.reflection().registerTypes(
|
||||
TypeReference.listOf(Isolation.class, Propagation.class, TransactionDefinition.class),
|
||||
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
|
||||
hints.reflection().registerTypes(TypeReference.listOf(
|
||||
Isolation.class, Propagation.class, TransactionDefinition.class),
|
||||
TypeHint.builtWith(MemberCategory.DECLARED_FIELDS));
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -51,7 +51,7 @@ public class DefaultTransactionAttribute extends DefaultTransactionDefinition im
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@code DefaultTransactionAttribute} with default settings.
|
||||
* Create a new DefaultTransactionAttribute, with default settings.
|
||||
* Can be modified through bean property setters.
|
||||
* @see #setPropagationBehavior
|
||||
* @see #setIsolationLevel
|
||||
@@ -76,7 +76,7 @@ public class DefaultTransactionAttribute extends DefaultTransactionDefinition im
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code DefaultTransactionAttribute} with the given
|
||||
* Create a new DefaultTransactionAttribute with the given
|
||||
* propagation behavior. Can be modified through bean property setters.
|
||||
* @param propagationBehavior one of the propagation constants in the
|
||||
* TransactionDefinition interface
|
||||
|
||||
+4
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -210,13 +210,11 @@ public abstract class AbstractReactiveTransactionManager implements ReactiveTran
|
||||
prepareSynchronization(synchronizationManager, status, definition)).thenReturn(status);
|
||||
}
|
||||
|
||||
// PROPAGATION_REQUIRED, PROPAGATION_SUPPORTS, PROPAGATION_MANDATORY:
|
||||
// regular participation in existing transaction.
|
||||
// Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.
|
||||
if (debugEnabled) {
|
||||
logger.debug("Participating in existing transaction");
|
||||
}
|
||||
return Mono.just(prepareReactiveTransaction(
|
||||
synchronizationManager, definition, transaction, false, debugEnabled, null));
|
||||
return Mono.just(prepareReactiveTransaction(synchronizationManager, definition, transaction, false, debugEnabled, null));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -332,7 +330,7 @@ public abstract class AbstractReactiveTransactionManager implements ReactiveTran
|
||||
if (resourcesHolder != null) {
|
||||
Object suspendedResources = resourcesHolder.suspendedResources;
|
||||
if (suspendedResources != null) {
|
||||
resume = doResume(synchronizationManager, transaction, suspendedResources);
|
||||
resume = doResume(synchronizationManager, transaction, suspendedResources);
|
||||
}
|
||||
List<TransactionSynchronization> suspendedSynchronizations = resourcesHolder.suspendedSynchronizations;
|
||||
if (suspendedSynchronizations != null) {
|
||||
|
||||
+3
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -451,7 +451,7 @@ public abstract class AbstractPlatformTransactionManager implements PlatformTran
|
||||
if (useSavepointForNestedTransaction()) {
|
||||
// Create savepoint within existing Spring-managed transaction,
|
||||
// through the SavepointManager API implemented by TransactionStatus.
|
||||
// Usually uses JDBC savepoints. Never activates Spring synchronization.
|
||||
// Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
|
||||
DefaultTransactionStatus status =
|
||||
prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
|
||||
status.createAndHoldSavepoint();
|
||||
@@ -465,8 +465,7 @@ public abstract class AbstractPlatformTransactionManager implements PlatformTran
|
||||
}
|
||||
}
|
||||
|
||||
// PROPAGATION_REQUIRED, PROPAGATION_SUPPORTS, PROPAGATION_MANDATORY:
|
||||
// regular participation in existing transaction.
|
||||
// Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.
|
||||
if (debugEnabled) {
|
||||
logger.debug("Participating in existing transaction");
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -65,7 +65,7 @@ public class DefaultTransactionDefinition implements TransactionDefinition, Seri
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@code DefaultTransactionDefinition} with default settings.
|
||||
* Create a new DefaultTransactionDefinition, with default settings.
|
||||
* Can be modified through bean property setters.
|
||||
* @see #setPropagationBehavior
|
||||
* @see #setIsolationLevel
|
||||
@@ -93,7 +93,7 @@ public class DefaultTransactionDefinition implements TransactionDefinition, Seri
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code DefaultTransactionDefinition} with the given
|
||||
* Create a new DefaultTransactionDefinition with the given
|
||||
* propagation behavior. Can be modified through bean property setters.
|
||||
* @param propagationBehavior one of the propagation constants in the
|
||||
* TransactionDefinition interface
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -110,7 +110,7 @@ public final class ResponseCookie extends HttpCookie {
|
||||
|
||||
/**
|
||||
* Return {@code true} if the cookie has the "HttpOnly" attribute.
|
||||
* @see <a href="https://owasp.org/www-community/HttpOnly">https://owasp.org/www-community/HttpOnly</a>
|
||||
* @see <a href="https://www.owasp.org/index.php/HTTPOnly">https://www.owasp.org/index.php/HTTPOnly</a>
|
||||
*/
|
||||
public boolean isHttpOnly() {
|
||||
return this.httpOnly;
|
||||
@@ -268,7 +268,7 @@ public final class ResponseCookie extends HttpCookie {
|
||||
|
||||
/**
|
||||
* Add the "HttpOnly" attribute to the cookie.
|
||||
* @see <a href="https://owasp.org/www-community/HttpOnly">https://owasp.org/www-community/HttpOnly</a>
|
||||
* @see <a href="https://www.owasp.org/index.php/HTTPOnly">https://www.owasp.org/index.php/HTTPOnly</a>
|
||||
*/
|
||||
ResponseCookieBuilder httpOnly(boolean httpOnly);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -277,6 +277,7 @@ public class ResponseEntity<T> extends HttpEntity<T> {
|
||||
*/
|
||||
public static HeadersBuilder<?> of(ProblemDetail body) {
|
||||
return new DefaultBuilder(body.getStatus()) {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> ResponseEntity<T> build() {
|
||||
|
||||
+10
-40
@@ -16,12 +16,8 @@
|
||||
|
||||
package org.springframework.http.client.reactive;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.reactivestreams.Subscriber;
|
||||
import org.reactivestreams.Subscription;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
@@ -59,7 +55,16 @@ public abstract class AbstractClientHttpResponse implements ClientHttpResponse {
|
||||
this.statusCode = statusCode;
|
||||
this.headers = headers;
|
||||
this.cookies = cookies;
|
||||
this.body = Flux.from(new SingleSubscriberPublisher<>(body));
|
||||
this.body = singleSubscription(body);
|
||||
}
|
||||
|
||||
private static Flux<DataBuffer> singleSubscription(Flux<DataBuffer> body) {
|
||||
AtomicBoolean subscribed = new AtomicBoolean();
|
||||
return body.doOnSubscribe(s -> {
|
||||
if (!subscribed.compareAndSet(false, true)) {
|
||||
throw new IllegalStateException("The client response body can only be consumed once");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -82,39 +87,4 @@ public abstract class AbstractClientHttpResponse implements ClientHttpResponse {
|
||||
public Flux<DataBuffer> getBody() {
|
||||
return this.body;
|
||||
}
|
||||
|
||||
|
||||
private static final class SingleSubscriberPublisher<T> implements Publisher<T> {
|
||||
|
||||
private static final Subscription NO_OP_SUBSCRIPTION = new Subscription() {
|
||||
@Override
|
||||
public void request(long l) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
}
|
||||
};
|
||||
|
||||
private final Publisher<T> delegate;
|
||||
|
||||
private final AtomicBoolean subscribed = new AtomicBoolean();
|
||||
|
||||
|
||||
public SingleSubscriberPublisher(Publisher<T> delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void subscribe(Subscriber<? super T> subscriber) {
|
||||
Objects.requireNonNull(subscriber, "Subscriber must not be null");
|
||||
if (this.subscribed.compareAndSet(false, true)) {
|
||||
this.delegate.subscribe(subscriber);
|
||||
}
|
||||
else {
|
||||
subscriber.onSubscribe(NO_OP_SUBSCRIPTION);
|
||||
subscriber.onError(new IllegalStateException("The client response body can only be consumed once"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -124,6 +124,7 @@ class HttpComponentsClientHttpRequest extends AbstractClientHttpRequest {
|
||||
@Override
|
||||
protected void applyHeaders() {
|
||||
HttpHeaders headers = getHeaders();
|
||||
|
||||
headers.entrySet()
|
||||
.stream()
|
||||
.filter(entry -> !HttpHeaders.CONTENT_LENGTH.equals(entry.getKey()))
|
||||
@@ -132,6 +133,7 @@ class HttpComponentsClientHttpRequest extends AbstractClientHttpRequest {
|
||||
if (!this.httpRequest.containsHeader(HttpHeaders.ACCEPT)) {
|
||||
this.httpRequest.addHeader(HttpHeaders.ACCEPT, MediaType.ALL_VALUE);
|
||||
}
|
||||
|
||||
this.contentLength = headers.getContentLength();
|
||||
}
|
||||
|
||||
@@ -142,6 +144,7 @@ class HttpComponentsClientHttpRequest extends AbstractClientHttpRequest {
|
||||
}
|
||||
|
||||
CookieStore cookieStore = this.context.getCookieStore();
|
||||
|
||||
getCookies().values()
|
||||
.stream()
|
||||
.flatMap(Collection::stream)
|
||||
|
||||
+2
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -37,7 +37,6 @@ import org.springframework.http.HttpCookie;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* {@link ClientHttpRequest} for the Java {@link HttpClient}.
|
||||
@@ -109,11 +108,7 @@ class JdkClientHttpRequest extends AbstractClientHttpRequest {
|
||||
|
||||
@Override
|
||||
protected void applyCookies() {
|
||||
MultiValueMap<String, HttpCookie> cookies = getCookies();
|
||||
if (cookies.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
this.builder.header(HttpHeaders.COOKIE, cookies.values().stream()
|
||||
this.builder.header(HttpHeaders.COOKIE, getCookies().values().stream()
|
||||
.flatMap(List::stream).map(HttpCookie::toString).collect(Collectors.joining(";")));
|
||||
}
|
||||
|
||||
|
||||
+14
-19
@@ -82,7 +82,6 @@ public class ResourceHttpMessageConverter extends AbstractHttpMessageConverter<R
|
||||
if (this.supportsReadStreaming && InputStreamResource.class == clazz) {
|
||||
return new InputStreamResource(inputMessage.getBody()) {
|
||||
@Override
|
||||
@Nullable
|
||||
public String getFilename() {
|
||||
return inputMessage.getHeaders().getContentDisposition().getFilename();
|
||||
}
|
||||
@@ -108,23 +107,6 @@ public class ResourceHttpMessageConverter extends AbstractHttpMessageConverter<R
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeInternal(Resource resource, HttpOutputMessage outputMessage)
|
||||
throws IOException, HttpMessageNotWritableException {
|
||||
|
||||
writeContent(resource, outputMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the default headers for the given resource to the given message.
|
||||
* @since 6.0
|
||||
*/
|
||||
public void addDefaultHeaders(HttpOutputMessage message, Resource resource, @Nullable MediaType contentType)
|
||||
throws IOException {
|
||||
|
||||
addDefaultHeaders(message.getHeaders(), resource, contentType);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MediaType getDefaultContentType(Resource resource) {
|
||||
return MediaTypeFactory.getMediaType(resource).orElse(MediaType.APPLICATION_OCTET_STREAM);
|
||||
@@ -142,10 +124,23 @@ public class ResourceHttpMessageConverter extends AbstractHttpMessageConverter<R
|
||||
return (contentLength < 0 ? null : contentLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the default headers for the given resource to the given message.
|
||||
* @since 6.0
|
||||
*/
|
||||
public void addDefaultHeaders(HttpOutputMessage message, Resource resource, @Nullable MediaType contentType) throws IOException {
|
||||
addDefaultHeaders(message.getHeaders(), resource, contentType);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeInternal(Resource resource, HttpOutputMessage outputMessage)
|
||||
throws IOException, HttpMessageNotWritableException {
|
||||
|
||||
writeContent(resource, outputMessage);
|
||||
}
|
||||
|
||||
protected void writeContent(Resource resource, HttpOutputMessage outputMessage)
|
||||
throws IOException, HttpMessageNotWritableException {
|
||||
|
||||
// We cannot use try-with-resources here for the InputStream, since we have
|
||||
// custom handling of the close() method in a finally-block.
|
||||
try {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user