mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
64 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 66f4509c2c | |||
| b8395a2321 | |||
| 6df8be8be3 | |||
| 4a599d0b22 | |||
| a1463c2bf2 | |||
| 23eff5c650 | |||
| c44bb29aa5 | |||
| 0c22866b72 | |||
| 0c6957e395 | |||
| 8d51fc0444 | |||
| bad01011da | |||
| 867a199507 | |||
| 1b4a4ac51f | |||
| dd0d26b4ba | |||
| d074f660a1 | |||
| 1b8baffbd7 | |||
| 56c63c779f | |||
| 198cf063fd | |||
| b1b6b544a2 | |||
| 9d13ea290f | |||
| c9163b77df | |||
| 033c8df53f | |||
| b272f4e615 | |||
| bafff5079f | |||
| ccaecab500 | |||
| 67e03105b5 | |||
| 76bc9cf325 | |||
| db52c77cca | |||
| 1e742aae34 | |||
| 20dd585c93 | |||
| 707eb701dc | |||
| 2c97996796 | |||
| 3a068b807b | |||
| f54b19ff90 | |||
| 494d2ab727 | |||
| 627d9cf8be | |||
| dd3a67c7ab | |||
| 85cc229063 | |||
| fa95f12be0 | |||
| 035cc72fc8 | |||
| 29a39b617e | |||
| a1471a9266 | |||
| 21793b4f93 | |||
| 2d255c4d5e | |||
| 10391586d1 | |||
| edadc79835 | |||
| 3783d31c09 | |||
| 87730f76b1 | |||
| 6fae3e150e | |||
| ad44e8ab0a | |||
| aadf96ba92 | |||
| 2baf064d04 | |||
| 6121e2f526 | |||
| 8e33805d29 | |||
| 1f19bb2311 | |||
| 2784410cc6 | |||
| f4ac323409 | |||
| 6db00e63c7 | |||
| ed613e767a | |||
| 65781046cf | |||
| 0ee36095e7 | |||
| 0fc38117df | |||
| 86b8a70ce6 | |||
| 4bf759d872 |
@@ -1,36 +0,0 @@
|
||||
Juergen Hoeller <jhoeller@vmware.com>
|
||||
Juergen Hoeller <jhoeller@vmware.com> <jhoeller@pivotal.io>
|
||||
Juergen Hoeller <jhoeller@vmware.com> <jhoeller@gopivotal.com>
|
||||
Rossen Stoyanchev <rstoyanchev@vmware.com>
|
||||
Rossen Stoyanchev <rstoyanchev@vmware.com> <rstoyanchev@pivotal.io>
|
||||
Rossen Stoyanchev <rstoyanchev@vmware.com> <rstoyanchev@gopivotal.com>
|
||||
Phillip Webb <pwebb@vmware.com>
|
||||
Phillip Webb <pwebb@vmware.com> <pwebb@pivotal.io>
|
||||
Phillip Webb <pwebb@vmware.com> <pwebb@gopivotal.com>
|
||||
Chris Beams <cbeams@vmware.com>
|
||||
Chris Beams <cbeams@vmware.com> <cbeams@pivotal.io>
|
||||
Chris Beams <cbeams@vmware.com> <cbeams@gopivotal.com>
|
||||
Arjen Poutsma <poutsmaa@vmware.com>
|
||||
Arjen Poutsma <poutsmaa@vmware.com> <apoutsma@pivotal.io>
|
||||
Arjen Poutsma <poutsmaa@vmware.com> <apoutsma@gopivotal.com>
|
||||
Arjen Poutsma <poutsmaa@vmware.com> <poutsma@mac.com>
|
||||
Arjen Poutsma <poutsmaa@vmware.com> <apoutsma@vmware.com>
|
||||
Oliver Drotbohm <odrotbohm@vmware.com>
|
||||
Oliver Drotbohm <odrotbohm@vmware.com> <ogierke@vmware.com>
|
||||
Oliver Drotbohm <odrotbohm@vmware.com> <ogierke@pivotal.io>
|
||||
Oliver Drotbohm <odrotbohm@vmware.com> <ogierke@gopivotal.com>
|
||||
Dave Syer <dsyer@vmware.com>
|
||||
Dave Syer <dsyer@vmware.com> <dsyer@pivotal.io>
|
||||
Dave Syer <dsyer@vmware.com> <dsyer@gopivotal.com>
|
||||
Dave Syer <dsyer@vmware.com> <david_syer@hotmail.com>
|
||||
Andy Clement <aclement@vmware.com>
|
||||
Andy Clement <aclement@vmware.com> <aclement@pivotal.io>
|
||||
Andy Clement <aclement@vmware.com> <aclement@gopivotal.com>
|
||||
Andy Clement <aclement@vmware.com> <andrew.clement@gmail.com>
|
||||
Sam Brannen <sbrannen@vmware.com>
|
||||
Sam Brannen <sbrannen@vmware.com> <sbrannen@pivotal.io>
|
||||
Sam Brannen <sbrannen@vmware.com> <sam@sambrannen.com>
|
||||
Simon Basle <sbasle@vmware.com>
|
||||
Simon Baslé <sbasle@vmware.com>
|
||||
<dmitry.katsubo@gmail.com> <dmitry.katsubo@gmai.com>
|
||||
Nick Williams <nicholas@nicholaswilliams.net>
|
||||
+2
-3
@@ -75,7 +75,7 @@ configure([rootProject] + javaProjects) { project ->
|
||||
}
|
||||
|
||||
checkstyle {
|
||||
toolVersion = "10.12.5"
|
||||
toolVersion = "10.12.7"
|
||||
configDirectory.set(rootProject.file("src/checkstyle"))
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ configure([rootProject] + javaProjects) { project ->
|
||||
// JSR-305 only used for non-required meta-annotations
|
||||
compileOnly("com.google.code.findbugs:jsr305")
|
||||
testCompileOnly("com.google.code.findbugs:jsr305")
|
||||
checkstyle("io.spring.javaformat:spring-javaformat-checkstyle:0.0.39")
|
||||
checkstyle("io.spring.javaformat:spring-javaformat-checkstyle:0.0.41")
|
||||
}
|
||||
|
||||
ext.javadocLinks = [
|
||||
@@ -146,7 +146,6 @@ configure(rootProject) {
|
||||
description = "Spring Framework"
|
||||
|
||||
apply plugin: "io.spring.nohttp"
|
||||
apply plugin: 'org.springframework.build.api-diff'
|
||||
|
||||
nohttp {
|
||||
source.exclude "**/test-output/**"
|
||||
|
||||
@@ -22,21 +22,6 @@ but doesn't affect the classpath of dependent projects.
|
||||
This plugin does not provide a `provided` configuration, as the native `compileOnly` and `testCompileOnly`
|
||||
configurations are preferred.
|
||||
|
||||
### API Diff
|
||||
|
||||
This plugin uses the [Gradle JApiCmp](https://github.com/melix/japicmp-gradle-plugin) plugin
|
||||
to generate API Diff reports for each Spring Framework module. This plugin is applied once on the root
|
||||
project and creates tasks in each framework module. Unlike previous versions of this part of the build,
|
||||
there is no need for checking out a specific tag. The plugin will fetch the JARs we want to compare the
|
||||
current working version with. You can generate the reports for all modules or a single module:
|
||||
|
||||
```
|
||||
./gradlew apiDiff -PbaselineVersion=5.1.0.RELEASE
|
||||
./gradlew :spring-core:apiDiff -PbaselineVersion=5.1.0.RELEASE
|
||||
```
|
||||
|
||||
The reports are located under `build/reports/api-diff/$OLDVERSION_to_$NEWVERSION/`.
|
||||
|
||||
|
||||
### RuntimeHints Java Agent
|
||||
|
||||
|
||||
@@ -19,16 +19,11 @@ ext {
|
||||
dependencies {
|
||||
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}")
|
||||
implementation("org.jetbrains.kotlin:kotlin-compiler-embeddable:${kotlinVersion}")
|
||||
implementation "me.champeau.gradle:japicmp-gradle-plugin:0.3.0"
|
||||
implementation "org.gradle:test-retry-gradle-plugin:1.4.1"
|
||||
implementation "org.gradle:test-retry-gradle-plugin:1.5.6"
|
||||
}
|
||||
|
||||
gradlePlugin {
|
||||
plugins {
|
||||
apiDiffPlugin {
|
||||
id = "org.springframework.build.api-diff"
|
||||
implementationClass = "org.springframework.build.api.ApiDiffPlugin"
|
||||
}
|
||||
conventionsPlugin {
|
||||
id = "org.springframework.build.conventions"
|
||||
implementationClass = "org.springframework.build.ConventionsPlugin"
|
||||
|
||||
@@ -1,141 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.build.api;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import me.champeau.gradle.japicmp.JapicmpPlugin;
|
||||
import me.champeau.gradle.japicmp.JapicmpTask;
|
||||
import org.gradle.api.GradleException;
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.artifacts.Dependency;
|
||||
import org.gradle.api.plugins.JavaBasePlugin;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
import org.gradle.api.publish.maven.plugins.MavenPublishPlugin;
|
||||
import org.gradle.api.tasks.TaskProvider;
|
||||
import org.gradle.jvm.tasks.Jar;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* {@link Plugin} that applies the {@code "japicmp-gradle-plugin"}
|
||||
* and create tasks for all subprojects named {@code "spring-*"}, diffing the public API one by one
|
||||
* and creating the reports in {@code "build/reports/api-diff/$OLDVERSION_to_$NEWVERSION/"}.
|
||||
* <p>{@code "./gradlew apiDiff -PbaselineVersion=5.1.0.RELEASE"} will output the
|
||||
* reports for the API diff between the baseline version and the current one for all modules.
|
||||
* You can limit the report to a single module with
|
||||
* {@code "./gradlew :spring-core:apiDiff -PbaselineVersion=5.1.0.RELEASE"}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
public class ApiDiffPlugin implements Plugin<Project> {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ApiDiffPlugin.class);
|
||||
|
||||
public static final String TASK_NAME = "apiDiff";
|
||||
|
||||
private static final String BASELINE_VERSION_PROPERTY = "baselineVersion";
|
||||
|
||||
private static final List<String> PACKAGE_INCLUDES = Collections.singletonList("org.springframework.*");
|
||||
|
||||
private static final URI SPRING_MILESTONE_REPOSITORY = URI.create("https://repo.spring.io/milestone");
|
||||
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
if (project.hasProperty(BASELINE_VERSION_PROPERTY) && project.equals(project.getRootProject())) {
|
||||
project.getPluginManager().apply(JapicmpPlugin.class);
|
||||
project.getPlugins().withType(JapicmpPlugin.class,
|
||||
plugin -> applyApiDiffConventions(project));
|
||||
}
|
||||
}
|
||||
|
||||
private void applyApiDiffConventions(Project project) {
|
||||
String baselineVersion = project.property(BASELINE_VERSION_PROPERTY).toString();
|
||||
project.subprojects(subProject -> {
|
||||
if (subProject.getName().startsWith("spring-")) {
|
||||
createApiDiffTask(baselineVersion, subProject);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void createApiDiffTask(String baselineVersion, Project project) {
|
||||
if (isProjectEligible(project)) {
|
||||
// Add Spring Milestone repository for generating diffs against previous milestones
|
||||
project.getRootProject()
|
||||
.getRepositories()
|
||||
.maven(mavenArtifactRepository -> mavenArtifactRepository.setUrl(SPRING_MILESTONE_REPOSITORY));
|
||||
JapicmpTask apiDiff = project.getTasks().create(TASK_NAME, JapicmpTask.class);
|
||||
apiDiff.setDescription("Generates an API diff report with japicmp");
|
||||
apiDiff.setGroup(JavaBasePlugin.DOCUMENTATION_GROUP);
|
||||
|
||||
apiDiff.setOldClasspath(createBaselineConfiguration(baselineVersion, project));
|
||||
TaskProvider<Jar> jar = project.getTasks().withType(Jar.class).named("jar");
|
||||
apiDiff.setNewArchives(project.getLayout().files(jar.get().getArchiveFile().get().getAsFile()));
|
||||
apiDiff.setNewClasspath(getRuntimeClassPath(project));
|
||||
apiDiff.setPackageIncludes(PACKAGE_INCLUDES);
|
||||
apiDiff.setOnlyModified(true);
|
||||
apiDiff.setIgnoreMissingClasses(true);
|
||||
// Ignore Kotlin metadata annotations since they contain
|
||||
// illegal HTML characters and fail the report generation
|
||||
apiDiff.setAnnotationExcludes(Collections.singletonList("@kotlin.Metadata"));
|
||||
|
||||
apiDiff.setHtmlOutputFile(getOutputFile(baselineVersion, project));
|
||||
|
||||
apiDiff.dependsOn(project.getTasks().getByName("jar"));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isProjectEligible(Project project) {
|
||||
return project.getPlugins().hasPlugin(JavaPlugin.class)
|
||||
&& project.getPlugins().hasPlugin(MavenPublishPlugin.class);
|
||||
}
|
||||
|
||||
private Configuration createBaselineConfiguration(String baselineVersion, Project project) {
|
||||
String baseline = String.join(":",
|
||||
project.getGroup().toString(), project.getName(), baselineVersion);
|
||||
Dependency baselineDependency = project.getDependencies().create(baseline + "@jar");
|
||||
Configuration baselineConfiguration = project.getRootProject().getConfigurations().detachedConfiguration(baselineDependency);
|
||||
try {
|
||||
// eagerly resolve the baseline configuration to check whether this is a new Spring module
|
||||
baselineConfiguration.resolve();
|
||||
return baselineConfiguration;
|
||||
}
|
||||
catch (GradleException exception) {
|
||||
logger.warn("Could not resolve {} - assuming this is a new Spring module.", baseline);
|
||||
}
|
||||
return project.getRootProject().getConfigurations().detachedConfiguration();
|
||||
}
|
||||
|
||||
private Configuration getRuntimeClassPath(Project project) {
|
||||
return project.getConfigurations().getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME);
|
||||
}
|
||||
|
||||
private File getOutputFile(String baseLineVersion, Project project) {
|
||||
String buildDirectoryPath = project.getRootProject()
|
||||
.getLayout().getBuildDirectory().getAsFile().get().getAbsolutePath();
|
||||
Path outDir = Paths.get(buildDirectoryPath, "reports", "api-diff",
|
||||
baseLineVersion + "_to_" + project.getRootProject().getVersion());
|
||||
return project.file(outDir.resolve(project.getName() + ".html").toString());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM ubuntu:jammy-20231004
|
||||
FROM ubuntu:jammy-20231211.1
|
||||
|
||||
ADD setup.sh /setup.sh
|
||||
ADD get-jdk-url.sh /get-jdk-url.sh
|
||||
|
||||
@@ -168,7 +168,7 @@ Kotlin::
|
||||
======
|
||||
|
||||
NOTE: Pooling stateless service objects is not usually necessary. We do not believe it should
|
||||
be the default choice, as most stateless objects are naturally thread safe, and instance
|
||||
be the default choice, as most stateless objects are naturally thread-safe, and instance
|
||||
pooling is problematic if resources are cached.
|
||||
|
||||
Simpler pooling is available by using auto-proxying. You can set the `TargetSource` implementations
|
||||
|
||||
@@ -324,7 +324,6 @@ Kotlin::
|
||||
|
||||
|
||||
|
||||
|
||||
[[beans-factory-scopes-application]]
|
||||
=== Application Scope
|
||||
|
||||
@@ -374,7 +373,6 @@ Kotlin::
|
||||
|
||||
|
||||
|
||||
|
||||
[[beans-factory-scopes-websocket]]
|
||||
=== WebSocket Scope
|
||||
|
||||
@@ -384,7 +382,6 @@ xref:web/websocket/stomp/scope.adoc[WebSocket scope] for more details.
|
||||
|
||||
|
||||
|
||||
|
||||
[[beans-factory-scopes-other-injection]]
|
||||
=== Scoped Beans as Dependencies
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ email when someone places an order:
|
||||
|
||||
// Call the collaborators to persist the order...
|
||||
|
||||
// Create a thread safe "copy" of the template message and customize it
|
||||
// Create a thread-safe "copy" of the template message and customize it
|
||||
SimpleMailMessage msg = new SimpleMailMessage(this.templateMessage);
|
||||
msg.setTo(order.getCustomer().getEmailAddress());
|
||||
msg.setText(
|
||||
|
||||
+4
-1
@@ -166,4 +166,7 @@ By default, any argument that is not a simple value type (as determined by
|
||||
and is not resolved by any other argument resolver is treated as if it were annotated
|
||||
with `@ModelAttribute`.
|
||||
|
||||
|
||||
WARNING: When compiling to a native image with GraalVM, the implicit `@ModelAttribute`
|
||||
support described above does not allow proper ahead-of-time inference of related data
|
||||
binding reflection hints. As a consequence, it is recommended to explicitly annotate
|
||||
method parameters with `@ModelAttribute` for use in a GraalVM native image.
|
||||
|
||||
@@ -70,8 +70,9 @@ it does the same, but it also compares the computed value against the `If-None-M
|
||||
request header and, if the two are equal, returns a 304 (NOT_MODIFIED).
|
||||
|
||||
This strategy saves network bandwidth but not CPU, as the full response must be computed for each request.
|
||||
State-changing HTTP methods and other HTTP conditional request headers such as `If-Match` and `If-Unmodified-Since` are outside the scope of this filter.
|
||||
Other strategies at the controller level can avoid the computation and have a broader support for HTTP conditional requests.
|
||||
State-changing HTTP methods and other HTTP conditional request headers such as `If-Match` and
|
||||
`If-Unmodified-Since` are outside the scope of this filter. Other strategies at the controller level
|
||||
can avoid the computation and have a broader support for HTTP conditional requests.
|
||||
See xref:web/webmvc/mvc-caching.adoc[HTTP Caching].
|
||||
|
||||
This filter has a `writeWeakETag` parameter that configures the filter to write weak ETags
|
||||
|
||||
+4
-1
@@ -216,4 +216,7 @@ By default, any argument that is not a simple value type (as determined by
|
||||
and is not resolved by any other argument resolver is treated as if it were annotated
|
||||
with `@ModelAttribute`.
|
||||
|
||||
|
||||
WARNING: When compiling to a native image with GraalVM, the implicit `@ModelAttribute`
|
||||
support described above does not allow proper ahead-of-time inference of related data
|
||||
binding reflection hints. As a consequence, it is recommended to explicitly annotate
|
||||
method parameters with `@ModelAttribute` for use in a GraalVM native image.
|
||||
|
||||
@@ -229,34 +229,27 @@ Java initialization API. The following example shows how to do so:
|
||||
|
||||
|
||||
[[websocket-server-runtime-configuration]]
|
||||
== Server Configuration
|
||||
== Configuring the Server
|
||||
[.small]#xref:web/webflux-websocket.adoc#webflux-websocket-server-config[See equivalent in the Reactive stack]#
|
||||
|
||||
Each underlying WebSocket engine exposes configuration properties that control
|
||||
runtime characteristics, such as the size of message buffer sizes, idle timeout,
|
||||
and others.
|
||||
You can configure of the underlying WebSocket server such as input message buffer size,
|
||||
idle timeout, and more.
|
||||
|
||||
For Tomcat, WildFly, and GlassFish, you can add a `ServletServerContainerFactoryBean` to your
|
||||
WebSocket Java config, as the following example shows:
|
||||
For Jakarta WebSocket servers, you can add a `ServletServerContainerFactoryBean` to your
|
||||
Java configuration. For example:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@Configuration
|
||||
@EnableWebSocket
|
||||
public class WebSocketConfig implements WebSocketConfigurer {
|
||||
|
||||
@Bean
|
||||
public ServletServerContainerFactoryBean createWebSocketContainer() {
|
||||
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
|
||||
container.setMaxTextMessageBufferSize(8192);
|
||||
container.setMaxBinaryMessageBufferSize(8192);
|
||||
return container;
|
||||
}
|
||||
|
||||
}
|
||||
@Bean
|
||||
public ServletServerContainerFactoryBean createWebSocketContainer() {
|
||||
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
|
||||
container.setMaxTextMessageBufferSize(8192);
|
||||
container.setMaxBinaryMessageBufferSize(8192);
|
||||
return container;
|
||||
}
|
||||
----
|
||||
|
||||
The following example shows the XML configuration equivalent of the preceding example:
|
||||
Or to your XML configuration:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes,attributes"]
|
||||
----
|
||||
@@ -277,12 +270,11 @@ The following example shows the XML configuration equivalent of the preceding ex
|
||||
</beans>
|
||||
----
|
||||
|
||||
NOTE: For client-side WebSocket configuration, you should use `WebSocketContainerFactoryBean`
|
||||
(XML) or `ContainerProvider.getWebSocketContainer()` (Java configuration).
|
||||
NOTE: For client Jakarta WebSocket configuration, use
|
||||
ContainerProvider.getWebSocketContainer() in Java configuration, or
|
||||
`WebSocketContainerFactoryBean` in XML.
|
||||
|
||||
For Jetty, you need to supply a pre-configured Jetty `WebSocketServerFactory` and plug
|
||||
that into Spring's `DefaultHandshakeHandler` through your WebSocket Java config.
|
||||
The following example shows how to do so:
|
||||
For Jetty, you can supply a `Consumer` callback to configure the WebSocket server:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@@ -298,11 +290,9 @@ The following example shows how to do so:
|
||||
|
||||
@Bean
|
||||
public DefaultHandshakeHandler handshakeHandler() {
|
||||
|
||||
WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);
|
||||
policy.setInputBufferSize(8192);
|
||||
policy.setIdleTimeout(600000);
|
||||
|
||||
return new DefaultHandshakeHandler(
|
||||
new JettyRequestUpgradeStrategy(new WebSocketServerFactory(policy)));
|
||||
}
|
||||
@@ -349,6 +339,10 @@ The following example shows the XML configuration equivalent of the preceding ex
|
||||
</beans>
|
||||
----
|
||||
|
||||
TIP: When using STOMP over WebSocket, you will also need to configure
|
||||
xref:web/websocket/stomp/server-config.adoc[STOMP WebSocket transport]
|
||||
properties.
|
||||
|
||||
|
||||
|
||||
[[websocket-server-allowed-origins]]
|
||||
|
||||
+3
-3
@@ -103,9 +103,9 @@ You can also use the WebSocket transport configuration shown earlier to configur
|
||||
maximum allowed size for incoming STOMP messages. In theory, a WebSocket
|
||||
message can be almost unlimited in size. In practice, WebSocket servers impose
|
||||
limits -- for example, 8K on Tomcat and 64K on Jetty. For this reason, STOMP clients
|
||||
(such as the JavaScript https://github.com/JSteunou/webstomp-client[webstomp-client]
|
||||
and others) split larger STOMP messages at 16K boundaries and send them as multiple
|
||||
WebSocket messages, which requires the server to buffer and re-assemble.
|
||||
such as https://github.com/stomp-js/stompjs[`stomp-js/stompjs`] and others split larger
|
||||
STOMP messages at 16K boundaries and send them as multiple WebSocket messages,
|
||||
which requires the server to buffer and re-assemble.
|
||||
|
||||
Spring's STOMP-over-WebSocket support does this ,so applications can configure the
|
||||
maximum size for STOMP messages irrespective of WebSocket server-specific message
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
[[websocket-stomp-server-config]]
|
||||
= WebSocket Server
|
||||
= WebSocket Transport
|
||||
|
||||
To configure the underlying WebSocket server, the information in
|
||||
xref:web/websocket/server.adoc#websocket-server-runtime-configuration[Server Configuration] applies. For Jetty, however you need to set
|
||||
the `HandshakeHandler` and `WebSocketPolicy` through the `StompEndpointRegistry`:
|
||||
This section explains how to configure the underlying WebSocket server transport.
|
||||
|
||||
For Jakarta WebSocket servers, add a `ServletServerContainerFactoryBean` to your
|
||||
configuration. For examples, see
|
||||
xref:web/websocket/server.adoc#websocket-server-runtime-configuration[Configuring the Server]
|
||||
under the WebSocket section.
|
||||
|
||||
For Jetty WebSocket servers, customize the `JettyRequestUpgradeStrategy` as follows:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@@ -29,5 +34,20 @@ the `HandshakeHandler` and `WebSocketPolicy` through the `StompEndpointRegistry`
|
||||
}
|
||||
----
|
||||
|
||||
In addition to WebSocket server properties, there are also STOMP WebSocket transport properties
|
||||
to customize as follows:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@Configuration
|
||||
@EnableWebSocketMessageBroker
|
||||
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
|
||||
|
||||
@Override
|
||||
public void configureWebSocketTransport(WebSocketTransportRegistration registry) {
|
||||
registry.setMessageSizeLimit(4 * 8192);
|
||||
registry.setTimeToFirstMessage(30000);
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
@@ -9,23 +9,23 @@ javaPlatform {
|
||||
dependencies {
|
||||
api(platform("com.fasterxml.jackson:jackson-bom:2.14.3"))
|
||||
api(platform("io.micrometer:micrometer-bom:1.10.13"))
|
||||
api(platform("io.netty:netty-bom:4.1.101.Final"))
|
||||
api(platform("io.netty:netty-bom:4.1.104.Final"))
|
||||
api(platform("io.netty:netty5-bom:5.0.0.Alpha5"))
|
||||
api(platform("io.projectreactor:reactor-bom:2022.0.13"))
|
||||
api(platform("io.projectreactor:reactor-bom:2022.0.15"))
|
||||
api(platform("io.rsocket:rsocket-bom:1.1.3"))
|
||||
api(platform("org.apache.groovy:groovy-bom:4.0.15"))
|
||||
api(platform("org.apache.groovy:groovy-bom:4.0.17"))
|
||||
api(platform("org.apache.logging.log4j:log4j-bom:2.21.1"))
|
||||
api(platform("org.eclipse.jetty:jetty-bom:11.0.18"))
|
||||
api(platform("org.eclipse.jetty:jetty-bom:11.0.19"))
|
||||
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.7.0"))
|
||||
api(platform("org.mockito:mockito-bom:5.8.0"))
|
||||
|
||||
constraints {
|
||||
api("com.fasterxml:aalto-xml:1.3.2")
|
||||
api("com.fasterxml.woodstox:woodstox-core:6.5.1")
|
||||
api("com.github.ben-manes.caffeine:caffeine:3.1.8")
|
||||
api("com.github.librepdf:openpdf:1.3.33")
|
||||
api("com.github.librepdf:openpdf:1.3.36")
|
||||
api("com.google.code.findbugs:findbugs:3.0.1")
|
||||
api("com.google.code.findbugs:jsr305:3.0.2")
|
||||
api("com.google.code.gson:gson:2.10.1")
|
||||
@@ -96,9 +96,9 @@ dependencies {
|
||||
api("org.apache.derby:derby:10.16.1.1")
|
||||
api("org.apache.derby:derbyclient:10.16.1.1")
|
||||
api("org.apache.derby:derbytools:10.16.1.1")
|
||||
api("org.apache.httpcomponents.client5:httpclient5:5.2.1")
|
||||
api("org.apache.httpcomponents.core5:httpcore5-reactive:5.2.3")
|
||||
api("org.apache.poi:poi-ooxml:5.2.4")
|
||||
api("org.apache.httpcomponents.client5:httpclient5:5.2.3")
|
||||
api("org.apache.httpcomponents.core5:httpcore5-reactive:5.2.4")
|
||||
api("org.apache.poi:poi-ooxml:5.2.5")
|
||||
api("org.apache.tomcat.embed:tomcat-embed-core:10.1.15")
|
||||
api("org.apache.tomcat.embed:tomcat-embed-websocket:10.1.15")
|
||||
api("org.apache.tomcat:tomcat-util:10.1.15")
|
||||
@@ -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.9")
|
||||
api("org.slf4j:slf4j-api:2.0.11")
|
||||
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.14-SNAPSHOT
|
||||
version=6.0.16
|
||||
|
||||
org.gradle.caching=true
|
||||
org.gradle.jvmargs=-Xmx2048m
|
||||
|
||||
Vendored
BIN
Binary file not shown.
+1
-1
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
+6
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -333,12 +333,12 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
|
||||
Object targetObject = null;
|
||||
Object thisObject = null;
|
||||
try {
|
||||
MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation();
|
||||
targetObject = mi.getThis();
|
||||
if (!(mi instanceof ProxyMethodInvocation _pmi)) {
|
||||
throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
|
||||
MethodInvocation curr = ExposeInvocationInterceptor.currentInvocation();
|
||||
targetObject = curr.getThis();
|
||||
if (!(curr instanceof ProxyMethodInvocation currPmi)) {
|
||||
throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + curr);
|
||||
}
|
||||
pmi = _pmi;
|
||||
pmi = currPmi;
|
||||
thisObject = pmi.getProxy();
|
||||
}
|
||||
catch (IllegalStateException ex) {
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -293,7 +293,7 @@ final class InstantiationModelAwarePointcutAdvisorImpl
|
||||
@Override
|
||||
public boolean matches(Method method, Class<?> targetClass, Object... args) {
|
||||
// This can match only on declared pointcut.
|
||||
return (isAspectMaterialized() && this.declaredPointcut.matches(method, targetClass));
|
||||
return (isAspectMaterialized() && this.declaredPointcut.matches(method, targetClass, args));
|
||||
}
|
||||
|
||||
private boolean isAspectMaterialized() {
|
||||
|
||||
@@ -102,6 +102,12 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
*/
|
||||
private List<Advisor> advisors = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* List of minimal {@link AdvisorKeyEntry} instances,
|
||||
* to be assigned to the {@link #advisors} field on reduction.
|
||||
* @since 6.0.10
|
||||
* @see #reduceToAdvisorKey
|
||||
*/
|
||||
private List<Advisor> advisorKey = this.advisors;
|
||||
|
||||
|
||||
@@ -557,18 +563,6 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Serialization support
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
|
||||
// Rely on default serialization; just initialize state after deserialization.
|
||||
ois.defaultReadObject();
|
||||
|
||||
// Initialize transient fields.
|
||||
this.methodCache = new ConcurrentHashMap<>(32);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toProxyConfigString() {
|
||||
return toString();
|
||||
@@ -590,6 +584,19 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Serialization support
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
|
||||
// Rely on default serialization; just initialize state after deserialization.
|
||||
ois.defaultReadObject();
|
||||
|
||||
// Initialize transient fields.
|
||||
this.methodCache = new ConcurrentHashMap<>(32);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Simple wrapper class around a Method. Used as the key when
|
||||
* caching methods, for efficient equals and hashCode comparisons.
|
||||
@@ -639,7 +646,7 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
* @see #getConfigurationOnlyCopy()
|
||||
* @see #getAdvisorKey()
|
||||
*/
|
||||
private static class AdvisorKeyEntry implements Advisor {
|
||||
private static final class AdvisorKeyEntry implements Advisor {
|
||||
|
||||
private final Class<?> adviceType;
|
||||
|
||||
@@ -649,7 +656,6 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
@Nullable
|
||||
private final String methodMatcherKey;
|
||||
|
||||
|
||||
public AdvisorKeyEntry(Advisor advisor) {
|
||||
this.adviceType = advisor.getAdvice().getClass();
|
||||
if (advisor instanceof PointcutAdvisor pointcutAdvisor) {
|
||||
|
||||
+2
-1
@@ -58,6 +58,7 @@ import org.springframework.util.function.SingletonSupplier;
|
||||
* @author Juergen Hoeller
|
||||
* @author Stephane Nicoll
|
||||
* @author He Bo
|
||||
* @author Sebastien Deleuze
|
||||
* @since 3.1.2
|
||||
*/
|
||||
public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
|
||||
@@ -290,7 +291,7 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
|
||||
else if (Future.class.isAssignableFrom(returnType)) {
|
||||
return executor.submit(task);
|
||||
}
|
||||
else if (void.class == returnType) {
|
||||
else if (void.class == returnType || "kotlin.Unit".equals(returnType.getName())) {
|
||||
executor.submit(task);
|
||||
return null;
|
||||
}
|
||||
|
||||
+23
-25
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,7 +23,6 @@ import java.util.Map;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.aspectj.weaver.tools.PointcutExpression;
|
||||
import org.aspectj.weaver.tools.PointcutPrimitive;
|
||||
import org.aspectj.weaver.tools.UnsupportedPointcutPrimitiveException;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -65,7 +64,7 @@ public class AspectJExpressionPointcutTests {
|
||||
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() throws NoSuchMethodException {
|
||||
public void setup() throws NoSuchMethodException {
|
||||
getAge = TestBean.class.getMethod("getAge");
|
||||
setAge = TestBean.class.getMethod("setAge", int.class);
|
||||
setSomeNumber = TestBean.class.getMethod("setSomeNumber", Number.class);
|
||||
@@ -246,14 +245,13 @@ public class AspectJExpressionPointcutTests {
|
||||
@Test
|
||||
public void testInvalidExpression() {
|
||||
String expression = "execution(void org.springframework.beans.testfixture.beans.TestBean.setSomeNumber(Number) && args(Double)";
|
||||
assertThatIllegalArgumentException().isThrownBy(
|
||||
getPointcut(expression)::getClassFilter); // call to getClassFilter forces resolution
|
||||
assertThatIllegalArgumentException().isThrownBy(getPointcut(expression)::getClassFilter); // call to getClassFilter forces resolution
|
||||
}
|
||||
|
||||
private TestBean getAdvisedProxy(String pointcutExpression, CallCountingInterceptor interceptor) {
|
||||
TestBean target = new TestBean();
|
||||
|
||||
Pointcut pointcut = getPointcut(pointcutExpression);
|
||||
AspectJExpressionPointcut pointcut = getPointcut(pointcutExpression);
|
||||
|
||||
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
|
||||
advisor.setAdvice(interceptor);
|
||||
@@ -277,40 +275,31 @@ public class AspectJExpressionPointcutTests {
|
||||
@Test
|
||||
public void testWithUnsupportedPointcutPrimitive() {
|
||||
String expression = "call(int org.springframework.beans.testfixture.beans.TestBean.getAge())";
|
||||
assertThatExceptionOfType(UnsupportedPointcutPrimitiveException.class).isThrownBy(() ->
|
||||
getPointcut(expression).getClassFilter()) // call to getClassFilter forces resolution...
|
||||
.satisfies(ex -> assertThat(ex.getUnsupportedPrimitive()).isEqualTo(PointcutPrimitive.CALL));
|
||||
assertThatExceptionOfType(UnsupportedPointcutPrimitiveException.class)
|
||||
.isThrownBy(() -> getPointcut(expression).getClassFilter()) // call to getClassFilter forces resolution...
|
||||
.satisfies(ex -> assertThat(ex.getUnsupportedPrimitive()).isEqualTo(PointcutPrimitive.CALL));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAndSubstitution() {
|
||||
Pointcut pc = getPointcut("execution(* *(..)) and args(String)");
|
||||
PointcutExpression expr = ((AspectJExpressionPointcut) pc).getPointcutExpression();
|
||||
assertThat(expr.getPointcutExpression()).isEqualTo("execution(* *(..)) && args(String)");
|
||||
AspectJExpressionPointcut pc = getPointcut("execution(* *(..)) and args(String)");
|
||||
String expr = pc.getPointcutExpression().getPointcutExpression();
|
||||
assertThat(expr).isEqualTo("execution(* *(..)) && args(String)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleAndSubstitutions() {
|
||||
Pointcut pc = getPointcut("execution(* *(..)) and args(String) and this(Object)");
|
||||
PointcutExpression expr = ((AspectJExpressionPointcut) pc).getPointcutExpression();
|
||||
assertThat(expr.getPointcutExpression()).isEqualTo("execution(* *(..)) && args(String) && this(Object)");
|
||||
AspectJExpressionPointcut pc = getPointcut("execution(* *(..)) and args(String) and this(Object)");
|
||||
String expr = pc.getPointcutExpression().getPointcutExpression();
|
||||
assertThat(expr).isEqualTo("execution(* *(..)) && args(String) && this(Object)");
|
||||
}
|
||||
|
||||
private Pointcut getPointcut(String expression) {
|
||||
private AspectJExpressionPointcut getPointcut(String expression) {
|
||||
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
|
||||
pointcut.setExpression(expression);
|
||||
return pointcut;
|
||||
}
|
||||
|
||||
|
||||
public static class OtherIOther implements IOther {
|
||||
|
||||
@Override
|
||||
public void absquatulate() {
|
||||
// Empty
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatchGenericArgument() {
|
||||
String expression = "execution(* set*(java.util.List<org.springframework.beans.testfixture.beans.TestBean>) )";
|
||||
@@ -505,6 +494,15 @@ public class AspectJExpressionPointcutTests {
|
||||
}
|
||||
|
||||
|
||||
public static class OtherIOther implements IOther {
|
||||
|
||||
@Override
|
||||
public void absquatulate() {
|
||||
// Empty
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class HasGeneric {
|
||||
|
||||
public void setFriends(List<TestBean> friends) {
|
||||
|
||||
+40
-17
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -42,26 +42,38 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
*/
|
||||
class ArgumentBindingTests {
|
||||
|
||||
@Test
|
||||
void bindingInPointcutUsedByAdvice() {
|
||||
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new TestBean());
|
||||
proxyFactory.addAspect(NamedPointcutWithArgs.class);
|
||||
|
||||
ITestBean proxiedTestBean = proxyFactory.getProxy();
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> proxiedTestBean.setName("enigma"))
|
||||
.withMessage("enigma");
|
||||
}
|
||||
|
||||
@Test
|
||||
void annotationArgumentNameBinding() {
|
||||
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new TransactionalBean());
|
||||
proxyFactory.addAspect(PointcutWithAnnotationArgument.class);
|
||||
|
||||
ITransactionalBean proxiedTestBean = proxyFactory.getProxy();
|
||||
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(proxiedTestBean::doInTransaction)
|
||||
.withMessage("Invoked with @Transactional");
|
||||
.isThrownBy(proxiedTestBean::doInTransaction)
|
||||
.withMessage("Invoked with @Transactional");
|
||||
}
|
||||
|
||||
@Test
|
||||
void bindingInPointcutUsedByAdvice() {
|
||||
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new TestBean());
|
||||
proxyFactory.addAspect(NamedPointcutWithArgs.class);
|
||||
ITestBean proxiedTestBean = proxyFactory.getProxy();
|
||||
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> proxiedTestBean.setName("enigma"))
|
||||
.withMessage("enigma");
|
||||
}
|
||||
|
||||
@Test
|
||||
void bindingWithDynamicAdvice() {
|
||||
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new TestBean());
|
||||
proxyFactory.addAspect(DynamicPointcutWithArgs.class);
|
||||
ITestBean proxiedTestBean = proxyFactory.getProxy();
|
||||
|
||||
proxiedTestBean.applyName(1);
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> proxiedTestBean.applyName("enigma"))
|
||||
.withMessage("enigma");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -94,6 +106,7 @@ class ArgumentBindingTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Mimics Spring's @Transactional annotation without actually introducing the dependency.
|
||||
*/
|
||||
@@ -101,16 +114,17 @@ class ArgumentBindingTests {
|
||||
@interface Transactional {
|
||||
}
|
||||
|
||||
|
||||
@Aspect
|
||||
static class PointcutWithAnnotationArgument {
|
||||
|
||||
@Around(value = "execution(* org.springframework..*.*(..)) && @annotation(transactional)")
|
||||
@Around("execution(* org.springframework..*.*(..)) && @annotation(transactional)")
|
||||
public Object around(ProceedingJoinPoint pjp, Transactional transactional) throws Throwable {
|
||||
throw new IllegalStateException("Invoked with @Transactional");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Aspect
|
||||
static class NamedPointcutWithArgs {
|
||||
|
||||
@@ -121,7 +135,16 @@ class ArgumentBindingTests {
|
||||
public Object doAround(ProceedingJoinPoint pjp, String aString) throws Throwable {
|
||||
throw new IllegalArgumentException(aString);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Aspect("pertarget(execution(* *(..)))")
|
||||
static class DynamicPointcutWithArgs {
|
||||
|
||||
@Around("execution(* *(..)) && args(java.lang.String)")
|
||||
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
|
||||
throw new IllegalArgumentException(String.valueOf(pjp.getArgs()[0]));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+17
-12
@@ -159,6 +159,9 @@ import org.springframework.util.StringUtils;
|
||||
public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor,
|
||||
MergedBeanDefinitionPostProcessor, BeanRegistrationAotProcessor, PriorityOrdered, BeanFactoryAware {
|
||||
|
||||
private static final Constructor<?>[] EMPTY_CONSTRUCTOR_ARRAY = new Constructor<?>[0];
|
||||
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final Set<Class<? extends Annotation>> autowiredAnnotationTypes = new LinkedHashSet<>(4);
|
||||
@@ -193,9 +196,10 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
this.autowiredAnnotationTypes.add(Autowired.class);
|
||||
this.autowiredAnnotationTypes.add(Value.class);
|
||||
|
||||
ClassLoader classLoader = AutowiredAnnotationBeanPostProcessor.class.getClassLoader();
|
||||
try {
|
||||
this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
|
||||
ClassUtils.forName("jakarta.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
|
||||
ClassUtils.forName("jakarta.inject.Inject", classLoader));
|
||||
logger.trace("'jakarta.inject.Inject' annotation found and supported for autowiring");
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
@@ -204,7 +208,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
|
||||
try {
|
||||
this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
|
||||
ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
|
||||
ClassUtils.forName("javax.inject.Inject", classLoader));
|
||||
logger.trace("'javax.inject.Inject' annotation found and supported for autowiring");
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
@@ -285,9 +289,16 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
|
||||
@Override
|
||||
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
|
||||
// Register externally managed config members on bean definition.
|
||||
findInjectionMetadata(beanName, beanType, beanDefinition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetBeanDefinition(String beanName) {
|
||||
this.lookupMethodsChecked.remove(beanName);
|
||||
this.injectionMetadataCache.remove(beanName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) {
|
||||
@@ -323,12 +334,6 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
return metadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetBeanDefinition(String beanName) {
|
||||
this.lookupMethodsChecked.remove(beanName);
|
||||
this.injectionMetadataCache.remove(beanName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> determineBeanType(Class<?> beanClass, String beanName) throws BeanCreationException {
|
||||
checkLookupMethods(beanClass, beanName);
|
||||
@@ -428,7 +433,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
"default constructor to fall back to: " + candidates.get(0));
|
||||
}
|
||||
}
|
||||
candidateConstructors = candidates.toArray(new Constructor<?>[0]);
|
||||
candidateConstructors = candidates.toArray(EMPTY_CONSTRUCTOR_ARRAY);
|
||||
}
|
||||
else if (rawCandidates.length == 1 && rawCandidates[0].getParameterCount() > 0) {
|
||||
candidateConstructors = new Constructor<?>[] {rawCandidates[0]};
|
||||
@@ -441,7 +446,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
candidateConstructors = new Constructor<?>[] {primaryConstructor};
|
||||
}
|
||||
else {
|
||||
candidateConstructors = new Constructor<?>[0];
|
||||
candidateConstructors = EMPTY_CONSTRUCTOR_ARRAY;
|
||||
}
|
||||
this.candidateConstructorsCache.put(beanClass, candidateConstructors);
|
||||
}
|
||||
@@ -1011,7 +1016,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
hints.reflection().registerField(field);
|
||||
CodeBlock resolver = CodeBlock.of("$T.$L($S)",
|
||||
AutowiredFieldValueResolver.class,
|
||||
(!required) ? "forField" : "forRequiredField", field.getName());
|
||||
(!required ? "forField" : "forRequiredField"), field.getName());
|
||||
AccessControl accessControl = AccessControl.forMember(field);
|
||||
if (!accessControl.isAccessibleFrom(targetClassName)) {
|
||||
return CodeBlock.of("$L.resolveAndSet($L, $L)", resolver,
|
||||
@@ -1026,7 +1031,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
|
||||
CodeBlock.Builder code = CodeBlock.builder();
|
||||
code.add("$T.$L", AutowiredMethodArgumentsResolver.class,
|
||||
(!required) ? "forMethod" : "forRequiredMethod");
|
||||
(!required ? "forMethod" : "forRequiredMethod"));
|
||||
code.add("($S", method.getName());
|
||||
if (method.getParameterCount() > 0) {
|
||||
code.add(", $L", generateParameterTypesCode(method.getParameterTypes()));
|
||||
|
||||
+4
-6
@@ -191,16 +191,14 @@ public final class AutowiredFieldValueResolver extends AutowiredElementResolver
|
||||
return value;
|
||||
}
|
||||
catch (BeansException ex) {
|
||||
throw new UnsatisfiedDependencyException(null, beanName,
|
||||
new InjectionPoint(field), ex);
|
||||
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
|
||||
}
|
||||
}
|
||||
|
||||
private Field getField(RegisteredBean registeredBean) {
|
||||
Field field = ReflectionUtils.findField(registeredBean.getBeanClass(),
|
||||
this.fieldName);
|
||||
Assert.notNull(field, () -> "No field '" + this.fieldName + "' found on "
|
||||
+ registeredBean.getBeanClass().getName());
|
||||
Field field = ReflectionUtils.findField(registeredBean.getBeanClass(), this.fieldName);
|
||||
Assert.notNull(field, () -> "No field '" + this.fieldName + "' found on " +
|
||||
registeredBean.getBeanClass().getName());
|
||||
return field;
|
||||
}
|
||||
|
||||
|
||||
+12
-14
@@ -92,10 +92,10 @@ import org.springframework.util.function.ThrowingSupplier;
|
||||
* Supports autowiring constructors, properties by name, and properties by type.
|
||||
*
|
||||
* <p>The main template method to be implemented by subclasses is
|
||||
* {@link #resolveDependency(DependencyDescriptor, String, Set, TypeConverter)},
|
||||
* used for autowiring by type. In case of a factory which is capable of searching
|
||||
* its bean definitions, matching beans will typically be implemented through such
|
||||
* a search. For other factory styles, simplified matching algorithms can be implemented.
|
||||
* {@link #resolveDependency(DependencyDescriptor, String, Set, TypeConverter)}, used for
|
||||
* autowiring. In case of a {@link org.springframework.beans.factory.ListableBeanFactory}
|
||||
* which is capable of searching its bean definitions, matching beans will typically be
|
||||
* implemented through such a search. Otherwise, simplified matching can be implemented.
|
||||
*
|
||||
* <p>Note that this class does <i>not</i> assume or implement bean definition
|
||||
* registry capabilities. See {@link DefaultListableBeanFactory} for an implementation
|
||||
@@ -493,15 +493,13 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
||||
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
|
||||
mbdToUse = new RootBeanDefinition(mbd);
|
||||
mbdToUse.setBeanClass(resolvedClass);
|
||||
}
|
||||
|
||||
// Prepare method overrides.
|
||||
try {
|
||||
mbdToUse.prepareMethodOverrides();
|
||||
}
|
||||
catch (BeanDefinitionValidationException ex) {
|
||||
throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
|
||||
beanName, "Validation of method overrides failed", ex);
|
||||
try {
|
||||
mbdToUse.prepareMethodOverrides();
|
||||
}
|
||||
catch (BeanDefinitionValidationException ex) {
|
||||
throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
|
||||
beanName, "Validation of method overrides failed", ex);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -652,7 +650,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
||||
// Apply SmartInstantiationAwareBeanPostProcessors to predict the
|
||||
// eventual type after a before-instantiation shortcut.
|
||||
if (targetType != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
|
||||
boolean matchingOnlyFactoryBean = typesToMatch.length == 1 && typesToMatch[0] == FactoryBean.class;
|
||||
boolean matchingOnlyFactoryBean = (typesToMatch.length == 1 && typesToMatch[0] == FactoryBean.class);
|
||||
for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
|
||||
Class<?> predicted = bp.predictBeanType(targetType, beanName);
|
||||
if (predicted != null &&
|
||||
|
||||
+10
-2
@@ -731,7 +731,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
aliases.add(fullBeanName);
|
||||
}
|
||||
String[] retrievedAliases = super.getAliases(beanName);
|
||||
String prefix = factoryPrefix ? FACTORY_BEAN_PREFIX : "";
|
||||
String prefix = (factoryPrefix ? FACTORY_BEAN_PREFIX : "");
|
||||
for (String retrievedAlias : retrievedAliases) {
|
||||
String alias = prefix + retrievedAlias;
|
||||
if (!alias.equals(name)) {
|
||||
@@ -1497,7 +1497,11 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
if (mbd.hasBeanClass()) {
|
||||
return mbd.getBeanClass();
|
||||
}
|
||||
return doResolveBeanClass(mbd, typesToMatch);
|
||||
Class<?> beanClass = doResolveBeanClass(mbd, typesToMatch);
|
||||
if (mbd.hasBeanClass()) {
|
||||
mbd.prepareMethodOverrides();
|
||||
}
|
||||
return beanClass;
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
throw new CannotLoadBeanClassException(mbd.getResourceDescription(), beanName, mbd.getBeanClassName(), ex);
|
||||
@@ -1505,6 +1509,10 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
catch (LinkageError err) {
|
||||
throw new CannotLoadBeanClassException(mbd.getResourceDescription(), beanName, mbd.getBeanClassName(), err);
|
||||
}
|
||||
catch (BeanDefinitionValidationException ex) {
|
||||
throw new BeanDefinitionStoreException(mbd.getResourceDescription(),
|
||||
beanName, "Validation of method overrides failed", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
+1
-5
@@ -120,11 +120,7 @@ class ConstructorResolver {
|
||||
|
||||
/**
|
||||
* "autowire constructor" (with constructor arguments by type) behavior.
|
||||
* Also applied if explicit constructor argument values are specified,
|
||||
* matching all remaining arguments with beans from the bean factory.
|
||||
* <p>This corresponds to constructor injection: In this mode, a Spring
|
||||
* bean factory is able to host components that expect constructor-based
|
||||
* dependency resolution.
|
||||
* Also applied if explicit constructor argument values are specified.
|
||||
* @param beanName the name of the bean
|
||||
* @param mbd the merged bean definition for the bean
|
||||
* @param chosenCtors chosen candidate constructors (or {@code null} if none)
|
||||
|
||||
+3
-3
@@ -71,7 +71,7 @@ public class SimpleInstantiationStrategy implements InstantiationStrategy {
|
||||
synchronized (bd.constructorArgumentLock) {
|
||||
constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
|
||||
if (constructorToUse == null) {
|
||||
final Class<?> clazz = bd.getBeanClass();
|
||||
Class<?> clazz = bd.getBeanClass();
|
||||
if (clazz.isInterface()) {
|
||||
throw new BeanInstantiationException(clazz, "Specified class is an interface");
|
||||
}
|
||||
@@ -104,7 +104,7 @@ public class SimpleInstantiationStrategy implements InstantiationStrategy {
|
||||
|
||||
@Override
|
||||
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
|
||||
final Constructor<?> ctor, Object... args) {
|
||||
Constructor<?> ctor, Object... args) {
|
||||
|
||||
if (!bd.hasMethodOverrides()) {
|
||||
return BeanUtils.instantiateClass(ctor, args);
|
||||
@@ -128,7 +128,7 @@ public class SimpleInstantiationStrategy implements InstantiationStrategy {
|
||||
|
||||
@Override
|
||||
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
|
||||
@Nullable Object factoryBean, final Method factoryMethod, Object... args) {
|
||||
@Nullable Object factoryBean, Method factoryMethod, Object... args) {
|
||||
|
||||
try {
|
||||
ReflectionUtils.makeAccessible(factoryMethod);
|
||||
|
||||
+7
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -78,8 +78,10 @@ public class PathEditor extends PropertyEditorSupport {
|
||||
if (nioPathCandidate && !text.startsWith("/")) {
|
||||
try {
|
||||
URI uri = ResourceUtils.toURI(text);
|
||||
if (uri.getScheme() != null) {
|
||||
nioPathCandidate = false;
|
||||
String scheme = uri.getScheme();
|
||||
if (scheme != null) {
|
||||
// No NIO candidate except for "C:" style drive letters
|
||||
nioPathCandidate = (scheme.length() == 1);
|
||||
// Let's try NIO file system providers via Paths.get(URI)
|
||||
setValue(Paths.get(uri).normalize());
|
||||
return;
|
||||
@@ -109,7 +111,8 @@ public class PathEditor extends PropertyEditorSupport {
|
||||
setValue(resource.getFile().toPath());
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalArgumentException("Failed to retrieve file for " + resource, ex);
|
||||
throw new IllegalArgumentException(
|
||||
"Could not retrieve file for " + resource + ": " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+39
-1
@@ -2102,7 +2102,7 @@ class DefaultListableBeanFactoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void beanProviderWithParentBeanFactoryReuseOrder() {
|
||||
void beanProviderWithParentBeanFactoryDetectsOrder() {
|
||||
DefaultListableBeanFactory parentBf = new DefaultListableBeanFactory();
|
||||
parentBf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
|
||||
parentBf.registerBeanDefinition("regular", new RootBeanDefinition(TestBean.class));
|
||||
@@ -2110,10 +2110,36 @@ class DefaultListableBeanFactoryTests {
|
||||
lbf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
|
||||
lbf.setParentBeanFactory(parentBf);
|
||||
lbf.registerBeanDefinition("low", new RootBeanDefinition(LowPriorityTestBean.class));
|
||||
|
||||
Stream<Class<?>> orderedTypes = lbf.getBeanProvider(TestBean.class).orderedStream().map(Object::getClass);
|
||||
assertThat(orderedTypes).containsExactly(HighPriorityTestBean.class, LowPriorityTestBean.class, TestBean.class);
|
||||
}
|
||||
|
||||
@Test // gh-28374
|
||||
void beanProviderWithParentBeanFactoryAndMixedOrder() {
|
||||
DefaultListableBeanFactory parentBf = new DefaultListableBeanFactory();
|
||||
parentBf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
|
||||
lbf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
|
||||
lbf.setParentBeanFactory(parentBf);
|
||||
|
||||
lbf.registerSingleton("plainTestBean", new TestBean());
|
||||
|
||||
RootBeanDefinition bd1 = new RootBeanDefinition(PriorityTestBeanFactory.class);
|
||||
bd1.setFactoryMethodName("lowPriorityTestBean");
|
||||
lbf.registerBeanDefinition("lowPriorityTestBean", bd1);
|
||||
|
||||
RootBeanDefinition bd2 = new RootBeanDefinition(PriorityTestBeanFactory.class);
|
||||
bd2.setFactoryMethodName("highPriorityTestBean");
|
||||
parentBf.registerBeanDefinition("highPriorityTestBean", bd2);
|
||||
|
||||
ObjectProvider<TestBean> testBeanProvider = lbf.getBeanProvider(ResolvableType.forClass(TestBean.class));
|
||||
List<TestBean> resolved = testBeanProvider.orderedStream().toList();
|
||||
assertThat(resolved.size()).isEqualTo(3);
|
||||
assertThat(resolved.get(0)).isSameAs(lbf.getBean("highPriorityTestBean"));
|
||||
assertThat(resolved.get(1)).isSameAs(lbf.getBean("lowPriorityTestBean"));
|
||||
assertThat(resolved.get(2)).isSameAs(lbf.getBean("plainTestBean"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void autowireExistingBeanByName() {
|
||||
RootBeanDefinition bd = new RootBeanDefinition(TestBean.class);
|
||||
@@ -3287,6 +3313,18 @@ class DefaultListableBeanFactoryTests {
|
||||
}
|
||||
|
||||
|
||||
private static class PriorityTestBeanFactory {
|
||||
|
||||
public static LowPriorityTestBean lowPriorityTestBean() {
|
||||
return new LowPriorityTestBean();
|
||||
}
|
||||
|
||||
public static HighPriorityTestBean highPriorityTestBean() {
|
||||
return new HighPriorityTestBean();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class NullTestBeanFactoryBean<T> implements FactoryBean<TestBean> {
|
||||
|
||||
@Override
|
||||
|
||||
+5
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -33,6 +33,10 @@ public interface ITestBean extends AgeHolder {
|
||||
|
||||
void setName(String name);
|
||||
|
||||
default void applyName(Object name) {
|
||||
setName(String.valueOf(name));
|
||||
}
|
||||
|
||||
ITestBean getSpouse();
|
||||
|
||||
void setSpouse(ITestBean spouse);
|
||||
|
||||
@@ -6,12 +6,12 @@ dependencies {
|
||||
api(project(":spring-core"))
|
||||
optional(project(":spring-jdbc")) // for Quartz support
|
||||
optional(project(":spring-tx")) // for Quartz support
|
||||
optional("com.github.ben-manes.caffeine:caffeine")
|
||||
optional("jakarta.activation:jakarta.activation-api")
|
||||
optional("jakarta.mail:jakarta.mail-api")
|
||||
optional("javax.cache:cache-api")
|
||||
optional("com.github.ben-manes.caffeine:caffeine")
|
||||
optional("org.quartz-scheduler:quartz")
|
||||
optional("org.freemarker:freemarker")
|
||||
optional("org.quartz-scheduler:quartz")
|
||||
testFixturesApi("org.junit.jupiter:junit-jupiter-api")
|
||||
testFixturesImplementation("org.assertj:assertj-core")
|
||||
testFixturesImplementation("org.mockito:mockito-core")
|
||||
@@ -20,10 +20,10 @@ dependencies {
|
||||
testImplementation(testFixtures(project(":spring-context")))
|
||||
testImplementation(testFixtures(project(":spring-core")))
|
||||
testImplementation(testFixtures(project(":spring-tx")))
|
||||
testImplementation("org.hsqldb:hsqldb")
|
||||
testImplementation("jakarta.annotation:jakarta.annotation-api")
|
||||
testRuntimeOnly("org.ehcache:jcache")
|
||||
testRuntimeOnly("org.ehcache:ehcache")
|
||||
testRuntimeOnly("org.glassfish:jakarta.el")
|
||||
testImplementation("org.hsqldb:hsqldb")
|
||||
testRuntimeOnly("com.sun.mail:jakarta.mail")
|
||||
testRuntimeOnly("org.ehcache:ehcache")
|
||||
testRuntimeOnly("org.ehcache:jcache")
|
||||
testRuntimeOnly("org.glassfish:jakarta.el")
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -236,7 +236,7 @@ public class CaffeineCacheManager implements CacheManager {
|
||||
* Build a common {@link CaffeineCache} instance for the specified cache name,
|
||||
* using the common Caffeine configuration specified on this cache manager.
|
||||
* <p>Delegates to {@link #adaptCaffeineCache} as the adaptation method to
|
||||
* Spring's cache abstraction (allowing for centralized decoration etc),
|
||||
* Spring's cache abstraction (allowing for centralized decoration etc.),
|
||||
* passing in a freshly built native Caffeine Cache instance.
|
||||
* @param name the name of the cache
|
||||
* @return the Spring CaffeineCache adapter (or a decorator thereof)
|
||||
|
||||
+1
@@ -121,6 +121,7 @@ public class SimpleMailMessageTests {
|
||||
assertThat(message2.hashCode()).isEqualTo(message1.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public final void testEqualsObject() {
|
||||
SimpleMailMessage message1;
|
||||
SimpleMailMessage message2;
|
||||
|
||||
+6
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2015 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,6 +32,7 @@ import java.lang.annotation.Target;
|
||||
* @author Stephane Nicoll
|
||||
* @author Sam Brannen
|
||||
* @since 4.1
|
||||
* @see Cacheable
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@@ -42,8 +43,10 @@ public @interface CacheConfig {
|
||||
* Names of the default caches to consider for caching operations defined
|
||||
* in the annotated class.
|
||||
* <p>If none is set at the operation level, these are used instead of the default.
|
||||
* <p>May be used to determine the target cache (or caches), matching the
|
||||
* qualifier value or the bean names of a specific bean definition.
|
||||
* <p>Names may be used to determine the target cache(s), to be resolved via the
|
||||
* configured {@link #cacheResolver()} which typically delegates to
|
||||
* {@link org.springframework.cache.CacheManager#getCache}.
|
||||
* For further details see {@link Cacheable#cacheNames()}.
|
||||
*/
|
||||
String[] cacheNames() default {};
|
||||
|
||||
|
||||
+6
-2
@@ -70,8 +70,12 @@ public @interface Cacheable {
|
||||
|
||||
/**
|
||||
* Names of the caches in which method invocation results are stored.
|
||||
* <p>Names may be used to determine the target cache (or caches), matching
|
||||
* the qualifier value or bean name of a specific bean definition.
|
||||
* <p>Names may be used to determine the target cache(s), to be resolved via the
|
||||
* configured {@link #cacheResolver()} which typically delegates to
|
||||
* {@link org.springframework.cache.CacheManager#getCache}.
|
||||
* <p>This will usually be a single cache name. If multiple names are specified,
|
||||
* they will be consulted for a cache hit in the order of definition, and they
|
||||
* will all receive a put/evict request for the same newly cached value.
|
||||
* @since 4.2
|
||||
* @see #value
|
||||
* @see CacheConfig#cacheNames
|
||||
|
||||
+3
-3
@@ -547,10 +547,10 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect the {@link CachePutRequest} for all {@link CacheOperation} using
|
||||
* the specified result value.
|
||||
* Collect a {@link CachePutRequest} for every {@link CacheOperation}
|
||||
* using the specified result value.
|
||||
* @param contexts the contexts to handle
|
||||
* @param result the result value (never {@code null})
|
||||
* @param result the result value
|
||||
* @param putRequests the collection to update
|
||||
*/
|
||||
private void collectPutRequests(Collection<CacheOperationContext> contexts,
|
||||
|
||||
Vendored
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,7 +22,7 @@ import org.springframework.lang.Nullable;
|
||||
* Abstract the invocation of a cache operation.
|
||||
*
|
||||
* <p>Does not provide a way to transmit checked exceptions but
|
||||
* provide a special exception that should be used to wrap any
|
||||
* provides a special exception that should be used to wrap any
|
||||
* exception that was thrown by the underlying invocation.
|
||||
* Callers are expected to handle this issue type specifically.
|
||||
*
|
||||
|
||||
+11
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -62,11 +62,13 @@ import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* A component provider that provides candidate components from a base package. Can
|
||||
* use {@link CandidateComponentsIndex the index} if it is available of scans the
|
||||
* classpath otherwise. Candidate components are identified by applying exclude and
|
||||
* include filters. {@link AnnotationTypeFilter}, {@link AssignableTypeFilter} include
|
||||
* filters on an annotation/superclass that are annotated with {@link Indexed} are
|
||||
* A component provider that scans for candidate components starting from a
|
||||
* specified base package. Can use the {@linkplain CandidateComponentsIndex component
|
||||
* index}, if it is available, and scans the classpath otherwise.
|
||||
*
|
||||
* <p>Candidate components are identified by applying exclude and include filters.
|
||||
* {@link AnnotationTypeFilter} and {@link AssignableTypeFilter} include filters
|
||||
* for an annotation/target-type that is annotated with {@link Indexed} are
|
||||
* supported: if any other include filter is specified, the index is ignored and
|
||||
* classpath scanning is used instead.
|
||||
*
|
||||
@@ -201,7 +203,6 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
|
||||
* {@link Controller @Controller} stereotype annotations.
|
||||
* <p>Also supports Jakarta EE's {@link jakarta.annotation.ManagedBean} and
|
||||
* JSR-330's {@link jakarta.inject.Named} annotations, if available.
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void registerDefaultFilters() {
|
||||
@@ -305,7 +306,7 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
|
||||
|
||||
|
||||
/**
|
||||
* Scan the class path for candidate components.
|
||||
* Scan the component index or class path for candidate components.
|
||||
* @param basePackage the package to check for annotated classes
|
||||
* @return a corresponding Set of autodetected bean definitions
|
||||
*/
|
||||
@@ -319,7 +320,7 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the index can be used by this instance.
|
||||
* Determine if the component index can be used by this instance.
|
||||
* @return {@code true} if the index is available and the configuration of this
|
||||
* instance is supported by it, {@code false} otherwise
|
||||
* @since 5.0
|
||||
@@ -460,8 +461,7 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new BeanDefinitionStoreException(
|
||||
"Failed to read candidate component class: " + resource, ex);
|
||||
throw new BeanDefinitionStoreException("Failed to read candidate component class: " + resource, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -387,8 +387,8 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
|
||||
return;
|
||||
}
|
||||
if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
|
||||
if (ejbAnnotationType != null && bridgedMethod.isAnnotationPresent(ejbAnnotationType)) {
|
||||
if (ejbAnnotationType != null && bridgedMethod.isAnnotationPresent(ejbAnnotationType)) {
|
||||
if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
|
||||
if (Modifier.isStatic(method.getModifiers())) {
|
||||
throw new IllegalStateException("@EJB annotation is not supported on static methods");
|
||||
}
|
||||
@@ -398,7 +398,9 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
|
||||
currElements.add(new EjbRefElement(method, bridgedMethod, pd));
|
||||
}
|
||||
else if (jakartaResourceType != null && bridgedMethod.isAnnotationPresent(jakartaResourceType)) {
|
||||
}
|
||||
else if (jakartaResourceType != null && bridgedMethod.isAnnotationPresent(jakartaResourceType)) {
|
||||
if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
|
||||
if (Modifier.isStatic(method.getModifiers())) {
|
||||
throw new IllegalStateException("@Resource annotation is not supported on static methods");
|
||||
}
|
||||
@@ -411,7 +413,9 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
currElements.add(new ResourceElement(method, bridgedMethod, pd));
|
||||
}
|
||||
}
|
||||
else if (javaxResourceType != null && bridgedMethod.isAnnotationPresent(javaxResourceType)) {
|
||||
}
|
||||
else if (javaxResourceType != null && bridgedMethod.isAnnotationPresent(javaxResourceType)) {
|
||||
if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
|
||||
if (Modifier.isStatic(method.getModifiers())) {
|
||||
throw new IllegalStateException("@Resource annotation is not supported on static methods");
|
||||
}
|
||||
|
||||
+1
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -44,7 +44,6 @@ import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
* public MyService myService() {
|
||||
* return new MyService();
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <p>If the configuration class above is processed, {@code MyHints} will be
|
||||
|
||||
+2
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -203,7 +203,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
/** Flag that indicates whether this context has been closed already. */
|
||||
private final AtomicBoolean closed = new AtomicBoolean();
|
||||
|
||||
/** Synchronization monitor for the "refresh" and "destroy". */
|
||||
/** Synchronization monitor for "refresh" and "close". */
|
||||
private final Object startupShutdownMonitor = new Object();
|
||||
|
||||
/** Reference to the JVM shutdown hook, if registered. */
|
||||
@@ -595,7 +595,6 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
|
||||
// Invoke factory processors registered as beans in the context.
|
||||
invokeBeanFactoryPostProcessors(beanFactory);
|
||||
|
||||
// Register bean processors that intercept bean creation.
|
||||
registerBeanPostProcessors(beanFactory);
|
||||
beanPostProcess.end();
|
||||
|
||||
+9
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,12 +18,13 @@ package org.springframework.ejb.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.jndi.JndiObjectFactoryBean;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.beans.factory.xml.BeanDefinitionParser}
|
||||
* implementation for parsing '{@code local-slsb}' tags and
|
||||
* creating plain {@link JndiObjectFactoryBean} definitions.
|
||||
* creating plain {@link JndiObjectFactoryBean} definitions on 6.0.
|
||||
*
|
||||
* @author Rob Harrop
|
||||
* @author Juergen Hoeller
|
||||
@@ -36,4 +37,10 @@ class LocalStatelessSessionBeanDefinitionParser extends AbstractJndiLocatingBean
|
||||
return JndiObjectFactoryBean.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isEligibleAttribute(String attributeName) {
|
||||
return (super.isEligibleAttribute(attributeName) &&
|
||||
BeanUtils.getPropertyDescriptor(JndiObjectFactoryBean.class, extractPropertyName(attributeName)) != null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+9
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,12 +18,13 @@ package org.springframework.ejb.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.jndi.JndiObjectFactoryBean;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.beans.factory.xml.BeanDefinitionParser}
|
||||
* implementation for parsing '{@code remote-slsb}' tags and
|
||||
* creating plain {@link JndiObjectFactoryBean} definitions.
|
||||
* creating plain {@link JndiObjectFactoryBean} definitions as of 6.0.
|
||||
*
|
||||
* @author Rob Harrop
|
||||
* @author Juergen Hoeller
|
||||
@@ -36,4 +37,10 @@ class RemoteStatelessSessionBeanDefinitionParser extends AbstractJndiLocatingBea
|
||||
return JndiObjectFactoryBean.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isEligibleAttribute(String attributeName) {
|
||||
return (super.isEligibleAttribute(attributeName) &&
|
||||
BeanUtils.getPropertyDescriptor(JndiObjectFactoryBean.class, extractPropertyName(attributeName)) != null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+18
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -50,6 +50,9 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
public class MetadataNamingStrategy implements ObjectNamingStrategy, InitializingBean {
|
||||
|
||||
private static final char[] QUOTABLE_CHARS = new char[] {',', '=', ':', '"'};
|
||||
|
||||
|
||||
/**
|
||||
* The {@code JmxAttributeSource} implementation to use for reading metadata.
|
||||
*/
|
||||
@@ -132,10 +135,23 @@ public class MetadataNamingStrategy implements ObjectNamingStrategy, Initializin
|
||||
}
|
||||
Hashtable<String, String> properties = new Hashtable<>();
|
||||
properties.put("type", ClassUtils.getShortName(managedClass));
|
||||
properties.put("name", beanKey);
|
||||
properties.put("name", quoteIfNecessary(beanKey));
|
||||
return ObjectNameManager.getInstance(domain, properties);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String quoteIfNecessary(String value) {
|
||||
return shouldQuote(value) ? ObjectName.quote(value) : value;
|
||||
}
|
||||
|
||||
private static boolean shouldQuote(String value) {
|
||||
for (char quotableChar : QUOTABLE_CHARS) {
|
||||
if (value.indexOf(quotableChar) != -1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ public interface TaskScheduler {
|
||||
* wrapping a cron expression
|
||||
* @return a {@link ScheduledFuture} representing pending completion of the task,
|
||||
* or {@code null} if the given Trigger object never fires (i.e. returns
|
||||
* {@code null} from {@link Trigger#nextExecutionTime})
|
||||
* {@code null} from {@link Trigger#nextExecution})
|
||||
* @throws org.springframework.core.task.TaskRejectedException if the given task was not accepted
|
||||
* for internal reasons (e.g. a pool overload handling policy or a pool shutdown in progress)
|
||||
* @see org.springframework.scheduling.support.CronTrigger
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -102,9 +102,9 @@ public @interface Scheduled {
|
||||
|
||||
/**
|
||||
* A time zone for which the cron expression will be resolved. By default, this
|
||||
* attribute is the empty String (i.e. the server's local time zone will be used).
|
||||
* attribute is the empty String (i.e. the scheduler's time zone will be used).
|
||||
* @return a zone id accepted by {@link java.util.TimeZone#getTimeZone(String)},
|
||||
* or an empty String to indicate the server's default time zone
|
||||
* or an empty String to indicate the scheduler's default time zone
|
||||
* @since 4.0
|
||||
* @see org.springframework.scheduling.support.CronTrigger#CronTrigger(String, java.util.TimeZone)
|
||||
* @see java.util.TimeZone
|
||||
|
||||
+5
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,7 +26,6 @@ import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TimeZone;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -430,14 +429,14 @@ public class ScheduledAnnotationBeanPostProcessor
|
||||
Assert.isTrue(initialDelay.isNegative(), "'initialDelay' not supported for cron triggers");
|
||||
processedSchedule = true;
|
||||
if (!Scheduled.CRON_DISABLED.equals(cron)) {
|
||||
TimeZone timeZone;
|
||||
CronTrigger trigger;
|
||||
if (StringUtils.hasText(zone)) {
|
||||
timeZone = StringUtils.parseTimeZoneString(zone);
|
||||
trigger = new CronTrigger(cron, StringUtils.parseTimeZoneString(zone));
|
||||
}
|
||||
else {
|
||||
timeZone = TimeZone.getDefault();
|
||||
trigger = new CronTrigger(cron);
|
||||
}
|
||||
tasks.add(this.registrar.scheduleCronTask(new CronTask(runnable, new CronTrigger(cron, timeZone))));
|
||||
tasks.add(this.registrar.scheduleCronTask(new CronTask(runnable, trigger)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -168,7 +168,7 @@ public final class CronExpression {
|
||||
* the cron format
|
||||
*/
|
||||
public static CronExpression parse(String expression) {
|
||||
Assert.hasLength(expression, "Expression string must not be empty");
|
||||
Assert.hasLength(expression, "Expression must not be empty");
|
||||
|
||||
expression = resolveMacros(expression);
|
||||
|
||||
|
||||
+17
-14
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -39,6 +39,7 @@ public class CronTrigger implements Trigger {
|
||||
|
||||
private final CronExpression expression;
|
||||
|
||||
@Nullable
|
||||
private final ZoneId zoneId;
|
||||
|
||||
|
||||
@@ -48,7 +49,8 @@ public class CronTrigger implements Trigger {
|
||||
* expression conventions
|
||||
*/
|
||||
public CronTrigger(String expression) {
|
||||
this(expression, ZoneId.systemDefault());
|
||||
this.expression = CronExpression.parse(expression);
|
||||
this.zoneId = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,7 +60,9 @@ public class CronTrigger implements Trigger {
|
||||
* @param timeZone a time zone in which the trigger times will be generated
|
||||
*/
|
||||
public CronTrigger(String expression, TimeZone timeZone) {
|
||||
this(expression, timeZone.toZoneId());
|
||||
this.expression = CronExpression.parse(expression);
|
||||
Assert.notNull(timeZone, "TimeZone must not be null");
|
||||
this.zoneId = timeZone.toZoneId();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,10 +74,8 @@ public class CronTrigger implements Trigger {
|
||||
* @see CronExpression#parse(String)
|
||||
*/
|
||||
public CronTrigger(String expression, ZoneId zoneId) {
|
||||
Assert.hasLength(expression, "Expression must not be empty");
|
||||
Assert.notNull(zoneId, "ZoneId must not be null");
|
||||
|
||||
this.expression = CronExpression.parse(expression);
|
||||
Assert.notNull(zoneId, "ZoneId must not be null");
|
||||
this.zoneId = zoneId;
|
||||
}
|
||||
|
||||
@@ -94,22 +96,23 @@ public class CronTrigger implements Trigger {
|
||||
*/
|
||||
@Override
|
||||
public Instant nextExecution(TriggerContext triggerContext) {
|
||||
Instant instant = triggerContext.lastCompletion();
|
||||
if (instant != null) {
|
||||
Instant timestamp = triggerContext.lastCompletion();
|
||||
if (timestamp != null) {
|
||||
Instant scheduled = triggerContext.lastScheduledExecution();
|
||||
if (scheduled != null && instant.isBefore(scheduled)) {
|
||||
if (scheduled != null && timestamp.isBefore(scheduled)) {
|
||||
// Previous task apparently executed too early...
|
||||
// Let's simply use the last calculated execution time then,
|
||||
// in order to prevent accidental re-fires in the same second.
|
||||
instant = scheduled;
|
||||
timestamp = scheduled;
|
||||
}
|
||||
}
|
||||
else {
|
||||
instant = triggerContext.getClock().instant();
|
||||
timestamp = triggerContext.getClock().instant();
|
||||
}
|
||||
ZonedDateTime dateTime = ZonedDateTime.ofInstant(instant, this.zoneId);
|
||||
ZonedDateTime next = this.expression.next(dateTime);
|
||||
return (next != null ? next.toInstant() : null);
|
||||
ZoneId zone = (this.zoneId != null ? this.zoneId : triggerContext.getClock().getZone());
|
||||
ZonedDateTime zonedTimestamp = ZonedDateTime.ofInstant(timestamp, zone);
|
||||
ZonedDateTime nextTimestamp = this.expression.next(zonedTimestamp);
|
||||
return (nextTimestamp != null ? nextTimestamp.toInstant() : null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="local-slsb" type="jndiLocatingType">
|
||||
<xsd:element name="local-slsb" type="ejbType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:org.springframework.jndi.JndiObjectFactoryBean"><![CDATA[
|
||||
Exposes a reference to a local EJB Stateless SessionBean.
|
||||
@@ -103,15 +103,56 @@
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="remote-slsb" type="jndiLocatingType">
|
||||
<xsd:element name="remote-slsb">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:org.springframework.jndi.JndiObjectFactoryBean"><![CDATA[
|
||||
Exposes a reference to a remote EJB Stateless SessionBean.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="ejbType">
|
||||
<xsd:attribute name="home-interface" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:java.lang.Class"><![CDATA[
|
||||
The home interface that will be narrowed to before performing the
|
||||
parameterless SLSB create() call that returns the actual SLSB proxy.
|
||||
NOTE: Effectively ignored as of 6.0 in favor of plain JNDI lookups.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="refresh-home-on-connect-failure" type="xsd:boolean" default="false">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Controls whether to refresh the EJB home on connect failure.
|
||||
NOTE: Effectively ignored as of 6.0 in favor of plain JNDI lookups.
|
||||
|
||||
Can be turned on to allow for hot restart of the EJB server.
|
||||
If a cached EJB home throws an RMI exception that indicates a
|
||||
remote connect failure, a fresh home will be fetched and the
|
||||
invocation will be retried.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="cache-session-bean" type="xsd:boolean" default="false">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Controls whether to cache the actual session bean object.
|
||||
NOTE: Effectively ignored as of 6.0 in favor of plain JNDI lookups.
|
||||
|
||||
Off by default for standard EJB compliance. Turn this flag
|
||||
on to optimize session bean access for servers that are
|
||||
known to allow for caching the actual session bean object.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:complexType name="jndiLocatingType">
|
||||
<!-- base types -->
|
||||
<xsd:complexType name="jndiLocatingType" abstract="true">
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="beans:identifiedType">
|
||||
<xsd:sequence>
|
||||
@@ -183,6 +224,40 @@
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="ejbType">
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="jndiLocatingType">
|
||||
<xsd:attribute name="lookup-home-on-startup" type="xsd:boolean" default="true">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Controls whether the lookup of the EJB home object is performed
|
||||
immediately on startup (if true, the default), or on first access
|
||||
(if false).
|
||||
NOTE: Effectively ignored as of 6.0 in favor of plain JNDI lookups.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="cache-home" type="xsd:boolean" default="true">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Controls whether the EJB home object is cached once it has been located.
|
||||
On by default; turn this flag off to always reobtain fresh home objects.
|
||||
NOTE: Effectively ignored as of 6.0 in favor of plain JNDI lookups.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="business-interface" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:java.lang.Class"><![CDATA[
|
||||
The business interface of the EJB being proxied.
|
||||
NOTE: Effectively ignored as of 6.0 in favor of plain JNDI lookups.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:simpleType name="environmentRefType">
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
|
||||
+11
-29
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -220,16 +220,16 @@ class AspectJAutoProxyCreatorTests {
|
||||
try (ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(configClass)) {
|
||||
testBean1 = context.getBean(TestBean.class);
|
||||
assertThat(AopUtils.isCglibProxy(testBean1)).as("CGLIB proxy").isTrue();
|
||||
assertThat(testBean1.getClass().getInterfaces())
|
||||
.containsExactlyInAnyOrder(Factory.class, SpringProxy.class, Advised.class);
|
||||
assertThat(testBean1.getClass().getInterfaces()).containsExactlyInAnyOrder(
|
||||
Factory.class, SpringProxy.class, Advised.class);
|
||||
}
|
||||
|
||||
// Round #2
|
||||
try (ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(configClass)) {
|
||||
testBean2 = context.getBean(TestBean.class);
|
||||
assertThat(AopUtils.isCglibProxy(testBean2)).as("CGLIB proxy").isTrue();
|
||||
assertThat(testBean2.getClass().getInterfaces())
|
||||
.containsExactlyInAnyOrder(Factory.class, SpringProxy.class, Advised.class);
|
||||
assertThat(testBean2.getClass().getInterfaces()).containsExactlyInAnyOrder(
|
||||
Factory.class, SpringProxy.class, Advised.class);
|
||||
}
|
||||
|
||||
assertThat(testBean1.getClass()).isSameAs(testBean2.getClass());
|
||||
@@ -344,8 +344,8 @@ class AspectJAutoProxyCreatorTests {
|
||||
Supplier<String> supplier = context.getBean(Supplier.class);
|
||||
assertThat(AopUtils.isAopProxy(supplier)).as("AOP proxy").isTrue();
|
||||
assertThat(AopUtils.isJdkDynamicProxy(supplier)).as("JDK Dynamic proxy").isTrue();
|
||||
assertThat(supplier.getClass().getInterfaces())
|
||||
.containsExactlyInAnyOrder(Supplier.class, SpringProxy.class, Advised.class, DecoratingProxy.class);
|
||||
assertThat(supplier.getClass().getInterfaces()).containsExactlyInAnyOrder(
|
||||
Supplier.class, SpringProxy.class, Advised.class, DecoratingProxy.class);
|
||||
assertThat(supplier.get()).isEqualTo("advised: lambda");
|
||||
}
|
||||
}
|
||||
@@ -357,26 +357,14 @@ class AspectJAutoProxyCreatorTests {
|
||||
MessageGenerator messageGenerator = context.getBean(MessageGenerator.class);
|
||||
assertThat(AopUtils.isAopProxy(messageGenerator)).as("AOP proxy").isTrue();
|
||||
assertThat(AopUtils.isJdkDynamicProxy(messageGenerator)).as("JDK Dynamic proxy").isTrue();
|
||||
assertThat(messageGenerator.getClass().getInterfaces())
|
||||
.containsExactlyInAnyOrder(MessageGenerator.class, Mixin.class, SpringProxy.class, Advised.class, DecoratingProxy.class);
|
||||
assertThat(messageGenerator.getClass().getInterfaces()).containsExactlyInAnyOrder(
|
||||
MessageGenerator.class, Mixin.class, SpringProxy.class, Advised.class, DecoratingProxy.class);
|
||||
assertThat(messageGenerator.generateMessage()).isEqualTo("mixin: lambda");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link ClassPathXmlApplicationContext} for the file ending in <var>fileSuffix</var>.
|
||||
*/
|
||||
private ClassPathXmlApplicationContext newContext(String fileSuffix) {
|
||||
return new ClassPathXmlApplicationContext(qName(fileSuffix), getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the relatively qualified name for <var>fileSuffix</var>.
|
||||
* e.g. for a fileSuffix='foo.xml', this method will return
|
||||
* 'AspectJAutoProxyCreatorTests-foo.xml'
|
||||
*/
|
||||
private String qName(String fileSuffix) {
|
||||
return String.format("%s-%s", getClass().getSimpleName(), fileSuffix);
|
||||
return new ClassPathXmlApplicationContext(getClass().getSimpleName() + "-" + fileSuffix, getClass());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -416,7 +404,6 @@ class DummyAspectWithParameter {
|
||||
public Object test(ProceedingJoinPoint pjp, int age) throws Throwable {
|
||||
return pjp.proceed();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class DummyFactoryBean implements FactoryBean<Object> {
|
||||
@@ -435,7 +422,6 @@ class DummyFactoryBean implements FactoryBean<Object> {
|
||||
public boolean isSingleton() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Aspect
|
||||
@@ -591,7 +577,6 @@ class UnreliableBean {
|
||||
}
|
||||
return this.calls;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@@ -607,7 +592,6 @@ class TestBeanAdvisor extends StaticMethodMatcherPointcutAdvisor {
|
||||
public boolean matches(Method method, @Nullable Class<?> targetClass) {
|
||||
return ITestBean.class.isAssignableFrom(targetClass);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
abstract class AbstractProxyTargetClassConfig {
|
||||
@@ -661,6 +645,7 @@ class PerTargetProxyTargetClassTrueConfig {
|
||||
|
||||
@FunctionalInterface
|
||||
interface MessageGenerator {
|
||||
|
||||
String generateMessage();
|
||||
}
|
||||
|
||||
@@ -678,7 +663,6 @@ class MixinIntroductionInterceptor implements IntroductionInterceptor {
|
||||
public boolean implementsInterface(Class<?> intf) {
|
||||
return Mixin.class.isAssignableFrom(intf);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@@ -708,7 +692,6 @@ class MixinAdvisor extends AbstractPointcutAdvisor implements IntroductionAdviso
|
||||
public void validateInterfaces() {
|
||||
/* no-op */
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
abstract class AbstractMixinConfig {
|
||||
@@ -722,7 +705,6 @@ abstract class AbstractMixinConfig {
|
||||
MixinAdvisor mixinAdvisor() {
|
||||
return new MixinAdvisor();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
|
||||
+13
-14
@@ -1086,8 +1086,8 @@ public abstract class AbstractAopProxyTests {
|
||||
// NameReverter saved it back
|
||||
assertThat(it.getName()).isEqualTo(name1);
|
||||
assertThat(saver.names).hasSize(2);
|
||||
assertThat(saver.names.get(0)).isEqualTo(name2);
|
||||
assertThat(saver.names.get(1)).isEqualTo(name1);
|
||||
assertThat(saver.names).element(0).isEqualTo(name2);
|
||||
assertThat(saver.names).element(1).isEqualTo(name1);
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@@ -1178,7 +1178,7 @@ public abstract class AbstractAopProxyTests {
|
||||
assertThat(i2).isEqualTo(i1);
|
||||
assertThat(proxyB).isEqualTo(proxyA);
|
||||
assertThat(proxyB.hashCode()).isEqualTo(proxyA.hashCode());
|
||||
assertThat(proxyA.equals(a)).isFalse();
|
||||
assertThat(proxyA).isNotEqualTo(a);
|
||||
|
||||
// Equality checks were handled by the proxy
|
||||
assertThat(i1.getCount()).isEqualTo(0);
|
||||
@@ -1187,7 +1187,7 @@ public abstract class AbstractAopProxyTests {
|
||||
// and won't think it's equal to B's NopInterceptor
|
||||
proxyA.absquatulate();
|
||||
assertThat(i1.getCount()).isEqualTo(1);
|
||||
assertThat(proxyA.equals(proxyB)).isFalse();
|
||||
assertThat(proxyA).isNotEqualTo(proxyB);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1874,6 +1874,14 @@ public abstract class AbstractAopProxyTests {
|
||||
return target.getClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.springframework.aop.TargetSource#isStatic()
|
||||
*/
|
||||
@Override
|
||||
public boolean isStatic() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.springframework.aop.TargetSource#getTarget()
|
||||
*/
|
||||
@@ -1903,19 +1911,10 @@ public abstract class AbstractAopProxyTests {
|
||||
throw new RuntimeException("Expectation failed: " + gets + " gets and " + releases + " releases");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.springframework.aop.TargetSource#isStatic()
|
||||
*/
|
||||
@Override
|
||||
public boolean isStatic() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
static abstract class ExposedInvocationTestBean extends TestBean {
|
||||
abstract static class ExposedInvocationTestBean extends TestBean {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
|
||||
+33
@@ -22,8 +22,13 @@ import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.StringWriter;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -56,6 +61,8 @@ import org.springframework.beans.testfixture.beans.ITestBean;
|
||||
import org.springframework.beans.testfixture.beans.IndexedTestBean;
|
||||
import org.springframework.beans.testfixture.beans.TestBean;
|
||||
import org.springframework.beans.testfixture.beans.factory.DummyFactory;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.UrlResource;
|
||||
@@ -1336,6 +1343,15 @@ class XmlBeanFactoryTests {
|
||||
assertThat(dos.lastArg).isEqualTo(s2);
|
||||
}
|
||||
|
||||
@Test // gh-31826
|
||||
void replaceNonOverloadedInterfaceMethodWithoutSpecifyingExplicitArgTypes() {
|
||||
try (ConfigurableApplicationContext context =
|
||||
new ClassPathXmlApplicationContext(DELEGATION_OVERRIDES_CONTEXT.getPath())) {
|
||||
EchoService echoService = context.getBean(EchoService.class);
|
||||
assertThat(echoService.echo("foo", "bar")).containsExactly("bar", "foo");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void lookupOverrideOneMethodWithConstructorInjection() {
|
||||
DefaultListableBeanFactory xbf = new DefaultListableBeanFactory();
|
||||
@@ -1891,3 +1907,20 @@ class XmlBeanFactoryTests {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface EchoService {
|
||||
|
||||
String[] echo(Object... objects);
|
||||
}
|
||||
|
||||
class ReverseArrayMethodReplacer implements MethodReplacer {
|
||||
|
||||
@Override
|
||||
public Object reimplement(Object obj, Method method, Object[] args) {
|
||||
List<String> list = Arrays.stream((Object[]) args[0])
|
||||
.map(Object::toString)
|
||||
.collect(Collectors.toCollection(ArrayList::new));
|
||||
Collections.reverse(list);
|
||||
return list.toArray(String[]::new);
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -54,7 +54,7 @@ class EnableCachingIntegrationTests {
|
||||
|
||||
|
||||
@AfterEach
|
||||
public void closeContext() {
|
||||
void closeContext() {
|
||||
if (this.context != null) {
|
||||
this.context.close();
|
||||
}
|
||||
|
||||
Vendored
+16
-13
@@ -60,6 +60,7 @@ class CacheErrorHandlerTests {
|
||||
|
||||
private SimpleService simpleService;
|
||||
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
this.context = new AnnotationConfigApplicationContext(Config.class);
|
||||
@@ -69,11 +70,13 @@ class CacheErrorHandlerTests {
|
||||
this.simpleService = context.getBean(SimpleService.class);
|
||||
}
|
||||
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
void closeContext() {
|
||||
this.context.close();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void getFail() {
|
||||
UnsupportedOperationException exception = new UnsupportedOperationException("Test exception on get");
|
||||
@@ -107,9 +110,9 @@ class CacheErrorHandlerTests {
|
||||
|
||||
this.cacheInterceptor.setErrorHandler(new SimpleCacheErrorHandler());
|
||||
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() ->
|
||||
this.simpleService.get(0L))
|
||||
.withMessage("Test exception on get");
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class)
|
||||
.isThrownBy(() -> this.simpleService.get(0L))
|
||||
.withMessage("Test exception on get");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -128,9 +131,9 @@ class CacheErrorHandlerTests {
|
||||
|
||||
this.cacheInterceptor.setErrorHandler(new SimpleCacheErrorHandler());
|
||||
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() ->
|
||||
this.simpleService.put(0L))
|
||||
.withMessage("Test exception on put");
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class)
|
||||
.isThrownBy(() -> this.simpleService.put(0L))
|
||||
.withMessage("Test exception on put");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -149,9 +152,9 @@ class CacheErrorHandlerTests {
|
||||
|
||||
this.cacheInterceptor.setErrorHandler(new SimpleCacheErrorHandler());
|
||||
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() ->
|
||||
this.simpleService.evict(0L))
|
||||
.withMessage("Test exception on evict");
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class)
|
||||
.isThrownBy(() -> this.simpleService.evict(0L))
|
||||
.withMessage("Test exception on evict");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -170,9 +173,9 @@ class CacheErrorHandlerTests {
|
||||
|
||||
this.cacheInterceptor.setErrorHandler(new SimpleCacheErrorHandler());
|
||||
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() ->
|
||||
this.simpleService.clear())
|
||||
.withMessage("Test exception on clear");
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class)
|
||||
.isThrownBy(() -> this.simpleService.clear())
|
||||
.withMessage("Test exception on clear");
|
||||
}
|
||||
|
||||
|
||||
|
||||
Vendored
+5
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -51,6 +51,7 @@ public class CachePutEvaluationTests {
|
||||
|
||||
private SimpleService service;
|
||||
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
this.context = new AnnotationConfigApplicationContext(Config.class);
|
||||
@@ -59,12 +60,11 @@ public class CachePutEvaluationTests {
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void close() {
|
||||
if (this.context != null) {
|
||||
this.context.close();
|
||||
}
|
||||
public void closeContext() {
|
||||
this.context.close();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void mutualGetPutExclusion() {
|
||||
String key = "1";
|
||||
|
||||
+9
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -72,7 +72,7 @@ class CacheResolverCustomizationTests {
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
void closeContext() {
|
||||
this.context.close();
|
||||
}
|
||||
|
||||
@@ -142,16 +142,17 @@ class CacheResolverCustomizationTests {
|
||||
@Test
|
||||
void noCacheResolved() {
|
||||
Method method = ReflectionUtils.findMethod(SimpleService.class, "noCacheResolved", Object.class);
|
||||
assertThatIllegalStateException().isThrownBy(() ->
|
||||
this.simpleService.noCacheResolved(new Object()))
|
||||
.withMessageContaining(method.toString());
|
||||
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> this.simpleService.noCacheResolved(new Object()))
|
||||
.withMessageContaining(method.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void unknownCacheResolver() {
|
||||
assertThatExceptionOfType(NoSuchBeanDefinitionException.class).isThrownBy(() ->
|
||||
this.simpleService.unknownCacheResolver(new Object()))
|
||||
.satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("unknownCacheResolver"));
|
||||
assertThatExceptionOfType(NoSuchBeanDefinitionException.class)
|
||||
.isThrownBy(() -> this.simpleService.unknownCacheResolver(new Object()))
|
||||
.satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("unknownCacheResolver"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
+6
-6
@@ -130,8 +130,8 @@ class PropertySourceAnnotationTests {
|
||||
@Test
|
||||
void withUnresolvablePlaceholder() {
|
||||
assertThatExceptionOfType(BeanDefinitionStoreException.class)
|
||||
.isThrownBy(() -> new AnnotationConfigApplicationContext(ConfigWithUnresolvablePlaceholder.class))
|
||||
.withCauseInstanceOf(IllegalArgumentException.class);
|
||||
.isThrownBy(() -> new AnnotationConfigApplicationContext(ConfigWithUnresolvablePlaceholder.class))
|
||||
.withCauseInstanceOf(IllegalArgumentException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -162,8 +162,8 @@ class PropertySourceAnnotationTests {
|
||||
@Test
|
||||
void withEmptyResourceLocations() {
|
||||
assertThatExceptionOfType(BeanDefinitionStoreException.class)
|
||||
.isThrownBy(() -> new AnnotationConfigApplicationContext(ConfigWithEmptyResourceLocations.class))
|
||||
.withCauseInstanceOf(IllegalArgumentException.class);
|
||||
.isThrownBy(() -> new AnnotationConfigApplicationContext(ConfigWithEmptyResourceLocations.class))
|
||||
.withCauseInstanceOf(IllegalArgumentException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -245,8 +245,8 @@ class PropertySourceAnnotationTests {
|
||||
@Test
|
||||
void withMissingPropertySource() {
|
||||
assertThatExceptionOfType(BeanDefinitionStoreException.class)
|
||||
.isThrownBy(() -> new AnnotationConfigApplicationContext(ConfigWithMissingPropertySource.class))
|
||||
.withCauseInstanceOf(FileNotFoundException.class);
|
||||
.isThrownBy(() -> new AnnotationConfigApplicationContext(ConfigWithMissingPropertySource.class))
|
||||
.withCauseInstanceOf(FileNotFoundException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+11
-11
@@ -126,16 +126,16 @@ class GenericApplicationContextTests {
|
||||
|
||||
assertThat(context.getBean(String.class)).isSameAs(context.getBean("testBean"));
|
||||
assertThat(context.getAutowireCapableBeanFactory().getBean(String.class))
|
||||
.isSameAs(context.getAutowireCapableBeanFactory().getBean("testBean"));
|
||||
.isSameAs(context.getAutowireCapableBeanFactory().getBean("testBean"));
|
||||
|
||||
context.close();
|
||||
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> context.getBean(String.class));
|
||||
.isThrownBy(() -> context.getBean(String.class));
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> context.getAutowireCapableBeanFactory().getBean(String.class));
|
||||
.isThrownBy(() -> context.getAutowireCapableBeanFactory().getBean(String.class));
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> context.getAutowireCapableBeanFactory().getBean("testBean"));
|
||||
.isThrownBy(() -> context.getAutowireCapableBeanFactory().getBean("testBean"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -236,9 +236,9 @@ class GenericApplicationContextTests {
|
||||
assertThat(context.getBeanNamesForType(BeanB.class)).containsExactly("b");
|
||||
assertThat(context.getBeanNamesForType(BeanC.class)).containsExactly("c");
|
||||
assertThat(context.getBeansOfType(BeanA.class)).isEmpty();
|
||||
assertThat(context.getBeansOfType(BeanB.class).values().iterator().next())
|
||||
assertThat(context.getBeansOfType(BeanB.class).values()).element(0)
|
||||
.isSameAs(context.getBean(BeanB.class));
|
||||
assertThat(context.getBeansOfType(BeanC.class).values().iterator().next())
|
||||
assertThat(context.getBeansOfType(BeanC.class).values()).element(0)
|
||||
.isSameAs(context.getBean(BeanC.class));
|
||||
}
|
||||
|
||||
@@ -281,8 +281,8 @@ class GenericApplicationContextTests {
|
||||
// java.nio.file.InvalidPathException: Illegal char <:> at index 4: ping:foo
|
||||
if (resourceLoader instanceof FileSystemResourceLoader && OS.WINDOWS.isCurrentOs()) {
|
||||
assertThatExceptionOfType(InvalidPathException.class)
|
||||
.isThrownBy(() -> context.getResource(pingLocation))
|
||||
.withMessageContaining(pingLocation);
|
||||
.isThrownBy(() -> context.getResource(pingLocation))
|
||||
.withMessageContaining(pingLocation);
|
||||
}
|
||||
else {
|
||||
resource = context.getResource(pingLocation);
|
||||
@@ -297,8 +297,8 @@ class GenericApplicationContextTests {
|
||||
assertThat(resource).isInstanceOf(FileUrlResource.class);
|
||||
resource = context.getResource(pingLocation);
|
||||
assertThat(resource).asInstanceOf(type(ByteArrayResource.class))
|
||||
.extracting(bar -> new String(bar.getByteArray(), UTF_8))
|
||||
.isEqualTo("pong:foo");
|
||||
.extracting(bar -> new String(bar.getByteArray(), UTF_8))
|
||||
.isEqualTo("pong:foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -536,7 +536,7 @@ class GenericApplicationContextTests {
|
||||
}
|
||||
}
|
||||
|
||||
static class BeanB implements ApplicationContextAware {
|
||||
static class BeanB implements ApplicationContextAware {
|
||||
|
||||
ApplicationContext applicationContext;
|
||||
|
||||
|
||||
+35
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,9 +16,12 @@
|
||||
|
||||
package org.springframework.ejb.config;
|
||||
|
||||
import javax.naming.NoInitialContextException;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
@@ -29,6 +32,7 @@ import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.jndi.JndiObjectFactoryBean;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* @author Rob Harrop
|
||||
@@ -93,6 +97,10 @@ public class JeeNamespaceHandlerTests {
|
||||
BeanDefinition beanDefinition = this.beanFactory.getMergedBeanDefinition("simpleLocalEjb");
|
||||
assertThat(beanDefinition.getBeanClassName()).isEqualTo(JndiObjectFactoryBean.class.getName());
|
||||
assertPropertyValue(beanDefinition, "jndiName", "ejb/MyLocalBean");
|
||||
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(() -> this.beanFactory.getBean("simpleLocalEjb"))
|
||||
.withCauseInstanceOf(NoInitialContextException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -100,6 +108,32 @@ public class JeeNamespaceHandlerTests {
|
||||
BeanDefinition beanDefinition = this.beanFactory.getMergedBeanDefinition("simpleRemoteEjb");
|
||||
assertThat(beanDefinition.getBeanClassName()).isEqualTo(JndiObjectFactoryBean.class.getName());
|
||||
assertPropertyValue(beanDefinition, "jndiName", "ejb/MyRemoteBean");
|
||||
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(() -> this.beanFactory.getBean("simpleRemoteEjb"))
|
||||
.withCauseInstanceOf(NoInitialContextException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testComplexLocalSlsb() {
|
||||
BeanDefinition beanDefinition = this.beanFactory.getMergedBeanDefinition("complexLocalEjb");
|
||||
assertThat(beanDefinition.getBeanClassName()).isEqualTo(JndiObjectFactoryBean.class.getName());
|
||||
assertPropertyValue(beanDefinition, "jndiName", "ejb/MyLocalBean");
|
||||
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(() -> this.beanFactory.getBean("complexLocalEjb"))
|
||||
.withCauseInstanceOf(NoInitialContextException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testComplexRemoteSlsb() {
|
||||
BeanDefinition beanDefinition = this.beanFactory.getMergedBeanDefinition("complexRemoteEjb");
|
||||
assertThat(beanDefinition.getBeanClassName()).isEqualTo(JndiObjectFactoryBean.class.getName());
|
||||
assertPropertyValue(beanDefinition, "jndiName", "ejb/MyRemoteBean");
|
||||
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(() -> this.beanFactory.getBean("complexRemoteEjb"))
|
||||
.withCauseInstanceOf(NoInitialContextException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.jmx.export.naming;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.management.MalformedObjectNameException;
|
||||
import javax.management.ObjectName;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.entry;
|
||||
|
||||
|
||||
/**
|
||||
* Tests for {@link MetadataNamingStrategy}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class MetadataNamingStrategyTests {
|
||||
|
||||
private static final TestBean TEST_BEAN = new TestBean();
|
||||
|
||||
private final MetadataNamingStrategy strategy;
|
||||
|
||||
MetadataNamingStrategyTests() {
|
||||
this.strategy = new MetadataNamingStrategy();
|
||||
this.strategy.setDefaultDomain("com.example");
|
||||
this.strategy.setAttributeSource(new AnnotationJmxAttributeSource());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getObjectNameWhenBeanNameIsSimple() throws MalformedObjectNameException {
|
||||
ObjectName name = this.strategy.getObjectName(TEST_BEAN, "myBean");
|
||||
assertThat(name.getDomain()).isEqualTo("com.example");
|
||||
assertThat(name).satisfies(hasDefaultProperties(TEST_BEAN, "myBean"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getObjectNameWhenBeanNameIsValidObjectName() throws MalformedObjectNameException {
|
||||
ObjectName name = this.strategy.getObjectName(TEST_BEAN, "com.another:name=myBean");
|
||||
assertThat(name.getDomain()).isEqualTo("com.another");
|
||||
assertThat(name.getKeyPropertyList()).containsOnly(entry("name", "myBean"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getObjectNameWhenBeanNamContainsComma() throws MalformedObjectNameException {
|
||||
ObjectName name = this.strategy.getObjectName(TEST_BEAN, "myBean,");
|
||||
assertThat(name).satisfies(hasDefaultProperties(TEST_BEAN, "\"myBean,\""));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getObjectNameWhenBeanNamContainsEquals() throws MalformedObjectNameException {
|
||||
ObjectName name = this.strategy.getObjectName(TEST_BEAN, "my=Bean");
|
||||
assertThat(name).satisfies(hasDefaultProperties(TEST_BEAN, "\"my=Bean\""));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getObjectNameWhenBeanNamContainsColon() throws MalformedObjectNameException {
|
||||
ObjectName name = this.strategy.getObjectName(TEST_BEAN, "my:Bean");
|
||||
assertThat(name).satisfies(hasDefaultProperties(TEST_BEAN, "\"my:Bean\""));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getObjectNameWhenBeanNamContainsQuote() throws MalformedObjectNameException {
|
||||
ObjectName name = this.strategy.getObjectName(TEST_BEAN, "\"myBean\"");
|
||||
assertThat(name).satisfies(hasDefaultProperties(TEST_BEAN, "\"\\\"myBean\\\"\""));
|
||||
}
|
||||
|
||||
private Consumer<ObjectName> hasDefaultProperties(Object instance, String expectedName) {
|
||||
return objectName -> assertThat(objectName.getKeyPropertyList()).containsOnly(
|
||||
entry("type", ClassUtils.getShortName(instance.getClass())),
|
||||
entry("name", expectedName));
|
||||
}
|
||||
|
||||
static class TestBean {}
|
||||
|
||||
}
|
||||
+3
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -848,6 +848,7 @@ class CronTriggerTests {
|
||||
assertThat(nextExecutionTime).isEqualTo(this.calendar.getTime());
|
||||
}
|
||||
|
||||
|
||||
private static void roundup(Calendar calendar) {
|
||||
calendar.add(Calendar.SECOND, 1);
|
||||
calendar.set(Calendar.MILLISECOND, 0);
|
||||
@@ -861,9 +862,7 @@ class CronTriggerTests {
|
||||
}
|
||||
|
||||
private static TriggerContext getTriggerContext(Date lastCompletionTime) {
|
||||
SimpleTriggerContext context = new SimpleTriggerContext();
|
||||
context.update(null, null, lastCompletionTime);
|
||||
return context;
|
||||
return new SimpleTriggerContext(null, null, lastCompletionTime);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.scheduling.annotation
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation
|
||||
import org.assertj.core.api.Assertions
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.mockito.BDDMockito.given
|
||||
import org.mockito.Mockito
|
||||
|
||||
/**
|
||||
* Kotlin tests for [AnnotationAsyncExecutionInterceptor].
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
class AnnotationAsyncExecutionInterceptorKotlinTests {
|
||||
|
||||
@Test
|
||||
fun nullableUnitReturnValue() {
|
||||
val interceptor = AnnotationAsyncExecutionInterceptor(null)
|
||||
|
||||
class C { @Async fun nullableUnit(): Unit? = null }
|
||||
val invocation = Mockito.mock<MethodInvocation>()
|
||||
given(invocation.method).willReturn(C::class.java.getDeclaredMethod("nullableUnit"))
|
||||
|
||||
Assertions.assertThat(interceptor.invoke(invocation)).isNull()
|
||||
}
|
||||
|
||||
}
|
||||
+12
-21
@@ -3,9 +3,6 @@
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
|
||||
|
||||
<!--
|
||||
Not yet in use: illustration of possible approach
|
||||
-->
|
||||
<bean id="overrideOneMethod" class="org.springframework.beans.factory.xml.OverrideOneMethod">
|
||||
|
||||
<lookup-method name="getPrototypeDependency" bean="jenny"/>
|
||||
@@ -27,39 +24,34 @@
|
||||
|
||||
<lookup-method name="protectedOverrideSingleton" bean="david"/>
|
||||
|
||||
<!--
|
||||
This method is not overloaded, so we don't need to specify any arg types
|
||||
-->
|
||||
<!-- This method is not overloaded, so we don't need to specify any arg types -->
|
||||
<replaced-method name="doSomething" replacer="doSomethingReplacer"/>
|
||||
|
||||
</bean>
|
||||
|
||||
<bean id="replaceVoidMethod" parent="someParent"
|
||||
class="org.springframework.beans.factory.xml.OverrideOneMethodSubclass">
|
||||
<bean id="replaceVoidMethod" parent="someParent" class="org.springframework.beans.factory.xml.OverrideOneMethodSubclass"/>
|
||||
|
||||
<bean id="replaceEchoMethod" class="org.springframework.beans.factory.xml.EchoService">
|
||||
<!-- This method is not overloaded, so we don't need to specify any arg types -->
|
||||
<replaced-method name="echo" replacer="reverseArrayReplacer" />
|
||||
</bean>
|
||||
|
||||
<bean id="reverseReplacer"
|
||||
class="org.springframework.beans.factory.xml.ReverseMethodReplacer"/>
|
||||
<bean id="reverseReplacer" class="org.springframework.beans.factory.xml.ReverseMethodReplacer"/>
|
||||
|
||||
<bean id="fixedReplacer"
|
||||
class="org.springframework.beans.factory.xml.FixedMethodReplacer"/>
|
||||
<bean id="reverseArrayReplacer" class="org.springframework.beans.factory.xml.ReverseArrayMethodReplacer"/>
|
||||
|
||||
<bean id="doSomethingReplacer"
|
||||
class="org.springframework.beans.factory.xml.XmlBeanFactoryTests$DoSomethingReplacer"/>
|
||||
<bean id="fixedReplacer" class="org.springframework.beans.factory.xml.FixedMethodReplacer"/>
|
||||
|
||||
<bean id="serializableReplacer"
|
||||
class="org.springframework.beans.factory.xml.SerializableMethodReplacerCandidate">
|
||||
<bean id="doSomethingReplacer" class="org.springframework.beans.factory.xml.XmlBeanFactoryTests$DoSomethingReplacer"/>
|
||||
|
||||
<bean id="serializableReplacer" class="org.springframework.beans.factory.xml.SerializableMethodReplacerCandidate">
|
||||
<!-- Arbitrary method replacer -->
|
||||
<replaced-method name="replaceMe" replacer="reverseReplacer">
|
||||
<arg-type>String</arg-type>
|
||||
</replaced-method>
|
||||
|
||||
</bean>
|
||||
|
||||
<bean id="jenny" class="org.springframework.beans.testfixture.beans.TestBean"
|
||||
scope="prototype">
|
||||
<bean id="jenny" class="org.springframework.beans.testfixture.beans.TestBean" scope="prototype">
|
||||
<property name="name"><value>Jenny</value></property>
|
||||
<property name="age"><value>30</value></property>
|
||||
<property name="spouse">
|
||||
@@ -68,8 +60,7 @@
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="david" class="org.springframework.beans.testfixture.beans.TestBean"
|
||||
scope="singleton">
|
||||
<bean id="david" class="org.springframework.beans.testfixture.beans.TestBean" scope="singleton">
|
||||
<description>
|
||||
Simple bean, without any collections.
|
||||
</description>
|
||||
|
||||
+32
-5
@@ -40,14 +40,41 @@
|
||||
</util:properties>
|
||||
|
||||
<!-- Local EJB Tests -->
|
||||
<jee:local-slsb id="simpleLocalEjb" jndi-name="ejb/MyLocalBean"/>
|
||||
<jee:local-slsb id="simpleLocalEjb" jndi-name="ejb/MyLocalBean"
|
||||
business-interface="org.springframework.beans.testfixture.beans.ITestBean"/>
|
||||
|
||||
<jee:local-slsb id="complexLocalEjb"
|
||||
jndi-name="ejb/MyLocalBean"
|
||||
business-interface="org.springframework.beans.testfixture.beans.ITestBean"
|
||||
cache-home="true"
|
||||
lookup-home-on-startup="true"
|
||||
resource-ref="true">
|
||||
<jee:environment>foo=bar</jee:environment>
|
||||
</jee:local-slsb>
|
||||
|
||||
<!-- Remote EJB Tests -->
|
||||
<jee:remote-slsb id="simpleRemoteEjb" jndi-name="ejb/MyRemoteBean"/>
|
||||
<jee:remote-slsb id="simpleRemoteEjb" jndi-name="ejb/MyRemoteBean"
|
||||
business-interface="org.springframework.beans.testfixture.beans.ITestBean"/>
|
||||
|
||||
<!-- Lazy beans Tests-->
|
||||
<jee:remote-slsb id="complexRemoteEjb"
|
||||
jndi-name="ejb/MyRemoteBean"
|
||||
business-interface="org.springframework.beans.testfixture.beans.ITestBean"
|
||||
cache-home="true"
|
||||
lookup-home-on-startup="true"
|
||||
resource-ref="true"
|
||||
home-interface="org.springframework.beans.testfixture.beans.ITestBean"
|
||||
refresh-home-on-connect-failure="true"
|
||||
cache-session-bean="true">
|
||||
<jee:environment>foo=bar</jee:environment>
|
||||
</jee:remote-slsb>
|
||||
|
||||
<!-- Lazy Lookup Tests-->
|
||||
<jee:jndi-lookup id="lazyDataSource" jndi-name="jdbc/MyDataSource" lazy-init="true"/>
|
||||
<jee:local-slsb id="lazyLocalBean" jndi-name="ejb/MyLocalBean" lazy-init="true"/>
|
||||
<jee:remote-slsb id="lazyRemoteBean" jndi-name="ejb/MyRemoteBean" lazy-init="true"/>
|
||||
|
||||
<jee:local-slsb id="lazyLocalBean" jndi-name="ejb/MyLocalBean"
|
||||
business-interface="org.springframework.beans.testfixture.beans.ITestBean" lazy-init="true"/>
|
||||
|
||||
<jee:remote-slsb id="lazyRemoteBean" jndi-name="ejb/MyRemoteBean"
|
||||
business-interface="org.springframework.beans.testfixture.beans.ITestBean" lazy-init="true"/>
|
||||
|
||||
</beans>
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.springframework.core.io.InputStreamSource;
|
||||
import org.springframework.javapoet.JavaFile;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.function.ThrowingConsumer;
|
||||
|
||||
/**
|
||||
@@ -43,6 +44,7 @@ public interface GeneratedFiles {
|
||||
* @param javaFile the java file to add
|
||||
*/
|
||||
default void addSourceFile(JavaFile javaFile) {
|
||||
validatePackage(javaFile.packageName, javaFile.typeSpec.name);
|
||||
String className = javaFile.packageName + "." + javaFile.typeSpec.name;
|
||||
addSourceFile(className, javaFile::writeTo);
|
||||
}
|
||||
@@ -161,11 +163,20 @@ public interface GeneratedFiles {
|
||||
|
||||
private static String getClassNamePath(String className) {
|
||||
Assert.hasLength(className, "'className' must not be empty");
|
||||
validatePackage(ClassUtils.getPackageName(className), className);
|
||||
Assert.isTrue(isJavaIdentifier(className),
|
||||
"'className' must be a valid identifier, got '" + className + "'");
|
||||
return ClassUtils.convertClassNameToResourcePath(className) + ".java";
|
||||
}
|
||||
|
||||
private static void validatePackage(String packageName, String className) {
|
||||
if (!StringUtils.hasLength(packageName)) {
|
||||
throw new IllegalArgumentException("Could not add '" + className + "', "
|
||||
+ "processing classes in the default package is not supported. "
|
||||
+ "Did you forget to add a package statement?");
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isJavaIdentifier(String className) {
|
||||
char[] chars = className.toCharArray();
|
||||
for (int i = 0; i < chars.length; i++) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -159,7 +159,7 @@ final class SerializableTypeWrapper {
|
||||
|
||||
/**
|
||||
* Return the source of the type, or {@code null} if not known.
|
||||
* <p>The default implementations returns {@code null}.
|
||||
* <p>The default implementation returns {@code null}.
|
||||
*/
|
||||
@Nullable
|
||||
default Object getSource() {
|
||||
@@ -214,7 +214,12 @@ final class SerializableTypeWrapper {
|
||||
return result;
|
||||
}
|
||||
|
||||
return ReflectionUtils.invokeMethod(method, this.provider.getType(), args);
|
||||
Type type = this.provider.getType();
|
||||
if (type instanceof TypeVariable<?> tv && method.getName().equals("getName")) {
|
||||
// Avoid reflection for common comparison of type variables
|
||||
return tv.getName();
|
||||
}
|
||||
return ReflectionUtils.invokeMethod(method, type, args);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+27
-12
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -757,7 +757,7 @@ public abstract class AnnotationUtils {
|
||||
* Google App Engine's late arrival of {@code TypeNotPresentExceptionProxy} for
|
||||
* {@code Class} values (instead of early {@code Class.getAnnotations() failure}).
|
||||
* <p>This method not failing indicates that {@link #getAnnotationAttributes(Annotation)}
|
||||
* won't failure either (when attempted later on).
|
||||
* won't fail either (when attempted later on).
|
||||
* @param annotation the annotation to validate
|
||||
* @throws IllegalStateException if a declared {@code Class} attribute could not be read
|
||||
* @since 4.3.15
|
||||
@@ -1056,8 +1056,7 @@ public abstract class AnnotationUtils {
|
||||
return null;
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
rethrowAnnotationConfigurationException(ex);
|
||||
handleIntrospectionFailure(annotation.getClass(), ex);
|
||||
handleValueRetrievalFailure(annotation, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1073,14 +1072,18 @@ public abstract class AnnotationUtils {
|
||||
* @return the value returned from the method invocation
|
||||
* @since 5.3.24
|
||||
*/
|
||||
static Object invokeAnnotationMethod(Method method, Object annotation) {
|
||||
@Nullable
|
||||
static Object invokeAnnotationMethod(Method method, @Nullable Object annotation) {
|
||||
if (annotation == null) {
|
||||
return null;
|
||||
}
|
||||
if (Proxy.isProxyClass(annotation.getClass())) {
|
||||
try {
|
||||
InvocationHandler handler = Proxy.getInvocationHandler(annotation);
|
||||
return handler.invoke(annotation, method, null);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// ignore and fall back to reflection below
|
||||
// Ignore and fall back to reflection below
|
||||
}
|
||||
}
|
||||
return ReflectionUtils.invokeMethod(method, annotation);
|
||||
@@ -1114,20 +1117,32 @@ public abstract class AnnotationUtils {
|
||||
* @see #rethrowAnnotationConfigurationException
|
||||
* @see IntrospectionFailureLogger
|
||||
*/
|
||||
static void handleIntrospectionFailure(@Nullable AnnotatedElement element, Throwable ex) {
|
||||
static void handleIntrospectionFailure(AnnotatedElement element, Throwable ex) {
|
||||
rethrowAnnotationConfigurationException(ex);
|
||||
IntrospectionFailureLogger logger = IntrospectionFailureLogger.INFO;
|
||||
boolean meta = false;
|
||||
if (element instanceof Class<?> clazz && Annotation.class.isAssignableFrom(clazz)) {
|
||||
// Meta-annotation or (default) value lookup on an annotation type
|
||||
// Meta-annotation introspection failure
|
||||
logger = IntrospectionFailureLogger.DEBUG;
|
||||
meta = true;
|
||||
}
|
||||
if (logger.isEnabled()) {
|
||||
String message = meta ?
|
||||
"Failed to meta-introspect annotation " :
|
||||
"Failed to introspect annotations on ";
|
||||
logger.log(message + element + ": " + ex);
|
||||
logger.log("Failed to " + (meta ? "meta-introspect annotation " : "introspect annotations on ") +
|
||||
element + ": " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the supplied value retrieval exception.
|
||||
* @param annotation the annotation instance from which to retrieve the value
|
||||
* @param ex the exception that we encountered
|
||||
* @see #handleIntrospectionFailure
|
||||
*/
|
||||
private static void handleValueRetrievalFailure(Annotation annotation, Throwable ex) {
|
||||
rethrowAnnotationConfigurationException(ex);
|
||||
IntrospectionFailureLogger logger = IntrospectionFailureLogger.INFO;
|
||||
if (logger.isEnabled()) {
|
||||
logger.log("Failed to retrieve value from " + annotation + ": " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -336,11 +336,10 @@ abstract class AnnotationsScanner {
|
||||
|
||||
Method[] methods = baseTypeMethodsCache.get(baseType);
|
||||
if (methods == null) {
|
||||
boolean isInterface = baseType.isInterface();
|
||||
methods = isInterface ? baseType.getMethods() : ReflectionUtils.getDeclaredMethods(baseType);
|
||||
methods = ReflectionUtils.getDeclaredMethods(baseType);
|
||||
int cleared = 0;
|
||||
for (int i = 0; i < methods.length; i++) {
|
||||
if ((!isInterface && Modifier.isPrivate(methods[i].getModifiers())) ||
|
||||
if (Modifier.isPrivate(methods[i].getModifiers()) ||
|
||||
hasPlainJavaAnnotationsOnly(methods[i]) ||
|
||||
getDeclaredAnnotations(methods[i], false).length == 0) {
|
||||
methods[i] = null;
|
||||
@@ -454,7 +453,7 @@ abstract class AnnotationsScanner {
|
||||
for (int i = 0; i < annotations.length; i++) {
|
||||
Annotation annotation = annotations[i];
|
||||
if (isIgnorable(annotation.annotationType()) ||
|
||||
!AttributeMethods.forAnnotationType(annotation.annotationType()).isValid(annotation)) {
|
||||
!AttributeMethods.forAnnotationType(annotation.annotationType()).canLoad(annotation)) {
|
||||
annotations[i] = null;
|
||||
}
|
||||
else {
|
||||
|
||||
+19
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -45,7 +45,7 @@ final class AttributeMethods {
|
||||
if (m1 != null && m2 != null) {
|
||||
return m1.getName().compareTo(m2.getName());
|
||||
}
|
||||
return m1 != null ? -1 : 1;
|
||||
return (m1 != null ? -1 : 1);
|
||||
};
|
||||
|
||||
|
||||
@@ -87,18 +87,26 @@ final class AttributeMethods {
|
||||
/**
|
||||
* Determine if values from the given annotation can be safely accessed without
|
||||
* causing any {@link TypeNotPresentException TypeNotPresentExceptions}.
|
||||
* <p>This method is designed to cover Google App Engine's late arrival of such
|
||||
* exceptions for {@code Class} values (instead of the more typical early
|
||||
* {@code Class.getAnnotations() failure} on a regular JVM).
|
||||
* @param annotation the annotation to check
|
||||
* @return {@code true} if all values are present
|
||||
* @see #validate(Annotation)
|
||||
*/
|
||||
boolean isValid(Annotation annotation) {
|
||||
boolean canLoad(Annotation annotation) {
|
||||
assertAnnotation(annotation);
|
||||
for (int i = 0; i < size(); i++) {
|
||||
if (canThrowTypeNotPresentException(i)) {
|
||||
try {
|
||||
AnnotationUtils.invokeAnnotationMethod(get(i), annotation);
|
||||
}
|
||||
catch (IllegalStateException ex) {
|
||||
// Plain invocation failure to expose -> leave up to attribute retrieval
|
||||
// (if any) where such invocation failure will be logged eventually.
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// TypeNotPresentException etc. -> annotation type not actually loadable.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -108,13 +116,13 @@ final class AttributeMethods {
|
||||
|
||||
/**
|
||||
* Check if values from the given annotation can be safely accessed without causing
|
||||
* any {@link TypeNotPresentException TypeNotPresentExceptions}. In particular,
|
||||
* this method is designed to cover Google App Engine's late arrival of such
|
||||
* any {@link TypeNotPresentException TypeNotPresentExceptions}.
|
||||
* <p>This method is designed to cover Google App Engine's late arrival of such
|
||||
* exceptions for {@code Class} values (instead of the more typical early
|
||||
* {@code Class.getAnnotations() failure}).
|
||||
* {@code Class.getAnnotations() failure} on a regular JVM).
|
||||
* @param annotation the annotation to validate
|
||||
* @throws IllegalStateException if a declared {@code Class} attribute could not be read
|
||||
* @see #isValid(Annotation)
|
||||
* @see #canLoad(Annotation)
|
||||
*/
|
||||
void validate(Annotation annotation) {
|
||||
assertAnnotation(annotation);
|
||||
@@ -123,6 +131,9 @@ final class AttributeMethods {
|
||||
try {
|
||||
AnnotationUtils.invokeAnnotationMethod(get(i), annotation);
|
||||
}
|
||||
catch (IllegalStateException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new IllegalStateException("Could not obtain annotation attribute value for " +
|
||||
get(i).getName() + " declared on " + annotation.annotationType(), ex);
|
||||
@@ -147,7 +158,7 @@ final class AttributeMethods {
|
||||
@Nullable
|
||||
Method get(String name) {
|
||||
int index = indexOf(name);
|
||||
return index != -1 ? this.attributeMethods[index] : null;
|
||||
return (index != -1 ? this.attributeMethods[index] : null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -475,7 +475,7 @@ public class TypeDescriptor implements Serializable {
|
||||
ObjectUtils.nullSafeEquals(getMapValueTypeDescriptor(), otherDesc.getMapValueTypeDescriptor()));
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
return Arrays.equals(getResolvableType().getGenerics(), otherDesc.getResolvableType().getGenerics());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -263,29 +263,35 @@ public interface DataBuffer {
|
||||
default DataBuffer write(CharSequence charSequence, Charset charset) {
|
||||
Assert.notNull(charSequence, "CharSequence must not be null");
|
||||
Assert.notNull(charset, "Charset must not be null");
|
||||
if (charSequence.length() > 0) {
|
||||
if (!charSequence.isEmpty()) {
|
||||
CharsetEncoder encoder = charset.newEncoder()
|
||||
.onMalformedInput(CodingErrorAction.REPLACE)
|
||||
.onUnmappableCharacter(CodingErrorAction.REPLACE);
|
||||
CharBuffer src = CharBuffer.wrap(charSequence);
|
||||
int cap = (int) (src.remaining() * encoder.averageBytesPerChar());
|
||||
int averageSize = (int) Math.ceil(src.remaining() * encoder.averageBytesPerChar());
|
||||
ensureWritable(averageSize);
|
||||
while (true) {
|
||||
ensureWritable(cap);
|
||||
CoderResult cr;
|
||||
try (ByteBufferIterator iterator = writableByteBuffers()) {
|
||||
Assert.state(iterator.hasNext(), "No ByteBuffer available");
|
||||
ByteBuffer dest = iterator.next();
|
||||
cr = encoder.encode(src, dest, true);
|
||||
if (cr.isUnderflow()) {
|
||||
cr = encoder.flush(dest);
|
||||
if (src.hasRemaining()) {
|
||||
try (ByteBufferIterator iterator = writableByteBuffers()) {
|
||||
Assert.state(iterator.hasNext(), "No ByteBuffer available");
|
||||
ByteBuffer dest = iterator.next();
|
||||
cr = encoder.encode(src, dest, true);
|
||||
if (cr.isUnderflow()) {
|
||||
cr = encoder.flush(dest);
|
||||
}
|
||||
writePosition(writePosition() + dest.position());
|
||||
}
|
||||
writePosition(dest.position());
|
||||
}
|
||||
else {
|
||||
cr = CoderResult.UNDERFLOW;
|
||||
}
|
||||
if (cr.isUnderflow()) {
|
||||
break;
|
||||
}
|
||||
if (cr.isOverflow()) {
|
||||
cap = 2 * cap + 1;
|
||||
else if (cr.isOverflow()) {
|
||||
int maxSize = (int) Math.ceil(src.remaining() * encoder.maxBytesPerChar());
|
||||
ensureWritable(maxSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -371,7 +371,7 @@ public class DefaultDataBuffer implements DataBuffer {
|
||||
.slice();
|
||||
this.writePosition = Math.max(this.writePosition, index) - index;
|
||||
this.readPosition = Math.max(this.readPosition, index) - index;
|
||||
capacity(this.byteBuffer.capacity());
|
||||
this.capacity = this.byteBuffer.capacity();
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -416,16 +416,15 @@ public class DefaultDataBuffer implements DataBuffer {
|
||||
|
||||
@Override
|
||||
public DataBuffer.ByteBufferIterator readableByteBuffers() {
|
||||
ByteBuffer readOnly = this.byteBuffer.asReadOnlyBuffer();
|
||||
readOnly.clear().position(this.readPosition).limit(this.writePosition - this.readPosition);
|
||||
ByteBuffer readOnly = this.byteBuffer.slice(this.readPosition, readableByteCount())
|
||||
.asReadOnlyBuffer();
|
||||
return new ByteBufferIterator(readOnly);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataBuffer.ByteBufferIterator writableByteBuffers() {
|
||||
ByteBuffer duplicate = this.byteBuffer.duplicate();
|
||||
duplicate.clear().position(this.writePosition).limit(this.capacity - this.writePosition);
|
||||
return new ByteBufferIterator(duplicate);
|
||||
ByteBuffer slice = this.byteBuffer.slice(this.writePosition, writableByteCount());
|
||||
return new ByteBufferIterator(slice);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+2
-2
@@ -24,6 +24,8 @@ import org.springframework.lang.Nullable;
|
||||
/**
|
||||
* Descriptor for a {@link org.springframework.core.env.PropertySource PropertySource}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 6.0
|
||||
* @param locations the locations to consider
|
||||
* @param ignoreResourceNotFound whether a failure to find a property resource
|
||||
* should be ignored
|
||||
@@ -31,8 +33,6 @@ import org.springframework.lang.Nullable;
|
||||
* @param propertySourceFactory the type of {@link PropertySourceFactory} to use,
|
||||
* or {@code null} to use the default
|
||||
* @param encoding the encoding, or {@code null} to use the default encoding
|
||||
* @author Stephane Nicoll
|
||||
* @since 6.0
|
||||
* @see org.springframework.core.env.PropertySource
|
||||
* @see org.springframework.context.annotation.PropertySource
|
||||
*/
|
||||
|
||||
@@ -330,10 +330,10 @@ public abstract class MimeTypeUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a string representation of the given list of {@code MimeType} objects.
|
||||
* @param mimeTypes the string to parse
|
||||
* @return the list of mime types
|
||||
* @throws IllegalArgumentException if the String cannot be parsed
|
||||
* Generate a string representation of the given collection of {@link MimeType}
|
||||
* objects.
|
||||
* @param mimeTypes the {@code MimeType} objects
|
||||
* @return a string representation of the {@code MimeType} objects
|
||||
*/
|
||||
public static String toString(Collection<? extends MimeType> mimeTypes) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
@@ -348,21 +348,19 @@ public abstract class MimeTypeUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the given list of {@code MimeType} objects by
|
||||
* Sort the given list of {@code MimeType} objects by
|
||||
* {@linkplain MimeType#isMoreSpecific(MimeType) specificity}.
|
||||
*
|
||||
* <p>Because of the computational cost, this method throws an exception
|
||||
* when the given list contains too many elements.
|
||||
* <p>Because of the computational cost, this method throws an exception if
|
||||
* the given list contains too many elements.
|
||||
* @param mimeTypes the list of mime types to be sorted
|
||||
* @throws IllegalArgumentException if {@code mimeTypes} contains more
|
||||
* than 50 elements
|
||||
* @throws InvalidMimeTypeException if {@code mimeTypes} contains more than 50 elements
|
||||
* @see <a href="https://tools.ietf.org/html/rfc7231#section-5.3.2">HTTP 1.1: Semantics
|
||||
* and Content, section 5.3.2</a>
|
||||
* @see MimeType#isMoreSpecific(MimeType)
|
||||
*/
|
||||
public static <T extends MimeType> void sortBySpecificity(List<T> mimeTypes) {
|
||||
Assert.notNull(mimeTypes, "'mimeTypes' must not be null");
|
||||
if (mimeTypes.size() >= 50) {
|
||||
if (mimeTypes.size() > 50) {
|
||||
throw new InvalidMimeTypeException(mimeTypes.toString(), "Too many elements");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,8 +23,6 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Writer;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -133,9 +131,8 @@ public abstract class StreamUtils {
|
||||
Assert.notNull(charset, "No Charset specified");
|
||||
Assert.notNull(out, "No OutputStream specified");
|
||||
|
||||
Writer writer = new OutputStreamWriter(out, charset);
|
||||
writer.write(in);
|
||||
writer.flush();
|
||||
out.write(in.getBytes(charset));
|
||||
out.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -60,6 +60,15 @@ class GeneratedFilesTests {
|
||||
.contains("Hello, World!");
|
||||
}
|
||||
|
||||
@Test
|
||||
void addSourceFileWithJavaFileInTheDefaultPackageThrowsException() {
|
||||
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld").build();
|
||||
JavaFile javaFile = JavaFile.builder("", helloWorld).build();
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.generatedFiles.addSourceFile(javaFile))
|
||||
.withMessage("Could not add 'HelloWorld', processing classes in the "
|
||||
+ "default package is not supported. Did you forget to add a package statement?");
|
||||
}
|
||||
|
||||
@Test
|
||||
void addSourceFileWithCharSequenceAddsFile() throws Exception {
|
||||
this.generatedFiles.addSourceFile("com.example.HelloWorld", "{}");
|
||||
@@ -73,6 +82,14 @@ class GeneratedFilesTests {
|
||||
.withMessage("'className' must not be empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
void addSourceFileWithCharSequenceWhenClassNameIsInTheDefaultPackageThrowsException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.generatedFiles.addSourceFile("HelloWorld", "{}"))
|
||||
.withMessage("Could not add 'HelloWorld', processing classes in the "
|
||||
+ "default package is not supported. Did you forget to add a package statement?");
|
||||
}
|
||||
|
||||
@Test
|
||||
void addSourceFileWithCharSequenceWhenClassNameIsInvalidThrowsException() {
|
||||
assertThatIllegalArgumentException()
|
||||
|
||||
+33
-44
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -43,55 +43,45 @@ class AnnotationIntrospectionFailureTests {
|
||||
|
||||
@Test
|
||||
void filteredTypeThrowsTypeNotPresentException() throws Exception {
|
||||
FilteringClassLoader classLoader = new FilteringClassLoader(
|
||||
getClass().getClassLoader());
|
||||
Class<?> withExampleAnnotation = ClassUtils.forName(
|
||||
WithExampleAnnotation.class.getName(), classLoader);
|
||||
Annotation annotation = withExampleAnnotation.getAnnotations()[0];
|
||||
FilteringClassLoader classLoader = new FilteringClassLoader(getClass().getClassLoader());
|
||||
Class<?> withAnnotation = ClassUtils.forName(WithExampleAnnotation.class.getName(), classLoader);
|
||||
Annotation annotation = withAnnotation.getAnnotations()[0];
|
||||
Method method = annotation.annotationType().getMethod("value");
|
||||
method.setAccessible(true);
|
||||
assertThatExceptionOfType(TypeNotPresentException.class).isThrownBy(() ->
|
||||
ReflectionUtils.invokeMethod(method, annotation))
|
||||
.withCauseInstanceOf(ClassNotFoundException.class);
|
||||
assertThatExceptionOfType(TypeNotPresentException.class)
|
||||
.isThrownBy(() -> ReflectionUtils.invokeMethod(method, annotation))
|
||||
.withCauseInstanceOf(ClassNotFoundException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void filteredTypeInMetaAnnotationWhenUsingAnnotatedElementUtilsHandlesException() throws Exception {
|
||||
FilteringClassLoader classLoader = new FilteringClassLoader(
|
||||
getClass().getClassLoader());
|
||||
Class<?> withExampleMetaAnnotation = ClassUtils.forName(
|
||||
WithExampleMetaAnnotation.class.getName(), classLoader);
|
||||
Class<Annotation> exampleAnnotationClass = (Class<Annotation>) ClassUtils.forName(
|
||||
ExampleAnnotation.class.getName(), classLoader);
|
||||
Class<Annotation> exampleMetaAnnotationClass = (Class<Annotation>) ClassUtils.forName(
|
||||
ExampleMetaAnnotation.class.getName(), classLoader);
|
||||
assertThat(AnnotatedElementUtils.getMergedAnnotationAttributes(
|
||||
withExampleMetaAnnotation, exampleAnnotationClass)).isNull();
|
||||
assertThat(AnnotatedElementUtils.getMergedAnnotationAttributes(
|
||||
withExampleMetaAnnotation, exampleMetaAnnotationClass)).isNull();
|
||||
assertThat(AnnotatedElementUtils.hasAnnotation(withExampleMetaAnnotation,
|
||||
exampleAnnotationClass)).isFalse();
|
||||
assertThat(AnnotatedElementUtils.hasAnnotation(withExampleMetaAnnotation,
|
||||
exampleMetaAnnotationClass)).isFalse();
|
||||
FilteringClassLoader classLoader = new FilteringClassLoader(getClass().getClassLoader());
|
||||
Class<?> withAnnotation = ClassUtils.forName(WithExampleMetaAnnotation.class.getName(), classLoader);
|
||||
Class<Annotation> annotationClass = (Class<Annotation>)
|
||||
ClassUtils.forName(ExampleAnnotation.class.getName(), classLoader);
|
||||
Class<Annotation> metaAnnotationClass = (Class<Annotation>)
|
||||
ClassUtils.forName(ExampleMetaAnnotation.class.getName(), classLoader);
|
||||
assertThat(AnnotatedElementUtils.getMergedAnnotationAttributes(withAnnotation, annotationClass)).isNull();
|
||||
assertThat(AnnotatedElementUtils.getMergedAnnotationAttributes(withAnnotation, metaAnnotationClass)).isNull();
|
||||
assertThat(AnnotatedElementUtils.hasAnnotation(withAnnotation, annotationClass)).isFalse();
|
||||
assertThat(AnnotatedElementUtils.hasAnnotation(withAnnotation, metaAnnotationClass)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void filteredTypeInMetaAnnotationWhenUsingMergedAnnotationsHandlesException() throws Exception {
|
||||
FilteringClassLoader classLoader = new FilteringClassLoader(
|
||||
getClass().getClassLoader());
|
||||
Class<?> withExampleMetaAnnotation = ClassUtils.forName(
|
||||
WithExampleMetaAnnotation.class.getName(), classLoader);
|
||||
Class<Annotation> exampleAnnotationClass = (Class<Annotation>) ClassUtils.forName(
|
||||
ExampleAnnotation.class.getName(), classLoader);
|
||||
Class<Annotation> exampleMetaAnnotationClass = (Class<Annotation>) ClassUtils.forName(
|
||||
ExampleMetaAnnotation.class.getName(), classLoader);
|
||||
MergedAnnotations annotations = MergedAnnotations.from(withExampleMetaAnnotation);
|
||||
assertThat(annotations.get(exampleAnnotationClass).isPresent()).isFalse();
|
||||
assertThat(annotations.get(exampleMetaAnnotationClass).isPresent()).isFalse();
|
||||
assertThat(annotations.isPresent(exampleMetaAnnotationClass)).isFalse();
|
||||
assertThat(annotations.isPresent(exampleAnnotationClass)).isFalse();
|
||||
FilteringClassLoader classLoader = new FilteringClassLoader(getClass().getClassLoader());
|
||||
Class<?> withAnnotation = ClassUtils.forName(WithExampleMetaAnnotation.class.getName(), classLoader);
|
||||
Class<Annotation> annotationClass = (Class<Annotation>)
|
||||
ClassUtils.forName(ExampleAnnotation.class.getName(), classLoader);
|
||||
Class<Annotation> metaAnnotationClass = (Class<Annotation>)
|
||||
ClassUtils.forName(ExampleMetaAnnotation.class.getName(), classLoader);
|
||||
MergedAnnotations annotations = MergedAnnotations.from(withAnnotation);
|
||||
assertThat(annotations.get(annotationClass).isPresent()).isFalse();
|
||||
assertThat(annotations.get(metaAnnotationClass).isPresent()).isFalse();
|
||||
assertThat(annotations.isPresent(metaAnnotationClass)).isFalse();
|
||||
assertThat(annotations.isPresent(annotationClass)).isFalse();
|
||||
}
|
||||
|
||||
|
||||
@@ -103,17 +93,16 @@ class AnnotationIntrospectionFailureTests {
|
||||
|
||||
@Override
|
||||
protected boolean isEligibleForOverriding(String className) {
|
||||
return className.startsWith(
|
||||
AnnotationIntrospectionFailureTests.class.getName());
|
||||
return className.startsWith(AnnotationIntrospectionFailureTests.class.getName()) ||
|
||||
className.startsWith("jdk.internal");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
||||
if (name.startsWith(AnnotationIntrospectionFailureTests.class.getName()) &&
|
||||
name.contains("Filtered")) {
|
||||
protected Class<?> loadClassForOverriding(String name) throws ClassNotFoundException {
|
||||
if (name.contains("Filtered") || name.startsWith("jdk.internal")) {
|
||||
throw new ClassNotFoundException(name);
|
||||
}
|
||||
return super.loadClass(name, resolve);
|
||||
return super.loadClassForOverriding(name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+36
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -197,7 +197,7 @@ class AnnotationsScannerTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void typeHierarchyStrategyOnClassWhenHasInterfaceDoesNotIncludeInterfaces() {
|
||||
void typeHierarchyStrategyOnClassWhenHasSingleInterfaceScansInterfaces() {
|
||||
Class<?> source = WithSingleInterface.class;
|
||||
assertThat(scan(source, SearchStrategy.TYPE_HIERARCHY)).containsExactly(
|
||||
"0:TestAnnotation1", "1:TestAnnotation2", "1:TestInheritedAnnotation2");
|
||||
@@ -353,10 +353,19 @@ class AnnotationsScannerTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void typeHierarchyStrategyOnMethodWhenHasInterfaceDoesNotIncludeInterfaces() {
|
||||
void typeHierarchyStrategyOnMethodWhenHasInterfaceScansInterfaces() {
|
||||
Method source = methodFrom(WithSingleInterface.class);
|
||||
assertThat(scan(source, SearchStrategy.TYPE_HIERARCHY)).containsExactly(
|
||||
"0:TestAnnotation1", "1:TestAnnotation2", "1:TestInheritedAnnotation2");
|
||||
|
||||
source = methodFrom(Hello1Impl.class);
|
||||
assertThat(scan(source, SearchStrategy.TYPE_HIERARCHY)).containsExactly("1:TestAnnotation1");
|
||||
}
|
||||
|
||||
@Test // gh-31803
|
||||
void typeHierarchyStrategyOnMethodWhenHasInterfaceHierarchyScansInterfacesOnlyOnce() {
|
||||
Method source = methodFrom(Hello2Impl.class);
|
||||
assertThat(scan(source, SearchStrategy.TYPE_HIERARCHY)).containsExactly("1:TestAnnotation1");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -691,6 +700,30 @@ class AnnotationsScannerTests {
|
||||
}
|
||||
}
|
||||
|
||||
interface Hello1 {
|
||||
|
||||
@TestAnnotation1
|
||||
void method();
|
||||
}
|
||||
|
||||
interface Hello2 extends Hello1 {
|
||||
}
|
||||
|
||||
static class Hello1Impl implements Hello1 {
|
||||
|
||||
@Override
|
||||
public void method() {
|
||||
}
|
||||
}
|
||||
|
||||
static class Hello2Impl implements Hello2 {
|
||||
|
||||
@Override
|
||||
public void method() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@TestAnnotation2
|
||||
@TestInheritedAnnotation2
|
||||
static class HierarchySuperclass extends HierarchySuperSuperclass {
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -112,7 +112,7 @@ class AttributeMethodsTests {
|
||||
ClassValue annotation = mockAnnotation(ClassValue.class);
|
||||
given(annotation.value()).willThrow(TypeNotPresentException.class);
|
||||
AttributeMethods attributes = AttributeMethods.forAnnotationType(annotation.annotationType());
|
||||
assertThat(attributes.isValid(annotation)).isFalse();
|
||||
assertThat(attributes.canLoad(annotation)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -121,7 +121,7 @@ class AttributeMethodsTests {
|
||||
ClassValue annotation = mock();
|
||||
given(annotation.value()).willReturn((Class) InputStream.class);
|
||||
AttributeMethods attributes = AttributeMethods.forAnnotationType(annotation.annotationType());
|
||||
assertThat(attributes.isValid(annotation)).isTrue();
|
||||
assertThat(attributes.canLoad(annotation)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+357
-428
File diff suppressed because it is too large
Load Diff
+23
-10
@@ -34,11 +34,13 @@ import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
@@ -663,12 +665,12 @@ class TypeDescriptorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void upCast() throws Exception {
|
||||
void upcast() throws Exception {
|
||||
Property property = new Property(getClass(), getClass().getMethod("getProperty"),
|
||||
getClass().getMethod("setProperty", Map.class));
|
||||
TypeDescriptor typeDescriptor = new TypeDescriptor(property);
|
||||
TypeDescriptor upCast = typeDescriptor.upcast(Object.class);
|
||||
assertThat(upCast.getAnnotation(MethodAnnotation1.class)).isNotNull();
|
||||
TypeDescriptor upcast = typeDescriptor.upcast(Object.class);
|
||||
assertThat(upcast.getAnnotation(MethodAnnotation1.class)).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -682,7 +684,7 @@ class TypeDescriptorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void elementTypeForCollectionSubclass() throws Exception {
|
||||
void elementTypeForCollectionSubclass() {
|
||||
@SuppressWarnings("serial")
|
||||
class CustomSet extends HashSet<String> {
|
||||
}
|
||||
@@ -692,7 +694,7 @@ class TypeDescriptorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void elementTypeForMapSubclass() throws Exception {
|
||||
void elementTypeForMapSubclass() {
|
||||
@SuppressWarnings("serial")
|
||||
class CustomMap extends HashMap<String, Integer> {
|
||||
}
|
||||
@@ -704,7 +706,7 @@ class TypeDescriptorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void createMapArray() throws Exception {
|
||||
void createMapArray() {
|
||||
TypeDescriptor mapType = TypeDescriptor.map(
|
||||
LinkedHashMap.class, TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(Integer.class));
|
||||
TypeDescriptor arrayType = TypeDescriptor.array(mapType);
|
||||
@@ -713,13 +715,13 @@ class TypeDescriptorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void createStringArray() throws Exception {
|
||||
void createStringArray() {
|
||||
TypeDescriptor arrayType = TypeDescriptor.array(TypeDescriptor.valueOf(String.class));
|
||||
assertThat(TypeDescriptor.valueOf(String[].class)).isEqualTo(arrayType);
|
||||
}
|
||||
|
||||
@Test
|
||||
void createNullArray() throws Exception {
|
||||
void createNullArray() {
|
||||
assertThat((Object) TypeDescriptor.array(null)).isNull();
|
||||
}
|
||||
|
||||
@@ -736,13 +738,13 @@ class TypeDescriptorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void createCollectionWithNullElement() throws Exception {
|
||||
void createCollectionWithNullElement() {
|
||||
TypeDescriptor typeDescriptor = TypeDescriptor.collection(List.class, null);
|
||||
assertThat(typeDescriptor.getElementTypeDescriptor()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void createMapWithNullElements() throws Exception {
|
||||
void createMapWithNullElements() {
|
||||
TypeDescriptor typeDescriptor = TypeDescriptor.map(LinkedHashMap.class, null, null);
|
||||
assertThat(typeDescriptor.getMapKeyTypeDescriptor()).isNull();
|
||||
assertThat(typeDescriptor.getMapValueTypeDescriptor()).isNull();
|
||||
@@ -757,6 +759,17 @@ class TypeDescriptorTests {
|
||||
assertThat(TypeDescriptor.valueOf(Integer.class).getSource()).isEqualTo(Integer.class);
|
||||
}
|
||||
|
||||
@Test // gh-31672
|
||||
void equalityWithGenerics() {
|
||||
ResolvableType rt1 = ResolvableType.forClassWithGenerics(Optional.class, Integer.class);
|
||||
ResolvableType rt2 = ResolvableType.forClassWithGenerics(Optional.class, String.class);
|
||||
|
||||
TypeDescriptor td1 = new TypeDescriptor(rt1, null, null);
|
||||
TypeDescriptor td2 = new TypeDescriptor(rt2, null, null);
|
||||
|
||||
assertThat(td1).isNotEqualTo(td2);
|
||||
}
|
||||
|
||||
|
||||
// Methods designed for test introspection
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -678,6 +678,38 @@ class DataBufferTests extends AbstractDataBufferAllocatingTests {
|
||||
void readableByteBuffers(DataBufferFactory bufferFactory) throws IOException {
|
||||
super.bufferFactory = bufferFactory;
|
||||
|
||||
DataBuffer dataBuffer = this.bufferFactory.allocateBuffer(3);
|
||||
dataBuffer.write("abc".getBytes(StandardCharsets.UTF_8));
|
||||
dataBuffer.readPosition(1);
|
||||
dataBuffer.writePosition(2);
|
||||
|
||||
|
||||
byte[] result = new byte[1];
|
||||
try (var iterator = dataBuffer.readableByteBuffers()) {
|
||||
assertThat(iterator).hasNext();
|
||||
int i = 0;
|
||||
while (iterator.hasNext()) {
|
||||
ByteBuffer byteBuffer = iterator.next();
|
||||
assertThat(byteBuffer.position()).isEqualTo(0);
|
||||
assertThat(byteBuffer.limit()).isEqualTo(1);
|
||||
assertThat(byteBuffer.capacity()).isEqualTo(1);
|
||||
assertThat(byteBuffer.remaining()).isEqualTo(1);
|
||||
|
||||
byteBuffer.get(result, i, 1);
|
||||
|
||||
assertThat(iterator).isExhausted();
|
||||
}
|
||||
}
|
||||
|
||||
assertThat(result).containsExactly('b');
|
||||
|
||||
release(dataBuffer);
|
||||
}
|
||||
|
||||
@ParameterizedDataBufferAllocatingTest
|
||||
void readableByteBuffersJoined(DataBufferFactory bufferFactory) {
|
||||
super.bufferFactory = bufferFactory;
|
||||
|
||||
DataBuffer dataBuffer = this.bufferFactory.join(Arrays.asList(stringBuffer("a"),
|
||||
stringBuffer("b"), stringBuffer("c")));
|
||||
|
||||
@@ -703,17 +735,26 @@ class DataBufferTests extends AbstractDataBufferAllocatingTests {
|
||||
void writableByteBuffers(DataBufferFactory bufferFactory) {
|
||||
super.bufferFactory = bufferFactory;
|
||||
|
||||
DataBuffer dataBuffer = this.bufferFactory.allocateBuffer(1);
|
||||
DataBuffer dataBuffer = this.bufferFactory.allocateBuffer(3);
|
||||
dataBuffer.write("ab".getBytes(StandardCharsets.UTF_8));
|
||||
dataBuffer.readPosition(1);
|
||||
|
||||
try (DataBuffer.ByteBufferIterator iterator = dataBuffer.writableByteBuffers()) {
|
||||
assertThat(iterator).hasNext();
|
||||
ByteBuffer byteBuffer = iterator.next();
|
||||
byteBuffer.put((byte) 'a');
|
||||
dataBuffer.writePosition(1);
|
||||
assertThat(byteBuffer.position()).isEqualTo(0);
|
||||
assertThat(byteBuffer.limit()).isEqualTo(1);
|
||||
assertThat(byteBuffer.capacity()).isEqualTo(1);
|
||||
assertThat(byteBuffer.remaining()).isEqualTo(1);
|
||||
|
||||
byteBuffer.put((byte) 'c');
|
||||
dataBuffer.writePosition(3);
|
||||
|
||||
assertThat(iterator).isExhausted();
|
||||
}
|
||||
assertThat(dataBuffer.read()).isEqualTo((byte) 'a');
|
||||
byte[] result = new byte[2];
|
||||
dataBuffer.read(result);
|
||||
assertThat(result).containsExactly('b', 'c');
|
||||
|
||||
release(dataBuffer);
|
||||
}
|
||||
@@ -945,4 +986,21 @@ class DataBufferTests extends AbstractDataBufferAllocatingTests {
|
||||
assertThat(StandardCharsets.UTF_8.decode(byteBuffer).toString()).isEqualTo("b");
|
||||
}
|
||||
|
||||
@ParameterizedDataBufferAllocatingTest // gh-31873
|
||||
void repeatedWrites(DataBufferFactory bufferFactory) {
|
||||
super.bufferFactory = bufferFactory;
|
||||
|
||||
DataBuffer buffer = bufferFactory.allocateBuffer(256);
|
||||
String name = "Müller";
|
||||
int repeatCount = 19;
|
||||
for (int i = 0; i < repeatCount; i++) {
|
||||
buffer.write(name, StandardCharsets.UTF_8);
|
||||
}
|
||||
String result = buffer.toString(StandardCharsets.UTF_8);
|
||||
String expected = name.repeat(repeatCount);
|
||||
assertThat(result).isEqualTo(expected);
|
||||
|
||||
release(buffer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+35
-36
@@ -117,6 +117,7 @@ public class ReflectiveMethodResolver implements MethodResolver {
|
||||
TypeConverter typeConverter = context.getTypeConverter();
|
||||
Class<?> type = (targetObject instanceof Class<?> clazz ? clazz : targetObject.getClass());
|
||||
ArrayList<Method> methods = new ArrayList<>(getMethods(type, targetObject));
|
||||
methods.removeIf(method -> !method.getName().equals(name));
|
||||
|
||||
// If a filter is registered for this type, call it
|
||||
MethodFilter filter = (this.filters != null ? this.filters.get(type) : null);
|
||||
@@ -160,48 +161,46 @@ public class ReflectiveMethodResolver implements MethodResolver {
|
||||
boolean multipleOptions = false;
|
||||
|
||||
for (Method method : methodsToIterate) {
|
||||
if (method.getName().equals(name)) {
|
||||
int paramCount = method.getParameterCount();
|
||||
List<TypeDescriptor> paramDescriptors = new ArrayList<>(paramCount);
|
||||
for (int i = 0; i < paramCount; i++) {
|
||||
paramDescriptors.add(new TypeDescriptor(new MethodParameter(method, i)));
|
||||
int paramCount = method.getParameterCount();
|
||||
List<TypeDescriptor> paramDescriptors = new ArrayList<>(paramCount);
|
||||
for (int i = 0; i < paramCount; i++) {
|
||||
paramDescriptors.add(new TypeDescriptor(new MethodParameter(method, i)));
|
||||
}
|
||||
ReflectionHelper.ArgumentsMatchInfo matchInfo = null;
|
||||
if (method.isVarArgs() && argumentTypes.size() >= (paramCount - 1)) {
|
||||
// *sigh* complicated
|
||||
matchInfo = ReflectionHelper.compareArgumentsVarargs(paramDescriptors, argumentTypes, typeConverter);
|
||||
}
|
||||
else if (paramCount == argumentTypes.size()) {
|
||||
// Name and parameter number match, check the arguments
|
||||
matchInfo = ReflectionHelper.compareArguments(paramDescriptors, argumentTypes, typeConverter);
|
||||
}
|
||||
if (matchInfo != null) {
|
||||
if (matchInfo.isExactMatch()) {
|
||||
return new ReflectiveMethodExecutor(method, type);
|
||||
}
|
||||
ReflectionHelper.ArgumentsMatchInfo matchInfo = null;
|
||||
if (method.isVarArgs() && argumentTypes.size() >= (paramCount - 1)) {
|
||||
// *sigh* complicated
|
||||
matchInfo = ReflectionHelper.compareArgumentsVarargs(paramDescriptors, argumentTypes, typeConverter);
|
||||
}
|
||||
else if (paramCount == argumentTypes.size()) {
|
||||
// Name and parameter number match, check the arguments
|
||||
matchInfo = ReflectionHelper.compareArguments(paramDescriptors, argumentTypes, typeConverter);
|
||||
}
|
||||
if (matchInfo != null) {
|
||||
if (matchInfo.isExactMatch()) {
|
||||
return new ReflectiveMethodExecutor(method, type);
|
||||
}
|
||||
else if (matchInfo.isCloseMatch()) {
|
||||
if (this.useDistance) {
|
||||
int matchDistance = ReflectionHelper.getTypeDifferenceWeight(paramDescriptors, argumentTypes);
|
||||
if (closeMatch == null || matchDistance < closeMatchDistance) {
|
||||
// This is a better match...
|
||||
closeMatch = method;
|
||||
closeMatchDistance = matchDistance;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Take this as a close match if there isn't one already
|
||||
if (closeMatch == null) {
|
||||
closeMatch = method;
|
||||
}
|
||||
else if (matchInfo.isCloseMatch()) {
|
||||
if (this.useDistance) {
|
||||
int matchDistance = ReflectionHelper.getTypeDifferenceWeight(paramDescriptors, argumentTypes);
|
||||
if (closeMatch == null || matchDistance < closeMatchDistance) {
|
||||
// This is a better match...
|
||||
closeMatch = method;
|
||||
closeMatchDistance = matchDistance;
|
||||
}
|
||||
}
|
||||
else if (matchInfo.isMatchRequiringConversion()) {
|
||||
if (matchRequiringConversion != null) {
|
||||
multipleOptions = true;
|
||||
else {
|
||||
// Take this as a close match if there isn't one already
|
||||
if (closeMatch == null) {
|
||||
closeMatch = method;
|
||||
}
|
||||
matchRequiringConversion = method;
|
||||
}
|
||||
}
|
||||
else if (matchInfo.isMatchRequiringConversion()) {
|
||||
if (matchRequiringConversion != null) {
|
||||
multipleOptions = true;
|
||||
}
|
||||
matchRequiringConversion = method;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (closeMatch != null) {
|
||||
|
||||
+4
-3
@@ -120,9 +120,10 @@ public class StandardTypeLocator implements TypeLocator {
|
||||
return cachedType;
|
||||
}
|
||||
Class<?> loadedType = loadType(typeName);
|
||||
if (loadedType != null &&
|
||||
!(this.classLoader instanceof SmartClassLoader scl && scl.isClassReloadable(loadedType))) {
|
||||
this.typeCache.put(typeName, loadedType);
|
||||
if (loadedType != null) {
|
||||
if (!(this.classLoader instanceof SmartClassLoader scl && scl.isClassReloadable(loadedType))) {
|
||||
this.typeCache.put(typeName, loadedType);
|
||||
}
|
||||
return loadedType;
|
||||
}
|
||||
throw new SpelEvaluationException(SpelMessage.TYPE_NOT_FOUND, typeName);
|
||||
|
||||
+3
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,7 +23,6 @@ import org.junit.jupiter.api.Test;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.TypeComparator;
|
||||
import org.springframework.expression.spel.support.StandardTypeComparator;
|
||||
import org.springframework.lang.NonNull;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -134,6 +133,7 @@ public class StandardTypeComparatorTests {
|
||||
assertThat(comparator.compare(t2, t1)).isPositive();
|
||||
}
|
||||
|
||||
|
||||
static class ComparableType implements Comparable<ComparableType> {
|
||||
|
||||
private final int id;
|
||||
@@ -143,10 +143,9 @@ public class StandardTypeComparatorTests {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NonNull ComparableType other) {
|
||||
public int compareTo(ComparableType other) {
|
||||
return this.id - other.id;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -60,7 +60,7 @@ import org.springframework.util.LinkedCaseInsensitiveMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* <b>This is the central class in the JDBC core package.</b>
|
||||
* <b>This is the central delegate in the JDBC core package.</b>
|
||||
* It simplifies the use of JDBC and helps to avoid common errors.
|
||||
* It executes core JDBC workflow, leaving application code to provide SQL
|
||||
* and extract results. This class executes SQL queries or updates, initiating
|
||||
@@ -1177,7 +1177,7 @@ public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
|
||||
Assert.notNull(action, "Callback object must not be null");
|
||||
if (logger.isDebugEnabled()) {
|
||||
String sql = getSql(csc);
|
||||
logger.debug("Calling stored procedure" + (sql != null ? " [" + sql + "]" : ""));
|
||||
logger.debug("Calling stored procedure" + (sql != null ? " [" + sql + "]" : ""));
|
||||
}
|
||||
|
||||
Connection con = DataSourceUtils.getConnection(obtainDataSource());
|
||||
|
||||
+20
-20
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 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.
|
||||
@@ -43,7 +43,7 @@ public interface CallMetaDataProvider {
|
||||
|
||||
/**
|
||||
* Initialize the database specific management of procedure column meta-data.
|
||||
* This is only called for databases that are supported. This initialization
|
||||
* <p>This is only called for databases that are supported. This initialization
|
||||
* can be turned off by specifying that column meta-data should not be used.
|
||||
* @param databaseMetaData used to retrieve database specific information
|
||||
* @param catalogName name of catalog to use (or {@code null} if none)
|
||||
@@ -55,30 +55,36 @@ public interface CallMetaDataProvider {
|
||||
void initializeWithProcedureColumnMetaData(DatabaseMetaData databaseMetaData, @Nullable String catalogName,
|
||||
@Nullable String schemaName, @Nullable String procedureName) throws SQLException;
|
||||
|
||||
/**
|
||||
* Get the call parameter meta-data that is currently used.
|
||||
* @return a List of {@link CallParameterMetaData}
|
||||
*/
|
||||
List<CallParameterMetaData> getCallParameterMetaData();
|
||||
|
||||
/**
|
||||
* Provide any modification of the procedure name passed in to match the meta-data currently used.
|
||||
* This could include altering the case.
|
||||
* <p>This could include altering the case.
|
||||
*/
|
||||
@Nullable
|
||||
String procedureNameToUse(@Nullable String procedureName);
|
||||
|
||||
/**
|
||||
* Provide any modification of the catalog name passed in to match the meta-data currently used.
|
||||
* This could include altering the case.
|
||||
* <p>This could include altering the case.
|
||||
*/
|
||||
@Nullable
|
||||
String catalogNameToUse(@Nullable String catalogName);
|
||||
|
||||
/**
|
||||
* Provide any modification of the schema name passed in to match the meta-data currently used.
|
||||
* This could include altering the case.
|
||||
* <p>This could include altering the case.
|
||||
*/
|
||||
@Nullable
|
||||
String schemaNameToUse(@Nullable String schemaName);
|
||||
|
||||
/**
|
||||
* Provide any modification of the catalog name passed in to match the meta-data currently used.
|
||||
* The returned value will be used for meta-data lookups. This could include altering the case
|
||||
* <p>The returned value will be used for meta-data lookups. This could include altering the case
|
||||
* used or providing a base catalog if none is provided.
|
||||
*/
|
||||
@Nullable
|
||||
@@ -86,7 +92,7 @@ public interface CallMetaDataProvider {
|
||||
|
||||
/**
|
||||
* Provide any modification of the schema name passed in to match the meta-data currently used.
|
||||
* The returned value will be used for meta-data lookups. This could include altering the case
|
||||
* <p>The returned value will be used for meta-data lookups. This could include altering the case
|
||||
* used or providing a base schema if none is provided.
|
||||
*/
|
||||
@Nullable
|
||||
@@ -94,7 +100,7 @@ public interface CallMetaDataProvider {
|
||||
|
||||
/**
|
||||
* Provide any modification of the column name passed in to match the meta-data currently used.
|
||||
* This could include altering the case.
|
||||
* <p>This could include altering the case.
|
||||
* @param parameterName name of the parameter of column
|
||||
*/
|
||||
@Nullable
|
||||
@@ -102,7 +108,7 @@ public interface CallMetaDataProvider {
|
||||
|
||||
/**
|
||||
* Create a default out parameter based on the provided meta-data.
|
||||
* This is used when no explicit parameter declaration has been made.
|
||||
* <p>This is used when no explicit parameter declaration has been made.
|
||||
* @param parameterName the name of the parameter
|
||||
* @param meta meta-data used for this call
|
||||
* @return the configured SqlOutParameter
|
||||
@@ -111,7 +117,7 @@ public interface CallMetaDataProvider {
|
||||
|
||||
/**
|
||||
* Create a default in/out parameter based on the provided meta-data.
|
||||
* This is used when no explicit parameter declaration has been made.
|
||||
* <p>This is used when no explicit parameter declaration has been made.
|
||||
* @param parameterName the name of the parameter
|
||||
* @param meta meta-data used for this call
|
||||
* @return the configured SqlInOutParameter
|
||||
@@ -120,7 +126,7 @@ public interface CallMetaDataProvider {
|
||||
|
||||
/**
|
||||
* Create a default in parameter based on the provided meta-data.
|
||||
* This is used when no explicit parameter declaration has been made.
|
||||
* <p>This is used when no explicit parameter declaration has been made.
|
||||
* @param parameterName the name of the parameter
|
||||
* @param meta meta-data used for this call
|
||||
* @return the configured SqlParameter
|
||||
@@ -142,7 +148,7 @@ public interface CallMetaDataProvider {
|
||||
|
||||
/**
|
||||
* Does this database support returning ResultSets as ref cursors to be retrieved with
|
||||
* {@link java.sql.CallableStatement#getObject(int)} for the specified column.
|
||||
* {@link java.sql.CallableStatement#getObject(int)} for the specified column?
|
||||
*/
|
||||
boolean isRefCursorSupported();
|
||||
|
||||
@@ -158,18 +164,12 @@ public interface CallMetaDataProvider {
|
||||
boolean isProcedureColumnMetaDataUsed();
|
||||
|
||||
/**
|
||||
* Should we bypass the return parameter with the specified name.
|
||||
* This allows the database specific implementation to skip the processing
|
||||
* Should we bypass the return parameter with the specified name?
|
||||
* <p>This allows the database specific implementation to skip the processing
|
||||
* for specific results returned by the database call.
|
||||
*/
|
||||
boolean byPassReturnParameter(String parameterName);
|
||||
|
||||
/**
|
||||
* Get the call parameter meta-data that is currently used.
|
||||
* @return a List of {@link CallParameterMetaData}
|
||||
*/
|
||||
List<CallParameterMetaData> getCallParameterMetaData();
|
||||
|
||||
/**
|
||||
* Does the database support the use of catalog name in procedure calls?
|
||||
*/
|
||||
|
||||
+5
-5
@@ -168,11 +168,6 @@ public class GenericCallMetaDataProvider implements CallMetaDataProvider {
|
||||
return identifierNameToUse(parameterName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean byPassReturnParameter(String parameterName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlParameter createDefaultOutParameter(String parameterName, CallParameterMetaData meta) {
|
||||
return new SqlOutParameter(parameterName, meta.getSqlType());
|
||||
@@ -213,6 +208,11 @@ public class GenericCallMetaDataProvider implements CallMetaDataProvider {
|
||||
return this.procedureColumnMetaDataUsed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean byPassReturnParameter(String parameterName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Specify whether the database supports the use of catalog name in procedure calls.
|
||||
|
||||
+5
-15
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 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.
|
||||
@@ -75,10 +75,10 @@ public abstract class AbstractDataSource implements DataSource {
|
||||
throw new UnsupportedOperationException("setLogWriter");
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Implementation of JDBC 4.0's Wrapper interface
|
||||
//---------------------------------------------------------------------
|
||||
@Override
|
||||
public Logger getParentLogger() {
|
||||
return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -95,14 +95,4 @@ public abstract class AbstractDataSource implements DataSource {
|
||||
return iface.isInstance(this);
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Implementation of JDBC 4.1's getParentLogger method
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public Logger getParentLogger() {
|
||||
return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+13
-23
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -104,6 +104,16 @@ public class DelegatingDataSource implements DataSource, InitializingBean {
|
||||
return obtainTargetDataSource().getConnection(username, password);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLoginTimeout() throws SQLException {
|
||||
return obtainTargetDataSource().getLoginTimeout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLoginTimeout(int seconds) throws SQLException {
|
||||
obtainTargetDataSource().setLoginTimeout(seconds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrintWriter getLogWriter() throws SQLException {
|
||||
return obtainTargetDataSource().getLogWriter();
|
||||
@@ -115,20 +125,10 @@ public class DelegatingDataSource implements DataSource, InitializingBean {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLoginTimeout() throws SQLException {
|
||||
return obtainTargetDataSource().getLoginTimeout();
|
||||
public Logger getParentLogger() {
|
||||
return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLoginTimeout(int seconds) throws SQLException {
|
||||
obtainTargetDataSource().setLoginTimeout(seconds);
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Implementation of JDBC 4.0's Wrapper interface
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T unwrap(Class<T> iface) throws SQLException {
|
||||
@@ -143,14 +143,4 @@ public class DelegatingDataSource implements DataSource, InitializingBean {
|
||||
return (iface.isInstance(this) || obtainTargetDataSource().isWrapperFor(iface));
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Implementation of JDBC 4.1's getParentLogger method
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public Logger getParentLogger() {
|
||||
return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -186,7 +186,7 @@ public class TransactionAwareDataSourceProxy extends DelegatingDataSource {
|
||||
// Allow for differentiating between the proxy and the raw Connection.
|
||||
StringBuilder sb = new StringBuilder("Transaction-aware proxy for target Connection ");
|
||||
if (this.target != null) {
|
||||
sb.append('[').append(this.target.toString()).append(']');
|
||||
sb.append('[').append(this.target).append(']');
|
||||
}
|
||||
else {
|
||||
sb.append(" from DataSource [").append(this.targetDataSource).append(']');
|
||||
|
||||
+3
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -61,7 +61,7 @@ public abstract class AbstractRoutingDataSource extends AbstractDataSource imple
|
||||
|
||||
/**
|
||||
* Specify the map of target DataSources, with the lookup key as key.
|
||||
* The mapped value can either be a corresponding {@link javax.sql.DataSource}
|
||||
* <p>The mapped value can either be a corresponding {@link javax.sql.DataSource}
|
||||
* instance or a data source name String (to be resolved via a
|
||||
* {@link #setDataSourceLookup DataSourceLookup}).
|
||||
* <p>The key can be of arbitrary type; this class implements the
|
||||
@@ -213,6 +213,7 @@ public abstract class AbstractRoutingDataSource extends AbstractDataSource imple
|
||||
return (iface.isInstance(this) || determineTargetDataSource().isWrapperFor(iface));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the current target DataSource. Determines the
|
||||
* {@link #determineCurrentLookupKey() current lookup key}, performs
|
||||
|
||||
+15
-14
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -87,8 +87,8 @@ class SQLErrorCodeSQLExceptionTranslatorTests {
|
||||
SQLException dupKeyEx = new SQLException("", "", 10);
|
||||
DataAccessException dataAccessException = translator.translate("task", "SQL", dupKeyEx);
|
||||
assertThat(dataAccessException)
|
||||
.isInstanceOf(DataIntegrityViolationException.class)
|
||||
.hasCause(dupKeyEx);
|
||||
.isInstanceOf(DataIntegrityViolationException.class)
|
||||
.hasCause(dupKeyEx);
|
||||
|
||||
// Test fallback. We assume that no database will ever return this error code,
|
||||
// but 07xxx will be bad grammar picked up by the fallback SQLState translator
|
||||
@@ -102,8 +102,8 @@ class SQLErrorCodeSQLExceptionTranslatorTests {
|
||||
SQLException sqlException = new SQLException("", "", errorCode);
|
||||
DataAccessException dataAccessException = this.translator.translate("", "", sqlException);
|
||||
assertThat(dataAccessException)
|
||||
.isInstanceOf(expectedType)
|
||||
.hasCause(sqlException);
|
||||
.isInstanceOf(expectedType)
|
||||
.hasCause(sqlException);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -122,8 +122,8 @@ class SQLErrorCodeSQLExceptionTranslatorTests {
|
||||
DataTruncation dataTruncation = new DataTruncation(1, true, true, 1, 1, dataAccessEx);
|
||||
DataAccessException dataAccessException = translator.translate("task", "SQL", dataTruncation);
|
||||
assertThat(dataAccessException)
|
||||
.isInstanceOf(DataAccessResourceFailureException.class)
|
||||
.hasCause(dataTruncation);
|
||||
.isInstanceOf(DataAccessResourceFailureException.class)
|
||||
.hasCause(dataTruncation);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -153,8 +153,8 @@ class SQLErrorCodeSQLExceptionTranslatorTests {
|
||||
// Shouldn't custom translate this
|
||||
DataAccessException dataAccessException = translator.translate(TASK, SQL, integrityViolationEx);
|
||||
assertThat(dataAccessException)
|
||||
.isInstanceOf(DataIntegrityViolationException.class)
|
||||
.hasCause(integrityViolationEx);
|
||||
.isInstanceOf(DataIntegrityViolationException.class)
|
||||
.hasCause(integrityViolationEx);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -176,15 +176,15 @@ class SQLErrorCodeSQLExceptionTranslatorTests {
|
||||
SQLException badSqlEx = new SQLException("", "", 1);
|
||||
DataAccessException dataAccessException = translator.translate(TASK, SQL, badSqlEx);
|
||||
assertThat(dataAccessException)
|
||||
.isInstanceOf(CustomErrorCodeException.class)
|
||||
.hasCause(badSqlEx);
|
||||
.isInstanceOf(CustomErrorCodeException.class)
|
||||
.hasCause(badSqlEx);
|
||||
|
||||
// Shouldn't custom translate this
|
||||
SQLException invResEx = new SQLException("", "", 3);
|
||||
dataAccessException = translator.translate(TASK, SQL, invResEx);
|
||||
assertThat(dataAccessException)
|
||||
.isInstanceOf(DataIntegrityViolationException.class)
|
||||
.hasCause(invResEx);
|
||||
.isInstanceOf(DataIntegrityViolationException.class)
|
||||
.hasCause(invResEx);
|
||||
|
||||
// Shouldn't custom translate this - invalid class
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> customTranslation.setExceptionClass(String.class));
|
||||
@@ -209,7 +209,8 @@ class SQLErrorCodeSQLExceptionTranslatorTests {
|
||||
|
||||
reset(dataSource);
|
||||
given(dataSource.getConnection()).willReturn(connection);
|
||||
assertThat(translator.translate("test", null, duplicateKeyException)).isInstanceOf(DuplicateKeyException.class);
|
||||
assertThat(translator.translate("test", null, duplicateKeyException))
|
||||
.isInstanceOf(DuplicateKeyException.class);
|
||||
|
||||
verify(connection).close();
|
||||
}
|
||||
|
||||
@@ -88,6 +88,7 @@ public abstract class SharedEntityManagerCreator {
|
||||
"getResultStream", // jakarta.persistence.Query.getResultStream()
|
||||
"getResultList", // jakarta.persistence.Query.getResultList()
|
||||
"list", // org.hibernate.query.Query.list()
|
||||
"scroll", // org.hibernate.query.Query.scroll()
|
||||
"stream", // org.hibernate.query.Query.stream()
|
||||
"uniqueResult", // org.hibernate.query.Query.uniqueResult()
|
||||
"uniqueResultOptional" // org.hibernate.query.Query.uniqueResultOptional()
|
||||
|
||||
+5
-5
@@ -353,6 +353,11 @@ public class PersistenceAnnotationBeanPostProcessor implements InstantiationAwar
|
||||
findInjectionMetadata(beanDefinition, beanType, beanName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetBeanDefinition(String beanName) {
|
||||
this.injectionMetadataCache.remove(beanName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) {
|
||||
Class<?> beanClass = registeredBean.getBeanClass();
|
||||
@@ -373,11 +378,6 @@ public class PersistenceAnnotationBeanPostProcessor implements InstantiationAwar
|
||||
return metadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetBeanDefinition(String beanName) {
|
||||
this.injectionMetadataCache.remove(beanName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
|
||||
InjectionMetadata metadata = findPersistenceMetadata(beanName, bean.getClass(), pvs);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user