mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
221 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2c18c33ce0 | |||
| 83667f808c | |||
| 7a8917b137 | |||
| 7baa86536f | |||
| 12b44f2545 | |||
| e8f10244e3 | |||
| a1826b725c | |||
| 7add5243b9 | |||
| 9bec52b1ec | |||
| fe68e337b2 | |||
| 322eef2942 | |||
| ba0a9b8923 | |||
| 8a192eab1f | |||
| 45812cb9d0 | |||
| 95bd3f7c67 | |||
| 6ca66afc7b | |||
| 1b32d8d41d | |||
| b4a378186f | |||
| 220fcaa1e3 | |||
| 80534f9df6 | |||
| d8bc54d2e7 | |||
| 6467fca05b | |||
| cddc671a8c | |||
| 2e653246b5 | |||
| 86a68a77c4 | |||
| fe27ad24d5 | |||
| ecc847c493 | |||
| c2191e3ce2 | |||
| 175c1f9437 | |||
| 0b77c3df45 | |||
| 6985d00fce | |||
| 1e45ad8fa6 | |||
| 6fd45c118d | |||
| f9b75b02da | |||
| 84efa46e09 | |||
| b3dfba8719 | |||
| 2782a2701c | |||
| af2b96192d | |||
| 121c0ac285 | |||
| 3e585830d7 | |||
| b95caa8331 | |||
| c17939ed5c | |||
| f3bfe27445 | |||
| 7651d5841f | |||
| 6e122d3aaa | |||
| f7be796c1c | |||
| 1e843fd3ec | |||
| 4bb20ea8db | |||
| ac24766510 | |||
| d5e85bd95e | |||
| c3c96b9f32 | |||
| 4d98ff5dcf | |||
| 48262b8efe | |||
| ca32ac5e7f | |||
| 63ccc9d9cc | |||
| 8fe1de4595 | |||
| c048074436 | |||
| b7882d703c | |||
| ca8a1ea375 | |||
| cfb8dc6cc8 | |||
| 665c9ad7df | |||
| d3152c11c7 | |||
| 856e1d5dc8 | |||
| 987d6cca6d | |||
| ec21dc0f91 | |||
| 628261d5bb | |||
| 715d33f001 | |||
| bb5142164e | |||
| 25b8d64cd0 | |||
| d72da90d3a | |||
| bff9899905 | |||
| 41cd6879bd | |||
| 3a91d90c28 | |||
| 08c5280843 | |||
| cd5fee5347 | |||
| 916cb64581 | |||
| b3ef834ae6 | |||
| a9d344b3ed | |||
| 39a746d30f | |||
| 952198b2ee | |||
| 8d93670430 | |||
| 29a7402adf | |||
| 3dfd6838c0 | |||
| 32170e5847 | |||
| bfb88cfc1c | |||
| c4dfb24116 | |||
| 4fb0244b5a | |||
| 0725bf4941 | |||
| 8965d9bccf | |||
| af7a5716e8 | |||
| 83e7aa1131 | |||
| 865cf3c117 | |||
| be70e1c5a7 | |||
| 91852546d9 | |||
| 802fa4d65e | |||
| 7052da4532 | |||
| 63817ce202 | |||
| 61bd79017f | |||
| ab6637c670 | |||
| c9b88b4ebd | |||
| 68c575ab14 | |||
| cb320468db | |||
| df828458fa | |||
| ff3f2937ab | |||
| d7450ce8e4 | |||
| 28f78f435e | |||
| a3705632c1 | |||
| 84221064c8 | |||
| 21f3b964fe | |||
| efb85d68ef | |||
| b3f01a7f92 | |||
| 22bcac1eb5 | |||
| 546ae15a44 | |||
| 78c32aa7d2 | |||
| 99b78adce3 | |||
| 644731c9f6 | |||
| e4d03f6625 | |||
| 6251b2c0c9 | |||
| 3d211b71b1 | |||
| 18b8f871aa | |||
| d7ddfe8d2e | |||
| 1687d90a8c | |||
| b8f1005897 | |||
| 0150c4ba06 | |||
| b560c7b85d | |||
| 0f05a2e153 | |||
| d4cc273c31 | |||
| f3b6c222f9 | |||
| 8d390f4e5a | |||
| f5a8c321c0 | |||
| 14466eef4c | |||
| 9d365906b5 | |||
| 97e10a5948 | |||
| bfbfe4a572 | |||
| 38464a15dc | |||
| c04b502866 | |||
| f2c0ad484e | |||
| d10460d775 | |||
| 1ead8bf1ab | |||
| 7b087d1a6c | |||
| 00fbd91cca | |||
| d87ebbff78 | |||
| e977bd7bf0 | |||
| 24c7d31ba7 | |||
| b6fc3a1b6f | |||
| d767806977 | |||
| c5b9c3d604 | |||
| 40ef6f6872 | |||
| b01fdb0140 | |||
| 3f01e3fe11 | |||
| e78af58af3 | |||
| aebf7c97f7 | |||
| 7b8d8693dd | |||
| 22adccd190 | |||
| d4d49fd546 | |||
| 730f824cad | |||
| 2dcd56bbaf | |||
| 00be6d3afd | |||
| ebe3b45743 | |||
| 9c4b246c8c | |||
| f850b051b5 | |||
| c9c137ec51 | |||
| b7a1157e66 | |||
| 27bc60e077 | |||
| 0abf97ff90 | |||
| f0f447a73d | |||
| b81a63fb5c | |||
| 529a6fc932 | |||
| e964ced1ad | |||
| ff30329861 | |||
| 6d3d3d11c4 | |||
| 9140ed6fa9 | |||
| 6cea397bd8 | |||
| 484599ecbb | |||
| 1ff369d1ce | |||
| ede46647e8 | |||
| 7f7f7c5f5b | |||
| d1950f598a | |||
| a01dd1cd79 | |||
| 86924d067b | |||
| f0472ea1c0 | |||
| 4b211643bb | |||
| f92e810831 | |||
| 9fba394b8a | |||
| 0005ec09c2 | |||
| 0e800538c0 | |||
| 43b01382ed | |||
| 3a35c4dbd0 | |||
| 4c854d564c | |||
| de562deab0 | |||
| 0b2e3900b9 | |||
| e1e4d52b61 | |||
| 8ca0262e2f | |||
| 29ea530525 | |||
| ac10d079df | |||
| 87c4acd7ac | |||
| 2ce21f7654 | |||
| 9e6504b05a | |||
| 399f779c43 | |||
| a40ec44cb7 | |||
| 4357bbd31b | |||
| 4548f1ec66 | |||
| 9e71dc6029 | |||
| 0daa5ce631 | |||
| 8817536e5a | |||
| cdbaee11c1 | |||
| 1256307c83 | |||
| 4c14abf0cd | |||
| a81367f686 | |||
| a21706c58a | |||
| de913642fb | |||
| c2c6130da5 | |||
| df817364b2 | |||
| bc07a451dc | |||
| 5eb0e99d58 | |||
| 17699103dc | |||
| 9b366b28b7 | |||
| 0f990e44a8 | |||
| e102bc4933 | |||
| be1bf91ce8 | |||
| 877825b4ab |
@@ -2,7 +2,7 @@ name: Build and Deploy Snapshot
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- 7.0.x
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
jobs:
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
branch: [ '6.2.x', 'main' ]
|
||||
branch: [ '6.2.x', '7.0.x', 'main' ]
|
||||
steps:
|
||||
- uses: spring-io/spring-doc-actions/update-antora-spring-ui@5a57bcc6a0da2a1474136cf29571b277850432bc
|
||||
name: Update
|
||||
|
||||
@@ -9,6 +9,7 @@ ivy-cache
|
||||
argfile*
|
||||
activemq-data/
|
||||
classes/
|
||||
.cursor/
|
||||
|
||||
# Log files
|
||||
jxl.log
|
||||
|
||||
+4
-2
@@ -61,7 +61,9 @@ configure([rootProject] + javaProjects) { project ->
|
||||
testImplementation("org.junit.platform:junit-platform-suite")
|
||||
testImplementation("org.mockito:mockito-core")
|
||||
testImplementation("org.mockito:mockito-junit-jupiter")
|
||||
testImplementation("io.mockk:mockk")
|
||||
testImplementation("io.mockk:mockk") {
|
||||
exclude group: 'junit', module: 'junit'
|
||||
}
|
||||
testImplementation("org.assertj:assertj-core")
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
testRuntimeOnly("org.apache.logging.log4j:log4j-core")
|
||||
@@ -72,7 +74,7 @@ configure([rootProject] + javaProjects) { project ->
|
||||
//"https://jakarta.ee/specifications/platform/11/apidocs/",
|
||||
"https://docs.hibernate.org/orm/7.2/javadocs/",
|
||||
"https://www.quartz-scheduler.org/api/2.3.0/",
|
||||
"https://hc.apache.org/httpcomponents-client-5.6.x/current/httpclient5/apidocs/",
|
||||
"https://hc.apache.org/httpcomponents-client-5.6.x/5.6/httpclient5/apidocs/",
|
||||
"https://projectreactor.io/docs/core/release/api/",
|
||||
"https://projectreactor.io/docs/test/release/api/",
|
||||
"https://junit.org/junit4/javadoc/4.13.2/",
|
||||
|
||||
@@ -50,7 +50,7 @@ public class CheckstyleConventions {
|
||||
project.getPlugins().apply(CheckstylePlugin.class);
|
||||
project.getTasks().withType(Checkstyle.class).forEach(checkstyle -> checkstyle.getMaxHeapSize().set("1g"));
|
||||
CheckstyleExtension checkstyle = project.getExtensions().getByType(CheckstyleExtension.class);
|
||||
checkstyle.setToolVersion("13.3.0");
|
||||
checkstyle.setToolVersion("13.4.2");
|
||||
checkstyle.getConfigDirectory().set(project.getRootProject().file("src/checkstyle"));
|
||||
String version = SpringJavaFormatPlugin.class.getPackage().getImplementationVersion();
|
||||
DependencySet checkstyleDependencies = project.getConfigurations().getByName("checkstyle").getDependencies();
|
||||
|
||||
@@ -15,8 +15,8 @@ content:
|
||||
# https://docs.antora.org/antora/latest/playbook/content-refname-matching/
|
||||
# branches: We include snapshots for main, 6.2.x, and 7.0.x to 9.*.x.
|
||||
branches: ['main', '6.2.x', '{7..9}.+({0..9}).x']
|
||||
# tags: We effectively include all releases from 6.0.9 to 9.*.*.
|
||||
tags: ['v{6..9}.+({0..9}).+({0..9})?(-{RC,M}*)', '!(v6.0.{0..8})', '!(v6.0.0-{RC,M}{0..9})']
|
||||
# tags: include all releases from 6.2.0 to 9.*.*.
|
||||
tags: ['v6.2.+({0..9})', 'v{7..9}.+({0..9}).+({0..9})?(-{RC,M}*)']
|
||||
start_path: framework-docs
|
||||
asciidoc:
|
||||
extensions:
|
||||
@@ -38,4 +38,4 @@ runtime:
|
||||
failure_level: warn
|
||||
ui:
|
||||
bundle:
|
||||
url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.25/ui-bundle.zip
|
||||
url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.26/ui-bundle.zip
|
||||
|
||||
@@ -31,7 +31,7 @@ asciidoc:
|
||||
spring-org: 'spring-projects'
|
||||
spring-github-org: "https://github.com/{spring-org}"
|
||||
spring-framework-github: "https://github.com/{spring-org}/spring-framework"
|
||||
spring-framework-code: '{spring-framework-github}/tree/main'
|
||||
spring-framework-code: '{spring-framework-github}/tree/7.0.x'
|
||||
spring-framework-issues: '{spring-framework-github}/issues'
|
||||
spring-framework-wiki: '{spring-framework-github}/wiki'
|
||||
# Docs
|
||||
|
||||
@@ -20,6 +20,10 @@ antora {
|
||||
]
|
||||
}
|
||||
|
||||
node {
|
||||
version = '24.15.0'
|
||||
}
|
||||
|
||||
tasks.named("generateAntoraYml") {
|
||||
asciidocAttributes = project.provider( {
|
||||
return ["spring-version": project.version ]
|
||||
|
||||
@@ -198,6 +198,7 @@
|
||||
*** xref:web/webmvc/mvc-uri-building.adoc[]
|
||||
*** xref:web/webmvc/mvc-ann-async.adoc[]
|
||||
*** xref:web/webmvc/mvc-range.adoc[]
|
||||
*** xref:web/webmvc/mvc-data-binding.adoc[]
|
||||
*** xref:web/webmvc-cors.adoc[]
|
||||
*** xref:web/webmvc-versioning.adoc[]
|
||||
*** xref:web/webmvc/mvc-ann-rest-exceptions.adoc[]
|
||||
@@ -295,6 +296,7 @@
|
||||
*** xref:web/webflux-functional.adoc[]
|
||||
*** xref:web/webflux/uri-building.adoc[]
|
||||
*** xref:web/webflux/range.adoc[]
|
||||
*** xref:web/webflux/data-binding.adoc[]
|
||||
*** xref:web/webflux-cors.adoc[]
|
||||
*** xref:web/webflux-versioning.adoc[]
|
||||
*** xref:web/webflux/ann-rest-exceptions.adoc[]
|
||||
|
||||
@@ -74,6 +74,11 @@ expressions used in XML bean definitions, `@Value`, etc.
|
||||
| The mode to use when compiling expressions for the
|
||||
xref:core/expressions/evaluation.adoc#expressions-compiler-configuration[Spring Expression Language].
|
||||
|
||||
| `spring.expression.maxOperations`
|
||||
| The default maximum number of operations permitted during
|
||||
xref:core/expressions/evaluation.adoc#expressions-parser-configuration[Spring Expression Language]
|
||||
expression evaluation.
|
||||
|
||||
| `spring.getenv.ignore`
|
||||
| Instructs Spring to ignore operating system environment variables if a Spring
|
||||
`Environment` property -- for example, a placeholder in a configuration String -- isn't
|
||||
@@ -138,6 +143,11 @@ xref:testing/testcontext-framework/ctx-management/context-pausing.adoc[Context P
|
||||
in the _Spring TestContext Framework_. See
|
||||
xref:testing/testcontext-framework/ctx-management/failure-threshold.adoc[Context Failure Threshold].
|
||||
|
||||
| `spring.test.extension.context.scope`
|
||||
| The default _extension context scope_ used by the `SpringExtension` in `@Nested` test
|
||||
class hierarchies. See
|
||||
xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-springextensionconfig[`@SpringExtensionConfig`].
|
||||
|
||||
| `spring.test.enclosing.configuration`
|
||||
| The default _enclosing configuration inheritance mode_ to use if
|
||||
`@NestedTestConfiguration` is not present on a test class. See
|
||||
|
||||
@@ -378,7 +378,7 @@ The container also supports creating a bean with {spring-framework-api}++/beans/
|
||||
|
||||
. The custom arguments require dynamic introspection of a matching constructor or factory method.
|
||||
Those arguments cannot be detected by AOT, so the necessary reflection hints will have to be provided manually.
|
||||
. By-passing the instance supplier means that all other optimizations after creation are skipped as well.
|
||||
. Bypassing the instance supplier means that all other optimizations after creation are skipped as well.
|
||||
For instance, autowiring on fields and methods will be skipped as they are handled in the instance supplier.
|
||||
|
||||
Rather than having prototype-scoped beans created with custom arguments, we recommend a manual factory pattern where a bean is responsible for the creation of the instance.
|
||||
|
||||
+1
-1
@@ -274,7 +274,7 @@ Kotlin::
|
||||
Next, you can provide the information for the candidate bean definitions. You can add
|
||||
`<qualifier/>` tags as sub-elements of the `<bean/>` tag and then specify the `type` and
|
||||
`value` to match your custom qualifier annotations. The type is matched against the
|
||||
fully-qualified class name of the annotation. Alternately, as a convenience if no risk of
|
||||
fully-qualified class name of the annotation. Alternatively, as a convenience if no risk of
|
||||
conflicting names exists, you can use the short class name. The following example
|
||||
demonstrates both approaches:
|
||||
|
||||
|
||||
@@ -575,7 +575,7 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
NOTE: Do not define such beans to be lazy as the `ApplicationContext` will honour that and will not register the method to listen to events.
|
||||
NOTE: Do not define such beans to be lazy as the `ApplicationContext` will honor that and will not register the method to listen to events.
|
||||
|
||||
The method signature once again declares the event type to which it listens,
|
||||
but, this time, with a flexible name and without implementing a specific listener interface.
|
||||
|
||||
+13
@@ -610,6 +610,19 @@ Kotlin::
|
||||
See the {spring-framework-api}/context/annotation/Conditional.html[`@Conditional`]
|
||||
javadoc for more detail.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
A `@Conditional` annotation declared on an enclosing `@Configuration` class is only
|
||||
applied to the registration of a nested `@Configuration` class if the nested class is
|
||||
reached through the parser's recursion from its enclosing class, or via `@Import`. If a
|
||||
nested class is discovered independently of its enclosing class — for example, via
|
||||
`@ComponentScan` or by directly registering it against the application context — it is
|
||||
processed using only its own `@Conditional` annotations. Thus, if you wish to ensure that
|
||||
the same `@Conditional` annotations apply in such scenarios, you must redeclare the
|
||||
relevant annotations on the nested class, or extract them into a composed annotation
|
||||
which you apply to both the enclosing class and the nested class.
|
||||
====
|
||||
|
||||
|
||||
[[beans-java-combining]]
|
||||
== Combining Java and XML Configuration
|
||||
|
||||
@@ -395,6 +395,16 @@ set a JVM system property or Spring property named `spring.context.expression.ma
|
||||
to the maximum expression length needed by your application (see
|
||||
xref:appendix.adoc#appendix-spring-properties[Supported Spring Properties]).
|
||||
|
||||
Similarly, the number of operations performed during the evaluation of a SpEL expression
|
||||
cannot exceed 10,000 by default; however, the `maxOperations` value is configurable. If
|
||||
you create a `SpelExpressionParser` programmatically (the recommend approach), you can
|
||||
specify a custom `maxOperations` value when creating the `SpelParserConfiguration` that
|
||||
you provide to the `SpelExpressionParser`. If you are not able to configure an explicit
|
||||
value for `maxOperations` via `SpelParserConfiguration`, you can set a JVM system
|
||||
property or Spring property named `spring.expression.maxOperations` to the maximum number
|
||||
of operations required by your application (see
|
||||
xref:appendix.adoc#appendix-spring-properties[Supported Spring Properties]).
|
||||
|
||||
|
||||
[[expressions-spel-compilation]]
|
||||
== SpEL Compilation
|
||||
|
||||
@@ -2,4 +2,12 @@
|
||||
= Language Reference
|
||||
:page-section-summary-toc: 1
|
||||
|
||||
This section describes how the Spring Expression Language works.
|
||||
Spring Expression Language (SpEL) expressions are composed of a sequence of tokens such
|
||||
as literals, operators, method invocations, and so forth.
|
||||
|
||||
Whitespace can be used freely between tokens to format and improve the readability of
|
||||
expressions. Specifically, the `\s` (space), `\t` (tab), `\r` (carriage return), and `\n`
|
||||
(newline) characters are all valid separators between tokens. However, whitespace is
|
||||
ignored by the expression parser unless it is part of a string literal.
|
||||
|
||||
The following sections describe the features and syntax of SpEL.
|
||||
|
||||
+1
-1
@@ -270,7 +270,7 @@ is applicable for typical implementations of indexed structures.
|
||||
NOTE: `ReflectiveIndexAccessor` also implements `CompilableIndexAccessor` in order to
|
||||
support xref:core/expressions/evaluation.adoc#expressions-spel-compilation[compilation]
|
||||
to bytecode for read access. Note, however, that the configured read-method must be
|
||||
invokable via a `public` class or `public` interface for compilation to succeed.
|
||||
invocable via a `public` class or `public` interface for compilation to succeed.
|
||||
|
||||
The following code listings define a `Color` enum and `FruitMap` type that behaves like a
|
||||
map but does not implement the `java.util.Map` interface. Thus, if you want to index into
|
||||
|
||||
@@ -25,7 +25,8 @@ public void sendNotification() {
|
||||
|
||||
By default, the method invocation will be retried for any exception thrown: with at most
|
||||
3 retry attempts (`maxRetries = 3`) after an initial failure, and a delay of 1 second
|
||||
between attempts.
|
||||
between attempts. If all attempts have failed and the retry policy has been exhausted,
|
||||
the last original exception from the target method will be propagated to the caller.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
@@ -69,12 +70,12 @@ Or for 4 retry attempts and an exponential back-off strategy with a bit of jitte
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@Retryable(
|
||||
includes = MessageDeliveryException.class,
|
||||
maxRetries = 4,
|
||||
delay = 100,
|
||||
jitter = 10,
|
||||
multiplier = 2,
|
||||
maxDelay = 1000)
|
||||
includes = MessageDeliveryException.class,
|
||||
maxRetries = 4,
|
||||
delay = 100,
|
||||
jitter = 10,
|
||||
multiplier = 2,
|
||||
maxDelay = 1000)
|
||||
public void sendNotification() {
|
||||
this.jmsClient.destination("notifications").send(...);
|
||||
}
|
||||
@@ -99,6 +100,13 @@ TIP: Several attributes in `@Retryable` have `String` variants that provide prop
|
||||
placeholder and SpEL support, as an alternative to the specifically typed annotation
|
||||
attributes used in the above examples.
|
||||
|
||||
[TIP]
|
||||
====
|
||||
During `@Retryable` processing, Spring publishes a `MethodRetryEvent` for every exception
|
||||
coming out of the target method. This can be used to track/log all original exceptions
|
||||
whereas the caller of the `@Retryable` method will only ever see the last exception.
|
||||
====
|
||||
|
||||
|
||||
[[resilience-annotations-concurrencylimit]]
|
||||
== `@ConcurrencyLimit`
|
||||
@@ -174,7 +182,7 @@ configured {spring-framework-api}/core/retry/RetryPolicy.html[`RetryPolicy`].
|
||||
----
|
||||
var retryTemplate = new RetryTemplate(); // <1>
|
||||
|
||||
retryTemplate.execute(
|
||||
retryTemplate.invoke(
|
||||
() -> jmsClient.destination("notifications").send(...));
|
||||
----
|
||||
<1> Implicitly uses `RetryPolicy.withDefaults()`.
|
||||
@@ -200,7 +208,7 @@ If you only need to customize the number of retry attempts, you can use the
|
||||
----
|
||||
var retryTemplate = new RetryTemplate(RetryPolicy.withMaxRetries(4)); // <1>
|
||||
|
||||
retryTemplate.execute(
|
||||
retryTemplate.invoke(
|
||||
() -> jmsClient.destination("notifications").send(...));
|
||||
----
|
||||
<1> Explicitly uses `RetryPolicy.withMaxRetries(4)`.
|
||||
@@ -218,7 +226,7 @@ matched against an exception thrown by a failed operation as well as nested caus
|
||||
|
||||
var retryTemplate = new RetryTemplate(retryPolicy);
|
||||
|
||||
retryTemplate.execute(
|
||||
retryTemplate.invoke(
|
||||
() -> jmsClient.destination("notifications").send(...));
|
||||
----
|
||||
<1> Specify one or more exception types to include.
|
||||
@@ -251,21 +259,101 @@ and an exponential back-off strategy with a bit of jitter.
|
||||
|
||||
var retryTemplate = new RetryTemplate(retryPolicy);
|
||||
|
||||
retryTemplate.execute(
|
||||
retryTemplate.invoke(
|
||||
() -> jmsClient.destination("notifications").send(...));
|
||||
----
|
||||
|
||||
[TIP]
|
||||
====
|
||||
A {spring-framework-api}/core/retry/RetryListener.html[`RetryListener`] can be registered
|
||||
with a `RetryTemplate` to react to events published during key retry phases (before a
|
||||
retry attempt, after a retry attempt, etc.), and you can compose multiple listeners via a
|
||||
{spring-framework-api}/core/retry/support/CompositeRetryListener.html[`CompositeRetryListener`].
|
||||
====
|
||||
|
||||
Although the factory methods and builder API for `RetryPolicy` cover most common
|
||||
configuration scenarios, you can implement a custom `RetryPolicy` for complete control
|
||||
over the types of exceptions that should trigger a retry as well as the
|
||||
{spring-framework-api}/util/backoff/BackOff.html[`BackOff`] strategy to use. Note that you
|
||||
can also configure a customized `BackOff` strategy via the `backOff()` method in the
|
||||
`RetryPolicy.Builder`.
|
||||
{spring-framework-api}/util/backoff/BackOff.html[`BackOff`] strategy to use. Note that
|
||||
you can also configure a customized `BackOff` strategy via the `backOff()` method in
|
||||
the `RetryPolicy.Builder`.
|
||||
====
|
||||
|
||||
Note that the examples above apply a pattern similar to `@Retryable` method invocations
|
||||
where the last original exception will be propagated to the caller, using the `invoke`
|
||||
variants on `RetryTemplate` which are available with and without a return value.
|
||||
The callback may throw unchecked exceptions, the last one of which is exposed for
|
||||
direct handling on the caller side:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
try {
|
||||
retryTemplate.invoke(
|
||||
() -> jmsClient.destination("notifications").send(...));
|
||||
}
|
||||
catch (MessageDeliveryException ex) {
|
||||
// coming out of the original JmsClient send method
|
||||
}
|
||||
----
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
try {
|
||||
var result = retryTemplate.invoke(() -> {
|
||||
jmsClient.destination("notifications").send(...);
|
||||
return "result";
|
||||
});
|
||||
}
|
||||
catch (MessageDeliveryException ex) {
|
||||
// coming out of the original JmsClient send method
|
||||
}
|
||||
----
|
||||
|
||||
`RetryTemplate` instances are very light and can be created on the fly,
|
||||
potentially with a specific retry policy to use for a given invocation:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
try {
|
||||
new RetryTemplate(RetryPolicy.withMaxRetries(4)).invoke(
|
||||
() -> jmsClient.destination("notifications").send(...));
|
||||
}
|
||||
catch (MessageDeliveryException ex) {
|
||||
// coming out of the original JmsClient send method
|
||||
}
|
||||
----
|
||||
|
||||
For deeper interaction, you may use RetryTemplate's `execute` method. The caller will
|
||||
have to handle the checked `RetryException` thrown by `RetryTemplate`, exposing the
|
||||
outcome of all attempts:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
try {
|
||||
var result = retryTemplate.execute(() -> {
|
||||
jmsClient.destination("notifications").send(...);
|
||||
return "result";
|
||||
});
|
||||
}
|
||||
catch (RetryException ex) {
|
||||
// ex.getExceptions() / ex.getLastException() ...
|
||||
}
|
||||
----
|
||||
|
||||
A {spring-framework-api}/core/retry/RetryListener.html[`RetryListener`] can be registered
|
||||
with a `RetryTemplate` to react to key retry steps (before or after a retry attempt etc.)
|
||||
or simply to every invocation attempt, being able to track all exceptions coming out of
|
||||
the callback and all retry outcomes (exhaustion, interruption, timeout). This is
|
||||
particularly useful when using `invoke` where no retry state other than the last
|
||||
original exception is exposed otherwise:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
var retryTemplate = new RetryTemplate();
|
||||
retryTemplate.setRetryListener(new RetryListener() {
|
||||
@Override
|
||||
public void onRetryableExecution(RetryPolicy retryPolicy, Retryable<?> retryable, RetryState retryState) {
|
||||
...
|
||||
}
|
||||
});
|
||||
|
||||
retryTemplate.invoke(
|
||||
() -> jmsClient.destination("notifications").send(...));
|
||||
----
|
||||
|
||||
You can also compose multiple listeners via a
|
||||
{spring-framework-api}/core/retry/support/CompositeRetryListener.html[`CompositeRetryListener`].
|
||||
|
||||
@@ -347,10 +347,10 @@ recognized and used as the `PropertyEditor` for `Something`-typed properties.
|
||||
[literal,subs="verbatim,quotes"]
|
||||
----
|
||||
com
|
||||
chank
|
||||
pop
|
||||
Something
|
||||
SomethingEditor // the PropertyEditor for the Something class
|
||||
└── example
|
||||
└── things
|
||||
├── *Something*
|
||||
└── *SomethingEditor* // the PropertyEditor for the Something class
|
||||
----
|
||||
|
||||
Note that you can also use the standard `BeanInfo` JavaBeans mechanism here as well
|
||||
@@ -362,10 +362,10 @@ following example uses the `BeanInfo` mechanism to explicitly register one or mo
|
||||
[literal,subs="verbatim,quotes"]
|
||||
----
|
||||
com
|
||||
chank
|
||||
pop
|
||||
Something
|
||||
SomethingBeanInfo // the BeanInfo for the Something class
|
||||
└── example
|
||||
└── things
|
||||
├── *Something*
|
||||
└── *SomethingBeanInfo* // the BeanInfo for the Something class
|
||||
----
|
||||
|
||||
The following Java source code for the referenced `SomethingBeanInfo` class
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ where all the underlying resources have to participate in the service-level tran
|
||||
|
||||
NOTE: By default, a participating transaction joins the characteristics of the outer scope,
|
||||
silently ignoring the local isolation level, timeout value, or read-only flag (if any).
|
||||
Consider switching the `validateExistingTransactions` flag to `true` on your transaction
|
||||
Consider switching the `validateExistingTransaction` flag to `true` on your transaction
|
||||
manager if you want isolation level declarations to be rejected when participating in
|
||||
an existing transaction with a different isolation level. This non-lenient mode also
|
||||
rejects read-only mismatches (that is, an inner read-write transaction that tries to participate
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
[[observability]]
|
||||
= Observability Support
|
||||
|
||||
With https://docs.micrometer.io/micrometer/reference/concepts.html[Micrometer], developers can instrument libraries and applications for metrics (timers, gauges, counters)
|
||||
that collect statistics about their runtime behavior. Metrics can help you to track error rates, usage patterns, performance, and more.
|
||||
https://docs.micrometer.io/tracing/reference/[Micrometer can also produce traces], giving you a holistic view of an entire system, crossing application boundaries; you can zoom in on particular user requests and follow their entire completion across applications.
|
||||
|
||||
Micrometer defines an {micrometer-docs}/observation.html[Observation concept that enables both Metrics and Traces] in applications.
|
||||
Metrics support offers a way to create timers, gauges, or counters for collecting statistics about the runtime behavior of your application.
|
||||
Metrics can help you to track error rates, usage patterns, performance, and more.
|
||||
Traces provide a holistic view of an entire system, crossing application boundaries; you can zoom in on particular user requests and follow their entire completion across applications.
|
||||
Each observation will produce:
|
||||
|
||||
* https://docs.micrometer.io/micrometer/reference/observation/components.html#micrometer-observation-default-meter-handler[several metrics - a timer, a long task timer and many counters]
|
||||
* a https://docs.micrometer.io/tracing/reference/glossary.html[span for the current trace]
|
||||
|
||||
Spring Framework instruments various parts of its own codebase to publish observations if an `ObservationRegistry` is configured.
|
||||
You can learn more about {spring-boot-docs-ref}/actuator/observability.html[configuring the observability infrastructure in Spring Boot].
|
||||
|
||||
@@ -1179,7 +1179,7 @@ built-in decorators to suppress 404 exceptions and return a `ResponseEntity` wit
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
// For RestClient
|
||||
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(restCqlientAdapter)
|
||||
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(restClientAdapter)
|
||||
.exchangeAdapterDecorator(NotFoundRestClientAdapterDecorator::new)
|
||||
.build();
|
||||
|
||||
|
||||
+22
-13
@@ -3,7 +3,7 @@
|
||||
|
||||
The following annotations are supported when used in conjunction with the
|
||||
xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit-jupiter-extension[`SpringExtension`]
|
||||
and JUnit Jupiter (that is, the programming model in JUnit):
|
||||
and the JUnit Jupiter testing framework:
|
||||
|
||||
* xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-springextensionconfig[`@SpringExtensionConfig`]
|
||||
* xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-junit-jupiter-springjunitconfig[`@SpringJUnitConfig`]
|
||||
@@ -30,30 +30,39 @@ developer wishes to switch to test-class scoped semantics — the `SpringExtensi
|
||||
configured to use a test-class scoped `ExtensionContext` by annotating a top-level test
|
||||
class with `@SpringExtensionConfig(useTestClassScopedExtensionContext = true)`.
|
||||
|
||||
Alternatively, you can change the global default by setting the
|
||||
`spring.test.extension.context.scope` property to `test_class`. The property is resolved
|
||||
first via the xref:appendix.adoc#appendix-spring-properties[`SpringProperties`] mechanism
|
||||
which also supports JVM system properties — for example,
|
||||
`-Dspring.test.extension.context.scope=test_class`. If the Spring property has not been
|
||||
set, the `SpringExtension` will attempt to resolve the property as a
|
||||
https://docs.junit.org/current/running-tests/configuration-parameters.html[JUnit Platform configuration parameter]
|
||||
as a fallback mechanism. If the property has not been set via either of those mechanisms,
|
||||
the `SpringExtension` will use a test-method scoped extension context by default. Note,
|
||||
however, that a `@SpringExtensionConfig` declaration always takes precedence over this
|
||||
property.
|
||||
|
||||
[TIP]
|
||||
====
|
||||
If your top-level test class is configured to use JUnit Jupiter's
|
||||
`@TestInstance(Lifecycle.PER_CLASS)` semantics, the `SpringExtension` will always use a
|
||||
test-class scoped `ExtensionContext`, and there is no need to declare
|
||||
`@SpringExtensionConfig(useTestClassScopedExtensionContext = true)`.
|
||||
If a test class uses JUnit Jupiter's `@TestInstance(Lifecycle.PER_CLASS)` semantics, the
|
||||
`SpringExtension` will always use a test-class scoped `ExtensionContext`, and
|
||||
configuration via `@SpringExtensionConfig(useTestClassScopedExtensionContext = true)` or
|
||||
the `spring.test.extension.context.scope` property will have no effect for that test
|
||||
class.
|
||||
====
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
This annotation is currently only applicable to `@Nested` test class hierarchies and
|
||||
should be applied to the top-level enclosing class of a `@Nested` test class hierarchy.
|
||||
|
||||
Consequently, there is no need to declare this annotation on a test class that does not
|
||||
contain `@Nested` test classes.
|
||||
====
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
In addition,
|
||||
xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-nestedtestconfiguration[`@NestedTestConfiguration`]
|
||||
does not apply to this annotation.
|
||||
|
||||
`@SpringExtensionConfig` will always be detected within a `@Nested` test class hierarchy,
|
||||
effectively disregarding any `@NestedTestConfiguration(OVERRIDE)` declarations.
|
||||
does not apply to this annotation. `@SpringExtensionConfig` will always be detected
|
||||
within a `@Nested` test class hierarchy, effectively disregarding any
|
||||
`@NestedTestConfiguration(OVERRIDE)` declarations.
|
||||
====
|
||||
|
||||
[[integration-testing-annotations-junit-jupiter-springjunitconfig]]
|
||||
|
||||
+7
@@ -72,6 +72,13 @@ bean definition profiles programmatically by implementing a custom
|
||||
xref:testing/testcontext-framework/ctx-management/env-profiles.adoc#testcontext-ctx-management-env-profiles-ActiveProfilesResolver[`ActiveProfilesResolver`]
|
||||
and registering it by using the `resolver` attribute of `@ActiveProfiles`.
|
||||
|
||||
NOTE: When `@ActiveProfiles` is declared on a test class, the `spring.profiles.active`
|
||||
property (whether configured as a JVM system property or environment variable) is not
|
||||
taken into account by the TestContext Framework when determining active profiles. If
|
||||
you need to allow `spring.profiles.active` to override the profiles configured via
|
||||
`@ActiveProfiles`, you can implement a custom `ActiveProfilesResolver` as described in
|
||||
xref:testing/testcontext-framework/ctx-management/env-profiles.adoc[Context Configuration with Environment Profiles].
|
||||
|
||||
See xref:testing/testcontext-framework/ctx-management/env-profiles.adoc[Context Configuration with Environment Profiles],
|
||||
xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit-jupiter-nested-test-configuration[`@Nested` test class configuration], and the
|
||||
{spring-framework-api}/test/context/ActiveProfiles.html[`@ActiveProfiles`] javadoc for
|
||||
|
||||
+143
@@ -140,6 +140,21 @@ Java::
|
||||
}
|
||||
----
|
||||
<1> Replace the bean with type `CustomService` with a Mockito mock.
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@SpringJUnitConfig(TestConfig::class)
|
||||
class BeanOverrideTests {
|
||||
|
||||
@MockitoBean // <1>
|
||||
lateinit var customService: CustomService
|
||||
|
||||
// tests...
|
||||
}
|
||||
----
|
||||
<1> Replace the bean with type `CustomService` with a Mockito mock.
|
||||
======
|
||||
|
||||
In the example above, we are creating a mock for `CustomService`. If more than one bean
|
||||
@@ -168,6 +183,22 @@ Java::
|
||||
}
|
||||
----
|
||||
<1> Replace the bean named `service` with a Mockito mock.
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@SpringJUnitConfig(TestConfig::class)
|
||||
class BeanOverrideTests {
|
||||
|
||||
@MockitoBean("service") // <1>
|
||||
lateinit var customService: CustomService
|
||||
|
||||
// tests...
|
||||
|
||||
}
|
||||
----
|
||||
<1> Replace the bean named `service` with a Mockito mock.
|
||||
======
|
||||
|
||||
The following `@SharedMocks` annotation registers two mocks by-type and one mock by-name.
|
||||
@@ -187,6 +218,19 @@ Java::
|
||||
----
|
||||
<1> Register `OrderService` and `UserService` mocks by-type.
|
||||
<2> Register `PrintingService` mock by-name.
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
@MockitoBean(types = [OrderService::class, UserService::class]) // <1>
|
||||
@MockitoBean(name = "ps1", types = [PrintingService::class]) // <2>
|
||||
annotation class SharedMocks
|
||||
----
|
||||
<1> Register `OrderService` and `UserService` mocks by-type.
|
||||
<2> Register `PrintingService` mock by-name.
|
||||
======
|
||||
|
||||
The following demonstrates how `@SharedMocks` can be used on a test class.
|
||||
@@ -217,6 +261,34 @@ Java::
|
||||
----
|
||||
<1> Register common mocks via the custom `@SharedMocks` annotation.
|
||||
<2> Optionally inject mocks to _stub_ or _verify_ them.
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@SpringJUnitConfig(TestConfig::class)
|
||||
@SharedMocks // <1>
|
||||
class BeanOverrideTests {
|
||||
|
||||
@Autowired
|
||||
lateinit var orderService: OrderService // <2>
|
||||
|
||||
@Autowired
|
||||
lateinit var userService: UserService // <2>
|
||||
|
||||
@Autowired
|
||||
lateinit var ps1: PrintingService // <2>
|
||||
|
||||
// Inject other components that rely on the mocks.
|
||||
|
||||
@Test
|
||||
fun testThatDependsOnMocks() {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
----
|
||||
<1> Register common mocks via the custom `@SharedMocks` annotation.
|
||||
<2> Optionally inject mocks to _stub_ or _verify_ them.
|
||||
======
|
||||
|
||||
TIP: The mocks can also be injected into `@Configuration` classes or other test-related
|
||||
@@ -246,6 +318,21 @@ Java::
|
||||
}
|
||||
----
|
||||
<1> Wrap the bean with type `CustomService` with a Mockito spy.
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@SpringJUnitConfig(TestConfig::class)
|
||||
class BeanOverrideTests {
|
||||
|
||||
@MockitoSpyBean // <1>
|
||||
lateinit var customService: CustomService
|
||||
|
||||
// tests...
|
||||
}
|
||||
----
|
||||
<1> Wrap the bean with type `CustomService` with a Mockito spy.
|
||||
======
|
||||
|
||||
In the example above, we are wrapping the bean with type `CustomService`. If more than
|
||||
@@ -271,6 +358,21 @@ Java::
|
||||
}
|
||||
----
|
||||
<1> Wrap the bean named `service` with a Mockito spy.
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@SpringJUnitConfig(TestConfig::class)
|
||||
class BeanOverrideTests {
|
||||
|
||||
@MockitoSpyBean("service") // <1>
|
||||
lateinit var customService: CustomService
|
||||
|
||||
// tests...
|
||||
}
|
||||
----
|
||||
<1> Wrap the bean named `service` with a Mockito spy.
|
||||
======
|
||||
|
||||
The following `@SharedSpies` annotation registers two spies by-type and one spy by-name.
|
||||
@@ -290,6 +392,19 @@ Java::
|
||||
----
|
||||
<1> Register `OrderService` and `UserService` spies by-type.
|
||||
<2> Register `PrintingService` spy by-name.
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
@MockitoSpyBean(types = [OrderService::class, UserService::class]) // <1>
|
||||
@MockitoSpyBean(name = "ps1", types = [PrintingService::class]) // <2>
|
||||
annotation class SharedSpies
|
||||
----
|
||||
<1> Register `OrderService` and `UserService` spies by-type.
|
||||
<2> Register `PrintingService` spy by-name.
|
||||
======
|
||||
|
||||
The following demonstrates how `@SharedSpies` can be used on a test class.
|
||||
@@ -320,6 +435,34 @@ Java::
|
||||
----
|
||||
<1> Register common spies via the custom `@SharedSpies` annotation.
|
||||
<2> Optionally inject spies to _stub_ or _verify_ them.
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@SpringJUnitConfig(TestConfig::class)
|
||||
@SharedSpies // <1>
|
||||
class BeanOverrideTests {
|
||||
|
||||
@Autowired
|
||||
lateinit var orderService: OrderService // <2>
|
||||
|
||||
@Autowired
|
||||
lateinit var userService: UserService // <2>
|
||||
|
||||
@Autowired
|
||||
lateinit var ps1: PrintingService // <2>
|
||||
|
||||
// Inject other components that rely on the spies.
|
||||
|
||||
@Test
|
||||
fun testThatDependsOnMocks() {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
----
|
||||
<1> Register common spies via the custom `@SharedSpies` annotation.
|
||||
<2> Optionally inject spies to _stub_ or _verify_ them.
|
||||
======
|
||||
|
||||
TIP: The spies can also be injected into `@Configuration` classes or other test-related
|
||||
|
||||
+43
@@ -73,6 +73,27 @@ Java::
|
||||
----
|
||||
<1> Mark a field for overriding the bean with type `CustomService`.
|
||||
<2> The result of this static method will be used as the instance and injected into the field.
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
class OverrideBeanTests {
|
||||
@TestBean // <1>
|
||||
lateinit var customService: CustomService
|
||||
|
||||
// test case body...
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun customService(): CustomService { // <2>
|
||||
return MyFakeCustomService()
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
<1> Mark a field for overriding the bean with type `CustomService`.
|
||||
<2> The result of this static method will be used as the instance and injected into the field.
|
||||
======
|
||||
|
||||
In the example above, we are overriding the bean with type `CustomService`. If more than
|
||||
@@ -102,6 +123,28 @@ Java::
|
||||
<1> Mark a field for overriding the bean with name `service`, and specify that the
|
||||
factory method is named `createCustomService`.
|
||||
<2> The result of this static method will be used as the instance and injected into the field.
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
class OverrideBeanTests {
|
||||
@TestBean(name = "service", methodName = "createCustomService") // <1>
|
||||
lateinit var customService: CustomService
|
||||
|
||||
// test case body...
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun createCustomService(): CustomService { // <2>
|
||||
return MyFakeCustomService()
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
<1> Mark a field for overriding the bean with name `service`, and specify that the
|
||||
factory method is named `createCustomService`.
|
||||
<2> The result of this static method will be used as the instance and injected into the field.
|
||||
======
|
||||
|
||||
[TIP]
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
In the previous sections, we have seen how to use MockMvc in conjunction with the raw
|
||||
HtmlUnit APIs. In this section, we use additional abstractions within the Selenium
|
||||
https://docs.seleniumhq.org/projects/webdriver/[WebDriver] to make things even easier.
|
||||
https://www.selenium.dev/documentation/webdriver/[WebDriver] to make things even easier.
|
||||
|
||||
[[mockmvc-server-htmlunit-webdriver-why]]
|
||||
== Why WebDriver and MockMvc?
|
||||
@@ -12,8 +12,8 @@ We can already use HtmlUnit and MockMvc, so why would we want to use WebDriver?
|
||||
Selenium WebDriver provides a very elegant API that lets us easily organize our code. To
|
||||
better show how it works, we explore an example in this section.
|
||||
|
||||
NOTE: Despite being a part of https://docs.seleniumhq.org/[Selenium], WebDriver does not
|
||||
require a Selenium Server to run your tests.
|
||||
NOTE: Despite being a part of https://www.selenium.dev/documentation/[Selenium],
|
||||
WebDriver does not require a Selenium Server to run your tests.
|
||||
|
||||
Suppose we need to ensure that a message is created properly. The tests involve finding
|
||||
the HTML form input elements, filling them out, and making various assertions.
|
||||
@@ -308,7 +308,7 @@ interested. These are of type `WebElement`. WebDriver's
|
||||
https://github.com/SeleniumHQ/selenium/wiki/PageFactory[`PageFactory`] lets us remove a
|
||||
lot of code from the HtmlUnit version of `CreateMessagePage` by automatically resolving
|
||||
each `WebElement`. The
|
||||
https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/support/PageFactory.html#initElements-org.openqa.selenium.WebDriver-java.lang.Class-[`PageFactory#initElements(WebDriver,Class<T>)`]
|
||||
https://www.selenium.dev/selenium/docs/api/java/org/openqa/selenium/support/PageFactory.html#initElements-org.openqa.selenium.WebDriver-java.lang.Class-[`PageFactory#initElements(WebDriver,Class<T>)`]
|
||||
method automatically resolves each `WebElement` by using the field name and looking it up
|
||||
by the `id` or `name` of the element within the HTML page.
|
||||
<3> We can use the
|
||||
@@ -352,7 +352,7 @@ interested. These are of type `WebElement`. WebDriver's
|
||||
https://github.com/SeleniumHQ/selenium/wiki/PageFactory[`PageFactory`] lets us remove a
|
||||
lot of code from the HtmlUnit version of `CreateMessagePage` by automatically resolving
|
||||
each `WebElement`. The
|
||||
https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/support/PageFactory.html#initElements-org.openqa.selenium.WebDriver-java.lang.Class-[`PageFactory#initElements(WebDriver,Class<T>)`]
|
||||
https://www.selenium.dev/selenium/docs/api/java/org/openqa/selenium/support/PageFactory.html#initElements-org.openqa.selenium.WebDriver-java.lang.Class-[`PageFactory#initElements(WebDriver,Class<T>)`]
|
||||
method automatically resolves each `WebElement` by using the field name and looking it up
|
||||
by the `id` or `name` of the element within the HTML page.
|
||||
<3> We can use the
|
||||
|
||||
@@ -19,6 +19,6 @@ meta-annotation. If a bootstrapper is not explicitly configured by using
|
||||
`WebTestContextBootstrapper` is used, depending on the presence of `@WebAppConfiguration`.
|
||||
|
||||
Since the `TestContextBootstrapper` SPI is likely to change in the future (to accommodate
|
||||
new requirements), we strongly encourage implementers not to implement this interface
|
||||
new requirements), we strongly encourage implementors not to implement this interface
|
||||
directly but rather to extend `AbstractTestContextBootstrapper` or one of its concrete
|
||||
subclasses instead.
|
||||
|
||||
+82
@@ -543,3 +543,85 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
The following example demonstrates how to implement and register a custom
|
||||
`SystemPropertyOverrideActiveProfilesResolver` that allows the `spring.profiles.active`
|
||||
property (when configured as a JVM system property) to override profiles configured via
|
||||
`@ActiveProfiles`:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
// profiles resolved programmatically via a custom resolver that
|
||||
// allows "spring.profiles.active" to override @ActiveProfiles
|
||||
@ActiveProfiles(
|
||||
resolver = SystemPropertyOverrideActiveProfilesResolver.class,
|
||||
inheritProfiles = false)
|
||||
class TransferServiceTest extends AbstractIntegrationTest {
|
||||
// test body
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
// profiles resolved programmatically via a custom resolver that
|
||||
// allows "spring.profiles.active" to override @ActiveProfiles
|
||||
@ActiveProfiles(
|
||||
resolver = SystemPropertyOverrideActiveProfilesResolver::class,
|
||||
inheritProfiles = false)
|
||||
class TransferServiceTest : AbstractIntegrationTest() {
|
||||
// test body
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary",fold="-imports"]
|
||||
----
|
||||
import org.springframework.core.env.AbstractEnvironment;
|
||||
import org.springframework.test.context.support.DefaultActiveProfilesResolver;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
public class SystemPropertyOverrideActiveProfilesResolver extends DefaultActiveProfilesResolver {
|
||||
|
||||
@Override
|
||||
public String[] resolve(Class<?> testClass) {
|
||||
String profiles = System.getProperty(AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME);
|
||||
if (StringUtils.hasText(profiles)) {
|
||||
return StringUtils.commaDelimitedListToStringArray(
|
||||
StringUtils.trimAllWhitespace(profiles));
|
||||
}
|
||||
return super.resolve(testClass);
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",fold="-imports"]
|
||||
----
|
||||
import org.springframework.core.env.AbstractEnvironment
|
||||
import org.springframework.test.context.support.DefaultActiveProfilesResolver
|
||||
import org.springframework.util.StringUtils
|
||||
|
||||
class SystemPropertyOverrideActiveProfilesResolver : DefaultActiveProfilesResolver() {
|
||||
|
||||
override fun resolve(testClass: Class<*>): Array<String> {
|
||||
val profiles = System.getProperty(AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME)
|
||||
if (StringUtils.hasText(profiles)) {
|
||||
return StringUtils.commaDelimitedListToStringArray(
|
||||
StringUtils.trimAllWhitespace(profiles)
|
||||
)
|
||||
}
|
||||
return super.resolve(testClass)
|
||||
}
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
+4
-5
@@ -396,11 +396,10 @@ recursively.
|
||||
====
|
||||
As of Spring Framework 7.0, the `SpringExtension` uses a test-method scoped
|
||||
`ExtensionContext` within `@Nested` test class hierarchies by default. However, the
|
||||
`SpringExtension` can be configured to use a test-class scoped `ExtensionContext`.
|
||||
|
||||
See the documentation for
|
||||
xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-springextensionconfig[`@SpringExtensionConfig`]
|
||||
for details.
|
||||
`SpringExtension` can be configured to use a test-class scoped `ExtensionContext` — for
|
||||
example via `@SpringExtensionConfig` or the `spring.test.extension.context.scope` Spring
|
||||
property (see
|
||||
xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-springextensionconfig[`@SpringExtensionConfig`]).
|
||||
====
|
||||
|
||||
[TIP]
|
||||
|
||||
@@ -107,7 +107,4 @@ Kotlin::
|
||||
|
||||
|
||||
[[webflux-ann-initbinder-model-design]]
|
||||
== Model Design
|
||||
[.small]#xref:web/webmvc/mvc-controller/ann-initbinder.adoc#mvc-ann-initbinder-model-design[See equivalent in the Servlet stack]#
|
||||
|
||||
include::partial$web/web-data-binding-model-design.adoc[]
|
||||
NOTE: For more guidance on model design, please see xref:web/webflux/data-binding.adoc[Data Binding].
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
[[webflux-data-binding]]
|
||||
= Data Binding
|
||||
:page-section-summary-toc: 1
|
||||
|
||||
[.small]#xref:web/webmvc/mvc-data-binding.adoc[See equivalent in the Servlet stack]#
|
||||
|
||||
Data binding is a mechanism that binds string parameters onto an object graph with type conversion.
|
||||
It is a core mechanism of the Spring Framework that helps with application configuration.
|
||||
In web applications it makes it easy to access query parameters and form data through richly typed objects rather than through maps of string values.
|
||||
|
||||
To learn more about the data binding mechanism, including constructor and setter binding, property name syntax, type conversion,
|
||||
and more, see xref:core/validation/data-binding.adoc[Data binding] in the Core Technologies section.
|
||||
|
||||
For annotated controllers, data binding applies to a
|
||||
xref:web/webflux/controller/ann-methods/modelattrib-method-args.adoc[@ModelAttribute] method argument.
|
||||
For functional endpoints, use the `bind` method of xref:web/webflux-functional.adoc#webflux-fn-request[ServerRequest].
|
||||
|
||||
TIP: For browser applications with annotated controllers, you can use
|
||||
xref:web/webflux/controller/ann-modelattrib-methods.adoc[@ModelAttribute methods]
|
||||
to initialize additional model attributes for use in rendered views.
|
||||
|
||||
Each request uses a separate `WebDataBinder` instance.
|
||||
For annotated controllers, this instance can be customized through
|
||||
xref:web/webflux/controller/ann-initbinder.adoc[@InitBinder methods] within a controller, or
|
||||
across controllers through xref:web/webmvc/mvc-controller/ann-advice.adoc[Controller Advice].
|
||||
For functional endpoints, use overloaded `ServerRequest.bind` methods.
|
||||
|
||||
|
||||
|
||||
|
||||
[[webflux-data-binding-design]]
|
||||
== Model Design
|
||||
[.small]#xref:web/webmvc/mvc-data-binding.adoc#mvc-data-binding-design[See equivalent in the Servlet stack]#
|
||||
|
||||
include::partial$web/web-data-binding-model-design.adoc[]
|
||||
|
||||
@@ -182,7 +182,7 @@ The parameters to any of the above macros have consistent meanings:
|
||||
For strictly sorted maps, you can use a `SortedMap` (such as a `TreeMap`) with a
|
||||
suitable `Comparator` and, for arbitrary Maps that should return values in insertion
|
||||
order, use a `LinkedHashMap` or a `LinkedMap` from `commons-collections`.
|
||||
* `separator`: Where multiple options are available as discreet elements (radio buttons
|
||||
* `separator`: Where multiple options are available as discrete elements (radio buttons
|
||||
or checkboxes), the sequence of characters used to separate each one in the list
|
||||
(such as `<br>`).
|
||||
* `attributes`: An additional string of arbitrary tags or text to be included within
|
||||
|
||||
+1
-1
@@ -188,7 +188,7 @@ the content negotiation during the error handling phase will decide which conten
|
||||
| `View`
|
||||
| A `View` instance to use for rendering together with the implicit model -- determined
|
||||
through command objects and `@ModelAttribute` methods. The handler method may also
|
||||
programmatically enrich the model by declaring a `Model` argument (descried earlier).
|
||||
programmatically enrich the model by declaring a `Model` argument (described earlier).
|
||||
|
||||
| `java.util.Map`, `org.springframework.ui.Model`
|
||||
| Attributes to be added to the implicit model with the view name implicitly determined
|
||||
|
||||
@@ -107,7 +107,4 @@ Kotlin::
|
||||
|
||||
|
||||
[[mvc-ann-initbinder-model-design]]
|
||||
== Model Design
|
||||
[.small]#xref:web/webflux/controller/ann-initbinder.adoc#webflux-ann-initbinder-model-design[See equivalent in the Reactive stack]#
|
||||
|
||||
include::partial$web/web-data-binding-model-design.adoc[]
|
||||
NOTE: For more guidance on model design, please see xref:web/webmvc/mvc-data-binding.adoc[Data Binding].
|
||||
@@ -0,0 +1,34 @@
|
||||
[[mvc-data-binding]]
|
||||
= Data Binding
|
||||
:page-section-summary-toc: 1
|
||||
|
||||
[.small]#xref:web/webflux/data-binding.adoc[See equivalent in the Reactive stack]#
|
||||
|
||||
Data binding is a mechanism that binds string parameters onto an object graph with type conversion.
|
||||
It is a core mechanism of the Spring Framework that helps with application configuration.
|
||||
In web applications it makes it easy to access query parameters and form data through richly typed objects rather than through maps of string values.
|
||||
|
||||
To learn more about the data binding mechanism, including constructor and setter binding, property name syntax, type conversion,
|
||||
and more, see xref:core/validation/data-binding.adoc[Data binding] in the Core Technologies section.
|
||||
|
||||
For annotated controllers, data binding applies to a
|
||||
xref:web/webmvc/mvc-controller/ann-methods/modelattrib-method-args.adoc[@ModelAttribute] method argument.
|
||||
For functional endpoints, use the `bind` method of xref:web/webmvc-functional.adoc#webmvc-fn-request[ServerRequest].
|
||||
|
||||
TIP: For browser applications with annotated controllers, you can use
|
||||
xref:web/webmvc/mvc-controller/ann-modelattrib-methods.adoc[@ModelAttribute methods]
|
||||
to initialize additional model attributes for use in rendered views.
|
||||
|
||||
Each request uses a separate `WebDataBinder` instance.
|
||||
For annotated controllers, this instance can be customized through
|
||||
xref:web/webmvc/mvc-controller/ann-initbinder.adoc[@InitBinder methods] within a controller, or
|
||||
across controllers through xref:web/webmvc/mvc-controller/ann-advice.adoc[Controller Advice].
|
||||
For functional endpoints, use overloaded `ServerRequest.bind` methods.
|
||||
|
||||
|
||||
|
||||
[[mvc-data-binding-design]]
|
||||
== Model Design
|
||||
[.small]#xref:web/webflux/data-binding.adoc#webflux-data-binding-design[See equivalent in the Reactive stack]#
|
||||
|
||||
include::partial$web/web-data-binding-model-design.adoc[]
|
||||
@@ -257,7 +257,7 @@ An `XhrTransport`, by definition, supports both `xhr-streaming` and `xhr-polling
|
||||
from a client perspective, there is no difference other than in the URL used to connect
|
||||
to the server. At present there are two implementations:
|
||||
|
||||
* `RestTemplateXhrTransport` uses Spring's `RestTemplate` for HTTP requests.
|
||||
* `RestClientXhrTransport` uses Spring's `RestClient` for HTTP requests.
|
||||
* `JettyXhrTransport` uses Jetty's `HttpClient` for HTTP requests.
|
||||
|
||||
The following example shows how to create a SockJS client and connect to a SockJS endpoint:
|
||||
@@ -266,7 +266,7 @@ The following example shows how to create a SockJS client and connect to a SockJ
|
||||
----
|
||||
List<Transport> transports = new ArrayList<>(2);
|
||||
transports.add(new WebSocketTransport(new StandardWebSocketClient()));
|
||||
transports.add(new RestTemplateXhrTransport());
|
||||
transports.add(new RestClientXhrTransport());
|
||||
|
||||
SockJsClient sockJsClient = new SockJsClient(transports);
|
||||
sockJsClient.doHandshake(new MyWebSocketHandler(), "ws://example.com:8080/sockjs");
|
||||
|
||||
@@ -1,91 +1,53 @@
|
||||
xref:core/validation/data-binding.adoc[Data binding] for web requests involves
|
||||
binding request parameters to a model object. By default, request parameters can be bound
|
||||
to any public property of the model object, which means malicious clients can provide
|
||||
extra values for properties that exist in the model object graph, but are not expected to
|
||||
be set. This is why model object design requires careful consideration.
|
||||
Data binding involves binding untrusted input onto application objects.
|
||||
For security reasons, it's crucial to ensure that input is properly constrained to expected fields only.
|
||||
This section provides guidance for safe binding.
|
||||
|
||||
TIP: The model object, and its nested object graph is also sometimes referred to as a
|
||||
_command object_, _form-backing object_, or _POJO_ (Plain Old Java Object).
|
||||
First, prefer **immutable object design** for web binding purposes.
|
||||
It is safe because a constructor naturally constrains binding to expected inputs.
|
||||
You can use a Java record or a class with a primary constructor, and either can have further nested objects.
|
||||
See xref:core/validation/data-binding.adoc#data-binding-constructor-binding[Constructor Binding] for details.
|
||||
|
||||
A good practice is to use a _dedicated model object_ rather than exposing your domain
|
||||
model such as JPA or Hibernate entities for web data binding. For example, on a form to
|
||||
change an email address, create a `ChangeEmailForm` model object that declares only
|
||||
the properties required for the input:
|
||||
Another option for safe binding is to use **dedicated objects** designed for the expected input.
|
||||
Such objects, even if mutable, are safe because they constrain binding to the expected inputs.
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
public class ChangeEmailForm {
|
||||
|
||||
private String oldEmailAddress;
|
||||
private String newEmailAddress;
|
||||
|
||||
public void setOldEmailAddress(String oldEmailAddress) {
|
||||
this.oldEmailAddress = oldEmailAddress;
|
||||
}
|
||||
|
||||
public String getOldEmailAddress() {
|
||||
return this.oldEmailAddress;
|
||||
}
|
||||
|
||||
public void setNewEmailAddress(String newEmailAddress) {
|
||||
this.newEmailAddress = newEmailAddress;
|
||||
}
|
||||
|
||||
public String getNewEmailAddress() {
|
||||
return this.newEmailAddress;
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
Another good practice is to apply
|
||||
xref:core/validation/data-binding.adoc#data-binding-constructor-binding[constructor binding],
|
||||
which uses only the request parameters it needs for constructor arguments, and any other
|
||||
input is ignored. This is in contrast to property binding which by default binds every
|
||||
request parameter for which there is a matching property.
|
||||
|
||||
If neither a dedicated model object nor constructor binding is sufficient, and you must
|
||||
use property binding, we strongly recommend registering `allowedFields` patterns (case
|
||||
sensitive) on `WebDataBinder` in order to prevent unexpected properties from being set.
|
||||
Domain objects such as JPA or Hibernate entities are generally not safe for web binding
|
||||
as they likely contain more properties than the expected inputs.
|
||||
For such cases, it's crucial to declare the properties to expose for binding.
|
||||
For example:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@Controller
|
||||
public class ChangeEmailController {
|
||||
public class PersonController {
|
||||
|
||||
@InitBinder
|
||||
void initBinder(WebDataBinder binder) {
|
||||
binder.setAllowedFields("oldEmailAddress", "newEmailAddress");
|
||||
// See Javadoc for supported pattern syntax
|
||||
binder.setAllowedFields("firstName", "lastName", "*Address");
|
||||
}
|
||||
|
||||
// @RequestMapping methods, etc.
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
You can also register `disallowedFields` patterns (case insensitive). However,
|
||||
"allowed" configuration is preferred over "disallowed" as it is more explicit and less
|
||||
prone to mistakes.
|
||||
NOTE: It is also possible to configure `disallowedFields`, but that's fragile, and
|
||||
due to be https://github.com/spring-projects/spring-framework/issues/36802[deprecated] in Spring Framework 7.1.
|
||||
It is easy to overlook fields or introduce additional fields over time that should also be excluded.
|
||||
|
||||
By default, constructor and property binding are both used. If you want to use
|
||||
constructor binding only, you can set the `declarativeBinding` flag on `WebDataBinder`
|
||||
through an `@InitBinder` method either locally within a controller or globally through an
|
||||
`@ControllerAdvice`. Turning this flag on ensures that only constructor binding is used
|
||||
and that property binding is not used unless `allowedFields` patterns are configured.
|
||||
For example:
|
||||
By default, `DataBinder` applies both constructor and setter binding.
|
||||
This is fine with immutable objects and dedicated objects, but for domain objects, you must
|
||||
remember to set `allowedFields`. To ensure data binding is only used in declarative style where
|
||||
expected inputs are explicitly declared, you can set `declarativeBinding` on `DataBinder`.
|
||||
That applies constructor binding always, and setter binding conditionally if `allowedFields` is set.
|
||||
The following shows how to set this flag globally, or
|
||||
you can also narrow it through attributes on `ControllerAdvice`:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@Controller
|
||||
public class MyController {
|
||||
@ControllerAdvice
|
||||
public class ControllerConfig {
|
||||
|
||||
@InitBinder
|
||||
void initBinder(WebDataBinder binder) {
|
||||
binder.setDeclarativeBinding(true);
|
||||
}
|
||||
|
||||
// @RequestMapping methods, etc.
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"antora": "3.2.0-alpha.11",
|
||||
"antora": "3.2.0-alpha.12",
|
||||
"@antora/atlas-extension": "1.0.0-alpha.5",
|
||||
"@antora/collector-extension": "1.0.2",
|
||||
"@antora/collector-extension": "1.0.3",
|
||||
"@asciidoctor/tabs": "1.0.0-beta.6",
|
||||
"@springio/antora-extensions": "1.14.7",
|
||||
"fast-xml-parser": "5.3.8",
|
||||
"@springio/asciidoctor-extensions": "1.0.0-alpha.17"
|
||||
"@springio/antora-extensions": "1.14.12",
|
||||
"fast-xml-parser": "5.7.0",
|
||||
"@springio/asciidoctor-extensions": "1.0.0-alpha.18"
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-present 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-present 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-present 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.
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ public class ExceptionController {
|
||||
|
||||
// tag::narrow[]
|
||||
@ExceptionHandler({FileSystemException.class, RemoteException.class})
|
||||
public ResponseEntity<String> handleIoException(IOException ex) {
|
||||
public ResponseEntity<String> handleIOException(IOException ex) {
|
||||
return ResponseEntity.internalServerError().body(ex.getMessage());
|
||||
}
|
||||
// end::narrow[]
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-present 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-present 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-present 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-present 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-present 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-present 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-present 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-present 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-present 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-present 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.
|
||||
|
||||
+1
-1
@@ -54,6 +54,6 @@ class ConcurrentOperationExecutor : Ordered {
|
||||
lockFailureException = ex
|
||||
}
|
||||
} while (numAttempts <= this.maxRetries)
|
||||
throw lockFailureException!!
|
||||
throw lockFailureException
|
||||
}
|
||||
} // end::snippet[]
|
||||
+1
-1
@@ -27,7 +27,7 @@ class ExceptionController {
|
||||
|
||||
// tag::narrow[]
|
||||
@ExceptionHandler(FileSystemException::class, RemoteException::class)
|
||||
fun handleIoException(ex: IOException): ResponseEntity<String> {
|
||||
fun handleIOException(ex: IOException): ResponseEntity<String> {
|
||||
return ResponseEntity.internalServerError().body(ex.message)
|
||||
}
|
||||
// end::narrow[]
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-present 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-present 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-present 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-present 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-present 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-present 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-present 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-present 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-present 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-present 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.
|
||||
|
||||
@@ -8,19 +8,19 @@ javaPlatform {
|
||||
|
||||
dependencies {
|
||||
api(platform("com.fasterxml.jackson:jackson-bom:2.20.2"))
|
||||
api(platform("io.micrometer:micrometer-bom:1.16.4"))
|
||||
api(platform("io.netty:netty-bom:4.2.10.Final"))
|
||||
api(platform("io.projectreactor:reactor-bom:2025.0.4"))
|
||||
api(platform("io.micrometer:micrometer-bom:1.16.6"))
|
||||
api(platform("io.netty:netty-bom:4.2.15.Final"))
|
||||
api(platform("io.projectreactor:reactor-bom:2025.0.6"))
|
||||
api(platform("io.rsocket:rsocket-bom:1.1.5"))
|
||||
api(platform("org.apache.groovy:groovy-bom:5.0.4"))
|
||||
api(platform("org.apache.logging.log4j:log4j-bom:2.25.3"))
|
||||
api(platform("org.apache.groovy:groovy-bom:5.0.6"))
|
||||
api(platform("org.apache.logging.log4j:log4j-bom:2.26.0"))
|
||||
api(platform("org.assertj:assertj-bom:3.27.7"))
|
||||
api(platform("org.eclipse.jetty:jetty-bom:12.1.7"))
|
||||
api(platform("org.eclipse.jetty.ee11:jetty-ee11-bom:12.1.7"))
|
||||
api(platform("org.eclipse.jetty:jetty-bom:12.1.9"))
|
||||
api(platform("org.eclipse.jetty.ee11:jetty-ee11-bom:12.1.9"))
|
||||
api(platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.10.2"))
|
||||
api(platform("org.jetbrains.kotlinx:kotlinx-serialization-bom:1.9.0"))
|
||||
api(platform("org.junit:junit-bom:6.0.3"))
|
||||
api(platform("org.mockito:mockito-bom:5.22.0"))
|
||||
api(platform("org.mockito:mockito-bom:5.23.0"))
|
||||
api(platform("tools.jackson:jackson-bom:3.0.4"))
|
||||
|
||||
constraints {
|
||||
@@ -31,7 +31,7 @@ dependencies {
|
||||
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.13.2")
|
||||
api("com.google.protobuf:protobuf-java-util:4.34.0")
|
||||
api("com.google.protobuf:protobuf-java-util:4.34.1")
|
||||
api("com.h2database:h2:2.4.240")
|
||||
api("com.jayway.jsonpath:json-path:2.10.0")
|
||||
api("com.networknt:json-schema-validator:1.5.3")
|
||||
@@ -96,10 +96,10 @@ dependencies {
|
||||
api("org.apache.httpcomponents.client5:httpclient5:5.6")
|
||||
api("org.apache.httpcomponents.core5:httpcore5-reactive:5.4.2")
|
||||
api("org.apache.poi:poi-ooxml:5.5.1")
|
||||
api("org.apache.tomcat.embed:tomcat-embed-core:11.0.18")
|
||||
api("org.apache.tomcat.embed:tomcat-embed-websocket:11.0.18")
|
||||
api("org.apache.tomcat:tomcat-util:11.0.18")
|
||||
api("org.apache.tomcat:tomcat-websocket:11.0.18")
|
||||
api("org.apache.tomcat.embed:tomcat-embed-core:11.0.22")
|
||||
api("org.apache.tomcat.embed:tomcat-embed-websocket:11.0.22")
|
||||
api("org.apache.tomcat:tomcat-util:11.0.22")
|
||||
api("org.apache.tomcat:tomcat-websocket:11.0.22")
|
||||
api("org.aspectj:aspectjrt:1.9.25")
|
||||
api("org.aspectj:aspectjtools:1.9.25")
|
||||
api("org.aspectj:aspectjweaver:1.9.25")
|
||||
@@ -111,7 +111,7 @@ dependencies {
|
||||
api("org.easymock:easymock:5.6.0")
|
||||
api("org.eclipse.angus:angus-mail:2.0.3")
|
||||
api("org.eclipse.jetty:jetty-reactive-httpclient:4.1.4")
|
||||
api("org.eclipse.persistence:org.eclipse.persistence.jpa:5.0.0-RC1")
|
||||
api("org.eclipse.persistence:org.eclipse.persistence.jpa:5.0.0")
|
||||
api("org.eclipse:yasson:3.0.4")
|
||||
api("org.ehcache:ehcache:3.10.8")
|
||||
api("org.ehcache:jcache:1.0.1")
|
||||
@@ -120,7 +120,7 @@ dependencies {
|
||||
api("org.glassfish:jakarta.el:4.0.2")
|
||||
api("org.graalvm.sdk:graal-sdk:22.3.1")
|
||||
api("org.hamcrest:hamcrest:3.0")
|
||||
api("org.hibernate.orm:hibernate-core:7.2.6.Final")
|
||||
api("org.hibernate.orm:hibernate-core:7.2.17.Final")
|
||||
api("org.hibernate.validator:hibernate-validator:9.1.0.Final")
|
||||
api("org.hsqldb:hsqldb:2.7.4")
|
||||
api("org.htmlunit:htmlunit:4.21.0")
|
||||
@@ -138,7 +138,6 @@ dependencies {
|
||||
api("org.seleniumhq.selenium:selenium-java:4.41.0")
|
||||
api("org.skyscreamer:jsonassert:1.5.3")
|
||||
api("org.testng:testng:7.12.0")
|
||||
api("org.webjars:underscorejs:1.8.3")
|
||||
api("org.webjars:webjars-locator-lite:1.1.0")
|
||||
api("org.xmlunit:xmlunit-assertj:2.10.4")
|
||||
api("org.xmlunit:xmlunit-matchers:2.10.4")
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
version=7.0.6
|
||||
version=7.0.8-SNAPSHOT
|
||||
|
||||
org.gradle.caching=true
|
||||
org.gradle.jvmargs=-Xmx2048m
|
||||
|
||||
@@ -84,19 +84,6 @@ eclipse.classpath.file.whenMerged {
|
||||
}
|
||||
}
|
||||
|
||||
// Due to an apparent bug in Gradle, even though we exclude the "main" classpath
|
||||
// entries for sources generated by XJC in spring-oxm.gradle, the Gradle eclipse
|
||||
// plugin still includes them in the generated .classpath file. So, we have to
|
||||
// manually remove those lingering "main" entries.
|
||||
if (project.name == "spring-oxm") {
|
||||
eclipse.classpath.file.whenMerged { classpath ->
|
||||
classpath.entries.removeAll {
|
||||
it.path =~ /build\/generated\/sources\/xjc\/.+/ &&
|
||||
it.entryAttributes.get("gradle_scope") == "main"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Include project specific settings
|
||||
tasks.register('eclipseSettings', Copy) {
|
||||
from rootProject.files(
|
||||
|
||||
Vendored
BIN
Binary file not shown.
+3
-1
@@ -1,7 +1,9 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip
|
||||
networkTimeout=10000
|
||||
retries=0
|
||||
retryBackOffMs=500
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/b631911858264c0b6e4d6603d677ff5218766cee/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
|
||||
Vendored
+10
-21
@@ -23,8 +23,8 @@
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
@rem Set local scope for the variables, and ensure extensions are enabled
|
||||
setlocal EnableExtensions
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@@ -51,7 +51,7 @@ echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
"%COMSPEC%" /c exit 1
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
@@ -65,7 +65,7 @@ echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
"%COMSPEC%" /c exit 1
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
@@ -73,21 +73,10 @@ goto fail
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||
@rem endlocal doesn't take effect until after the line is parsed and variables are expanded
|
||||
@rem which allows us to clear the local environment before executing the java command
|
||||
endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
:exitWithErrorLevel
|
||||
@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
|
||||
"%COMSPEC%" /c exit %ERRORLEVEL%
|
||||
|
||||
+8
-13
@@ -61,10 +61,9 @@ class AopNamespaceHandlerScopeIntegrationTests {
|
||||
|
||||
|
||||
@Test
|
||||
void testSingletonScoping() throws Exception {
|
||||
void singletonScoping() throws Exception {
|
||||
assertThat(AopUtils.isAopProxy(singletonScoped)).as("Should be AOP proxy").isTrue();
|
||||
boolean condition = singletonScoped instanceof TestBean;
|
||||
assertThat(condition).as("Should be target class proxy").isTrue();
|
||||
assertThat(singletonScoped).as("Should be target class proxy").isInstanceOf(TestBean.class);
|
||||
String rob = "Rob Harrop";
|
||||
String bram = "Bram Smeets";
|
||||
assertThat(singletonScoped.getName()).isEqualTo(rob);
|
||||
@@ -75,19 +74,17 @@ class AopNamespaceHandlerScopeIntegrationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRequestScoping() {
|
||||
void requestScoping() {
|
||||
MockHttpServletRequest oldRequest = new MockHttpServletRequest();
|
||||
MockHttpServletRequest newRequest = new MockHttpServletRequest();
|
||||
|
||||
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(oldRequest));
|
||||
|
||||
assertThat(AopUtils.isAopProxy(requestScoped)).as("Should be AOP proxy").isTrue();
|
||||
boolean condition = requestScoped instanceof TestBean;
|
||||
assertThat(condition).as("Should be target class proxy").isTrue();
|
||||
assertThat(requestScoped).as("Should be target class proxy").isInstanceOf(TestBean.class);
|
||||
|
||||
assertThat(AopUtils.isAopProxy(testBean)).as("Should be AOP proxy").isTrue();
|
||||
boolean condition1 = testBean instanceof TestBean;
|
||||
assertThat(condition1).as("Regular bean should be JDK proxy").isFalse();
|
||||
assertThat(testBean).as("Regular bean should be JDK proxy").isNotInstanceOf(TestBean.class);
|
||||
|
||||
String rob = "Rob Harrop";
|
||||
String bram = "Bram Smeets";
|
||||
@@ -103,7 +100,7 @@ class AopNamespaceHandlerScopeIntegrationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSessionScoping() {
|
||||
void sessionScoping() {
|
||||
MockHttpSession oldSession = new MockHttpSession();
|
||||
MockHttpSession newSession = new MockHttpSession();
|
||||
|
||||
@@ -112,14 +109,12 @@ class AopNamespaceHandlerScopeIntegrationTests {
|
||||
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));
|
||||
|
||||
assertThat(AopUtils.isAopProxy(sessionScoped)).as("Should be AOP proxy").isTrue();
|
||||
boolean condition1 = sessionScoped instanceof TestBean;
|
||||
assertThat(condition1).as("Should not be target class proxy").isFalse();
|
||||
assertThat(sessionScoped).as("Should not be target class proxy").isNotInstanceOf(TestBean.class);
|
||||
|
||||
assertThat(sessionScopedAlias).isSameAs(sessionScoped);
|
||||
|
||||
assertThat(AopUtils.isAopProxy(testBean)).as("Should be AOP proxy").isTrue();
|
||||
boolean condition = testBean instanceof TestBean;
|
||||
assertThat(condition).as("Regular bean should be JDK proxy").isFalse();
|
||||
assertThat(testBean).as("Regular bean should be JDK proxy").isNotInstanceOf(TestBean.class);
|
||||
|
||||
String rob = "Rob Harrop";
|
||||
String bram = "Bram Smeets";
|
||||
|
||||
+9
-10
@@ -65,7 +65,7 @@ class AdvisorAutoProxyCreatorIntegrationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDefaultExclusionPrefix() {
|
||||
void defaultExclusionPrefix() {
|
||||
DefaultAdvisorAutoProxyCreator aapc = (DefaultAdvisorAutoProxyCreator) getBeanFactory().getBean(ADVISOR_APC_BEAN_NAME);
|
||||
assertThat(aapc.getAdvisorBeanNamePrefix()).isEqualTo((ADVISOR_APC_BEAN_NAME + DefaultAdvisorAutoProxyCreator.SEPARATOR));
|
||||
assertThat(aapc.isUsePrefix()).isFalse();
|
||||
@@ -75,21 +75,21 @@ class AdvisorAutoProxyCreatorIntegrationTests {
|
||||
* If no pointcuts match (no attrs) there should be proxying.
|
||||
*/
|
||||
@Test
|
||||
void testNoProxy() {
|
||||
void noProxy() {
|
||||
BeanFactory bf = getBeanFactory();
|
||||
Object o = bf.getBean("noSetters");
|
||||
assertThat(AopUtils.isAopProxy(o)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTxIsProxied() {
|
||||
void txIsProxied() {
|
||||
BeanFactory bf = getBeanFactory();
|
||||
ITestBean test = (ITestBean) bf.getBean("test");
|
||||
assertThat(AopUtils.isAopProxy(test)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRegexpApplied() {
|
||||
void regexpApplied() {
|
||||
BeanFactory bf = getBeanFactory();
|
||||
ITestBean test = (ITestBean) bf.getBean("test");
|
||||
MethodCounter counter = (MethodCounter) bf.getBean("countingAdvice");
|
||||
@@ -99,7 +99,7 @@ class AdvisorAutoProxyCreatorIntegrationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTransactionAttributeOnMethod() {
|
||||
void transactionAttributeOnMethod() {
|
||||
BeanFactory bf = getBeanFactory();
|
||||
ITestBean test = (ITestBean) bf.getBean("test");
|
||||
|
||||
@@ -121,7 +121,7 @@ class AdvisorAutoProxyCreatorIntegrationTests {
|
||||
* Should not roll back on servlet exception.
|
||||
*/
|
||||
@Test
|
||||
void testRollbackRulesOnMethodCauseRollback() throws Exception {
|
||||
void rollbackRulesOnMethodCauseRollback() throws Exception {
|
||||
BeanFactory bf = getBeanFactory();
|
||||
Rollback rb = (Rollback) bf.getBean("rollback");
|
||||
|
||||
@@ -147,7 +147,7 @@ class AdvisorAutoProxyCreatorIntegrationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRollbackRulesOnMethodPreventRollback() throws Exception {
|
||||
void rollbackRulesOnMethodPreventRollback() throws Exception {
|
||||
BeanFactory bf = getBeanFactory();
|
||||
Rollback rb = (Rollback) bf.getBean("rollback");
|
||||
|
||||
@@ -164,12 +164,11 @@ class AdvisorAutoProxyCreatorIntegrationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testProgrammaticRollback() {
|
||||
void programmaticRollback() {
|
||||
BeanFactory bf = getBeanFactory();
|
||||
|
||||
Object bean = bf.getBean(TXMANAGER_BEAN_NAME);
|
||||
boolean condition = bean instanceof CallCountingTransactionManager;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(bean).isInstanceOf(CallCountingTransactionManager.class);
|
||||
CallCountingTransactionManager txMan = (CallCountingTransactionManager) bf.getBean(TXMANAGER_BEAN_NAME);
|
||||
|
||||
Rollback rb = (Rollback) bf.getBean("rollback");
|
||||
|
||||
+3
-3
@@ -50,13 +50,13 @@ class ComponentBeanDefinitionParserTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBionicBasic() {
|
||||
void bionicBasic() {
|
||||
Component cp = getBionicFamily();
|
||||
assertThat(cp.getName()).isEqualTo("Bionic-1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBionicFirstLevelChildren() {
|
||||
void bionicFirstLevelChildren() {
|
||||
Component cp = getBionicFamily();
|
||||
List<Component> components = cp.getComponents();
|
||||
assertThat(components).hasSize(2);
|
||||
@@ -65,7 +65,7 @@ class ComponentBeanDefinitionParserTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBionicSecondLevelChildren() {
|
||||
void bionicSecondLevelChildren() {
|
||||
Component cp = getBionicFamily();
|
||||
List<Component> components = cp.getComponents().get(0).getComponents();
|
||||
assertThat(components).hasSize(2);
|
||||
|
||||
+15
-20
@@ -83,7 +83,7 @@ class ClassPathBeanDefinitionScannerJsr330ScopeIntegrationTests {
|
||||
|
||||
|
||||
@Test
|
||||
void testPrototype() {
|
||||
void prototype() {
|
||||
ApplicationContext context = createContext(ScopedProxyMode.NO);
|
||||
ScopedTestBean bean = (ScopedTestBean) context.getBean("prototype");
|
||||
assertThat(bean).isNotNull();
|
||||
@@ -92,7 +92,7 @@ class ClassPathBeanDefinitionScannerJsr330ScopeIntegrationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSingletonScopeWithNoProxy() {
|
||||
void singletonScopeWithNoProxy() {
|
||||
RequestContextHolder.setRequestAttributes(oldRequestAttributes);
|
||||
ApplicationContext context = createContext(ScopedProxyMode.NO);
|
||||
ScopedTestBean bean = (ScopedTestBean) context.getBean("singleton");
|
||||
@@ -115,7 +115,7 @@ class ClassPathBeanDefinitionScannerJsr330ScopeIntegrationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSingletonScopeIgnoresProxyInterfaces() {
|
||||
void singletonScopeIgnoresProxyInterfaces() {
|
||||
RequestContextHolder.setRequestAttributes(oldRequestAttributes);
|
||||
ApplicationContext context = createContext(ScopedProxyMode.INTERFACES);
|
||||
ScopedTestBean bean = (ScopedTestBean) context.getBean("singleton");
|
||||
@@ -136,7 +136,7 @@ class ClassPathBeanDefinitionScannerJsr330ScopeIntegrationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSingletonScopeIgnoresProxyTargetClass() {
|
||||
void singletonScopeIgnoresProxyTargetClass() {
|
||||
RequestContextHolder.setRequestAttributes(oldRequestAttributes);
|
||||
ApplicationContext context = createContext(ScopedProxyMode.TARGET_CLASS);
|
||||
ScopedTestBean bean = (ScopedTestBean) context.getBean("singleton");
|
||||
@@ -157,7 +157,7 @@ class ClassPathBeanDefinitionScannerJsr330ScopeIntegrationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRequestScopeWithNoProxy() {
|
||||
void requestScopeWithNoProxy() {
|
||||
RequestContextHolder.setRequestAttributes(oldRequestAttributes);
|
||||
ApplicationContext context = createContext(ScopedProxyMode.NO);
|
||||
ScopedTestBean bean = (ScopedTestBean) context.getBean("request");
|
||||
@@ -178,15 +178,14 @@ class ClassPathBeanDefinitionScannerJsr330ScopeIntegrationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRequestScopeWithProxiedInterfaces() {
|
||||
void requestScopeWithProxiedInterfaces() {
|
||||
RequestContextHolder.setRequestAttributes(oldRequestAttributes);
|
||||
ApplicationContext context = createContext(ScopedProxyMode.INTERFACES);
|
||||
IScopedTestBean bean = (IScopedTestBean) context.getBean("request");
|
||||
|
||||
// should be dynamic proxy, implementing both interfaces
|
||||
assertThat(AopUtils.isJdkDynamicProxy(bean)).isTrue();
|
||||
boolean condition = bean instanceof AnotherScopeTestInterface;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(bean).isInstanceOf(AnotherScopeTestInterface.class);
|
||||
|
||||
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
|
||||
bean.setName(MODIFIED_NAME);
|
||||
@@ -200,15 +199,14 @@ class ClassPathBeanDefinitionScannerJsr330ScopeIntegrationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRequestScopeWithProxiedTargetClass() {
|
||||
void requestScopeWithProxiedTargetClass() {
|
||||
RequestContextHolder.setRequestAttributes(oldRequestAttributes);
|
||||
ApplicationContext context = createContext(ScopedProxyMode.TARGET_CLASS);
|
||||
IScopedTestBean bean = (IScopedTestBean) context.getBean("request");
|
||||
|
||||
// should be a class-based proxy
|
||||
assertThat(AopUtils.isCglibProxy(bean)).isTrue();
|
||||
boolean condition = bean instanceof RequestScopedTestBean;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(bean).isInstanceOf(RequestScopedTestBean.class);
|
||||
|
||||
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
|
||||
bean.setName(MODIFIED_NAME);
|
||||
@@ -222,7 +220,7 @@ class ClassPathBeanDefinitionScannerJsr330ScopeIntegrationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSessionScopeWithNoProxy() {
|
||||
void sessionScopeWithNoProxy() {
|
||||
RequestContextHolder.setRequestAttributes(oldRequestAttributesWithSession);
|
||||
ApplicationContext context = createContext(ScopedProxyMode.NO);
|
||||
ScopedTestBean bean = (ScopedTestBean) context.getBean("session");
|
||||
@@ -243,15 +241,14 @@ class ClassPathBeanDefinitionScannerJsr330ScopeIntegrationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSessionScopeWithProxiedInterfaces() {
|
||||
void sessionScopeWithProxiedInterfaces() {
|
||||
RequestContextHolder.setRequestAttributes(oldRequestAttributesWithSession);
|
||||
ApplicationContext context = createContext(ScopedProxyMode.INTERFACES);
|
||||
IScopedTestBean bean = (IScopedTestBean) context.getBean("session");
|
||||
|
||||
// should be dynamic proxy, implementing both interfaces
|
||||
assertThat(AopUtils.isJdkDynamicProxy(bean)).isTrue();
|
||||
boolean condition = bean instanceof AnotherScopeTestInterface;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(bean).isInstanceOf(AnotherScopeTestInterface.class);
|
||||
|
||||
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
|
||||
bean.setName(MODIFIED_NAME);
|
||||
@@ -271,17 +268,15 @@ class ClassPathBeanDefinitionScannerJsr330ScopeIntegrationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSessionScopeWithProxiedTargetClass() {
|
||||
void sessionScopeWithProxiedTargetClass() {
|
||||
RequestContextHolder.setRequestAttributes(oldRequestAttributesWithSession);
|
||||
ApplicationContext context = createContext(ScopedProxyMode.TARGET_CLASS);
|
||||
IScopedTestBean bean = (IScopedTestBean) context.getBean("session");
|
||||
|
||||
// should be a class-based proxy
|
||||
assertThat(AopUtils.isCglibProxy(bean)).isTrue();
|
||||
boolean condition1 = bean instanceof ScopedTestBean;
|
||||
assertThat(condition1).isTrue();
|
||||
boolean condition = bean instanceof SessionScopedTestBean;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(bean).isInstanceOf(ScopedTestBean.class);
|
||||
assertThat(bean).isInstanceOf(SessionScopedTestBean.class);
|
||||
|
||||
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
|
||||
bean.setName(MODIFIED_NAME);
|
||||
|
||||
+5
-10
@@ -166,8 +166,7 @@ class ClassPathBeanDefinitionScannerScopeIntegrationTests {
|
||||
|
||||
// should be dynamic proxy, implementing both interfaces
|
||||
assertThat(AopUtils.isJdkDynamicProxy(bean)).isTrue();
|
||||
boolean condition = bean instanceof AnotherScopeTestInterface;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(bean).isInstanceOf(AnotherScopeTestInterface.class);
|
||||
|
||||
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
|
||||
bean.setName(MODIFIED_NAME);
|
||||
@@ -188,8 +187,7 @@ class ClassPathBeanDefinitionScannerScopeIntegrationTests {
|
||||
|
||||
// should be a class-based proxy
|
||||
assertThat(AopUtils.isCglibProxy(bean)).isTrue();
|
||||
boolean condition = bean instanceof RequestScopedTestBean;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(bean).isInstanceOf(RequestScopedTestBean.class);
|
||||
|
||||
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
|
||||
bean.setName(MODIFIED_NAME);
|
||||
@@ -231,8 +229,7 @@ class ClassPathBeanDefinitionScannerScopeIntegrationTests {
|
||||
|
||||
// should be dynamic proxy, implementing both interfaces
|
||||
assertThat(AopUtils.isJdkDynamicProxy(bean)).isTrue();
|
||||
boolean condition = bean instanceof AnotherScopeTestInterface;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(bean).isInstanceOf(AnotherScopeTestInterface.class);
|
||||
|
||||
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
|
||||
bean.setName(MODIFIED_NAME);
|
||||
@@ -259,10 +256,8 @@ class ClassPathBeanDefinitionScannerScopeIntegrationTests {
|
||||
|
||||
// should be a class-based proxy
|
||||
assertThat(AopUtils.isCglibProxy(bean)).isTrue();
|
||||
boolean condition1 = bean instanceof ScopedTestBean;
|
||||
assertThat(condition1).isTrue();
|
||||
boolean condition = bean instanceof SessionScopedTestBean;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(bean).isInstanceOf(ScopedTestBean.class);
|
||||
assertThat(bean).isInstanceOf(SessionScopedTestBean.class);
|
||||
|
||||
assertThat(bean.getName()).isEqualTo(DEFAULT_NAME);
|
||||
bean.setName(MODIFIED_NAME);
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2002-present 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.core.env;
|
||||
|
||||
/**
|
||||
* Constants used both locally and in scan* sub-packages
|
||||
*/
|
||||
public class Constants {
|
||||
|
||||
public static final String XML_PATH = "org/springframework/core/env/EnvironmentSystemIntegrationTests-context.xml";
|
||||
|
||||
public static final String ENVIRONMENT_AWARE_BEAN_NAME = "envAwareBean";
|
||||
|
||||
public static final String PROD_BEAN_NAME = "prodBean";
|
||||
public static final String DEV_BEAN_NAME = "devBean";
|
||||
public static final String DERIVED_DEV_BEAN_NAME = "derivedDevBean";
|
||||
public static final String TRANSITIVE_BEAN_NAME = "transitiveBean";
|
||||
|
||||
public static final String PROD_ENV_NAME = "prod";
|
||||
public static final String DEV_ENV_NAME = "dev";
|
||||
public static final String DERIVED_DEV_ENV_NAME = "derivedDev";
|
||||
}
|
||||
Vendored
+11
-31
@@ -58,15 +58,15 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.springframework.beans.factory.support.BeanDefinitionBuilder.rootBeanDefinition;
|
||||
import static org.springframework.context.ConfigurableApplicationContext.ENVIRONMENT_BEAN_NAME;
|
||||
import static org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.DERIVED_DEV_BEAN_NAME;
|
||||
import static org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.DERIVED_DEV_ENV_NAME;
|
||||
import static org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.DEV_BEAN_NAME;
|
||||
import static org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.DEV_ENV_NAME;
|
||||
import static org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.ENVIRONMENT_AWARE_BEAN_NAME;
|
||||
import static org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.PROD_BEAN_NAME;
|
||||
import static org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.PROD_ENV_NAME;
|
||||
import static org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.TRANSITIVE_BEAN_NAME;
|
||||
import static org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.XML_PATH;
|
||||
import static org.springframework.core.env.Constants.DERIVED_DEV_BEAN_NAME;
|
||||
import static org.springframework.core.env.Constants.DERIVED_DEV_ENV_NAME;
|
||||
import static org.springframework.core.env.Constants.DEV_BEAN_NAME;
|
||||
import static org.springframework.core.env.Constants.DEV_ENV_NAME;
|
||||
import static org.springframework.core.env.Constants.ENVIRONMENT_AWARE_BEAN_NAME;
|
||||
import static org.springframework.core.env.Constants.PROD_BEAN_NAME;
|
||||
import static org.springframework.core.env.Constants.PROD_ENV_NAME;
|
||||
import static org.springframework.core.env.Constants.TRANSITIVE_BEAN_NAME;
|
||||
import static org.springframework.core.env.Constants.XML_PATH;
|
||||
|
||||
/**
|
||||
* System integration tests for container support of the {@link Environment} API.
|
||||
@@ -87,7 +87,7 @@ import static org.springframework.core.env.EnvironmentSystemIntegrationTests.Con
|
||||
* @author Sam Brannen
|
||||
* @see org.springframework.context.support.EnvironmentIntegrationTests
|
||||
*/
|
||||
public class EnvironmentSystemIntegrationTests {
|
||||
class EnvironmentSystemIntegrationTests {
|
||||
|
||||
private final ConfigurableEnvironment prodEnv = new StandardEnvironment();
|
||||
|
||||
@@ -648,7 +648,7 @@ public class EnvironmentSystemIntegrationTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Profile(DERIVED_DEV_ENV_NAME)
|
||||
@Profile(Constants.DERIVED_DEV_ENV_NAME)
|
||||
@Configuration
|
||||
static class DerivedDevConfig extends DevConfig {
|
||||
@Bean
|
||||
@@ -666,24 +666,4 @@ public class EnvironmentSystemIntegrationTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constants used both locally and in scan* sub-packages
|
||||
*/
|
||||
public static class Constants {
|
||||
|
||||
public static final String XML_PATH = "org/springframework/core/env/EnvironmentSystemIntegrationTests-context.xml";
|
||||
|
||||
public static final String ENVIRONMENT_AWARE_BEAN_NAME = "envAwareBean";
|
||||
|
||||
public static final String PROD_BEAN_NAME = "prodBean";
|
||||
public static final String DEV_BEAN_NAME = "devBean";
|
||||
public static final String DERIVED_DEV_BEAN_NAME = "derivedDevBean";
|
||||
public static final String TRANSITIVE_BEAN_NAME = "transitiveBean";
|
||||
|
||||
public static final String PROD_ENV_NAME = "prod";
|
||||
public static final String DEV_ENV_NAME = "dev";
|
||||
public static final String DERIVED_DEV_ENV_NAME = "derivedDev";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
|
||||
@Profile(org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.DEV_ENV_NAME)
|
||||
@Profile(org.springframework.core.env.Constants.DEV_ENV_NAME)
|
||||
@Configuration
|
||||
class DevConfig {
|
||||
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
|
||||
@Profile(org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.PROD_ENV_NAME)
|
||||
@Profile(org.springframework.core.env.Constants.PROD_ENV_NAME)
|
||||
@Configuration
|
||||
class ProdConfig {
|
||||
|
||||
|
||||
+2
-2
@@ -19,7 +19,7 @@ package org.springframework.core.env.scan2;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Profile(org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.DEV_ENV_NAME)
|
||||
@Component(org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.DEV_BEAN_NAME)
|
||||
@Profile(org.springframework.core.env.Constants.DEV_ENV_NAME)
|
||||
@Component(org.springframework.core.env.Constants.DEV_BEAN_NAME)
|
||||
class DevBean {
|
||||
}
|
||||
|
||||
+2
-2
@@ -19,8 +19,8 @@ package org.springframework.core.env.scan2;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Profile(org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.PROD_ENV_NAME)
|
||||
@Component(org.springframework.core.env.EnvironmentSystemIntegrationTests.Constants.PROD_BEAN_NAME)
|
||||
@Profile(org.springframework.core.env.Constants.PROD_ENV_NAME)
|
||||
@Component(org.springframework.core.env.Constants.PROD_BEAN_NAME)
|
||||
class ProdBean {
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -163,7 +163,7 @@ public class MethodInvocationProceedingJoinPoint implements ProceedingJoinPoint,
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "execution(" + getSignature().toString() + ")";
|
||||
return "execution(" + getSignature() + ")";
|
||||
}
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -83,7 +83,7 @@ public class AspectJProxyFactory extends ProxyCreatorSupport {
|
||||
|
||||
/**
|
||||
* Add the supplied aspect instance to the chain. The type of the aspect instance
|
||||
* supplied must be a singleton aspect. True singleton lifecycle is not honoured when
|
||||
* supplied must be a singleton aspect. True singleton lifecycle is not honored when
|
||||
* using this method - the caller is responsible for managing the lifecycle of any
|
||||
* aspects added in this way.
|
||||
* @param aspectInstance the AspectJ aspect instance
|
||||
|
||||
+2
-2
@@ -119,7 +119,7 @@ final class InstantiationModelAwarePointcutAdvisorImpl
|
||||
|
||||
/**
|
||||
* The pointcut for Spring AOP to use.
|
||||
* Actual behaviour of the pointcut will change depending on the state of the advice.
|
||||
* Actual behavior of the pointcut will change depending on the state of the advice.
|
||||
*/
|
||||
@Override
|
||||
public Pointcut getPointcut() {
|
||||
@@ -258,7 +258,7 @@ final class InstantiationModelAwarePointcutAdvisorImpl
|
||||
|
||||
|
||||
/**
|
||||
* Pointcut implementation that changes its behaviour when the advice is instantiated.
|
||||
* Pointcut implementation that changes its behavior when the advice is instantiated.
|
||||
* Note that this is a <i>dynamic</i> pointcut; otherwise it might be optimized out
|
||||
* if it does not at first match statically.
|
||||
*/
|
||||
|
||||
+1
-1
@@ -82,7 +82,7 @@ public class DelegatePerTargetObjectIntroductionInterceptor extends Introduction
|
||||
|
||||
/**
|
||||
* Subclasses may need to override this if they want to perform custom
|
||||
* behaviour in around advice. However, subclasses should invoke this
|
||||
* behavior in around advice. However, subclasses should invoke this
|
||||
* method, which handles introduced interfaces and forwarding to the target.
|
||||
*/
|
||||
@Override
|
||||
|
||||
+1
-1
@@ -97,7 +97,7 @@ public class DelegatingIntroductionInterceptor extends IntroductionInfoSupport
|
||||
|
||||
/**
|
||||
* Subclasses may need to override this if they want to perform custom
|
||||
* behaviour in around advice. However, subclasses should invoke this
|
||||
* behavior in around advice. However, subclasses should invoke this
|
||||
* method, which handles introduced interfaces and forwarding to the target.
|
||||
*/
|
||||
@Override
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ import org.springframework.aop.Pointcut;
|
||||
* Convenient superclass when we want to force subclasses to
|
||||
* implement MethodMatcher interface, but subclasses
|
||||
* will want to be pointcuts. The getClassFilter() method can
|
||||
* be overridden to customize ClassFilter behaviour as well.
|
||||
* be overridden to customize ClassFilter behavior as well.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
*/
|
||||
|
||||
+36
-44
@@ -23,7 +23,6 @@ import java.util.Map;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import test.annotation.EmptySpringAnnotation;
|
||||
import test.annotation.transaction.Tx;
|
||||
@@ -37,6 +36,7 @@ import org.springframework.beans.testfixture.beans.IOther;
|
||||
import org.springframework.beans.testfixture.beans.ITestBean;
|
||||
import org.springframework.beans.testfixture.beans.TestBean;
|
||||
import org.springframework.beans.testfixture.beans.subpkg.DeepBean;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
@@ -50,30 +50,22 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
*/
|
||||
class AspectJExpressionPointcutTests {
|
||||
|
||||
private Method getAge;
|
||||
|
||||
private Method setAge;
|
||||
|
||||
private Method setSomeNumber;
|
||||
|
||||
private final Method getAge = ClassUtils.getMethod(TestBean.class, "getAge");
|
||||
private final Method setAge = ClassUtils.getMethod(TestBean.class, "setAge", int.class);
|
||||
private final Method setSomeNumber = ClassUtils.getMethod(TestBean.class, "setSomeNumber", Number.class);
|
||||
private final Map<String, Method> methodsOnHasGeneric = new HashMap<>();
|
||||
|
||||
|
||||
@BeforeEach
|
||||
void setup() throws NoSuchMethodException {
|
||||
getAge = TestBean.class.getMethod("getAge");
|
||||
setAge = TestBean.class.getMethod("setAge", int.class);
|
||||
setSomeNumber = TestBean.class.getMethod("setSomeNumber", Number.class);
|
||||
|
||||
AspectJExpressionPointcutTests() throws NoSuchMethodException {
|
||||
// Assumes no overloading
|
||||
for (Method method : HasGeneric.class.getMethods()) {
|
||||
methodsOnHasGeneric.put(method.getName(), method);
|
||||
this.methodsOnHasGeneric.put(method.getName(), method);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void testMatchExplicit() {
|
||||
void matchExplicit() {
|
||||
String expression = "execution(int org.springframework.beans.testfixture.beans.TestBean.getAge())";
|
||||
|
||||
Pointcut pointcut = getPointcut(expression);
|
||||
@@ -91,7 +83,7 @@ class AspectJExpressionPointcutTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMatchWithTypePattern() {
|
||||
void matchWithTypePattern() {
|
||||
String expression = "execution(* *..TestBean.*Age(..))";
|
||||
|
||||
Pointcut pointcut = getPointcut(expression);
|
||||
@@ -110,12 +102,12 @@ class AspectJExpressionPointcutTests {
|
||||
|
||||
|
||||
@Test
|
||||
void testThis() throws SecurityException, NoSuchMethodException{
|
||||
void thisCase() throws SecurityException, NoSuchMethodException{
|
||||
testThisOrTarget("this");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTarget() throws SecurityException, NoSuchMethodException {
|
||||
void target() throws SecurityException, NoSuchMethodException {
|
||||
testThisOrTarget("target");
|
||||
}
|
||||
|
||||
@@ -139,12 +131,12 @@ class AspectJExpressionPointcutTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testWithinRootPackage() throws SecurityException, NoSuchMethodException {
|
||||
void withinRootPackage() throws SecurityException, NoSuchMethodException {
|
||||
testWithinPackage(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testWithinRootAndSubpackages() throws SecurityException, NoSuchMethodException {
|
||||
void withinRootAndSubpackages() throws SecurityException, NoSuchMethodException {
|
||||
testWithinPackage(true);
|
||||
}
|
||||
|
||||
@@ -168,7 +160,7 @@ class AspectJExpressionPointcutTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFriendlyErrorOnNoLocationClassMatching() {
|
||||
void friendlyErrorOnNoLocationClassMatching() {
|
||||
AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> pc.getClassFilter().matches(ITestBean.class))
|
||||
@@ -176,7 +168,7 @@ class AspectJExpressionPointcutTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFriendlyErrorOnNoLocation2ArgMatching() {
|
||||
void friendlyErrorOnNoLocation2ArgMatching() {
|
||||
AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> pc.getMethodMatcher().matches(getAge, ITestBean.class))
|
||||
@@ -184,7 +176,7 @@ class AspectJExpressionPointcutTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFriendlyErrorOnNoLocation3ArgMatching() {
|
||||
void friendlyErrorOnNoLocation3ArgMatching() {
|
||||
AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> pc.getMethodMatcher().matches(getAge, ITestBean.class, (Object[]) null))
|
||||
@@ -193,7 +185,7 @@ class AspectJExpressionPointcutTests {
|
||||
|
||||
|
||||
@Test
|
||||
void testMatchWithArgs() {
|
||||
void matchWithArgs() {
|
||||
String expression = "execution(void org.springframework.beans.testfixture.beans.TestBean.setSomeNumber(Number)) && args(Double)";
|
||||
|
||||
Pointcut pointcut = getPointcut(expression);
|
||||
@@ -214,7 +206,7 @@ class AspectJExpressionPointcutTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSimpleAdvice() {
|
||||
void simpleAdvice() {
|
||||
String expression = "execution(int org.springframework.beans.testfixture.beans.TestBean.getAge())";
|
||||
CallCountingInterceptor interceptor = new CallCountingInterceptor();
|
||||
TestBean testBean = getAdvisedProxy(expression, interceptor);
|
||||
@@ -227,7 +219,7 @@ class AspectJExpressionPointcutTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDynamicMatchingProxy() {
|
||||
void dynamicMatchingProxy() {
|
||||
String expression = "execution(void org.springframework.beans.testfixture.beans.TestBean.setSomeNumber(Number)) && args(Double)";
|
||||
CallCountingInterceptor interceptor = new CallCountingInterceptor();
|
||||
TestBean testBean = getAdvisedProxy(expression, interceptor);
|
||||
@@ -241,7 +233,7 @@ class AspectJExpressionPointcutTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInvalidExpression() {
|
||||
void invalidExpression() {
|
||||
String expression = "execution(void org.springframework.beans.testfixture.beans.TestBean.setSomeNumber(Number) && args(Double)";
|
||||
assertThat(getPointcut(expression).getClassFilter().matches(Object.class)).isFalse();
|
||||
}
|
||||
@@ -271,20 +263,20 @@ class AspectJExpressionPointcutTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testWithUnsupportedPointcutPrimitive() {
|
||||
void withUnsupportedPointcutPrimitive() {
|
||||
String expression = "call(int org.springframework.beans.testfixture.beans.TestBean.getAge())";
|
||||
assertThat(getPointcut(expression).getClassFilter().matches(Object.class)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAndSubstitution() {
|
||||
void andSubstitution() {
|
||||
AspectJExpressionPointcut pc = getPointcut("execution(* *(..)) and args(String)");
|
||||
String expr = pc.getPointcutExpression().getPointcutExpression();
|
||||
assertThat(expr).isEqualTo("execution(* *(..)) && args(String)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMultipleAndSubstitutions() {
|
||||
void multipleAndSubstitutions() {
|
||||
AspectJExpressionPointcut pc = getPointcut("execution(* *(..)) and args(String) and this(Object)");
|
||||
String expr = pc.getPointcutExpression().getPointcutExpression();
|
||||
assertThat(expr).isEqualTo("execution(* *(..)) && args(String) && this(Object)");
|
||||
@@ -297,7 +289,7 @@ class AspectJExpressionPointcutTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMatchGenericArgument() {
|
||||
void matchGenericArgument() {
|
||||
String expression = "execution(* set*(java.util.List<org.springframework.beans.testfixture.beans.TestBean>) )";
|
||||
AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut();
|
||||
ajexp.setExpression(expression);
|
||||
@@ -316,7 +308,7 @@ class AspectJExpressionPointcutTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMatchVarargs() throws Exception {
|
||||
void matchVarargs() throws Exception {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
class MyTemplate {
|
||||
@@ -342,19 +334,19 @@ class AspectJExpressionPointcutTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMatchAnnotationOnClassWithAtWithin() throws Exception {
|
||||
void matchAnnotationOnClassWithAtWithin() throws Exception {
|
||||
String expression = "@within(test.annotation.transaction.Tx)";
|
||||
testMatchAnnotationOnClass(expression);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMatchAnnotationOnClassWithoutBinding() throws Exception {
|
||||
void matchAnnotationOnClassWithoutBinding() throws Exception {
|
||||
String expression = "within(@test.annotation.transaction.Tx *)";
|
||||
testMatchAnnotationOnClass(expression);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMatchAnnotationOnClassWithSubpackageWildcard() throws Exception {
|
||||
void matchAnnotationOnClassWithSubpackageWildcard() throws Exception {
|
||||
String expression = "within(@(test.annotation..*) *)";
|
||||
AspectJExpressionPointcut springAnnotatedPc = testMatchAnnotationOnClass(expression);
|
||||
assertThat(springAnnotatedPc.matches(TestBean.class.getMethod("setName", String.class), TestBean.class)).isFalse();
|
||||
@@ -366,7 +358,7 @@ class AspectJExpressionPointcutTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMatchAnnotationOnClassWithExactPackageWildcard() throws Exception {
|
||||
void matchAnnotationOnClassWithExactPackageWildcard() throws Exception {
|
||||
String expression = "within(@(test.annotation.transaction.*) *)";
|
||||
testMatchAnnotationOnClass(expression);
|
||||
}
|
||||
@@ -384,7 +376,7 @@ class AspectJExpressionPointcutTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAnnotationOnMethodWithFQN() throws Exception {
|
||||
void annotationOnMethodWithFQN() throws Exception {
|
||||
String expression = "@annotation(test.annotation.transaction.Tx)";
|
||||
AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut();
|
||||
ajexp.setExpression(expression);
|
||||
@@ -398,7 +390,7 @@ class AspectJExpressionPointcutTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAnnotationOnCglibProxyMethod() throws Exception {
|
||||
void annotationOnCglibProxyMethod() throws Exception {
|
||||
String expression = "@annotation(test.annotation.transaction.Tx)";
|
||||
AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut();
|
||||
ajexp.setExpression(expression);
|
||||
@@ -410,7 +402,7 @@ class AspectJExpressionPointcutTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNotAnnotationOnCglibProxyMethod() throws Exception {
|
||||
void notAnnotationOnCglibProxyMethod() throws Exception {
|
||||
String expression = "!@annotation(test.annotation.transaction.Tx)";
|
||||
AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut();
|
||||
ajexp.setExpression(expression);
|
||||
@@ -422,7 +414,7 @@ class AspectJExpressionPointcutTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAnnotationOnDynamicProxyMethod() throws Exception {
|
||||
void annotationOnDynamicProxyMethod() throws Exception {
|
||||
String expression = "@annotation(test.annotation.transaction.Tx)";
|
||||
AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut();
|
||||
ajexp.setExpression(expression);
|
||||
@@ -434,7 +426,7 @@ class AspectJExpressionPointcutTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNotAnnotationOnDynamicProxyMethod() throws Exception {
|
||||
void notAnnotationOnDynamicProxyMethod() throws Exception {
|
||||
String expression = "!@annotation(test.annotation.transaction.Tx)";
|
||||
AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut();
|
||||
ajexp.setExpression(expression);
|
||||
@@ -446,7 +438,7 @@ class AspectJExpressionPointcutTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAnnotationOnMethodWithWildcard() throws Exception {
|
||||
void annotationOnMethodWithWildcard() throws Exception {
|
||||
String expression = "execution(@(test.annotation..*) * *(..))";
|
||||
AspectJExpressionPointcut anySpringMethodAnnotation = new AspectJExpressionPointcut();
|
||||
anySpringMethodAnnotation.setExpression(expression);
|
||||
@@ -462,7 +454,7 @@ class AspectJExpressionPointcutTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAnnotationOnMethodArgumentsWithFQN() throws Exception {
|
||||
void annotationOnMethodArgumentsWithFQN() throws Exception {
|
||||
String expression = "@args(*, test.annotation.EmptySpringAnnotation))";
|
||||
AspectJExpressionPointcut takesSpringAnnotatedArgument2 = new AspectJExpressionPointcut();
|
||||
takesSpringAnnotatedArgument2.setExpression(expression);
|
||||
@@ -491,7 +483,7 @@ class AspectJExpressionPointcutTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAnnotationOnMethodArgumentsWithWildcards() throws Exception {
|
||||
void annotationOnMethodArgumentsWithWildcards() throws Exception {
|
||||
String expression = "execution(* *(*, @(test..*) *))";
|
||||
AspectJExpressionPointcut takesSpringAnnotatedArgument2 = new AspectJExpressionPointcut();
|
||||
takesSpringAnnotatedArgument2.setExpression(expression);
|
||||
|
||||
+2
-2
@@ -31,7 +31,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
class BeanNamePointcutMatchingTests {
|
||||
|
||||
@Test
|
||||
void testMatchingPointcuts() {
|
||||
void matchingPointcuts() {
|
||||
assertMatch("someName", "bean(someName)");
|
||||
|
||||
// Spring bean names are less restrictive compared to AspectJ names (methods, types etc.)
|
||||
@@ -66,7 +66,7 @@ class BeanNamePointcutMatchingTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNonMatchingPointcuts() {
|
||||
void nonMatchingPointcuts() {
|
||||
assertMisMatch("someName", "bean(someNamex)");
|
||||
assertMisMatch("someName", "bean(someX*Name)");
|
||||
|
||||
|
||||
+7
-8
@@ -17,7 +17,6 @@
|
||||
package org.springframework.aop.aspectj;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
@@ -49,17 +48,17 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
class MethodInvocationProceedingJoinPointTests {
|
||||
|
||||
@Test
|
||||
void testingBindingWithJoinPoint() {
|
||||
void bindingWithJoinPoint() {
|
||||
assertThatIllegalStateException().isThrownBy(AbstractAspectJAdvice::currentJoinPoint);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testingBindingWithProceedingJoinPoint() {
|
||||
void bindingWithProceedingJoinPoint() {
|
||||
assertThatIllegalStateException().isThrownBy(AbstractAspectJAdvice::currentJoinPoint);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCanGetMethodSignatureFromJoinPoint() {
|
||||
void canGetMethodSignatureFromJoinPoint() {
|
||||
final Object raw = new TestBean();
|
||||
// Will be set by advice during a method call
|
||||
final int newAge = 23;
|
||||
@@ -106,9 +105,9 @@ class MethodInvocationProceedingJoinPointTests {
|
||||
assertThat(AbstractAspectJAdvice.currentJoinPoint().getSignature()).as("Return same MethodSignature repeatedly").isSameAs(msig);
|
||||
assertThat(AbstractAspectJAdvice.currentJoinPoint()).as("Return same JoinPoint repeatedly").isSameAs(AbstractAspectJAdvice.currentJoinPoint());
|
||||
assertThat(msig.getDeclaringType()).isEqualTo(method.getDeclaringClass());
|
||||
assertThat(Arrays.equals(method.getParameterTypes(), msig.getParameterTypes())).isTrue();
|
||||
assertThat(method.getParameterTypes()).isEqualTo(msig.getParameterTypes());
|
||||
assertThat(msig.getReturnType()).isEqualTo(method.getReturnType());
|
||||
assertThat(Arrays.equals(method.getExceptionTypes(), msig.getExceptionTypes())).isTrue();
|
||||
assertThat(method.getExceptionTypes()).isEqualTo(msig.getExceptionTypes());
|
||||
msig.toLongString();
|
||||
msig.toShortString();
|
||||
});
|
||||
@@ -118,7 +117,7 @@ class MethodInvocationProceedingJoinPointTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCanGetSourceLocationFromJoinPoint() {
|
||||
void canGetSourceLocationFromJoinPoint() {
|
||||
final Object raw = new TestBean();
|
||||
ProxyFactory pf = new ProxyFactory(raw);
|
||||
pf.addAdvisor(ExposeInvocationInterceptor.ADVISOR);
|
||||
@@ -135,7 +134,7 @@ class MethodInvocationProceedingJoinPointTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCanGetStaticPartFromJoinPoint() {
|
||||
void canGetStaticPartFromJoinPoint() {
|
||||
final Object raw = new TestBean();
|
||||
ProxyFactory pf = new ProxyFactory(raw);
|
||||
pf.addAdvisor(ExposeInvocationInterceptor.ADVISOR);
|
||||
|
||||
+5
-5
@@ -43,14 +43,14 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
class TrickyAspectJPointcutExpressionTests {
|
||||
|
||||
@Test
|
||||
void testManualProxyJavaWithUnconditionalPointcut() {
|
||||
void manualProxyJavaWithUnconditionalPointcut() {
|
||||
TestService target = new TestServiceImpl();
|
||||
LogUserAdvice logAdvice = new LogUserAdvice();
|
||||
testAdvice(new DefaultPointcutAdvisor(logAdvice), logAdvice, target, "TestServiceImpl");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testManualProxyJavaWithStaticPointcut() {
|
||||
void manualProxyJavaWithStaticPointcut() {
|
||||
TestService target = new TestServiceImpl();
|
||||
LogUserAdvice logAdvice = new LogUserAdvice();
|
||||
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
|
||||
@@ -59,7 +59,7 @@ class TrickyAspectJPointcutExpressionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testManualProxyJavaWithDynamicPointcut() {
|
||||
void manualProxyJavaWithDynamicPointcut() {
|
||||
TestService target = new TestServiceImpl();
|
||||
LogUserAdvice logAdvice = new LogUserAdvice();
|
||||
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
|
||||
@@ -68,7 +68,7 @@ class TrickyAspectJPointcutExpressionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testManualProxyJavaWithDynamicPointcutAndProxyTargetClass() {
|
||||
void manualProxyJavaWithDynamicPointcutAndProxyTargetClass() {
|
||||
TestService target = new TestServiceImpl();
|
||||
LogUserAdvice logAdvice = new LogUserAdvice();
|
||||
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
|
||||
@@ -77,7 +77,7 @@ class TrickyAspectJPointcutExpressionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testManualProxyJavaWithStaticPointcutAndTwoClassLoaders() throws Exception {
|
||||
void manualProxyJavaWithStaticPointcutAndTwoClassLoaders() throws Exception {
|
||||
|
||||
LogUserAdvice logAdvice = new LogUserAdvice();
|
||||
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
|
||||
|
||||
+3
-3
@@ -97,7 +97,7 @@ class TypePatternClassFilterTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEquals() {
|
||||
void equals() {
|
||||
TypePatternClassFilter filter1 = new TypePatternClassFilter("org.springframework.beans.testfixture.beans.*");
|
||||
TypePatternClassFilter filter2 = new TypePatternClassFilter("org.springframework.beans.testfixture.beans.*");
|
||||
TypePatternClassFilter filter3 = new TypePatternClassFilter("org.springframework.tests.*");
|
||||
@@ -107,7 +107,7 @@ class TypePatternClassFilterTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHashCode() {
|
||||
void hashCodeBehavior() {
|
||||
TypePatternClassFilter filter1 = new TypePatternClassFilter("org.springframework.beans.testfixture.beans.*");
|
||||
TypePatternClassFilter filter2 = new TypePatternClassFilter("org.springframework.beans.testfixture.beans.*");
|
||||
TypePatternClassFilter filter3 = new TypePatternClassFilter("org.springframework.tests.*");
|
||||
@@ -117,7 +117,7 @@ class TypePatternClassFilterTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testToString() {
|
||||
void toStringOutput() {
|
||||
TypePatternClassFilter filter1 = new TypePatternClassFilter("org.springframework.beans.testfixture.beans.*");
|
||||
TypePatternClassFilter filter2 = new TypePatternClassFilter("org.springframework.beans.testfixture.beans.*");
|
||||
|
||||
|
||||
+2
-2
@@ -430,8 +430,8 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(itb::getAge);
|
||||
}
|
||||
|
||||
// TODO document this behaviour.
|
||||
// Is it different AspectJ behaviour, at least for checked exceptions?
|
||||
// TODO document this behavior.
|
||||
// Is it different AspectJ behavior, at least for checked exceptions?
|
||||
@Test
|
||||
void aspectMethodThrowsExceptionIllegalOnSignature() {
|
||||
TestBean target = new TestBean();
|
||||
|
||||
+5
-6
@@ -39,7 +39,7 @@ class AspectJPointcutAdvisorTests {
|
||||
|
||||
|
||||
@Test
|
||||
void testSingleton() throws SecurityException, NoSuchMethodException {
|
||||
void singleton() throws SecurityException, NoSuchMethodException {
|
||||
AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut();
|
||||
ajexp.setExpression(CommonExpressions.MATCH_ALL_METHODS);
|
||||
|
||||
@@ -53,7 +53,7 @@ class AspectJPointcutAdvisorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPerTarget() throws SecurityException, NoSuchMethodException {
|
||||
void perTarget() throws SecurityException, NoSuchMethodException {
|
||||
AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut();
|
||||
ajexp.setExpression(CommonExpressions.MATCH_ALL_METHODS);
|
||||
|
||||
@@ -63,8 +63,7 @@ class AspectJPointcutAdvisorTests {
|
||||
1, "someBean");
|
||||
|
||||
assertThat(ajpa.getAspectMetadata().getPerClausePointcut()).isNotSameAs(Pointcut.TRUE);
|
||||
boolean condition = ajpa.getAspectMetadata().getPerClausePointcut() instanceof AspectJExpressionPointcut;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(ajpa.getAspectMetadata().getPerClausePointcut()).isInstanceOf(AspectJExpressionPointcut.class);
|
||||
assertThat(ajpa.isPerInstance()).isTrue();
|
||||
|
||||
assertThat(ajpa.getAspectMetadata().getPerClausePointcut().getClassFilter().matches(TestBean.class)).isTrue();
|
||||
@@ -76,13 +75,13 @@ class AspectJPointcutAdvisorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPerCflowTarget() {
|
||||
void perCflowTarget() {
|
||||
assertThatExceptionOfType(AopConfigException.class).isThrownBy(() ->
|
||||
testIllegalInstantiationModel(AbstractAspectJAdvisorFactoryTests.PerCflowAspect.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPerCflowBelowTarget() {
|
||||
void perCflowBelowTarget() {
|
||||
assertThatExceptionOfType(AopConfigException.class).isThrownBy(() ->
|
||||
testIllegalInstantiationModel(AbstractAspectJAdvisorFactoryTests.PerCflowBelowAspect.class));
|
||||
}
|
||||
|
||||
+9
-9
@@ -39,13 +39,13 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
class AspectProxyFactoryTests {
|
||||
|
||||
@Test
|
||||
void testWithNonAspect() {
|
||||
void withNonAspect() {
|
||||
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new TestBean());
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> proxyFactory.addAspect(TestBean.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testWithSimpleAspect() {
|
||||
void withSimpleAspect() {
|
||||
TestBean bean = new TestBean();
|
||||
bean.setAge(2);
|
||||
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(bean);
|
||||
@@ -55,7 +55,7 @@ class AspectProxyFactoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testWithPerThisAspect() {
|
||||
void withPerThisAspect() {
|
||||
TestBean bean1 = new TestBean();
|
||||
TestBean bean2 = new TestBean();
|
||||
|
||||
@@ -75,14 +75,14 @@ class AspectProxyFactoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testWithInstanceWithNonAspect() {
|
||||
void withInstanceWithNonAspect() {
|
||||
AspectJProxyFactory pf = new AspectJProxyFactory();
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> pf.addAspect(new TestBean()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void testSerializable() throws Exception {
|
||||
void serializable() throws Exception {
|
||||
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new TestBean());
|
||||
proxyFactory.addAspect(LoggingAspectOnVarargs.class);
|
||||
ITestBean proxy = proxyFactory.getProxy();
|
||||
@@ -92,7 +92,7 @@ class AspectProxyFactoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testWithInstance() throws Exception {
|
||||
void withInstance() throws Exception {
|
||||
MultiplyReturnValue aspect = new MultiplyReturnValue();
|
||||
int multiple = 3;
|
||||
aspect.setMultiple(multiple);
|
||||
@@ -111,14 +111,14 @@ class AspectProxyFactoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testWithNonSingletonAspectInstance() {
|
||||
void withNonSingletonAspectInstance() {
|
||||
AspectJProxyFactory pf = new AspectJProxyFactory();
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> pf.addAspect(new PerThisAspect()));
|
||||
}
|
||||
|
||||
@Test // SPR-13328
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testProxiedVarargsWithEnumArray() {
|
||||
void proxiedVarargsWithEnumArray() {
|
||||
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new TestBean());
|
||||
proxyFactory.addAspect(LoggingAspectOnVarargs.class);
|
||||
ITestBean proxy = proxyFactory.getProxy();
|
||||
@@ -127,7 +127,7 @@ class AspectProxyFactoryTests {
|
||||
|
||||
@Test // SPR-13328
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testUnproxiedVarargsWithEnumArray() {
|
||||
void unproxiedVarargsWithEnumArray() {
|
||||
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new TestBean());
|
||||
proxyFactory.addAspect(LoggingAspectOnSetter.class);
|
||||
ITestBean proxy = proxyFactory.getProxy();
|
||||
|
||||
+5
-5
@@ -47,7 +47,7 @@ class AspectJNamespaceHandlerTests {
|
||||
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
void setUp() {
|
||||
SourceExtractor sourceExtractor = new PassThroughSourceExtractor();
|
||||
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this.registry);
|
||||
XmlReaderContext readerContext =
|
||||
@@ -56,7 +56,7 @@ class AspectJNamespaceHandlerTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRegisterAutoProxyCreator() {
|
||||
void registerAutoProxyCreator() {
|
||||
AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(this.parserContext, null);
|
||||
assertThat(registry.getBeanDefinitionCount()).as("Incorrect number of definitions registered").isEqualTo(1);
|
||||
|
||||
@@ -65,7 +65,7 @@ class AspectJNamespaceHandlerTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRegisterAspectJAutoProxyCreator() {
|
||||
void registerAspectJAutoProxyCreator() {
|
||||
AopNamespaceUtils.registerAspectJAutoProxyCreatorIfNecessary(this.parserContext, null);
|
||||
assertThat(registry.getBeanDefinitionCount()).as("Incorrect number of definitions registered").isEqualTo(1);
|
||||
|
||||
@@ -77,7 +77,7 @@ class AspectJNamespaceHandlerTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRegisterAspectJAutoProxyCreatorWithExistingAutoProxyCreator() {
|
||||
void registerAspectJAutoProxyCreatorWithExistingAutoProxyCreator() {
|
||||
AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(this.parserContext, null);
|
||||
assertThat(registry.getBeanDefinitionCount()).isEqualTo(1);
|
||||
|
||||
@@ -89,7 +89,7 @@ class AspectJNamespaceHandlerTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRegisterAutoProxyCreatorWhenAspectJAutoProxyCreatorAlreadyExists() {
|
||||
void registerAutoProxyCreatorWhenAspectJAutoProxyCreatorAlreadyExists() {
|
||||
AopNamespaceUtils.registerAspectJAutoProxyCreatorIfNecessary(this.parserContext, null);
|
||||
assertThat(registry.getBeanDefinitionCount()).isEqualTo(1);
|
||||
|
||||
|
||||
+21
-16
@@ -33,6 +33,7 @@ import org.springframework.aop.aspectj.AspectJExpressionPointcut;
|
||||
import org.springframework.aop.aspectj.AspectJMethodBeforeAdvice;
|
||||
import org.springframework.aop.aspectj.AspectJPointcutAdvisor;
|
||||
import org.springframework.aop.support.DefaultPointcutAdvisor;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -48,24 +49,21 @@ class AspectJPrecedenceComparatorTests {
|
||||
private static final int LATE_ADVICE_DECLARATION_ORDER = 10;
|
||||
|
||||
|
||||
private AspectJPrecedenceComparator comparator;
|
||||
private final AspectJPrecedenceComparator comparator = new AspectJPrecedenceComparator();
|
||||
|
||||
private Method anyOldMethod;
|
||||
private final Method anyOldMethod = ClassUtils.getMethod(MessageService.class, "getMessage");
|
||||
|
||||
private AspectJExpressionPointcut anyOldPointcut;
|
||||
private final AspectJExpressionPointcut anyOldPointcut = new AspectJExpressionPointcut();
|
||||
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
this.comparator = new AspectJPrecedenceComparator();
|
||||
this.anyOldMethod = getClass().getMethods()[0];
|
||||
this.anyOldPointcut = new AspectJExpressionPointcut();
|
||||
void setUp() {
|
||||
this.anyOldPointcut.setExpression("execution(* *(..))");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void testSameAspectNoAfterAdvice() {
|
||||
void sameAspectNoAfterAdvice() {
|
||||
Advisor advisor1 = createAspectJBeforeAdvice(HIGH_PRECEDENCE_ADVISOR_ORDER, EARLY_ADVICE_DECLARATION_ORDER, "someAspect");
|
||||
Advisor advisor2 = createAspectJBeforeAdvice(HIGH_PRECEDENCE_ADVISOR_ORDER, LATE_ADVICE_DECLARATION_ORDER, "someAspect");
|
||||
assertThat(this.comparator.compare(advisor1, advisor2)).as("advisor1 sorted before advisor2").isEqualTo(-1);
|
||||
@@ -76,7 +74,7 @@ class AspectJPrecedenceComparatorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSameAspectAfterAdvice() {
|
||||
void sameAspectAfterAdvice() {
|
||||
Advisor advisor1 = createAspectJAfterAdvice(HIGH_PRECEDENCE_ADVISOR_ORDER, EARLY_ADVICE_DECLARATION_ORDER, "someAspect");
|
||||
Advisor advisor2 = createAspectJAroundAdvice(HIGH_PRECEDENCE_ADVISOR_ORDER, LATE_ADVICE_DECLARATION_ORDER, "someAspect");
|
||||
assertThat(this.comparator.compare(advisor1, advisor2)).as("advisor2 sorted before advisor1").isEqualTo(1);
|
||||
@@ -87,14 +85,14 @@ class AspectJPrecedenceComparatorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSameAspectOneOfEach() {
|
||||
void sameAspectOneOfEach() {
|
||||
Advisor advisor1 = createAspectJAfterAdvice(HIGH_PRECEDENCE_ADVISOR_ORDER, EARLY_ADVICE_DECLARATION_ORDER, "someAspect");
|
||||
Advisor advisor2 = createAspectJBeforeAdvice(HIGH_PRECEDENCE_ADVISOR_ORDER, LATE_ADVICE_DECLARATION_ORDER, "someAspect");
|
||||
assertThat(this.comparator.compare(advisor1, advisor2)).as("advisor1 and advisor2 not comparable").isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSameAdvisorPrecedenceDifferentAspectNoAfterAdvice() {
|
||||
void sameAdvisorPrecedenceDifferentAspectNoAfterAdvice() {
|
||||
Advisor advisor1 = createAspectJBeforeAdvice(HIGH_PRECEDENCE_ADVISOR_ORDER, EARLY_ADVICE_DECLARATION_ORDER, "someAspect");
|
||||
Advisor advisor2 = createAspectJBeforeAdvice(HIGH_PRECEDENCE_ADVISOR_ORDER, LATE_ADVICE_DECLARATION_ORDER, "someOtherAspect");
|
||||
assertThat(this.comparator.compare(advisor1, advisor2)).as("nothing to say about order here").isEqualTo(0);
|
||||
@@ -105,7 +103,7 @@ class AspectJPrecedenceComparatorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSameAdvisorPrecedenceDifferentAspectAfterAdvice() {
|
||||
void sameAdvisorPrecedenceDifferentAspectAfterAdvice() {
|
||||
Advisor advisor1 = createAspectJAfterAdvice(HIGH_PRECEDENCE_ADVISOR_ORDER, EARLY_ADVICE_DECLARATION_ORDER, "someAspect");
|
||||
Advisor advisor2 = createAspectJAroundAdvice(HIGH_PRECEDENCE_ADVISOR_ORDER, LATE_ADVICE_DECLARATION_ORDER, "someOtherAspect");
|
||||
assertThat(this.comparator.compare(advisor1, advisor2)).as("nothing to say about order here").isEqualTo(0);
|
||||
@@ -116,7 +114,7 @@ class AspectJPrecedenceComparatorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHigherAdvisorPrecedenceNoAfterAdvice() {
|
||||
void higherAdvisorPrecedenceNoAfterAdvice() {
|
||||
Advisor advisor1 = createSpringAOPBeforeAdvice(HIGH_PRECEDENCE_ADVISOR_ORDER);
|
||||
Advisor advisor2 = createAspectJBeforeAdvice(LOW_PRECEDENCE_ADVISOR_ORDER, EARLY_ADVICE_DECLARATION_ORDER, "someOtherAspect");
|
||||
assertThat(this.comparator.compare(advisor1, advisor2)).as("advisor1 sorted before advisor2").isEqualTo(-1);
|
||||
@@ -127,7 +125,7 @@ class AspectJPrecedenceComparatorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHigherAdvisorPrecedenceAfterAdvice() {
|
||||
void higherAdvisorPrecedenceAfterAdvice() {
|
||||
Advisor advisor1 = createAspectJAfterAdvice(HIGH_PRECEDENCE_ADVISOR_ORDER, EARLY_ADVICE_DECLARATION_ORDER, "someAspect");
|
||||
Advisor advisor2 = createAspectJAroundAdvice(LOW_PRECEDENCE_ADVISOR_ORDER, LATE_ADVICE_DECLARATION_ORDER, "someOtherAspect");
|
||||
assertThat(this.comparator.compare(advisor1, advisor2)).as("advisor1 sorted before advisor2").isEqualTo(-1);
|
||||
@@ -138,7 +136,7 @@ class AspectJPrecedenceComparatorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLowerAdvisorPrecedenceNoAfterAdvice() {
|
||||
void lowerAdvisorPrecedenceNoAfterAdvice() {
|
||||
Advisor advisor1 = createAspectJBeforeAdvice(LOW_PRECEDENCE_ADVISOR_ORDER, EARLY_ADVICE_DECLARATION_ORDER, "someAspect");
|
||||
Advisor advisor2 = createAspectJBeforeAdvice(HIGH_PRECEDENCE_ADVISOR_ORDER, EARLY_ADVICE_DECLARATION_ORDER, "someOtherAspect");
|
||||
assertThat(this.comparator.compare(advisor1, advisor2)).as("advisor1 sorted after advisor2").isEqualTo(1);
|
||||
@@ -149,7 +147,7 @@ class AspectJPrecedenceComparatorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLowerAdvisorPrecedenceAfterAdvice() {
|
||||
void lowerAdvisorPrecedenceAfterAdvice() {
|
||||
Advisor advisor1 = createAspectJAfterAdvice(LOW_PRECEDENCE_ADVISOR_ORDER, EARLY_ADVICE_DECLARATION_ORDER, "someAspect");
|
||||
Advisor advisor2 = createAspectJAroundAdvice(HIGH_PRECEDENCE_ADVISOR_ORDER, LATE_ADVICE_DECLARATION_ORDER, "someOtherAspect");
|
||||
assertThat(this.comparator.compare(advisor1, advisor2)).as("advisor1 sorted after advisor2").isEqualTo(1);
|
||||
@@ -209,4 +207,11 @@ class AspectJPrecedenceComparatorTests {
|
||||
return advisor;
|
||||
}
|
||||
|
||||
static class MessageService {
|
||||
|
||||
public String getMessage() {
|
||||
return "test";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -35,7 +35,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
class MethodInvocationTests {
|
||||
|
||||
@Test
|
||||
void testValidInvocation() throws Throwable {
|
||||
void validInvocation() throws Throwable {
|
||||
Method method = Object.class.getMethod("hashCode");
|
||||
Object proxy = new Object();
|
||||
Object returnValue = new Object();
|
||||
@@ -49,7 +49,7 @@ class MethodInvocationTests {
|
||||
* toString on target can cause failure.
|
||||
*/
|
||||
@Test
|
||||
void testToStringDoesntHitTarget() throws Throwable {
|
||||
void toStringDoesntHitTarget() throws Throwable {
|
||||
Object target = new TestBean() {
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user