Compare commits

..

269 Commits

Author SHA1 Message Date
Spring Builds 9bd6aef090 Release v6.1.4 2024-02-15 10:13:25 +00:00
Juergen Hoeller f41c7ceaf7 Upgrade to Undertow 2.3.11 2024-02-15 10:55:28 +01:00
Juergen Hoeller 9a068869ef Upgrade to Reactor 2023.0.3, Micrometer 1.12.3, Context Propagation 1.1.1
Includes Groovy 4.0.18, Netty 4.1.107, OpenPDF 1.3.40, Protobuf 3.25.2
2024-02-15 10:13:47 +01:00
Sébastien Deleuze d147609e04 Update a reference to the build pipeline 2024-02-15 10:11:14 +01:00
Stéphane Nicoll e9a359f873 Fix pipeline reference 2024-02-15 10:08:19 +01:00
rstoyanchev 78f0688ed0 Fix javadoc error
See gh-32205
2024-02-14 16:48:58 +00:00
rstoyanchev 504b7619bd WebSocketMessageBrokerConfigurer allows to configure Lifecycle phase
Closes gh-32205
2024-02-14 16:26:33 +00:00
Brian Clozel f9ae54d91e Allow sending SSE events without data
Prior to this commit, the SseBuilder API in WebMvc.fn would only allow
to send Server-Sent Events with `data:` items in them. The spec doesnn't
disallow this and other specifications rely on such patterns to signal
the completion of a stream, like:

```
event:next
data:some data

event:complete

```

This commit adds a new `send()` method without any argument that sends
the buffered content (id, event comment and retry) without data.

Fixes: gh-32270
2024-02-14 16:58:02 +01:00
Cirus Thenter 2284254d39 Fix Kotlin syntax errors in RestClient builder example
Closes gh-32265
2024-02-14 13:52:33 +01:00
Sébastien Deleuze 6e9607ce99 Use double dot in META-INF/aop.xml documentation
Closes gh-32264
2024-02-14 10:42:15 +01:00
Arjen Poutsma b4bec4ca61 Allow repeatable writes in Client Interceptors
See gh-31449
2024-02-14 10:05:26 +01:00
Stéphane Nicoll 48da9524c3 Polish 2024-02-14 02:40:02 +01:00
Sam Brannen 68189f3de9 Document that "functions are variables" in SpEL evaluation contexts
Although EvaluationContext defines the API for setting and looking up
variables, the internals of the Spring Expression Language (SpEL)
actually provide explicit support for registering functions as
variables.

This is self-evident in the two registerFunction() variants in
StandardEvaluationContext; however, functions can also be registered as
variables when using the SimpleEvaluationContext.

Since custom functions are also viable in use cases involving the
SimpleEvaluationContext, this commit documents that functions may be
registered in a SimpleEvaluationContext via setVariable().

This commit also explicitly documents the "function as a variable"
behavior in the class-level Javadoc for both StandardEvaluationContext
and SimpleEvaluationContext, as well as in the reference manual.

Closes gh-32258
2024-02-13 17:22:35 +01:00
Sam Brannen 10bc93c058 Polishing 2024-02-13 17:22:35 +01:00
Simon Baslé 9b93c948a7 Infer reflection hints for Jackson annotations builder attributes
This notably enables Jackson to reflectively call a user-provided
builder class and invoke its declared methods (setters and build) in
a native app.

Closes gh-32238
2024-02-13 15:14:08 +01:00
Sébastien Deleuze abe381488a Add missing RequestPredicate variants in coRouter
Closes gh-32256
2024-02-13 14:50:49 +01:00
Sébastien Deleuze cc6dd19324 Polishing 2024-02-13 14:29:18 +01:00
Sébastien Deleuze 2fe3321813 Fix pathExtension null-safety in Kotlin DSLs
Closes gh-32254
2024-02-13 14:24:19 +01:00
Sébastien Deleuze 8c3fc8c549 Add documentation for functional resource handling
Closes gh-27257
2024-02-13 13:36:48 +01:00
Sébastien Deleuze d3ca6f9f6a Add resource redirection to WebMVC functional router
See gh-27257
2024-02-13 13:24:13 +01:00
Sébastien Deleuze 2d7b2e59b6 Add resource redirection to WebFlux functional router
See gh-27257
2024-02-13 13:24:13 +01:00
Sam Brannen 46108deff4 Make SimpleEvaluationContext.Builder final and its constructor private
Closes gh-32253
2024-02-13 13:18:54 +01:00
Sam Brannen 4454b3b5ef Polishing 2024-02-13 13:15:51 +01:00
rstoyanchev 2dd22f64e1 Improve cancellation of read/write inactivity
The cancellation of read and write inactivity tasks was done via
WebSocketHandler#afterConnectionClosed, relying on the WebSocket
library to always invoke the callback.

This change moves the cancellation to the `close` method instead
that in turn is called from DefaultStompSession#resetConnection,
in effect making the cancellation more proactive and aligned with
connection cleanup in DefaultStompSession vs relying on a
subsequent call from the WebSocket library after the connection
is closed.

Closes gh-32195
2024-02-13 11:17:34 +00:00
rstoyanchev 6be0432e3d Use ConnectionLostException after heartbeat failures
ConnectionLostException was applies only after the WebSocket library
notified us of a session close. However, read inactivity and heartbeat
send failures are also cases of the connection being lost rather than
closed intentionally.

This commit also ensures resetConnection is called after a heartbeat
write failure, consistent with other places where a transport error
is handled that implies the connection is lost.

See gh-32195
2024-02-13 11:17:34 +00:00
rstoyanchev 6a5953dca3 Polishing in WebSocketStompClient
See gh-32195
2024-02-13 11:17:34 +00:00
Juergen Hoeller b4153618a4 Consistent Lock field declaration (instead of ReentrantLock field type) 2024-02-13 11:07:20 +01:00
Juergen Hoeller 0b09f1e12f Use ReentrantLock instead of synchronization for concurrency throttle
Closes gh-32251
2024-02-13 11:03:40 +01:00
rstoyanchev 120ea0a51c Update user info pattern
Closes gh-32211
2024-02-13 07:09:46 +00:00
Stéphane Nicoll c46d6286ee Review AOT recommendations in the reference guide
This commit focuses on improving the AOT section on programmatic
registration of additional beans. This makes it more clear that a
`BeanDefinitionRegistry` must be used and that singletons are not
processed.

Closes gh-32240
Closes gh-32241
2024-02-12 16:48:26 +01:00
Arjen Poutsma 750cb73902 Introduce single-value request predicates
This commit introduces new HTTP method, Content-Type, and Accept header
request predicates that handle single values. Previously, these
predicates were always dealt with as single-value collections, which
introduced computational overhead.

Closes gh-32244
2024-02-12 16:09:00 +01:00
rstoyanchev 5851cdc679 Refine invocation of checkSessions
It makes more sense to call this from afterConnectionEstablished as it
relates to the creation of new sessions.

See gh-32195
2024-02-12 12:50:45 +00:00
Stéphane Nicoll 2e833d908a Merge pull request #32231 from OlgaMaciaszek
* pr/32231:
  Polish "Document @RequestAttribute"
  Document @RequestAttribute

Closes gh-32231
2024-02-12 10:39:00 +01:00
Stéphane Nicoll 728d5eeb74 Polish "Document @RequestAttribute"
See gh-32231
2024-02-12 10:38:29 +01:00
Olga MaciaszekSharma 89e34ae5ff Document @RequestAttribute
See gh-32231
2024-02-12 10:35:20 +01:00
Stéphane Nicoll 9ee7c6383f Merge pull request #32232 from OlgaMaciaszek
* pr/32232:
  Update copyright year of changed file
  Fix supportsRequestAttributes for RestClientAdapter

Closes gh-32232
2024-02-12 10:31:26 +01:00
Stéphane Nicoll ee801d1b28 Update copyright year of changed file
See gh-32232
2024-02-12 10:29:16 +01:00
Olga MaciaszekSharma eebdff23e7 Fix supportsRequestAttributes for RestClientAdapter
Previously, RestClientAdapter claimed that it supports request
attributes when, in fact, it does not. This commit updates the
implementation accordingly.

See gh-32232
2024-02-12 10:27:32 +01:00
Juergen Hoeller a2000dba33 Leniently accept tasks after context close in lifecycle stop phase
Schedulers remain strict, just plain executors are lenient on shutdown now.
An early shutdown for executors can be enforced via setStrictEarlyShutdown.

Closes gh-32226
2024-02-11 21:33:53 +01:00
Sam Brannen 4a3ef3e24a Document safe navigation semantics within compound expressions in SpEL
Closes gh-21827
2024-02-11 18:36:14 +01:00
Sam Brannen 4a5dc7c1b0 Document null-safe collection selection/projection support in SpEL
Closes gh-32208
2024-02-11 17:21:47 +01:00
Sam Brannen 347d085020 Polishing 2024-02-11 17:14:24 +01:00
Sam Brannen f295def2a8 Include function name in SpelMessage.INCORRECT_NUMBER_OF_ARGUMENTS_TO_FUNCTION
Closes gh-32239
2024-02-11 13:52:25 +01:00
Sam Brannen dc2dbd9700 Polishing 2024-02-11 13:52:25 +01:00
Sam Brannen 6b67972ec4 Polishing 2024-02-10 16:56:02 +01:00
Sam Brannen 64fc9ee301 Test function registration with SimpleEvaluationContext 2024-02-10 16:55:21 +01:00
Sam Brannen ce43d1b1da Simplify logic in ApplicationContextAwareProcessor.postProcessBeforeInitialization() 2024-02-10 15:46:38 +01:00
Sam Brannen dc73ec76fc Address TODOs in SpEL's Indexer
This commit deletes outdated TODOs and addresses a remaining "current"
TODO in SpEL's Indexer.
2024-02-10 15:39:18 +01:00
Sam Brannen 888e50175d Polish SpEL Javadocs and internals 2024-02-10 15:39:18 +01:00
Sam Brannen 1080c145e3 Polish ApplicationContextAwareProcessor 2024-02-10 11:46:09 +01:00
Stéphane Nicoll f726e806cd Merge pull request #32236 from spencergibb
* pr/32236:
  Polish "Fixes syntax error in JdbcClient examples"
  Fixes syntax error in JdbcClient examples

Closes gh-32236
2024-02-10 09:04:55 +01:00
Stéphane Nicoll b1f6401e4f Polish "Fixes syntax error in JdbcClient examples"
See gh-32236
2024-02-10 09:04:44 +01:00
Spencer Gibb 169b9abeef Fixes syntax error in JdbcClient examples
See gh-32236
2024-02-10 09:01:02 +01:00
Sam Brannen e72b523995 Polish SpEL support 2024-02-09 14:04:08 +01:00
Sébastien Deleuze 99bdc4211d Add Coroutines support to NonReactiveHandlerMethodPredicate
Closes gh-32227
2024-02-09 12:07:17 +01:00
Stéphane Nicoll d47c69746b Upgrade CI to Ubuntu Jammy 20240125 2024-02-08 19:03:42 +01:00
Sam Brannen 052bbcc530 Cache parameter types array in ClassUtils.findInterfaceMethodIfPossible() 2024-02-08 18:14:22 +01:00
Arjen Poutsma f9791664ef Implement MatchableHandlerMapping in RouterFunctionMapping
Closes gh-32221
2024-02-08 12:22:14 +01:00
Arjen Poutsma af44b3e6c0 Fix delegation in ServerRequest decorators
Closes gh-31955
2024-02-08 11:33:22 +01:00
John Gesimondo 2724c6d8fe Update beanvalidation.adoc
FieldErrro to FieldError
2024-02-07 23:27:31 +01:00
anil.senocak f7e5c9fbb2 In Kotlin variables should be defined as val or var. "mockMvc" was not defined properly 2024-02-07 23:27:11 +01:00
Patrick Strawderman 4486ab1cb7 Initialize Map with correct size in RequestPredicates
Fix another instance where a LinkedHashMap was initialized with an initial
capacity that would always cause a resize / rehash to occur. Switch to
CollectionUtils.newLinkedHashMap to size the map appropiately for the expected
number of items.

Closes gh-32215
2024-02-07 21:20:57 +01:00
Sam Brannen 78c96b6d78 Fix SpEL collection selection/projection examples in reference manual
This commit also updates and polishes the documentation tests.
2024-02-07 18:49:01 +01:00
Sam Brannen 43bbe8f3e8 Add tests for collection selection with Iterables 2024-02-07 13:32:22 +01:00
Sam Brannen 7d612e8958 Polishing 2024-02-07 13:29:33 +01:00
Sam Brannen 9a38355896 Improve tests for indexing and collection selection/projection in SpEL 2024-02-07 11:46:13 +01:00
Juergen Hoeller 3ecbc4de13 Polishing 2024-02-06 17:57:47 +01:00
Juergen Hoeller 81c156eefb Replace public hasRestTemplateDefaults() method with hasBaseUri()
See gh-32180
2024-02-06 17:57:27 +01:00
Juergen Hoeller d8c4a33bea Upgrade to SLF4J 2.0.12, Jetty 12.0.6, Apache HttpClient 5.3.1, OpenPDF 1.3.39, Mockito 5.10, Checkstyle 10.13 2024-02-06 17:54:26 +01:00
Juergen Hoeller cfa47fa4fb Polishing 2024-02-06 16:46:16 +01:00
Juergen Hoeller 80949eb30f Store known attribute names in session (for distributed sessions)
Closes gh-30463
2024-02-06 16:46:11 +01:00
Juergen Hoeller 4ed337247c Avoid sendError call when response committed already (Tomcat 10.1.16)
Closes gh-32206
2024-02-06 16:46:04 +01:00
Sam Brannen 81cdfafa78 Polishing 2024-02-06 12:55:25 +01:00
Patrick Strawderman d5cb1d9adb Initialize Map with correct size in RequestPredicates
Prior to this commit, the `RequestPredicates` would add new attributes
to the existing request attributes by creating a new `LinkedHashMap`
with the total number of elements as its new initial capacity.

This would not achieve optimal performance as initial resize or rehash
operations could be expected. Consistently using
`CollectionUtils#newLinkedHashMap` avoids this problem.

Closes gh-32201
2024-02-06 10:54:00 +01:00
Juergen Hoeller 9698dbc232 Add javadoc and rename merge method to mergeProperties
See gh-32118
2024-02-06 09:13:09 +01:00
Stéphane Nicoll 9c15b3fa4c Upgrade to AssertJ 3.25.3 2024-02-06 08:33:22 +01:00
Andrei Bastun c559ec4dfb Refactor ReloadableResourceBundleMessageSource
This change allows subclasses to reuse collecting and merging
algorithm when overriding getMergedProperties method.
2024-02-06 08:23:51 +01:00
Juergen Hoeller 341ac76209 Rely on HashSet for uniqueness of mapped names
See gh-32199
2024-02-06 08:20:15 +01:00
Juergen Hoeller 8ff102115a Let BeanPropertyRowMapper subclasses customize mapped names
Closes gh-32199
2024-02-05 18:24:38 +01:00
Brian Clozel f50a262cf2 Polish
See gh-32189
2024-02-05 11:16:32 +01:00
Arjen Poutsma c570f3b2da Fix off-by-one error in PartEvent part count
This commit fixes an off-by-one error in the
PartEventHttpMessageReader,  so that it no longer counts empty windows.

Closes gh-32122
2024-02-05 11:04:24 +01:00
Patrick Strawderman 0fdf759896 Optimize Map methods in ServletAttributesMap
ServletAttributesMap inherited default implementations of the size
and isEmpty methods from AbstractMap which delegates to the Set returned
by entrySet. ServletAttributesMap's entrySet method made this fairly
expensive, since it would copy the attributes to a List, then use a
Stream to build the Set. To avoid the cost, add implementations of
isEmpty / size that don't need to call entrySet at all.

Additionally, change entrySet to return a Set view that simply lazily
delegates to the underlying servlet request for iteration.

Closes gh-32189
2024-02-05 10:33:14 +01:00
Stéphane Nicoll c04d4da9a3 Polish 2024-02-05 09:11:04 +01:00
Sam Brannen b737f36f39 Upgrade to JUnit 5.10.2 2024-02-04 14:00:56 +01:00
Stéphane Nicoll 8d601384d3 Upgrade to Gradle 8.6
Closes gh-32192
2024-02-03 11:38:56 +01:00
Stéphane Nicoll ea52ecc5e0 Polish 2024-02-02 18:56:41 +01:00
Sam Brannen 9b5febea20 Document SpEL limitations for minimum values for numeric literals
Closes gh-20779
2024-02-02 15:43:42 +01:00
Sam Brannen 7627d8c6fc Improve layout for Literal Expressions section 2024-02-02 15:43:42 +01:00
Sam Brannen 20f91b7dc3 Fix tabs in SpEL "Classes Used in the Examples" section 2024-02-02 15:43:42 +01:00
Arjen Poutsma e15c150696 Only copy UriBuilderFactory when customized
This commit ensures that, when creating a RestClient.Builder from a
RestTemplate, the UriBuilderFactory is only copied if it has been
changed from the default values.
Before this commit, the UriBuilderFactory was effectively alway copied,
resulting in not being able to use a baseUrl.

This commit also introduces a small memory optimization in
DefaultUriBuilderFactory, so that default environment variables are
created lazily.

Closes gh-32180
2024-02-02 15:16:10 +01:00
Sam Brannen 7025b7aac2 Provide example for calculating Integer.MIN_VALUE with SpEL's power operator 2024-02-02 14:43:56 +01:00
Sam Brannen 1e432ff95d Improve documentation for overloaded operators in SpEL
See gh-32182
2024-02-02 14:39:09 +01:00
Juergen Hoeller ae17b11b70 Preserve overridden toString result in HandlerMethod copy constructor
Closes gh-32184
2024-02-01 19:14:11 +01:00
Juergen Hoeller 1a783f41aa Use target class for candidate retrieval but not for method matching
Closes gh-32181
See gh-21843
2024-02-01 18:59:45 +01:00
Sam Brannen 521fbfdb85 Update copyright headers in contribution 2024-02-01 17:58:06 +01:00
Siddik ACIL 87377d6f3e Fix modelAttribitePredicate typos
This commit replaces occurrences of modelAttribitePredicate with modelAttributePredicate
in HandlerMethodValidationException and the associated validator.

Closes gh-32183
2024-02-01 17:56:07 +01:00
Sam Brannen af2934c09b Document support for overloading operators in SpEL in reference manual
Closes gh-32182
2024-02-01 17:24:25 +01:00
Sam Brannen 17ee82e004 Organize and clean up SpEL documentation tests 2024-02-01 16:22:14 +01:00
Sam Brannen a82108ec73 Clarify semantics of SpEL's between operator
See gh-32140
2024-02-01 14:58:21 +01:00
Stéphane Nicoll d4401cc69a Polish 2024-02-01 14:40:02 +01:00
Sam Brannen 2367314b52 Polish SpEL chapter 2024-02-01 13:57:25 +01:00
Sam Brannen 80a3c1923f Link to Jakarta EL instead of mentioning Unified EL in SpEL overview
As a bit of trivia, Jakarta EL was originally Unified EL, which was
originally JSP EL, which was originally SPEL.

SPEL: Simplest Possible Expression Language
SpEL: Spring Expression Language

So one could say that SPEL inspired SpEL.
2024-02-01 13:50:14 +01:00
Sam Brannen c989cba963 Add Checkstyle rule to enforce use of class literals for primitives & void
This commit introduces a globally applied Checkstyle rule which
enforces the use of class literals for primitive types and void by
forbidding the use of the TYPE constants in Boolean, Character, Byte,
Short, Integer, Long, Float, Double, and Void.

For example, if MyClass uses one of the TYPE constants, the build will
now fail with a message similar to the following.

[ERROR] <...>/MyClass.java:39: Please use class literals for primitives and void -- for example, int.class instead of Integer.TYPE.
2024-02-01 12:51:35 +01:00
Juergen Hoeller 3d4d68c26f Run listener/send task locally as fallback on RejectedExecutionException
Closes gh-32171
2024-02-01 11:07:02 +01:00
Stéphane Nicoll b61552b9df Merge pull request #32176 from quaff
* pr/32176:
  Fix assertion in BeanWrapperAutoGrowingTests

Closes gh-32176
2024-02-01 09:35:01 +01:00
Yanming Zhou 615973cce4 Fix assertion in BeanWrapperAutoGrowingTests
See gh-32176
2024-02-01 09:34:32 +01:00
Juergen Hoeller 00577ed80a Polishing 2024-01-31 17:12:20 +01:00
Juergen Hoeller 9b2b485444 Disabled test for auto-growing nested map values
See gh-32154
2024-01-31 17:12:12 +01:00
Juergen Hoeller d586513d66 Introduce SqlBinaryValue/SqlCharacterValue as alternative to SqlLobValue
Includes direct byte array support via setBytes in StatementCreatorUtils.

Closes gh-32161
2024-01-31 17:12:05 +01:00
Sam Brannen 2b52582dff Polishing 2024-01-31 16:41:15 +01:00
Stéphane Nicoll 67958656e4 Polish 2024-01-31 15:01:03 +01:00
Stéphane Nicoll a4db0e7448 Merge pull request #32164 from quaff
* pr/32164:
  Polish contribution
  Polish StatementCreatorUtilsTests

Closes gh-32164
2024-01-31 08:57:53 +01:00
Stéphane Nicoll ef2cae36ac Polish contribution
See gh-32164
2024-01-31 08:57:11 +01:00
Yanming Zhou efaf41862c Polish StatementCreatorUtilsTests
See gh-32164
2024-01-31 08:56:59 +01:00
Juergen Hoeller af5acb6d34 Avoid pre-conversion attempt in case of overloaded write methods
Closes gh-32159
See gh-31872
2024-01-30 21:57:14 +01:00
Sam Brannen 067638ae6e Introduce ClassUtils.isVoidType() utility method 2024-01-30 16:27:34 +01:00
Sam Brannen 398cc01650 Polishing 2024-01-30 16:24:55 +01:00
Stéphane Nicoll 3a518b60e7 Merge pull request #32157 from jee14
* pr/32157:
  Upgrade copyright year of changed file
  Wrap ternary operator within parantheses

Closes gh-32157
2024-01-30 15:59:39 +01:00
Stéphane Nicoll 298f308ce1 Upgrade copyright year of changed file
See gh-32157
2024-01-30 15:59:06 +01:00
jee14 6ffb74def3 Wrap ternary operator within parantheses
See gh-32157
2024-01-30 15:57:51 +01:00
Stéphane Nicoll b55a4d3908 Prevent AOT from failing with spring-orm without JPA
This commit improves PersistenceManagedTypesBeanRegistrationAotProcessor
so that it does not attempt to load JPA classes when checking for the
presence of a PersistenceManagedTypes bean. To make it more clear a
check on the presence for JPA has been added to prevent the nested
classes to be loaded regardless of the presence of the bean.

Closes gh-32155
2024-01-30 15:43:07 +01:00
Sam Brannen db535863dd Consistently use class literals for primitive types
To improve consistency and avoid confusion regarding primitive types
and their wrapper types, this commit ensures that we always use class
literals for primitive types.

For example, instead of using the `Void.TYPE` constant, we now
consistently use `void.class`.
2024-01-30 15:26:12 +01:00
Stéphane Nicoll 95a3f3bb6e Upgrade CI to Java 22ea+33 2024-01-29 21:19:53 +01:00
Sam Brannen 84cce6018c Document the between operator in SpEL
Closes gh-32140
2024-01-29 18:34:12 +01:00
Sam Brannen e97fc7be38 Improve documentation for alternative textual operators in SpEL 2024-01-29 17:47:46 +01:00
Sam Brannen 9eae0ba50e Clean up warnings in build 2024-01-29 17:47:46 +01:00
Sam Brannen f3c8102882 Polish SpEL Tokenizer 2024-01-29 17:47:46 +01:00
Juergen Hoeller 005d5ef922 Polishing 2024-01-29 16:53:35 +01:00
Juergen Hoeller 5dc26460fb Eagerly initialize ZERO_NANOS constant 2024-01-29 16:48:08 +01:00
Sam Brannen 542502b2b6 Upgrade to AssertJ 3.25.2 2024-01-29 16:08:04 +01:00
Sam Brannen f6d8443781 Fix logic in SupplierUtils 2024-01-29 16:07:44 +01:00
Juergen Hoeller a44341ece3 Consistent method declaration order in Call/TableMetaDataProvider 2024-01-29 15:25:55 +01:00
Juergen Hoeller 969b18b0e8 Polishing 2024-01-29 15:25:43 +01:00
Juergen Hoeller 2e9d6a1d4e Polishing 2024-01-29 13:04:42 +01:00
Juergen Hoeller 7e5efdd8dd Reuse MapPropertySource for DynamicValuesPropertySource implementation
Closes gh-32110
2024-01-29 13:04:02 +01:00
Juergen Hoeller 08e6df8832 Revise shutdown phase log message and executor shutdown documentation
Closes gh-32109
2024-01-29 13:03:20 +01:00
Juergen Hoeller a738e4d5fd Explicit documentation note on cron-vs-quartz parsing convention
Closes gh-32128
2024-01-29 13:02:43 +01:00
Sébastien Deleuze 9c4b4ab81e Update basics.adoc
Closes gh-32145
2024-01-29 09:08:37 +01:00
Sam Brannen 0ee2d41528 Delete obsolete test utility method
Commit dc6ce30663 made this method obsolete.
2024-01-28 18:44:56 +01:00
Sam Brannen dc6ce30663 Polishing 2024-01-28 18:31:31 +01:00
Sam Brannen 62fa3f11c1 Correctly request primitive conversion in SpEL's Indexer
Prior to this commit, SpEL's Indexer incorrectly requested conversion
to wrappers instead of primitives when setting an element in a
primitive array.

This commit addresses this by requesting primitive conversion -- for
example, conversion to `int.class` instead of `Integer.class` when
setting a value in an `int[]` array.

For greater clarity, this commit also switches from using `TYPE`
constants in wrapper classes to primitive class literals -- for
example, from `Integer.TYPE` to `int.class`.

Closes gh-32147
2024-01-28 18:26:39 +01:00
Sam Brannen 2e56361fe4 Simplify implementation of internal VariableNotAvailableException
Since VariableNotAvailableException is not a public type, there is no
need to store the variable name in a field/property.
2024-01-28 17:05:30 +01:00
Sam Brannen 9b0162da49 Document increment and decrement operators in SpEL
Closes gh-32136
2024-01-28 16:43:55 +01:00
Sam Brannen ab98210e6d Polishing 2024-01-28 16:43:55 +01:00
Sam Brannen 97ad479250 Sync assignment operator test with example used in reference manual 2024-01-28 16:43:15 +01:00
Sam Brannen 24d6565cad Provide example for SpEL's exponential power operator (^) 2024-01-28 16:43:15 +01:00
Sam Brannen e34ad6bf5f Support prefix notation for SpEL increment/decrement in AST representation
Closes gh-32144
2024-01-28 15:21:07 +01:00
Sam Brannen 179b976964 Introduce tests for SpEL's Inc/Dec operators and polishing 2024-01-28 15:10:15 +01:00
Sam Brannen 1ff84671f8 Remove obsolete InProgressTests
Since SpEL is no longer "in progress", this commit removes the obsolete
InProgressTests class and moves all non-duplicated test cases to other
test classes.
2024-01-28 14:25:37 +01:00
Sam Brannen e1c22c5385 Clean up InProgressTests 2024-01-28 13:29:34 +01:00
Sam Brannen 3f30a1540c Additional SpEL setValue() tests and polishing 2024-01-28 13:29:22 +01:00
Sam Brannen ae9153e644 Polish SpEL-related tests 2024-01-27 19:09:02 +01:00
Sam Brannen 003407a7e3 Polish Javadoc for SpelEvaluationException and Expression 2024-01-27 19:09:02 +01:00
Stéphane Nicoll a7764dc61d Merge pull request #32141 from kzander91
* pr/32141:
  Polish "Simplify use of Reactor's cast operator"
  Simplify use of Reactor's cast operator

Closes gh-32141
2024-01-27 12:23:24 +01:00
Stéphane Nicoll 4b4778d569 Polish "Simplify use of Reactor's cast operator"
See gh-32141
2024-01-27 12:21:46 +01:00
Kai Zander d96a63944c Simplify use of Reactor's cast operator
This commit replaces filter(x -> x instanceof C).cast(C.class) with the
built-in ofType(C.class).

See gh-32141
2024-01-27 12:21:46 +01:00
Stéphane Nicoll ad7c090f4c Use catalog name in SimpleJdbcInsert
This commit harmonizes SimpleJdbcCall and SimpleJdbcInsert to
consistently use a catalog name if one is set. Previously,
SimpleJdbcInsert only used the catalog name to retrieve database
metadata.

Closes gh-32124
2024-01-26 17:58:41 +01:00
Sam Brannen b9bad56fc1 Document repeat and characer subtraction String operators in SpEL
Closes gh-32137
2024-01-26 17:39:15 +01:00
Sam Brannen fdf0a6f6c7 Polishing 2024-01-26 17:37:45 +01:00
Sam Brannen 86266b3d67 Update documentation for supported letters in variable names in SpEL
Closes gh-32138
2024-01-26 16:42:09 +01:00
Sam Brannen 68cf3b928b Remove obsolete reference to local variable support in SpEL 2024-01-26 16:21:50 +01:00
Sam Brannen 3024c6efa9 Polishing 2024-01-26 15:36:39 +01:00
Sam Brannen 500767a0fb Annotate core functional SPIs in SpEL with @FunctionalInterface
Prior to this commit, only the MethodFilter and ConstructorResolver
functional SPIs in the org.springframework.expression package were
annotated with @⁠FunctionalInterface.

For consistency, this commit designates each of the following
functional SPIs in that package as a @⁠FunctionalInterface as well.

- BeanResolver
- ConstructorExecutor
- MethodExecutor
- MethodResolver

Closes gh-32135
2024-01-26 14:46:10 +01:00
Sam Brannen 5b5319a659 Polishing 2024-01-26 14:45:44 +01:00
Sam Brannen 3ce7c52030 Update copyright headers 2024-01-26 14:11:37 +01:00
Sam Brannen bafcd1dc1c Remove SpEL README and tests for unsupported features 2024-01-26 13:41:50 +01:00
Sam Brannen 00b07659d9 Polish SpEL documentation and tests 2024-01-26 13:41:36 +01:00
Sam Brannen 9df94357de Enable test for argument conversion in SpEL 2024-01-26 12:43:34 +01:00
Sam Brannen 0e45f4cec4 Polishing 2024-01-26 11:08:58 +01:00
Stéphane Nicoll 8815788004 Allow an existing TaskExecutor to be configured in ChannelRegistration
This commit introduces a new method to configure an existing
TaskExecutor in ChannelRegistration. Contrary to
TaskExecutorRegistration, a ThreadPoolTaskExecutor is not necessary,
and it can't be further configured. This includes the thread name
prefix.

Closes gh-32081
2024-01-26 10:46:14 +01:00
Sam Brannen b7e4fa16ca Upgrade com.gradle.enterprise plugin to 3.16.1 2024-01-26 10:37:52 +01:00
Stéphane Nicoll 645d0db260 Merge pull request #32123 from wfouche
* pr/32123:
  Upgrade to gradle-versions-plugin 0.51.0

Closes gh-32123
2024-01-26 06:39:16 +01:00
Werner Fouché f4b3c768e8 Upgrade to gradle-versions-plugin 0.51.0
See gh-32123
2024-01-26 06:39:03 +01:00
Yanming Zhou 6b3bf554ce Fix typo
Introduced by commit f9726ae0c8

Closes gh-32111
2024-01-25 08:32:43 +01:00
Juergen Hoeller c6121da151 Polishing 2024-01-24 22:30:33 +01:00
Juergen Hoeller c5a75219ce Compare qualifier value arrays with equality semantics
Closes gh-32106
2024-01-24 22:30:28 +01:00
Juergen Hoeller 89e7174cc4 Share cached interceptors for entire Advised instance if possible
Closes gh-32104
2024-01-24 22:30:22 +01:00
Sébastien Deleuze 4f16297e45 Polishing
See gh-32074
2024-01-24 16:53:34 +01:00
Felipe 11898daed7 Add support for JSON streams to Kotlin Serialization
Closes gh-32074
2024-01-24 16:53:34 +01:00
Stéphane Nicoll 4d7da0059e Revert "Merge pull request #32071 from mnhock"
This reverts commit c3127249ac, reversing
changes made to 11c8b22c5a.

See gh-32071
2024-01-24 14:34:44 +01:00
Arjen Poutsma a4fcef4a62 Upgrade to jetty-reactive-httpclient:4.0.2
Closes gh-31931
2024-01-24 14:09:43 +01:00
Arjen Poutsma f2e267b494 Guard against multiple body subscriptions
Before this commit, the JDK and Jetty connectors do not have any
safeguards against multiple body subscriptions. Such as check has now
been added.

Closes gh-32100
2024-01-24 14:05:20 +01:00
rstoyanchev f9726ae0c8 Update description of web validation in docs
Closes gh-32082
2024-01-24 12:41:05 +00:00
Juergen Hoeller 199a675692 Declare current observation context as available since 6.0.15
See gh-31609
See gh-31646
2024-01-24 12:40:00 +01:00
Juergen Hoeller 2f921dd13d Declare allowPrivateNetwork as available since 5.3.32
See gh-28546
See gh-31974
2024-01-24 12:37:19 +01:00
Juergen Hoeller b92877990d Consistent nullability for concurrent result 2024-01-24 11:43:36 +01:00
Juergen Hoeller d7778c0212 Polishing 2024-01-23 18:31:31 +01:00
Juergen Hoeller 531ac89e7e Expose sendError message as default body
Closes gh-26720
2024-01-23 18:31:26 +01:00
Stéphane Nicoll 358555929d Revert "Merge pull request #32088 from Ryan-Dia"
This reverts commit 484aee069e, reversing
changes made to 6bd7f0231d.

See gh-32088
2024-01-23 14:46:08 +01:00
rstoyanchev 9230a7db16 WebAsyncManager handles disconnected client errors
See gh-32042
2024-01-22 17:17:18 +00:00
Sam Brannen 7e53a1f048 Re-enable Jaxb2MarshallerTests on JDK 22+
Since the bug in JDK 22-ea builds has been fixed [1], this commit
re-enables the disabled tests.

Verified using: OpenJDK Runtime Environment (build 22-ea+31-2314)

[1] https://bugs.openjdk.org/browse/JDK-8322214
2024-01-23 11:36:24 +01:00
Sam Brannen 45a1f98bd6 Polishing 2024-01-23 11:36:24 +01:00
Juergen Hoeller 5faace0eb3 Predetermine validation groups on initialization
Closes gh-32068
2024-01-23 11:15:12 +01:00
Juergen Hoeller 5656eaccb7 Avoid bridge resolution for method from unrelated class hierarchy
Closes gh-32087
2024-01-23 10:50:53 +01:00
Sam Brannen 3b2f6e74a6 Make assertions based on Annotation#toString() lenient
The behavior for the toString() implementation for annotations changed
in JDK 19, per my request to the JDK team (see link below).

Specifically, since JDK 19, the toString() implementation for annotation
proxies created by the JDK started using canonical names instead of
binary names for types.

See https://bugs.openjdk.org/browse/JDK-8281462
2024-01-23 10:41:40 +01:00
Stéphane Nicoll 484aee069e Merge pull request #32088 from Ryan-Dia
* pr/32088:
  Upgrade copyright year of changed file
  Use count in ParamsRequestCondition#getValueMatchCount

Closes gh-32088
2024-01-23 09:04:28 +01:00
Stéphane Nicoll def7075695 Upgrade copyright year of changed file
See gh-32088
2024-01-23 09:00:41 +01:00
ryan-dia 2daa074561 Use count in ParamsRequestCondition#getValueMatchCount
See gh-32088
2024-01-23 08:59:59 +01:00
Juergen Hoeller 6bd7f0231d Avoid double exists() call for common resource resolution
See gh-30369
See gh-18990
2024-01-22 13:40:51 +01:00
Brian Clozel 70d9f7c62c Record errors thrown by custom handler in RestTemplate observations
Prior to this commit, the `RestTemplate` observation instrumentation
would only record `RestClientException` and `IOException` as errors in
the observation. Other types of errors can be thrown by custom
components, such as `ResponseErrorHandler` and in this case they aren't
recorded with the observation.
Also, the current instrumentation does not create any observation scope
around the execution. While this would have a limited benefit as no
application code is executed there, developers could set up custom
components (such as, again, `ResponseErrorHandler`) that could use
contextual logging with trace ids.

This commit ensures that all `Throwable` are recorded as errors with the
observations and that an observation `Scope` is created around the
execution of the client exchange.

Fixes gh-32060
2024-01-22 11:03:57 +01:00
Sam Brannen 5856d2e54e Polish contribution
See gh-32069
2024-01-21 16:27:59 +01:00
mnhock a8fa98e2a6 Remove unnecessary semicolons in enum declarations
Closes gh-32069
2024-01-21 16:25:36 +01:00
Stéphane Nicoll 73725905ba Merge pull request #32072 from mnhock
* pr/32072:
  Use 'Map.getOrDefault' rather than explicit check with fallback

Closes gh-32072
2024-01-20 14:42:50 +01:00
mnhock 1e71d3d363 Use 'Map.getOrDefault' rather than explicit check with fallback
See gh-32072
2024-01-20 14:37:23 +01:00
Stéphane Nicoll c3127249ac Merge pull request #32071 from mnhock
* pr/32071:
  Upgrade copyright year of changed file
  Remove unnecessary conversion to 'String'

Closes gh-32071
2024-01-20 14:35:19 +01:00
Stéphane Nicoll 91b980f5cb Upgrade copyright year of changed file
See gh-32071
2024-01-20 14:35:10 +01:00
mnhock d9e86dd68c Remove unnecessary conversion to 'String'
See gh-32071
2024-01-20 14:33:39 +01:00
Stéphane Nicoll 11c8b22c5a Polish 2024-01-20 14:26:48 +01:00
Sam Brannen 1a52c56bd4 Polishing 2024-01-19 19:16:41 +01:00
Sam Brannen 4cc91a2869 Ensure inherited @⁠HttpExchange annotation can be overridden in controller
This commit revises the RequestMappingHandlerMapping implementations in
Spring MVC and Spring WebFlux to ensure that a @⁠Controller class which
implements an interface annotated with @⁠HttpExchange annotations can
inherit the @⁠HttpExchange declarations from the interface or
optionally override them locally with @⁠HttpExchange or
@⁠RequestMapping annotations.

Closes gh-32065
2024-01-19 18:54:31 +01:00
Sébastien Deleuze 17cef18760 Upgrade Antora extensions versions
Sync with the versions used in the docs-build
branch.

See gh-32044
2024-01-19 18:36:43 +01:00
Juergen Hoeller 00bda65848 Polishing 2024-01-19 17:09:58 +01:00
Sam Brannen 6697461003 Reject mixed @⁠RequestMapping and @⁠HttpExchange declarations in MVC & WebFlux
This commit updates the RequestMappingHandlerMapping implementations in
Spring MVC and Spring WebFlux so that mixed @⁠RequestMapping and
@⁠HttpExchange declarations on the same element are rejected.

Note, however, that a @⁠Controller class which implements an interface
annotated with @⁠HttpExchange annotations can still inherit the
@⁠HttpExchange declarations from the interface or optionally override
them locally with @⁠HttpExchange or @⁠RequestMapping annotations.

See gh-31962
See gh-32049
Closes gh-32065
2024-01-19 16:23:42 +01:00
Arjen Poutsma c820e44a99 Unwrap request factory when creating RestClient from RestTemplate
This commit makes sure that, when building a RestClient based on the
configuration of a RestTemplate, the request factory is unwrapped if it
is a InterceptingClientHttpRequestFactory.

Closes gh-32038
2024-01-19 15:41:31 +01:00
Stéphane Nicoll 472dcdb59c Allow JPA entities to be filtered
This commit provides more control over JPA entities scanning by allowing
the result to be filtered.

Closes gh-27892
2024-01-19 15:38:53 +01:00
Sam Brannen 6691ff2072 Reject multiple @⁠HttpExchange declarations in HTTP interface clients
This commit updates HttpServiceMethod so that multiple @⁠HttpExchange
declarations on the same element (class or method) are rejected.

Closes gh-32049
2024-01-19 13:02:41 +01:00
Stéphane Nicoll 9c6e55939e Improve toString of ExponentialBackOff
Closes gh-32061
2024-01-19 12:10:36 +01:00
Stéphane Nicoll 7e511931b3 Merge pull request #32057 from erichaagdev
* pr/32057:
  Rename Gradle enterprise badge to Develocity

Closes gh-32057
2024-01-19 10:01:00 +01:00
Eric Haag 4bd898c359 Rename Gradle enterprise badge to Develocity
See gh-32057
2024-01-19 10:00:00 +01:00
rstoyanchev 1fba430dfc Update HttpExchange Javadoc to mention uses
See gh-32008
2024-01-18 18:35:28 +00:00
Sam Brannen efe85c0d70 Remove tautological ternary statement 2024-01-18 17:30:07 +01:00
Sam Brannen 2ec0c16889 Polishing 2024-01-18 17:30:07 +01:00
Stéphane Nicoll 899de4f3bf Upgrade SDK env to Java 17.0.10
See b9d366d203
2024-01-18 16:39:11 +01:00
Sam Brannen d1d9d483fe Document @⁠HttpExchange support in RequestMappingHandlerMapping 2024-01-18 16:28:40 +01:00
Arjen Poutsma 375e0e6827 Handle Content-Length in ShallowEtagHeaderFilter more robustly
This commit ensures that setting the Content-Length through
setHeader("Content-Length", x") has the same effect as calling
setContentLength in the ShallowEtagHeaderFilter. It also filters out
Content-Type headers similarly to Content-Length.

Closes gh-32039
2024-01-18 15:42:16 +01:00
Sam Brannen b8b31ff8a1 Reject multiple @⁠HttpExchange declarations in MVC and WebFlux
This commit updates the RequestMappingHandlerMapping implementations in
Spring MVC and Spring WebFlux so that multiple @⁠HttpExchange
declarations on the same element are rejected.

Closes gh-32049
2024-01-18 15:29:30 +01:00
Sam Brannen c5c77b93fe Polishing 2024-01-18 15:29:30 +01:00
Sam Brannen 6b905049eb Polishing
See gh-31962
2024-01-18 14:21:45 +01:00
Sébastien Deleuze 88a7ca0b0a Rename class-data-sharing.adoc to cds.adoc
Closes gh-32044
2024-01-18 10:16:45 +01:00
Brian Clozel e8012a64c3 Upgrade CI image to Ubuntu Jammy 20240111 2024-01-17 21:30:59 +01:00
Brian Clozel b9d366d203 Upgrade CI to JDK 17.0.10+13 21.0.2+14 and 22-ea+31 2024-01-17 21:29:50 +01:00
Stéphane Nicoll f5b0d9509d Polish 2024-01-17 18:41:15 +01:00
Sam Brannen 699da7c383 Log warning if multiple @⁠RequestMapping annotations are declared
If multiple request mapping annotations are discovered, Spring MVC and
Spring WebFlux now log a warning similar to the following (without
newlines).

Multiple @⁠RequestMapping annotations found on
void org.example.MyController.put(), but only the first will be used:
[
@⁠org.springframework.web.bind.annotation.PutMapping(consumes={}, headers={}, name="", params={}, path={"/put"}, produces={}, value={"/put"}),
@⁠org.springframework.web.bind.annotation.PostMapping(consumes={}, headers={}, name="", params={}, path={"/put"}, produces={}, value={"/put"})
]

Closes gh-31962
2024-01-17 17:46:31 +01:00
Sam Brannen 5bf74cae11 Polish documentation for @⁠RequestMapping 2024-01-17 17:46:26 +01:00
Sam Brannen 46128cb4b9 Delete duplicate content 2024-01-17 17:45:17 +01:00
Sébastien Deleuze 5dd48fc068 Mention PathPattern in functional router Javadoc
This commit makes the various supported patterns
more discoverable.

Closes gh-32045
2024-01-17 15:08:34 +01:00
Arjen Poutsma 0ada78ad84 Enable RestClient.defaultRequest
This commit enables the defaultRequest setting in the RestClient
builder.

Closes gh-32028
2024-01-17 11:23:50 +01:00
rstoyanchev 682f4715cf Polishing
See gh-30393
2024-01-17 10:15:09 +00:00
rstoyanchev c4a34fa26c Improve cancel handling in AbstractListenerReadPublisher
Closes gh-30393
2024-01-17 10:15:09 +00:00
Stéphane Nicoll e3f185a696 Upgrade to Gradle Enterprise Plugin 3.15
This reverts commit e1c8a84fec as our
version of Gradle Enterprise is not yet compatible with it.
2024-01-17 08:12:42 +01:00
Stéphane Nicoll 1b312b6f3f Polish 2024-01-16 18:45:59 +01:00
Stéphane Nicoll dc5a21fbd1 Remove outdated note
See gh-20606
2024-01-16 16:43:28 +01:00
Sam Brannen e1c8a84fec Upgrade com.gradle.enterprise plugin to 3.16.1 2024-01-16 15:58:22 +01:00
Sam Brannen 410fe409d2 Upgrade io.spring.ge.conventions plugin to 0.0.15 2024-01-16 15:55:30 +01:00
Stéphane Nicoll e0c5068d0b Fix incorrect assertions using json path
Closes gh-32036
2024-01-16 10:59:43 +01:00
Stéphane Nicoll faba044735 Polish JsonPathExpectationsHelperTests 2024-01-15 17:04:11 +01:00
Stéphane Nicoll 4d885f9aad Polish 2024-01-15 16:45:53 +01:00
Sam Brannen fdf187ec46 Polishing 2024-01-15 15:45:33 +01:00
Stéphane Nicoll 0c42965fc3 Polish 2024-01-15 11:17:19 +01:00
Sam Brannen e1236a8672 Polishing 2024-01-14 16:00:50 +01:00
Sam Brannen 7daff59cb1 Fix alias resolution error message in SimpleAliasRegistry
Prior to this commit, the alias resolution error message in
SimpleAliasRegistry was misleading.

When a resolution conflict is detected, the IllegalStateException
thrown by resolveAliases(...) now states that the resolved alias is
already registered for an `existingName` instead of the `registeredName`.

See gh-31353
Closes gh-32025
2024-01-14 15:19:18 +01:00
Sam Brannen 5d309d5724 Improve SimpleAliasRegistryTests
This commit improves SimpleAliasRegistryTests in the following ways.

- Some existing methods have been split up into smaller test methods
  which focus on a single use case.

- The use of Mockito mocks has been replaced by a hand-crafted
  StubStringValueResolver which ensures that existing aliases and names
  are not accidentally replaced by null thereby removing their removing
  there mappings inadvertently.

- Several test methods now include inline comments that document the
  current state of the aliasMap in order to clarify what is happening
  behind the scenes in the ConcurrentHashMap.

- Several test methods warn that the current expectations are based on
  ConcurrentHashMap iteration order!

- New @⁠ParameterizedTest which is currently @⁠Disabled but
  demonstrates that complex use cases involving placeholder replacement
  can be supported consistently -- regardless of the values of the
  aliases involved -- but only if alias registration order is honored.

Closes gh-31353
2024-01-14 14:22:02 +01:00
Sam Brannen eeb145ee0b Fix link to global appendix 2024-01-13 14:52:57 +01:00
Sam Brannen 9f5cda958e Enable table striping by default in the reference manual
Closes gh-32022
2024-01-13 14:50:16 +01:00
rstoyanchev 47779d6a53 Double-checked lock in ChannelSendOperator#request
Closes gh-31865
2024-01-12 17:15:47 +00:00
Stéphane Nicoll 2a43cc7574 Polish 2024-01-12 17:21:21 +01:00
Sam Brannen c4831d2586 Document that Conditions can be ordered
Closes gh-32019
2024-01-12 16:30:24 +01:00
Sam Brannen e4569defd9 Polish Javadoc for Condition 2024-01-12 16:24:20 +01:00
rstoyanchev 65cb59517d Polishing contribution
Closes gh-32008
2024-01-12 15:15:59 +00:00
Olga MaciaszekSharma b729008f4d Improve docs on server use of @HttpExchange
See gh-32008
2024-01-12 15:15:59 +00:00
Stéphane Nicoll 122d8b9e4e Polish 2024-01-12 14:55:21 +01:00
Arjen Poutsma b16f379788 Improve RestTemplate Javadoc
See gh-32016
2024-01-12 12:52:19 +01:00
Yanming Zhou c868bc554f Refine Kotlin internal modifier impact documentation
This commit is a refinement of 01b2856114.

Closes gh-32010
2024-01-12 12:34:19 +01:00
Sébastien Deleuze 5c77c3739e Find destroy methods in superclass interfaces
Related tests will be added in
https://github.com/spring-projects/spring-aot-smoke-tests.

Closes gh-32006
2024-01-12 11:37:42 +01:00
Stéphane Nicoll ee04442be7 Polish 2024-01-12 09:20:09 +01:00
Stéphane Nicoll f067ff862b Merge pull request #32015 from wfouche
* pr/32015:
  Upgrade to JMH 1.37

Closes gh-32015
2024-01-12 08:43:02 +01:00
Werner Fouché 68864674cc Upgrade to JMH 1.37
See gh-32015
2024-01-12 08:40:47 +01:00
Stéphane Nicoll f1a335708a Polish 2024-01-11 14:05:37 +01:00
Spring Builds d786635239 Next development version (v6.1.4-SNAPSHOT) 2024-01-11 09:11:17 +00:00
906 changed files with 14937 additions and 10242 deletions
+1 -1
View File
@@ -1,3 +1,3 @@
# Enable auto-env through the sdkman_auto_env config
# Add key=value pairs of SDKs to use below
java=17.0.9-librca
java=17.0.10-librca
+1 -1
View File
@@ -1,4 +1,4 @@
# <img src="framework-docs/src/docs/spring-framework.png" width="80" height="80"> Spring Framework [![Build Status](https://ci.spring.io/api/v1/teams/spring-framework/pipelines/spring-framework-6.1.x/jobs/build/badge)](https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-6.1.x?groups=Build") [![Revved up by Gradle Enterprise](https://img.shields.io/badge/Revved%20up%20by-Gradle%20Enterprise-06A0CE?logo=Gradle&labelColor=02303A)](https://ge.spring.io/scans?search.rootProjectNames=spring)
# <img src="framework-docs/src/docs/spring-framework.png" width="80" height="80"> Spring Framework [![Build Status](https://ci.spring.io/api/v1/teams/spring-framework/pipelines/spring-framework-6.1.x/jobs/build/badge)](https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-6.1.x?groups=Build") [![Revved up by Develocity](https://img.shields.io/badge/Revved%20up%20by-Develocity-06A0CE?logo=Gradle&labelColor=02303A)](https://ge.spring.io/scans?search.rootProjectNames=spring)
This is the home of the Spring Framework: the foundation for all [Spring projects](https://spring.io/projects). Collectively the Spring Framework and the family of Spring projects are often referred to simply as "Spring".
+2 -2
View File
@@ -4,7 +4,7 @@ plugins {
id 'org.jetbrains.kotlin.plugin.serialization' version "${kotlinVersion}" apply false
id 'org.jetbrains.dokka' version '1.8.20'
id 'org.unbroken-dome.xjc' version '2.0.0' apply false
id 'com.github.ben-manes.versions' version '0.50.0'
id 'com.github.ben-manes.versions' version '0.51.0'
id 'com.github.johnrengelman.shadow' version '8.1.1' apply false
id 'de.undercouch.download' version '5.4.0'
id 'me.champeau.jmh' version '0.7.2' apply false
@@ -104,7 +104,7 @@ configure([rootProject] + javaProjects) { project ->
// TODO Uncomment link to JUnit 5 docs once we execute Gradle with Java 18+.
// See https://github.com/spring-projects/spring-framework/issues/27497
//
// "https://junit.org/junit5/docs/5.10.1/api/",
// "https://junit.org/junit5/docs/5.10.2/api/",
"https://www.reactive-streams.org/reactive-streams-1.0.3-javadoc/",
//"https://javadoc.io/static/io.rsocket/rsocket-core/1.1.1/",
"https://r2dbc.io/spec/1.0.0.RELEASE/api/",
@@ -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("10.12.7");
checkstyle.setToolVersion("10.13.0");
checkstyle.getConfigDirectory().set(project.getRootProject().file("src/checkstyle"));
String version = SpringJavaFormatPlugin.class.getPackage().getImplementationVersion();
DependencySet checkstyleDependencies = project.getConfigurations().getByName("checkstyle").getDependencies();
+2 -2
View File
@@ -2,7 +2,7 @@
The Spring Framework uses https://concourse-ci.org/[Concourse] for its CI build and other automated tasks.
The Spring team has a dedicated Concourse instance available at https://ci.spring.io with a build pipeline
for https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-6.0.x[Spring Framework 6.0.x].
for https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-6.1.x[Spring Framework 6.1.x].
=== Setting up your development environment
@@ -51,7 +51,7 @@ The pipeline can be deployed using the following command:
[source]
----
$ fly -t spring set-pipeline -p spring-framework-6.0.x -c ci/pipeline.yml -l ci/parameters.yml
$ fly -t spring set-pipeline -p spring-framework-6.1.x -c ci/pipeline.yml -l ci/parameters.yml
----
NOTE: This assumes that you have credhub integration configured with the appropriate secrets.
+1 -1
View File
@@ -1,4 +1,4 @@
FROM ubuntu:jammy-20231211.1
FROM ubuntu:jammy-20240125
ADD setup.sh /setup.sh
ADD get-jdk-url.sh /get-jdk-url.sh
+3 -3
View File
@@ -3,13 +3,13 @@ set -e
case "$1" in
java17)
echo "https://download.bell-sw.com/java/17.0.9+11/bellsoft-jdk17.0.9+11-linux-amd64.tar.gz"
echo "https://github.com/bell-sw/Liberica/releases/download/17.0.10%2B13/bellsoft-jdk17.0.10+13-linux-amd64.tar.gz"
;;
java21)
echo "https://download.bell-sw.com/java/21.0.1+12/bellsoft-jdk21.0.1+12-linux-amd64.tar.gz"
echo "https://github.com/bell-sw/Liberica/releases/download/21.0.2%2B14/bellsoft-jdk21.0.2+14-linux-amd64.tar.gz"
;;
java22)
echo "https://download.java.net/java/early_access/jdk22/28/GPL/openjdk-22-ea+28_linux-x64_bin.tar.gz"
echo "https://download.java.net/java/early_access/jdk22/33/GPL/openjdk-22-ea+33_linux-x64_bin.tar.gz"
;;
*)
echo $"Unknown java version"
+1
View File
@@ -18,6 +18,7 @@ asciidoc:
# FIXME: The package is not renamed
chomp: 'all'
fold: 'all'
table-stripes: 'odd'
include-java: 'example$docs-src/main/java/org/springframework/docs'
spring-site: 'https://spring.io'
spring-site-blog: '{spring-site}/blog'
+2 -2
View File
@@ -28,8 +28,8 @@ antora {
'@antora/atlas-extension': '1.0.0-alpha.1',
'@antora/collector-extension': '1.0.0-alpha.3',
'@asciidoctor/tabs': '1.0.0-beta.3',
'@opendevise/antora-release-line-extension': '1.0.0-alpha.2',
'@springio/antora-extensions': '1.3.0',
'@opendevise/antora-release-line-extension': '1.0.0',
'@springio/antora-extensions': '1.8.2',
'@springio/asciidoctor-extensions': '1.0.0-alpha.9'
]
}
+1 -1
View File
@@ -421,7 +421,7 @@
*** xref:integration/cache/specific-config.adoc[]
** xref:integration/observability.adoc[]
** xref:integration/checkpoint-restore.adoc[]
** xref:integration/class-data-sharing.adoc[]
** xref:integration/cds.adoc[]
** xref:integration/appendix.adoc[]
* xref:languages.adoc[]
** xref:languages/kotlin.adoc[]
@@ -19,7 +19,7 @@ of the classpath -- for example, deployed within the application's JAR file.
The following table lists all currently supported Spring properties.
.Supported Spring Properties
[cols="1,1", stripes=odd]
[cols="1,1"]
|===
| Name | Description
@@ -521,8 +521,8 @@ standard AspectJ. The following example shows the `aop.xml` file:
<aspectj>
<weaver>
<!-- only weave classes in our application-specific packages -->
<include within="com.xyz.*"/>
<!-- only weave classes in our application-specific packages and sub-packages -->
<include within="com.xyz..*"/>
</weaver>
<aspects>
@@ -731,8 +731,8 @@ For example:
<aspectj>
<weaver>
<!-- only weave classes in our application-specific packages -->
<include within="com.xyz.*"/>
<!-- only weave classes in our application-specific packages and sub-packages -->
<include within="com.xyz..*"/>
</weaver>
</aspectj>
+48 -15
View File
@@ -17,8 +17,11 @@ Applying such optimizations early implies the following restrictions:
* The beans defined in your application cannot change at runtime, meaning:
** `@Profile`, in particular profile-specific configuration needs to be chosen at build time.
** `Environment` properties that impact the presence of a bean (`@Conditional`) are only considered at build time.
* Bean definitions with instance suppliers (lambdas or method references) cannot be transformed ahead-of-time (see related {spring-framework-issues}/29555[spring-framework#29555] issue).
* Make sure that the bean type is as precise as possible.
* Bean definitions with instance suppliers (lambdas or method references) cannot be transformed ahead-of-time.
* Beans registered as singletons (using `registerSingleton`, typically from
`ConfigurableListableBeanFactory`) cannot be transformed ahead-of-time either.
* As we cannot rely on the instance, make sure that the bean type is as precise as
possible.
TIP: See also the xref:core/aot.adoc#aot.bestpractices[] section.
@@ -27,7 +30,7 @@ A Spring AOT processed application typically generates:
* Java source code
* Bytecode (usually for dynamic proxies)
* {spring-framework-api}/aot/hint/RuntimeHints.html[`RuntimeHints`] for the use of reflection, resource loading, serialization, and JDK proxies.
* {spring-framework-api}/aot/hint/RuntimeHints.html[`RuntimeHints`] for the use of reflection, resource loading, serialization, and JDK proxies
NOTE: At the moment, AOT is focused on allowing Spring applications to be deployed as native images using GraalVM.
We intend to support more JVM-based use cases in future generations.
@@ -35,7 +38,7 @@ We intend to support more JVM-based use cases in future generations.
[[aot.basics]]
== AOT engine overview
The entry point of the AOT engine for processing an `ApplicationContext` arrangement is `ApplicationContextAotGenerator`. It takes care of the following steps, based on a `GenericApplicationContext` that represents the application to optimize and a {spring-framework-api}/aot/generate/GenerationContext.html[`GenerationContext`]:
The entry point of the AOT engine for processing an `ApplicationContext` is `ApplicationContextAotGenerator`. It takes care of the following steps, based on a `GenericApplicationContext` that represents the application to optimize and a {spring-framework-api}/aot/generate/GenerationContext.html[`GenerationContext`]:
* Refresh an `ApplicationContext` for AOT processing. Contrary to a traditional refresh, this version only creates bean definitions, not bean instances.
* Invoke the available `BeanFactoryInitializationAotProcessor` implementations and apply their contributions against the `GenerationContext`.
@@ -67,7 +70,14 @@ include-code::./AotProcessingSample[tag=aotcontext]
In this mode, xref:core/beans/factory-extension.adoc#beans-factory-extension-factory-postprocessors[`BeanFactoryPostProcessor` implementations] are invoked as usual.
This includes configuration class parsing, import selectors, classpath scanning, etc.
Such steps make sure that the `BeanRegistry` contains the relevant bean definitions for the application.
If bean definitions are guarded by conditions (such as `@Profile`), these are discarded at this stage.
If bean definitions are guarded by conditions (such as `@Profile`), these are evaluated,
and bean definitions that don't match their conditions are discarded at this stage.
If custom code needs to register extra beans programmatically, make sure that custom
registration code uses `BeanDefinitionRegistry` instead of `BeanFactory` as only bean
definitions are taken into account. A good pattern is to implement
`ImportBeanDefinitionRegistrar` and register it via an `@Import` on one of your
configuration classes.
Because this mode does not actually create bean instances, `BeanPostProcessor` implementations are not invoked, except for specific variants that are relevant for AOT processing.
These are:
@@ -84,12 +94,12 @@ Once this part completes, the `BeanFactory` contains the bean definitions that a
Components that want to participate in this step can implement the {spring-framework-api}/beans/factory/aot/BeanFactoryInitializationAotProcessor.html[`BeanFactoryInitializationAotProcessor`] interface.
Each implementation can return an AOT contribution, based on the state of the bean factory.
An AOT contribution is a component that contributes generated code that reproduces a particular behavior.
An AOT contribution is a component that contributes generated code which reproduces a particular behavior.
It can also contribute `RuntimeHints` to indicate the need for reflection, resource loading, serialization, or JDK proxies.
A `BeanFactoryInitializationAotProcessor` implementation can be registered in `META-INF/spring/aot.factories` with a key equal to the fully qualified name of the interface.
A `BeanFactoryInitializationAotProcessor` implementation can be registered in `META-INF/spring/aot.factories` with a key equal to the fully-qualified name of the interface.
A `BeanFactoryInitializationAotProcessor` can also be implemented directly by a bean.
The `BeanFactoryInitializationAotProcessor` interface can also be implemented directly by a bean.
In this mode, the bean provides an AOT contribution equivalent to the feature it provides with a regular runtime.
Consequently, such a bean is automatically excluded from the AOT-optimized context.
@@ -111,7 +121,7 @@ This interface is used as follows:
* Implemented by a `BeanPostProcessor` bean, to replace its runtime behavior.
For instance xref:core/beans/factory-extension.adoc#beans-factory-extension-bpp-examples-aabpp[`AutowiredAnnotationBeanPostProcessor`] implements this interface to generate code that injects members annotated with `@Autowired`.
* Implemented by a type registered in `META-INF/spring/aot.factories` with a key equal to the fully qualified name of the interface.
* Implemented by a type registered in `META-INF/spring/aot.factories` with a key equal to the fully-qualified name of the interface.
Typically used when the bean definition needs to be tuned for specific features of the core framework.
[NOTE]
@@ -207,11 +217,33 @@ However, keep in mind that some optimizations are made at build time based on a
This section lists the best practices that make sure your application is ready for AOT.
[[aot.bestpractices.bean-registration]]
== Programmatic bean registration
The AOT engine takes care of the `@Configuration` model and any callback that might be
invoked as part of processing your configuration. If you need to register additional
beans programmatically, make sure to use a `BeanDefinitionRegistry` to register
bean definitions.
This can typically be done via a `BeanDefinitionRegistryPostProcessor`. Note that, if it
is registered itself as a bean, it will be invoked again at runtime unless you make
sure to implement `BeanFactoryInitializationAotProcessor` as well. A more idiomatic
way is to implement `ImportBeanDefinitionRegistrar` and register it using `@Import` on
one of your configuration classes. This invokes your custom code as part of configuration
class parsing.
If you declare additional beans programmatically using a different callback, they are
likely not going to be handled by the AOT engine, and therefore no hints are going to be
generated for them. Depending on the environment, those beans may not be registered at
all. For instance, classpath scanning does not work in a native image as there is no
notion of a classpath. For cases like this, it is crucial that the scanning happens at
build time.
[[aot.bestpractices.bean-type]]
=== Expose The Most Precise Bean Type
While your application may interact with an interface that a bean implements, it is still very important to declare the most precise type.
The AOT engine performs additional checks on the bean type, such as detecting the presence of `@Autowired` members, or lifecycle callback methods.
The AOT engine performs additional checks on the bean type, such as detecting the presence of `@Autowired` members or lifecycle callback methods.
For `@Configuration` classes, make sure that the return type of the factory `@Bean` method is as precise as possible.
Consider the following example:
@@ -262,10 +294,11 @@ If you are registering bean definitions programmatically, consider using `RootBe
[[aot.bestpractices.constructors]]
=== Avoid Multiple Constructors
The container is able to choose the most appropriate constructor to use based on several candidates.
However, this is not a best practice and flagging the preferred constructor with `@Autowired` if necessary is preferred.
In case you are working on a code base that you can't modify, you can set the {spring-framework-api}/beans/factory/support/AbstractBeanDefinition.html#PREFERRED_CONSTRUCTORS_ATTRIBUTE[`preferredConstructors` attribute] on the related bean definition to indicate which constructor should be used.
In case you are working on a code base that you cannot modify, you can set the {spring-framework-api}/beans/factory/support/AbstractBeanDefinition.html#PREFERRED_CONSTRUCTORS_ATTRIBUTE[`preferredConstructors` attribute] on the related bean definition to indicate which constructor should be used.
[[aot.bestpractices.factory-bean]]
=== FactoryBean
@@ -283,7 +316,7 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class ClientFactoryBean<T extends AbstractClient> implements FactoryBean<T> {
// ...
}
----
======
@@ -380,7 +413,7 @@ Java::
Running an application as a native image requires additional information compared to a regular JVM runtime.
For instance, GraalVM needs to know ahead of time if a component uses reflection.
Similarly, classpath resources are not shipped in a native image unless specified explicitly.
Similarly, classpath resources are not included in a native image unless specified explicitly.
Consequently, if the application needs to load a resource, it must be referenced from the corresponding GraalVM native image configuration file.
The {spring-framework-api}/aot/hint/RuntimeHints.html[`RuntimeHints`] API collects the need for reflection, resource loading, serialization, and JDK proxies at runtime.
@@ -415,7 +448,7 @@ include-code::./SpellCheckService[]
If at all possible, `@ImportRuntimeHints` should be used as close as possible to the component that requires the hints.
This way, if the component is not contributed to the `BeanFactory`, the hints won't be contributed either.
It is also possible to register an implementation statically by adding an entry in `META-INF/spring/aot.factories` with a key equal to the fully qualified name of the `RuntimeHintsRegistrar` interface.
It is also possible to register an implementation statically by adding an entry in `META-INF/spring/aot.factories` with a key equal to the fully-qualified name of the `RuntimeHintsRegistrar` interface.
[[aot.hints.reflective]]
@@ -424,7 +457,7 @@ It is also possible to register an implementation statically by adding an entry
{spring-framework-api}/aot/hint/annotation/Reflective.html[`@Reflective`] provides an idiomatic way to flag the need for reflection on an annotated element.
For instance, `@EventListener` is meta-annotated with `@Reflective` since the underlying implementation invokes the annotated method using reflection.
By default, only Spring beans are considered and an invocation hint is registered for the annotated element.
By default, only Spring beans are considered, and an invocation hint is registered for the annotated element.
This can be tuned by specifying a custom `ReflectiveProcessor` implementation via the
`@Reflective` annotation.
@@ -141,7 +141,7 @@ Kotlin::
====
After you learn about Spring's IoC container, you may want to know more about Spring's
`Resource` abstraction (as described in
xref:web/webflux-webclient/client-builder.adoc#webflux-client-builder-reactor-resources[Resources])
xref:core/resources.adoc[Resources])
which provides a convenient mechanism for reading an InputStream from locations defined
in a URI syntax. In particular, `Resource` paths are used to construct applications contexts,
as described in xref:core/resources.adoc#resources-app-ctx[Application Contexts and Resource Paths].
@@ -1,10 +1,11 @@
[[expressions]]
= Spring Expression Language (SpEL)
The Spring Expression Language ("`SpEL`" for short) is a powerful expression language that
The Spring Expression Language ("SpEL" for short) is a powerful expression language that
supports querying and manipulating an object graph at runtime. The language syntax is
similar to Unified EL but offers additional features, most notably method invocation and
basic string templating functionality.
similar to the https://jakarta.ee/specifications/expression-language/[Jakarta Expression
Language] but offers additional features, most notably method invocation and basic string
templating functionality.
While there are several other Java expression languages available -- OGNL, MVEL, and JBoss
EL, to name a few -- the Spring Expression Language was created to provide the Spring
@@ -33,26 +34,24 @@ populate them are listed at the end of the chapter.
The expression language supports the following functionality:
* Literal expressions
* Boolean and relational operators
* Regular expressions
* Class expressions
* Accessing properties, arrays, lists, and maps
* Method invocation
* Assignment
* Calling constructors
* Bean references
* Array construction
* Inline lists
* Inline maps
* Ternary operator
* Array construction
* Relational operators
* Regular expressions
* Logical operators
* String operators
* Mathematical operators
* Assignment
* Type expressions
* Method invocation
* Constructor invocation
* Variables
* User-defined functions added to the context
* reflective invocation of `Method`
* various cases of `MethodHandle`
* User-defined functions
* Bean references
* Ternary, Elvis, and safe-navigation operators
* Collection projection
* Collection selection
* Templated expressions
@@ -517,6 +517,7 @@ following kinds of expressions cannot be compiled.
* Expressions involving assignment
* Expressions relying on the conversion service
* Expressions using custom resolvers or accessors
* Expressions using overloaded operators
* Expressions using selection or projection
Compilation of additional kinds of expressions may be supported in the future.
@@ -3,9 +3,11 @@
This section lists the classes used in the examples throughout this chapter.
== `Inventor`
[tabs]
======
Inventor.Java::
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
----
@@ -80,7 +82,7 @@ Inventor.Java::
}
----
Inventor.kt::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
----
@@ -95,9 +97,11 @@ Inventor.kt::
----
======
== `PlaceOfBirth`
[tabs]
======
PlaceOfBirth.java::
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
----
@@ -135,7 +139,7 @@ PlaceOfBirth.java::
}
----
PlaceOfBirth.kt::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
----
@@ -145,9 +149,11 @@ PlaceOfBirth.kt::
----
======
== `Society`
[tabs]
======
Society.java::
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
----
@@ -192,7 +198,7 @@ Society.java::
}
----
Society.kt::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
----
@@ -13,16 +13,18 @@ Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
// returns ['Smiljan', 'Idvor' ]
List placesOfBirth = (List)parser.parseExpression("members.![placeOfBirth.city]");
// evaluates to ["Smiljan", "Idvor"]
List placesOfBirth = parser.parseExpression("members.![placeOfBirth.city]")
.getValue(societyContext, List.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
// returns ['Smiljan', 'Idvor' ]
val placesOfBirth = parser.parseExpression("members.![placeOfBirth.city]") as List<*>
// evaluates to ["Smiljan", "Idvor"]
val placesOfBirth = parser.parseExpression("members.![placeOfBirth.city]")
.getValue(societyContext) as List<*>
----
======
@@ -32,5 +34,12 @@ evaluated against each entry in the map (represented as a Java `Map.Entry`). The
of a projection across a map is a list that consists of the evaluation of the projection
expression against each map entry.
[NOTE]
====
The Spring Expression Language also supports safe navigation for collection projection.
See
xref:core/expressions/language-ref/operator-safe-navigation.adoc#expressions-operator-safe-navigation-selection-and-projection[Safe Collection Selection and Projection]
for details.
====
@@ -28,13 +28,14 @@ Kotlin::
======
Selection is supported for arrays and anything that implements `java.lang.Iterable` or
`java.util.Map`. For a list or array, the selection criteria is evaluated against each
individual element. Against a map, the selection criteria is evaluated against each map
entry (objects of the Java type `Map.Entry`). Each map entry has its `key` and `value`
accessible as properties for use in the selection.
`java.util.Map`. For an array or `Iterable`, the selection expression is evaluated
against each individual element. Against a map, the selection expression is evaluated
against each map entry (objects of the Java type `Map.Entry`). Each map entry has its
`key` and `value` accessible as properties for use in the selection.
The following expression returns a new map that consists of those elements of the
original map where the entry's value is less than 27:
Given a `Map` stored in a variable named `#map`, the following expression returns a new
map that consists of those elements of the original map where the entry's value is less
than 27:
[tabs]
======
@@ -42,21 +43,28 @@ Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
Map newMap = parser.parseExpression("map.?[value<27]").getValue();
Map newMap = parser.parseExpression("#map.?[value < 27]").getValue(Map.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val newMap = parser.parseExpression("map.?[value<27]").getValue()
val newMap = parser.parseExpression("#map.?[value < 27]").getValue() as Map
----
======
In addition to returning all the selected elements, you can retrieve only the first or
the last element. To obtain the first element matching the selection, the syntax is
`.^[selectionExpression]`. To obtain the last matching selection, the syntax is
`.$[selectionExpression]`.
the last element. To obtain the first element matching the selection expression, the
syntax is `.^[selectionExpression]`. To obtain the last element matching the selection
expression, the syntax is `.$[selectionExpression]`.
[NOTE]
====
The Spring Expression Language also supports safe navigation for collection selection.
See
xref:core/expressions/language-ref/operator-safe-navigation.adoc#expressions-operator-safe-navigation-selection-and-projection[Safe Collection Selection and Projection]
for details.
====
@@ -1,10 +1,26 @@
[[expressions-ref-functions]]
= Functions
You can extend SpEL by registering user-defined functions that can be called within the
expression string. The function is registered through the `EvaluationContext`. The
following example shows how to register a user-defined function to be invoked via reflection
(i.e. a `Method`):
You can extend SpEL by registering user-defined functions that can be called within
expressions by using the `#functionName(...)` syntax. Functions can be registered as
variables in `EvaluationContext` implementations via the `setVariable()` method.
[TIP]
====
`StandardEvaluationContext` also defines `registerFunction(...)` methods that provide a
convenient way to register a function as a `java.lang.reflect.Method` or a
`java.lang.invoke.MethodHandle`.
====
[WARNING]
====
Since functions share a common namespace with
xref:core/expressions/language-ref/variables.adoc[variables] in the evaluation context,
care must be taken to ensure that function names and variable names do not overlap.
====
The following example shows how to register a user-defined function to be invoked via
reflection using a `java.lang.reflect.Method`:
[tabs]
======
@@ -40,11 +56,7 @@ Java::
public abstract class StringUtils {
public static String reverseString(String input) {
StringBuilder backwards = new StringBuilder(input.length());
for (int i = 0; i < input.length(); i++) {
backwards.append(input.charAt(input.length() - 1 - i));
}
return backwards.toString();
return new StringBuilder(input).reverse().toString();
}
}
----
@@ -54,16 +66,12 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
fun reverseString(input: String): String {
val backwards = StringBuilder(input.length)
for (i in 0 until input.length) {
backwards.append(input[input.length - 1 - i])
}
return backwards.toString()
return StringBuilder(input).reverse().toString()
}
----
======
You can then register and use the preceding method, as the following example shows:
You can register and use the preceding method, as the following example shows:
[tabs]
======
@@ -75,8 +83,9 @@ Java::
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
context.setVariable("reverseString",
StringUtils.class.getDeclaredMethod("reverseString", String.class));
StringUtils.class.getMethod("reverseString", String.class));
// evaluates to "olleh"
String helloWorldReversed = parser.parseExpression(
"#reverseString('hello')").getValue(context, String.class);
----
@@ -88,16 +97,18 @@ Kotlin::
val parser = SpelExpressionParser()
val context = SimpleEvaluationContext.forReadOnlyDataBinding().build()
context.setVariable("reverseString", ::reverseString::javaMethod)
context.setVariable("reverseString", ::reverseString.javaMethod)
// evaluates to "olleh"
val helloWorldReversed = parser.parseExpression(
"#reverseString('hello')").getValue(context, String::class.java)
----
======
The use of `MethodHandle` is also supported. This enables potentially more efficient use
cases if the `MethodHandle` target and parameters have been fully bound prior to
registration, but partially bound handles are also supported.
A function can also be registered as a `java.lang.invoke.MethodHandle`. This enables
potentially more efficient use cases if the `MethodHandle` target and parameters have
been fully bound prior to registration; however, partially bound handles are also
supported.
Consider the `String#formatted(String, Object...)` instance method, which produces a
message according to a template and a variable number of arguments.
@@ -118,9 +129,9 @@ Java::
MethodType.methodType(String.class, Object[].class));
context.setVariable("message", mh);
// evaluates to "Simple message: <Hello World>"
String message = parser.parseExpression("#message('Simple message: <%s>', 'Hello World', 'ignored')")
.getValue(context, String.class);
//returns "Simple message: <Hello World>"
----
Kotlin::
@@ -134,6 +145,7 @@ Kotlin::
MethodType.methodType(String::class.java, Array<Any>::class.java))
context.setVariable("message", mh)
// evaluates to "Simple message: <Hello World>"
val message = parser.parseExpression("#message('Simple message: <%s>', 'Hello World', 'ignored')")
.getValue(context, String::class.java)
----
@@ -161,9 +173,9 @@ Java::
.bindTo(varargs); //here we have to provide arguments in a single array binding
context.setVariable("message", mh);
// evaluates to "This is a prerecorded message with 3 words: <Oh Hello World!>"
String message = parser.parseExpression("#message()")
.getValue(context, String.class);
//returns "This is a prerecorded message with 3 words: <Oh Hello World!>"
----
Kotlin::
@@ -182,6 +194,7 @@ Kotlin::
.bindTo(varargs) //here we have to provide arguments in a single array binding
context.setVariable("message", mh)
// evaluates to "This is a prerecorded message with 3 words: <Oh Hello World!>"
val message = parser.parseExpression("#message()")
.getValue(context, String::class.java)
----
@@ -3,20 +3,46 @@
SpEL supports the following types of literal expressions.
- strings
- numeric values: integer (`int` or `long`), hexadecimal (`int` or `long`), real (`float`
or `double`)
- boolean values: `true` or `false`
- null
String ::
Strings can be delimited by single quotation marks (`'`) or double quotation marks
(`"`). To include a single quotation mark within a string literal enclosed in single
quotation marks, use two adjacent single quotation mark characters. Similarly, to
include a double quotation mark within a string literal enclosed in double quotation
marks, use two adjacent double quotation mark characters.
Number ::
Numbers support the use of the negative sign, exponential notation, and decimal points.
* Integer: `int` or `long`
* Hexadecimal: `int` or `long`
* Real: `float` or `double`
** By default, real numbers are parsed using `Double.parseDouble()`.
Boolean ::
`true` or `false`
Null ::
`null`
Strings can delimited by single quotation marks (`'`) or double quotation marks (`"`). To
include a single quotation mark within a string literal enclosed in single quotation
marks, use two adjacent single quotation mark characters. Similarly, to include a double
quotation mark within a string literal enclosed in double quotation marks, use two
adjacent double quotation mark characters.
[NOTE]
====
Due to the design and implementation of the Spring Expression Language, literal numbers
are always stored internally as positive numbers.
Numbers support the use of the negative sign, exponential notation, and decimal points.
By default, real numbers are parsed by using `Double.parseDouble()`.
For example, `-2` is stored internally as a positive `2` which is then negated while
evaluating the expression (by calculating the value of `0 - 2`).
This means that it is not possible to represent a negative literal number equal to the
minimum value of that type of number in Java. For example, the minimum supported value
for an `int` in Java is `Integer.MIN_VALUE` which has a value of `-2147483648`. However,
if you include `-2147483648` in a SpEL expression, an exception will be thrown informing
you that the value `2147483648` cannot be parsed as an `int` (because it exceeds the
value of `Integer.MAX_VALUE` which is `2147483647`).
If you need to use the minimum value for a particular type of number within a SpEL
expression, you can either reference the `MIN_VALUE` constant for the respective wrapper
type (such as `Integer.MIN_VALUE`, `Long.MIN_VALUE`, etc.) or calculate the minimum
value. For example, to use the minimum integer value:
- `T(Integer).MIN_VALUE` -- requires a `StandardEvaluationContext`
- `-2^31` -- can be used with any type of `EvaluationContext`
====
The following listing shows simple usage of literals. Typically, they are not used in
isolation like this but, rather, as part of a more complex expression -- for example,
@@ -1,12 +1,27 @@
[[expressions-operator-safe-navigation]]
= Safe Navigation Operator
The safe navigation operator is used to avoid a `NullPointerException` and comes from
the https://www.groovy-lang.org/operators.html#_safe_navigation_operator[Groovy]
language. Typically, when you have a reference to an object, you might need to verify that
it is not null before accessing methods or properties of the object. To avoid this, the
safe navigation operator returns null instead of throwing an exception. The following
example shows how to use the safe navigation operator:
The safe navigation operator (`?`) is used to avoid a `NullPointerException` and comes
from the https://www.groovy-lang.org/operators.html#_safe_navigation_operator[Groovy]
language. Typically, when you have a reference to an object, you might need to verify
that it is not `null` before accessing methods or properties of the object. To avoid
this, the safe navigation operator returns `null` for the particular null-safe operation
instead of throwing an exception.
[WARNING]
====
When the safe navigation operator evaluates to `null` for a particular null-safe
operation within a compound expression, the remainder of the compound expression will
still be evaluated.
See <<expressions-operator-safe-navigation-compound-expressions>> for details.
====
[[expressions-operator-safe-navigation-property-access]]
== Safe Property and Method Access
The following example shows how to use the safe navigation operator for property access
(`?.`).
[tabs]
======
@@ -20,13 +35,18 @@ Java::
Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
tesla.setPlaceOfBirth(new PlaceOfBirth("Smiljan"));
String city = parser.parseExpression("placeOfBirth?.city").getValue(context, tesla, String.class);
System.out.println(city); // Smiljan
// evaluates to "Smiljan"
String city = parser.parseExpression("placeOfBirth?.city") // <1>
.getValue(context, tesla, String.class);
tesla.setPlaceOfBirth(null);
city = parser.parseExpression("placeOfBirth?.city").getValue(context, tesla, String.class);
System.out.println(city); // null - does not throw NullPointerException!!!
// evaluates to null - does not throw NullPointerException
city = parser.parseExpression("placeOfBirth?.city") // <2>
.getValue(context, tesla, String.class);
----
<1> Use safe navigation operator on non-null `placeOfBirth` property
<2> Use safe navigation operator on null `placeOfBirth` property
Kotlin::
+
@@ -38,14 +58,306 @@ Kotlin::
val tesla = Inventor("Nikola Tesla", "Serbian")
tesla.setPlaceOfBirth(PlaceOfBirth("Smiljan"))
var city = parser.parseExpression("placeOfBirth?.city").getValue(context, tesla, String::class.java)
println(city) // Smiljan
// evaluates to "Smiljan"
var city = parser.parseExpression("placeOfBirth?.city") // <1>
.getValue(context, tesla, String::class.java)
tesla.setPlaceOfBirth(null)
city = parser.parseExpression("placeOfBirth?.city").getValue(context, tesla, String::class.java)
println(city) // null - does not throw NullPointerException!!!
// evaluates to null - does not throw NullPointerException
city = parser.parseExpression("placeOfBirth?.city") // <2>
.getValue(context, tesla, String::class.java)
----
<1> Use safe navigation operator on non-null `placeOfBirth` property
<2> Use safe navigation operator on null `placeOfBirth` property
======
[NOTE]
====
The safe navigation operator also applies to method invocations on an object.
For example, the expression `#calculator?.max(4, 2)` evaluates to `null` if the
`#calculator` variable has not been configured in the context. Otherwise, the
`max(int, int)` method will be invoked on the `#calculator`.
====
[[expressions-operator-safe-navigation-selection-and-projection]]
== Safe Collection Selection and Projection
The Spring Expression Language supports safe navigation for
xref:core/expressions/language-ref/collection-selection.adoc[collection selection] and
xref:core/expressions/language-ref/collection-projection.adoc[collection projection] via
the following operators.
* null-safe selection: `?.?`
* null-safe select first: `?.^`
* null-safe select last: `?.$`
* null-safe projection: `?.!`
The following example shows how to use the safe navigation operator for collection
selection (`?.?`).
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
ExpressionParser parser = new SpelExpressionParser();
IEEE society = new IEEE();
StandardEvaluationContext context = new StandardEvaluationContext(society);
String expression = "members?.?[nationality == 'Serbian']"; // <1>
// evaluates to [Inventor("Nikola Tesla")]
List<Inventor> list = (List<Inventor>) parser.parseExpression(expression)
.getValue(context);
society.members = null;
// evaluates to null - does not throw a NullPointerException
list = (List<Inventor>) parser.parseExpression(expression)
.getValue(context);
----
<1> Use null-safe selection operator on potentially null `members` list
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val parser = SpelExpressionParser()
val society = IEEE()
val context = StandardEvaluationContext(society)
val expression = "members?.?[nationality == 'Serbian']" // <1>
// evaluates to [Inventor("Nikola Tesla")]
var list = parser.parseExpression(expression)
.getValue(context) as List<Inventor>
society.members = null
// evaluates to null - does not throw a NullPointerException
list = parser.parseExpression(expression)
.getValue(context) as List<Inventor>
----
<1> Use null-safe selection operator on potentially null `members` list
======
The following example shows how to use the "null-safe select first" operator for
collections (`?.^`).
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
ExpressionParser parser = new SpelExpressionParser();
IEEE society = new IEEE();
StandardEvaluationContext context = new StandardEvaluationContext(society);
String expression =
"members?.^[nationality == 'Serbian' || nationality == 'Idvor']"; // <1>
// evaluates to Inventor("Nikola Tesla")
Inventor inventor = parser.parseExpression(expression)
.getValue(context, Inventor.class);
society.members = null;
// evaluates to null - does not throw a NullPointerException
inventor = parser.parseExpression(expression)
.getValue(context, Inventor.class);
----
<1> Use "null-safe select first" operator on potentially null `members` list
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val parser = SpelExpressionParser()
val society = IEEE()
val context = StandardEvaluationContext(society)
val expression =
"members?.^[nationality == 'Serbian' || nationality == 'Idvor']" // <1>
// evaluates to Inventor("Nikola Tesla")
var inventor = parser.parseExpression(expression)
.getValue(context, Inventor::class.java)
society.members = null
// evaluates to null - does not throw a NullPointerException
inventor = parser.parseExpression(expression)
.getValue(context, Inventor::class.java)
----
<1> Use "null-safe select first" operator on potentially null `members` list
======
The following example shows how to use the "null-safe select last" operator for
collections (`?.$`).
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
ExpressionParser parser = new SpelExpressionParser();
IEEE society = new IEEE();
StandardEvaluationContext context = new StandardEvaluationContext(society);
String expression =
"members?.$[nationality == 'Serbian' || nationality == 'Idvor']"; // <1>
// evaluates to Inventor("Pupin")
Inventor inventor = parser.parseExpression(expression)
.getValue(context, Inventor.class);
society.members = null;
// evaluates to null - does not throw a NullPointerException
inventor = parser.parseExpression(expression)
.getValue(context, Inventor.class);
----
<1> Use "null-safe select last" operator on potentially null `members` list
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val parser = SpelExpressionParser()
val society = IEEE()
val context = StandardEvaluationContext(society)
val expression =
"members?.$[nationality == 'Serbian' || nationality == 'Idvor']" // <1>
// evaluates to Inventor("Pupin")
var inventor = parser.parseExpression(expression)
.getValue(context, Inventor::class.java)
society.members = null
// evaluates to null - does not throw a NullPointerException
inventor = parser.parseExpression(expression)
.getValue(context, Inventor::class.java)
----
<1> Use "null-safe select last" operator on potentially null `members` list
======
The following example shows how to use the safe navigation operator for collection
projection (`?.!`).
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
ExpressionParser parser = new SpelExpressionParser();
IEEE society = new IEEE();
StandardEvaluationContext context = new StandardEvaluationContext(society);
// evaluates to ["Smiljan", "Idvor"]
List placesOfBirth = parser.parseExpression("members?.![placeOfBirth.city]") // <1>
.getValue(context, List.class);
society.members = null;
// evaluates to null - does not throw a NullPointerException
placesOfBirth = parser.parseExpression("members?.![placeOfBirth.city]") // <2>
.getValue(context, List.class);
----
<1> Use null-safe projection operator on non-null `members` list
<2> Use null-safe projection operator on null `members` list
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val parser = SpelExpressionParser()
val society = IEEE()
val context = StandardEvaluationContext(society)
// evaluates to ["Smiljan", "Idvor"]
var placesOfBirth = parser.parseExpression("members?.![placeOfBirth.city]") // <1>
.getValue(context, List::class.java)
society.members = null
// evaluates to null - does not throw a NullPointerException
placesOfBirth = parser.parseExpression("members?.![placeOfBirth.city]") // <2>
.getValue(context, List::class.java)
----
<1> Use null-safe projection operator on non-null `members` list
<2> Use null-safe projection operator on null `members` list
======
[[expressions-operator-safe-navigation-compound-expressions]]
== Null-safe Operations in Compound Expressions
As mentioned at the beginning of this section, when the safe navigation operator
evaluates to `null` for a particular null-safe operation within a compound expression,
the remainder of the compound expression will still be evaluated. This means that the
safe navigation operator must be applied throughout a compound expression in order to
avoid any unwanted `NullPointerException`.
Given the expression `#person?.address.city`, if `#person` is `null` the safe navigation
operator (`?.`) ensures that no exception will be thrown when attempting to access the
`address` property of `#person`. However, since `#person?.address` evaluates to `null`, a
`NullPointerException` will be thrown when attempting to access the `city` property of
`null`. To address that, you can apply null-safe navigation throughout the compound
expression as in `#person?.address?.city`. That expression will safely evaluate to `null`
if either `#person` or `#person?.address` evaluates to `null`.
The following example demonstrates how to use the "null-safe select first" operator
(`?.^`) on a collection combined with null-safe property access (`?.`) within a compound
expression. If `members` is `null`, the result of the "null-safe select first" operator
(`members?.^[nationality == 'Serbian']`) evaluates to `null`, and the additional use of
the safe navigation operator (`?.name`) ensures that the entire compound expression
evaluates to `null` instead of throwing an exception.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
ExpressionParser parser = new SpelExpressionParser();
IEEE society = new IEEE();
StandardEvaluationContext context = new StandardEvaluationContext(society);
String expression = "members?.^[nationality == 'Serbian']?.name"; // <1>
// evaluates to "Nikola Tesla"
String name = parser.parseExpression(expression)
.getValue(context, String.class);
society.members = null;
// evaluates to null - does not throw a NullPointerException
name = parser.parseExpression(expression)
.getValue(context, String.class);
----
<1> Use "null-safe select first" and null-safe property access operators within compound expression.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val parser = SpelExpressionParser()
val society = IEEE()
val context = StandardEvaluationContext(society)
val expression = "members?.^[nationality == 'Serbian']?.name" // <1>
// evaluates to "Nikola Tesla"
String name = parser.parseExpression(expression)
.getValue(context, String::class.java)
society.members = null
// evaluates to null - does not throw a NullPointerException
name = parser.parseExpression(expression)
.getValue(context, String::class.java)
----
<1> Use "null-safe select first" and null-safe property access operators within compound expression.
======
@@ -5,8 +5,11 @@ The Spring Expression Language supports the following kinds of operators:
* xref:core/expressions/language-ref/operators.adoc#expressions-operators-relational[Relational Operators]
* xref:core/expressions/language-ref/operators.adoc#expressions-operators-logical[Logical Operators]
* xref:core/expressions/language-ref/operators.adoc#expressions-operators-string[String Operators]
* xref:core/expressions/language-ref/operators.adoc#expressions-operators-mathematical[Mathematical Operators]
* xref:core/expressions/language-ref/operators.adoc#expressions-assignment[The Assignment Operator]
* xref:core/expressions/language-ref/operators.adoc#expressions-operators-overloaded[Overloaded Operators]
[[expressions-operators-relational]]
@@ -15,7 +18,7 @@ The Spring Expression Language supports the following kinds of operators:
The relational operators (equal, not equal, less than, less than or equal, greater than,
and greater than or equal) are supported by using standard operator notation.
These operators work on `Number` types as well as types implementing `Comparable`.
The following listing shows a few examples of operators:
The following listing shows a few examples of relational operators:
[tabs]
======
@@ -65,53 +68,9 @@ If you prefer numeric comparisons instead, avoid number-based `null` comparisons
in favor of comparisons against zero (for example, `X > 0` or `X < 0`).
====
In addition to the standard relational operators, SpEL supports the `instanceof` and regular
expression-based `matches` operator. The following listing shows examples of both:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
// evaluates to false
boolean falseValue = parser.parseExpression(
"'xyz' instanceof T(Integer)").getValue(Boolean.class);
// evaluates to true
boolean trueValue = parser.parseExpression(
"'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);
// evaluates to false
boolean falseValue = parser.parseExpression(
"'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
// evaluates to false
val falseValue = parser.parseExpression(
"'xyz' instanceof T(Integer)").getValue(Boolean::class.java)
// evaluates to true
val trueValue = parser.parseExpression(
"'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean::class.java)
// evaluates to false
val falseValue = parser.parseExpression(
"'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean::class.java)
----
======
CAUTION: Be careful with primitive types, as they are immediately boxed up to their
wrapper types. For example, `1 instanceof T(int)` evaluates to `false`, while
`1 instanceof T(Integer)` evaluates to `true`, as expected.
Each symbolic operator can also be specified as a purely alphabetic equivalent. This
avoids problems where the symbols used have special meaning for the document type in
which the expression is embedded (such as in an XML document). The textual equivalents are:
Each symbolic operator can also be specified as a purely textual equivalent. This avoids
problems where the symbols used have special meaning for the document type in which the
expression is embedded (such as in an XML document). The textual equivalents are:
* `lt` (`<`)
* `gt` (`>`)
@@ -119,22 +78,117 @@ which the expression is embedded (such as in an XML document). The textual equiv
* `ge` (`>=`)
* `eq` (`==`)
* `ne` (`!=`)
* `div` (`/`)
* `mod` (`%`)
* `not` (`!`).
All of the textual operators are case-insensitive.
In addition to the standard relational operators, SpEL supports the `between`,
`instanceof`, and regular expression-based `matches` operators. The following listing
shows examples of all three:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
boolean result;
// evaluates to true
result = parser.parseExpression(
"1 between {1, 5}").getValue(Boolean.class);
// evaluates to false
result = parser.parseExpression(
"1 between {10, 15}").getValue(Boolean.class);
// evaluates to true
result = parser.parseExpression(
"'elephant' between {'aardvark', 'zebra'}").getValue(Boolean.class);
// evaluates to false
result = parser.parseExpression(
"'elephant' between {'aardvark', 'cobra'}").getValue(Boolean.class);
// evaluates to true
result = parser.parseExpression(
"123 instanceof T(Integer)").getValue(Boolean.class);
// evaluates to false
result = parser.parseExpression(
"'xyz' instanceof T(Integer)").getValue(Boolean.class);
// evaluates to true
result = parser.parseExpression(
"'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);
// evaluates to false
result = parser.parseExpression(
"'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
// evaluates to true
var result = parser.parseExpression(
"1 between {1, 5}").getValue(Boolean::class.java)
// evaluates to false
result = parser.parseExpression(
"1 between {10, 15}").getValue(Boolean::class.java)
// evaluates to true
result = parser.parseExpression(
"'elephant' between {'aardvark', 'zebra'}").getValue(Boolean::class.java)
// evaluates to false
result = parser.parseExpression(
"'elephant' between {'aardvark', 'cobra'}").getValue(Boolean::class.java)
// evaluates to true
result = parser.parseExpression(
"123 instanceof T(Integer)").getValue(Boolean::class.java)
// evaluates to false
result = parser.parseExpression(
"'xyz' instanceof T(Integer)").getValue(Boolean::class.java)
// evaluates to true
result = parser.parseExpression(
"'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean::class.java)
// evaluates to false
result = parser.parseExpression(
"'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean::class.java)
----
======
[CAUTION]
====
The syntax for the `between` operator is `<input> between {<range_begin>, <range_end>}`,
which is effectively a shortcut for `<input> >= <range_begin> && <input> \<= <range_end>}`.
Consequently, `1 between {1, 5}` evaluates to `true`, while `1 between {5, 1}` evaluates
to `false`.
====
CAUTION: Be careful with primitive types, as they are immediately boxed up to their
wrapper types. For example, `1 instanceof T(int)` evaluates to `false`, while
`1 instanceof T(Integer)` evaluates to `true`.
[[expressions-operators-logical]]
== Logical Operators
SpEL supports the following logical operators:
SpEL supports the following logical (`boolean`) operators:
* `and` (`&&`)
* `or` (`||`)
* `not` (`!`)
All of the textual operators are case-insensitive.
The following example shows how to use the logical operators:
[tabs]
@@ -167,6 +221,7 @@ Java::
boolean falseValue = parser.parseExpression("!true").getValue(Boolean.class);
// -- AND and NOT --
String expression = "isMember('Nikola Tesla') and !isMember('Mihajlo Pupin')";
boolean falseValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);
----
@@ -199,20 +254,24 @@ Kotlin::
val falseValue = parser.parseExpression("!true").getValue(Boolean::class.java)
// -- AND and NOT --
val expression = "isMember('Nikola Tesla') and !isMember('Mihajlo Pupin')"
val falseValue = parser.parseExpression(expression).getValue(societyContext, Boolean::class.java)
----
======
[[expressions-operators-mathematical]]
== Mathematical Operators
[[expressions-operators-string]]
== String Operators
You can use the addition operator (`+`) on both numbers and strings. You can use the
subtraction (`-`), multiplication (`*`), and division (`/`) operators only on numbers.
You can also use the modulus (`%`) and exponential power (`^`) operators on numbers.
Standard operator precedence is enforced. The following example shows the mathematical
operators in use:
You can use the following operators on strings.
* concatenation (`+`)
* subtraction (`-`)
- for use with a string containing a single character
* repeat (`*`)
The following example shows the `String` operators in use:
[tabs]
======
@@ -220,67 +279,212 @@ Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
// Addition
int two = parser.parseExpression("1 + 1").getValue(Integer.class); // 2
// -- Concatenation --
String testString = parser.parseExpression(
"'test' + ' ' + 'string'").getValue(String.class); // 'test string'
// evaluates to "hello world"
String helloWorld = parser.parseExpression("'hello' + ' ' + 'world'")
.getValue(String.class);
// Subtraction
int four = parser.parseExpression("1 - -3").getValue(Integer.class); // 4
// -- Character Subtraction --
double d = parser.parseExpression("1000.00 - 1e4").getValue(Double.class); // -9000
// evaluates to 'a'
char ch = parser.parseExpression("'d' - 3")
.getValue(char.class);
// Multiplication
int six = parser.parseExpression("-2 * -3").getValue(Integer.class); // 6
// -- Repeat --
double twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Double.class); // 24.0
// Division
int minusTwo = parser.parseExpression("6 / -3").getValue(Integer.class); // -2
double one = parser.parseExpression("8.0 / 4e0 / 2").getValue(Double.class); // 1.0
// Modulus
int three = parser.parseExpression("7 % 4").getValue(Integer.class); // 3
int one = parser.parseExpression("8 / 5 % 2").getValue(Integer.class); // 1
// Operator precedence
int minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Integer.class); // -21
// evaluates to "abcabc"
String repeated = parser.parseExpression("'abc' * 2")
.getValue(String.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
// Addition
val two = parser.parseExpression("1 + 1").getValue(Int::class.java) // 2
// -- Concatenation --
val testString = parser.parseExpression(
"'test' + ' ' + 'string'").getValue(String::class.java) // 'test string'
// evaluates to "hello world"
val helloWorld = parser.parseExpression("'hello' + ' ' + 'world'")
.getValue(String::class.java)
// -- Character Subtraction --
// evaluates to 'a'
val ch = parser.parseExpression("'d' - 3")
.getValue(Character::class.java);
// -- Repeat --
// evaluates to "abcabc"
val repeated = parser.parseExpression("'abc' * 2")
.getValue(String::class.java);
----
======
[[expressions-operators-mathematical]]
== Mathematical Operators
You can use the following operators on numbers, and standard operator precedence is enforced.
* addition (`+`)
* subtraction (`-`)
* increment (`{pp}`)
* decrement (`--`)
* multiplication (`*`)
* division (`/`)
* modulus (`%`)
* exponential power (`^`)
The division and modulus operators can also be specified as a purely textual equivalent.
This avoids problems where the symbols used have special meaning for the document type in
which the expression is embedded (such as in an XML document). The textual equivalents
are:
* `div` (`/`)
* `mod` (`%`)
All of the textual operators are case-insensitive.
[NOTE]
====
The increment and decrement operators can be used with either prefix (`{pp}A`, `--A`) or
postfix (`A{pp}`, `A--`) notation with variables or properties that can be written to.
====
The following example shows the mathematical operators in use:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
Inventor inventor = new Inventor();
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();
// -- Addition --
int two = parser.parseExpression("1 + 1").getValue(int.class); // 2
// -- Subtraction --
int four = parser.parseExpression("1 - -3").getValue(int.class); // 4
double d = parser.parseExpression("1000.00 - 1e4").getValue(double.class); // -9000
// -- Increment --
// The counter property in Inventor has an initial value of 0.
// evaluates to 2; counter is now 1
two = parser.parseExpression("counter++ + 2").getValue(context, inventor, int.class);
// evaluates to 5; counter is now 2
int five = parser.parseExpression("3 + ++counter").getValue(context, inventor, int.class);
// -- Decrement --
// The counter property in Inventor has a value of 2.
// evaluates to 6; counter is now 1
int six = parser.parseExpression("counter-- + 4").getValue(context, inventor, int.class);
// evaluates to 5; counter is now 0
five = parser.parseExpression("5 + --counter").getValue(context, inventor, int.class);
// -- Multiplication --
six = parser.parseExpression("-2 * -3").getValue(int.class); // 6
double twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(double.class); // 24.0
// -- Division --
int minusTwo = parser.parseExpression("6 / -3").getValue(int.class); // -2
double one = parser.parseExpression("8.0 / 4e0 / 2").getValue(double.class); // 1.0
// -- Modulus --
int three = parser.parseExpression("7 % 4").getValue(int.class); // 3
int oneInt = parser.parseExpression("8 / 5 % 2").getValue(int.class); // 1
// -- Exponential power --
int maxInt = parser.parseExpression("(2^31) - 1").getValue(int.class); // Integer.MAX_VALUE
int minInt = parser.parseExpression("-2^31").getValue(int.class); // Integer.MIN_VALUE
// -- Operator precedence --
int minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(int.class); // -21
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val inventor = Inventor()
val context = SimpleEvaluationContext.forReadWriteDataBinding().build()
// -- Addition --
var two = parser.parseExpression("1 + 1").getValue(Int::class.java) // 2
// -- Subtraction --
// Subtraction
val four = parser.parseExpression("1 - -3").getValue(Int::class.java) // 4
val d = parser.parseExpression("1000.00 - 1e4").getValue(Double::class.java) // -9000
// Multiplication
val six = parser.parseExpression("-2 * -3").getValue(Int::class.java) // 6
// -- Increment --
// The counter property in Inventor has an initial value of 0.
// evaluates to 2; counter is now 1
two = parser.parseExpression("counter++ + 2").getValue(context, inventor, Int::class.java)
// evaluates to 5; counter is now 2
var five = parser.parseExpression("3 + ++counter").getValue(context, inventor, Int::class.java)
// -- Decrement --
// The counter property in Inventor has a value of 2.
// evaluates to 6; counter is now 1
var six = parser.parseExpression("counter-- + 4").getValue(context, inventor, Int::class.java)
// evaluates to 5; counter is now 0
five = parser.parseExpression("5 + --counter").getValue(context, inventor, Int::class.java)
// -- Multiplication --
six = parser.parseExpression("-2 * -3").getValue(Int::class.java) // 6
val twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Double::class.java) // 24.0
// Division
// -- Division --
val minusTwo = parser.parseExpression("6 / -3").getValue(Int::class.java) // -2
val one = parser.parseExpression("8.0 / 4e0 / 2").getValue(Double::class.java) // 1.0
// Modulus
// -- Modulus --
val three = parser.parseExpression("7 % 4").getValue(Int::class.java) // 3
val one = parser.parseExpression("8 / 5 % 2").getValue(Int::class.java) // 1
val oneInt = parser.parseExpression("8 / 5 % 2").getValue(Int::class.java) // 1
// -- Exponential power --
val maxInt = parser.parseExpression("(2^31) - 1").getValue(Int::class.java) // Integer.MAX_VALUE
val minInt = parser.parseExpression("-2^31").getValue(Int::class.java) // Integer.MIN_VALUE
// -- Operator precedence --
// Operator precedence
val minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Int::class.java) // -21
----
======
@@ -325,3 +529,83 @@ Kotlin::
======
[[expressions-operators-overloaded]]
== Overloaded Operators
By default, the mathematical operations defined in SpEL's `Operation` enum (`ADD`,
`SUBTRACT`, `DIVIDE`, `MULTIPLY`, `MODULUS`, and `POWER`) support simple types like
numbers. By providing an implementation of `OperatorOverloader`, the expression language
can support these operations on other types.
For example, if we want to overload the `ADD` operator to allow two lists to be
concatenated using the `+` sign, we can implement a custom `OperatorOverloader` as
follows.
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
pubic class ListConcatenation implements OperatorOverloader {
@Override
public boolean overridesOperation(Operation operation, Object left, Object right) {
return (operation == Operation.ADD &&
left instanceof List && right instanceof List);
}
@Override
@SuppressWarnings("unchecked")
public Object operate(Operation operation, Object left, Object right) {
if (operation == Operation.ADD &&
left instanceof List list1 && right instanceof List list2) {
List result = new ArrayList(list1);
result.addAll(list2);
return result;
}
throw new UnsupportedOperationException(
"No overload for operation %s and operands [%s] and [%s]"
.formatted(operation, left, right));
}
}
----
If we register `ListConcatenation` as the `OperatorOverloader` in a
`StandardEvaluationContext`, we can then evaluate expressions like `{1, 2, 3} + {4, 5}`
as demonstrated in the following example.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
StandardEvaluationContext context = new StandardEvaluationContext();
context.setOperatorOverloader(new ListConcatenation());
// evaluates to a new list: [1, 2, 3, 4, 5]
parser.parseExpression("{1, 2, 3} + {2 + 2, 5}").getValue(context, List.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
StandardEvaluationContext context = StandardEvaluationContext()
context.setOperatorOverloader(ListConcatenation())
// evaluates to a new list: [1, 2, 3, 4, 5]
parser.parseExpression("{1, 2, 3} + {2 + 2, 5}").getValue(context, List::class.java)
----
======
[NOTE]
====
An `OperatorOverloader` does not change the default semantics for an operator. For
example, `2 + 2` in the above example still evaluates to `4`.
====
[CAUTION]
====
Any expression that uses an overloaded operator cannot be compiled. See
xref:core/expressions/evaluation.adoc#expressions-compiler-limitations[Compiler Limitations]
for details.
====
@@ -1,5 +1,5 @@
[[expressions-templating]]
= Expression templating
= Expression Templating
Expression templates allow mixing literal text with one or more evaluation blocks.
Each evaluation block is delimited with prefix and suffix characters that you can
@@ -32,53 +32,13 @@ Kotlin::
======
The string is evaluated by concatenating the literal text `'random number is '` with the
result of evaluating the expression inside the `#{ }` delimiter (in this case, the result
of calling that `random()` method). The second argument to the `parseExpression()` method
is of the type `ParserContext`. The `ParserContext` interface is used to influence how
the expression is parsed in order to support the expression templating functionality.
The definition of `TemplateParserContext` follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class TemplateParserContext implements ParserContext {
public String getExpressionPrefix() {
return "#{";
}
public String getExpressionSuffix() {
return "}";
}
public boolean isTemplate() {
return true;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class TemplateParserContext : ParserContext {
override fun getExpressionPrefix(): String {
return "#{"
}
override fun getExpressionSuffix(): String {
return "}"
}
override fun isTemplate(): Boolean {
return true
}
}
----
======
result of evaluating the expression inside the `#{ }` delimiters (in this case, the
result of calling that `random()` method). The second argument to the `parseExpression()`
method is of the type `ParserContext`. The `ParserContext` interface is used to influence
how the expression is parsed in order to support the expression templating functionality.
The `TemplateParserContext` used in the previous example resides in the
`org.springframework.expression.common` package and is an implementation of the
`ParserContext` which by default configures the prefix and suffix to `#{` and `}`,
respectively.
@@ -6,15 +6,27 @@ are set by using the `setVariable()` method in `EvaluationContext` implementatio
[NOTE]
====
Valid variable names must be composed of one or more of the following supported
Variable names must be begin with a letter (as defined below), an underscore, or a dollar
sign.
Variable names must be composed of one or more of the following supported types of
characters.
* letters: `A` to `Z` and `a` to `z`
* digits: `0` to `9`
* letter: any character for which `java.lang.Character.isLetter(char)` returns `true`
- This includes letters such as `A` to `Z`, `a` to `z`, `ü`, `ñ`, and `é` as well as
letters from other character sets such as Chinese, Japanese, Cyrillic, etc.
* digit: `0` to `9`
* underscore: `_`
* dollar sign: `$`
====
[WARNING]
====
Since variables share a common namespace with
xref:core/expressions/language-ref/functions.adoc[functions] in the evaluation context,
care must be taken to ensure that variable names and functions names do not overlap.
====
The following example shows how to use variables.
[tabs]
@@ -429,7 +429,7 @@ Kotlin::
----
======
A `ConstraintViolation` on `Person.name()` is adapted to a `FieldErrro` with the following:
A `ConstraintViolation` on `Person.name()` is adapted to a `FieldError` with the following:
- Error codes `"Size.student.name"`, `"Size.name"`, `"Size.java.lang.String"`, and `"Size"`
- Message arguments `"name"`, `10`, and `1` (the field name and the constraint attributes)
@@ -717,7 +717,7 @@ For example, with positional parameters:
public int countOfActorsByFirstName(String firstName) {
return this.jdbcClient.sql("select count(*) from t_actor where first_name = ?")
.param(firstName);
.param(firstName)
.query(Integer.class).single();
}
----
@@ -730,7 +730,7 @@ For example, with named parameters:
public int countOfActorsByFirstName(String firstName) {
return this.jdbcClient.sql("select count(*) from t_actor where first_name = :firstName")
.param("firstName", firstName);
.param("firstName", firstName)
.query(Integer.class).single();
}
----
@@ -759,8 +759,8 @@ With a required single object result:
[source,java,indent=0,subs="verbatim,quotes"]
----
Actor actor = this.jdbcClient.sql("select first_name, last_name from t_actor where id = ?",
.param(1212L);
Actor actor = this.jdbcClient.sql("select first_name, last_name from t_actor where id = ?")
.param(1212L)
.query(Actor.class)
.single();
----
@@ -769,8 +769,8 @@ With a `java.util.Optional` result:
[source,java,indent=0,subs="verbatim,quotes"]
----
Optional<Actor> actor = this.jdbcClient.sql("select first_name, last_name from t_actor where id = ?",
.param(1212L);
Optional<Actor> actor = this.jdbcClient.sql("select first_name, last_name from t_actor where id = ?")
.param(1212L)
.query(Actor.class)
.optional();
----
@@ -780,7 +780,7 @@ And for an update statement:
[source,java,indent=0,subs="verbatim,quotes"]
----
this.jdbcClient.sql("insert into t_actor (first_name, last_name) values (?, ?)")
.param("Leonor").param("Watling");
.param("Leonor").param("Watling")
.update();
----
@@ -789,7 +789,7 @@ Or an update statement with named parameters:
[source,java,indent=0,subs="verbatim,quotes"]
----
this.jdbcClient.sql("insert into t_actor (first_name, last_name) values (:firstName, :lastName)")
.param("firstName", "Leonor").param("lastName", "Watling");
.param("firstName", "Leonor").param("lastName", "Watling")
.update();
----
@@ -800,7 +800,7 @@ provides `firstName` and `lastName` properties, such as the `Actor` class from a
[source,java,indent=0,subs="verbatim,quotes"]
----
this.jdbcClient.sql("insert into t_actor (first_name, last_name) values (:firstName, :lastName)")
.paramSource(new Actor("Leonor", "Watling");
.paramSource(new Actor("Leonor", "Watling")
.update();
----
+1 -1
View File
@@ -18,7 +18,7 @@ WebSocket, RSocket.
xref:integration.adoc[Integration] :: REST Clients, JMS, JCA, JMX,
Email, Tasks, Scheduling, Caching, Observability, JVM Checkpoint Restore.
xref:languages.adoc[Languages] :: Kotlin, Groovy, Dynamic Languages.
xref:testing/appendix.adoc[Appendix] :: Spring properties.
xref:appendix.adoc[Appendix] :: Spring properties.
{spring-framework-wiki}[Wiki] :: What's New,
Upgrade Notes, Supported Versions, additional cross-version information.
@@ -1,5 +1,6 @@
[[class-data-sharing]]
= Class Data Sharing
[[cds]]
= CDS
:page-aliases: integration/class-data-sharing.adoc
Class Data Sharing (CDS) is a https://docs.oracle.com/en/java/javase/17/vm/class-data-sharing.html[JVM feature]
that can help reduce the startup time and memory footprint of Java applications.
@@ -51,9 +51,9 @@ val defaultClient = RestClient.create()
val customClient = RestClient.builder()
.requestFactory(HttpComponentsClientHttpRequestFactory())
.messageConverters(converters -> converters.add(MyCustomMessageConverter()))
.messageConverters { converters -> converters.add(MyCustomMessageConverter()) }
.baseUrl("https://example.com")
.defaultUriVariables(Map.of("variable", "foo"))
.defaultUriVariables(mapOf("variable" to "foo"))
.defaultHeader("My-Header", "Foo")
.requestInterceptor(myCustomInterceptor)
.requestInitializer(myCustomInitializer)
@@ -973,6 +973,9 @@ method parameters:
`Map<String, ?>` with multiple variables, or an individual value. Type conversion
is supported for non-String values.
| `@RequestAttribute`
| Provide an `Object` to add as a request attribute. Only supported by `WebClient`.
| `@RequestBody`
| Provide the body of the request either as an Object to be serialized, or a
Reactive Streams `Publisher` such as `Mono`, `Flux`, or any other async type supported
@@ -167,7 +167,7 @@ public class SampleConfiguration {
As a consequence, the related bean name represented as a Kotlin string is `"sampleBean\$demo_kotlin_internal_test"`,
instead of `"sampleBean"` for the regular `public` function use-case. Make sure to use the mangled name when injecting
such bean by name.
such bean by name, or add `@JvmName("sampleBean")` to disable name mangling.
[[injecting-configuration-properties]]
== Injecting Configuration Properties
@@ -152,7 +152,7 @@ Kotlin::
@Autowired
lateinit var accountService: AccountService
lateinit mockMvc: MockMvc
lateinit var mockMvc: MockMvc
@BeforeEach
fun setup(wac: WebApplicationContext) {
@@ -782,6 +782,73 @@ Kotlin::
======
[[webflux-fn-serving-resources]]
== Serving Resources
WebFlux.fn provides built-in support for serving resources.
NOTE: In addition to the capabilities described below, it is possible to implement even more flexible resource handling thanks to
{spring-framework-api}++/web/reactive/function/server/RouterFunctions.html#resources(java.util.function.Function)++[`RouterFunctions#resource(java.util.function.Function)`].
[[webflux-fn-resource]]
=== Redirecting to a resource
It is possible to redirect requests matching a specified predicate to a resource. This can be useful, for example,
for handling redirects in Single Page Applications.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
ClassPathResource index = new ClassPathResource("static/index.html");
List<String> extensions = List.of("js", "css", "ico", "png", "jpg", "gif");
RequestPredicate spaPredicate = path("/api/**").or(path("/error")).or(pathExtension(extensions::contains)).negate();
RouterFunction<ServerResponse> redirectToIndex = route()
.resource(spaPredicate, index)
.build();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val redirectToIndex = router {
val index = ClassPathResource("static/index.html")
val extensions = listOf("js", "css", "ico", "png", "jpg", "gif")
val spaPredicate = !(path("/api/**") or path("/error") or
pathExtension(extensions::contains))
resource(spaPredicate, index)
}
----
======
[[webflux-fn-resources]]
=== Serving resources from a root location
It is also possible to route requests that match a given pattern to resources relative to a given root location.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
Resource location = new FileSystemResource("public-resources/");
RouterFunction<ServerResponse> resources = RouterFunctions.resources("/resources/**", location);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val location = FileSystemResource("public-resources/")
val resources = router { resources("/resources/**", location) }
----
======
[[webflux-fn-running]]
== Running a Server
[.small]#xref:web/webmvc-functional.adoc#webmvc-fn-running[See equivalent in the Servlet stack]#
@@ -28,6 +28,12 @@ because, arguably, most controller methods should be mapped to a specific HTTP m
using `@RequestMapping`, which, by default, matches to all HTTP methods. At the same time, a
`@RequestMapping` is still needed at the class level to express shared mappings.
NOTE: `@RequestMapping` cannot be used in conjunction with other `@RequestMapping`
annotations that are declared on the same element (class, interface, or method). If
multiple `@RequestMapping` annotations are detected on the same element, a warning will
be logged, and only the first mapping will be used. This also applies to composed
`@RequestMapping` annotations such as `@GetMapping`, `@PostMapping`, etc.
The following example uses type and method level mappings:
[tabs]
@@ -436,8 +442,14 @@ attributes with a narrower, more specific purpose.
`@GetMapping`, `@PostMapping`, `@PutMapping`, `@DeleteMapping`, and `@PatchMapping` are
examples of composed annotations. They are provided, because, arguably, most
controller methods should be mapped to a specific HTTP method versus using `@RequestMapping`,
which, by default, matches to all HTTP methods. If you need an example of composed
annotations, look at how those are declared.
which, by default, matches to all HTTP methods. If you need an example of how to implement
a composed annotation, look at how those are declared.
NOTE: `@RequestMapping` cannot be used in conjunction with other `@RequestMapping`
annotations that are declared on the same element (class, interface, or method). If
multiple `@RequestMapping` annotations are detected on the same element, a warning will
be logged, and only the first mapping will be used. This also applies to composed
`@RequestMapping` annotations such as `@GetMapping`, `@PostMapping`, etc.
Spring WebFlux also supports custom request mapping attributes with custom request matching
logic. This is a more advanced option that requires sub-classing
@@ -511,10 +523,17 @@ Kotlin::
== `@HttpExchange`
[.small]#xref:web/webmvc/mvc-controller/ann-requestmapping.adoc#mvc-ann-httpexchange-annotation[See equivalent in the Servlet stack]#
As an alternative to `@RequestMapping`, you can also handle requests with `@HttpExchange`
methods. Such methods are declared on an
xref:integration/rest-clients.adoc#rest-http-interface[HTTP Interface] and can be used as
a client via `HttpServiceProxyFactory` or implemented by a server `@Controller`.
While the main purpose of `@HttpExchange` is to abstract HTTP client code with a
generated proxy, the
xref:integration/rest-clients.adoc#rest-http-interface[HTTP Interface] on which
such annotations are placed is a contract neutral to client vs server use.
In addition to simplifying client code, there are also cases where an HTTP Interface
may be a convenient way for servers to expose their API for client access. This leads
to increased coupling between client and server and is often not a good choice,
especially for public API's, but may be exactly the goal for an internal API.
It is an approach commonly used in Spring Cloud, and it is why `@HttpExchange` is
supported as an alternative to `@RequestMapping` for server side handling in
controller classes.
For example:
@@ -524,16 +543,23 @@ Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@RestController
@HttpExchange("/persons")
class PersonController {
interface PersonService {
@GetExchange("/{id}")
Person getPerson(@PathVariable Long id);
@PostExchange
void add(@RequestBody Person person);
}
@RestController
class PersonController implements PersonService {
public Person getPerson(@PathVariable Long id) {
// ...
}
@PostExchange
@ResponseStatus(HttpStatus.CREATED)
public void add(@RequestBody Person person) {
// ...
@@ -545,30 +571,38 @@ Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@RestController
@HttpExchange("/persons")
class PersonController {
interface PersonService {
@GetExchange("/{id}")
fun getPerson(@PathVariable id: Long): Person {
fun getPerson(@PathVariable id: Long): Person
@PostExchange
fun add(@RequestBody person: Person)
}
@RestController
class PersonController : PersonService {
override fun getPerson(@PathVariable id: Long): Person {
// ...
}
@PostExchange
@ResponseStatus(HttpStatus.CREATED)
fun add(@RequestBody person: Person) {
override fun add(@RequestBody person: Person) {
// ...
}
}
----
======
There some differences between `@HttpExchange` and `@RequestMapping` since the
former needs to remain suitable for client and server use. For example, while
`@RequestMapping` can be declared to handle any number of paths and each path can
be a pattern, `@HttpExchange` must be declared with a single, concrete path. There are
also differences in the supported method parameters. Generally, `@HttpExchange` supports
a subset of method parameters that `@RequestMapping` does, excluding any parameters that
are server side only. For details see the list of supported method parameters for
xref:integration/rest-clients.adoc#rest-http-interface-method-parameters[HTTP interface] and for
`@HttpExchange` and `@RequestMapping` have differences.
`@RequestMapping` can map to any number of requests by path patterns, HTTP methods,
and more, while `@HttpExchange` declares a single endpoint with a concrete HTTP method,
path, and content types.
For method parameters and returns values, generally, `@HttpExchange` supports a
subset of the method parameters that `@RequestMapping` does. Notably, it excludes any
server-side specific parameter types. For details, see the list for
xref:integration/rest-clients.adoc#rest-http-interface-method-parameters[@HttpExchange] and
xref:web/webflux/controller/ann-methods/arguments.adoc[@RequestMapping].
@@ -8,22 +8,23 @@ Spring WebFlux has built-in xref:core/validation/validator.adoc[Validation] supp
xref:core/validation/beanvalidation.adoc[Java Bean Validation].
The validation support works on two levels.
First, method parameters such as
First, resolvers for
xref:web/webflux/controller/ann-methods/modelattrib-method-args.adoc[@ModelAttribute],
xref:web/webflux/controller/ann-methods/requestbody.adoc[@RequestBody], and
xref:web/webflux/controller/ann-methods/multipart-forms.adoc[@RequestPart] do perform
validation if annotated with Jakarta's `@Valid` or Spring's `@Validated` annotation, and
raise `MethodArgumentNotValidException` in case of validation errors. If you want to handle
the errors in the controller method instead, you can declare an `Errors` or `BindingResult`
method parameter immediately after the validated parameter.
xref:web/webflux/controller/ann-methods/multipart-forms.adoc[@RequestPart] method
parameters perform validation if the parameter has Jakarta's `@Valid` or Spring's
`@Validated` annotation, and raise `MethodArgumentNotValidException` if necessary.
Alternatively, you can handle the errors in the controller method by adding an
`Errors` or `BindingResult` method parameter immediately after the validated one.
Second, if {bean-validation-site}[Java Bean Validation] is present _AND_ other method
parameters, e.g. `@RequestHeader`, `@RequestParam`, `@PathVariable` have `@Constraint`
annotations, then method validation is applied to all method arguments, raising
`HandlerMethodValidationException` in case of validation errors. You can still declare an
`Errors` or `BindingResult` after an `@Valid` method parameter, and handle validation
errors within the controller method, as long as there are no validation errors on other
method arguments.
Second, if {bean-validation-site}[Java Bean Validation] is present _AND_ any method
parameter has `@Constraint` annotations, then method validation is applied instead,
raising `HandlerMethodValidationException` if necessary. For this case you can still add
an `Errors` or `BindingResult` method parameter to handle validation errors within the
controller method, but if other method arguments have validation errors then
`HandlerMethodValidationException` is raised instead. Method validation can apply
to the return value if the method is annotated with `@Valid` or with `@Constraint`
annotations.
You can configure a `Validator` globally through the
xref:web/webflux/config.adoc#webflux-config-validation[WebMvc config], or locally
@@ -760,6 +760,73 @@ Kotlin::
======
[[webmvc-fn-serving-resources]]
== Serving Resources
WebMvc.fn provides built-in support for serving resources.
NOTE: In addition to the capabilities described below, it is possible to implement even more flexible resource handling thanks to
{spring-framework-api}++/web/servlet/function/RouterFunctions.html#resources(java.util.function.Function)++[`RouterFunctions#resource(java.util.function.Function)`].
[[webmvc-fn-resource]]
=== Redirecting to a resource
It is possible to redirect requests matching a specified predicate to a resource. This can be useful, for example,
for handling redirects in Single Page Applications.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
ClassPathResource index = new ClassPathResource("static/index.html");
List<String> extensions = List.of("js", "css", "ico", "png", "jpg", "gif");
RequestPredicate spaPredicate = path("/api/**").or(path("/error")).or(pathExtension(extensions::contains)).negate();
RouterFunction<ServerResponse> redirectToIndex = route()
.resource(spaPredicate, index)
.build();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val redirectToIndex = router {
val index = ClassPathResource("static/index.html")
val extensions = listOf("js", "css", "ico", "png", "jpg", "gif")
val spaPredicate = !(path("/api/**") or path("/error") or
pathExtension(extensions::contains))
resource(spaPredicate, index)
}
----
======
[[webmvc-fn-resources]]
=== Serving resources from a root location
It is also possible to route requests that match a given pattern to resources relative to a given root location.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
Resource location = new FileSystemResource("public-resources/");
RouterFunction<ServerResponse> resources = RouterFunctions.resources("/resources/**", location);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val location = FileSystemResource("public-resources/")
val resources = router { resources("/resources/**", location) }
----
======
[[webmvc-fn-running]]
== Running a Server
[.small]#xref:web/webflux-functional.adoc#webflux-fn-running[See equivalent in the Reactive stack]#
@@ -30,6 +30,12 @@ arguably, most controller methods should be mapped to a specific HTTP method ver
using `@RequestMapping`, which, by default, matches to all HTTP methods.
A `@RequestMapping` is still needed at the class level to express shared mappings.
NOTE: `@RequestMapping` cannot be used in conjunction with other `@RequestMapping`
annotations that are declared on the same element (class, interface, or method). If
multiple `@RequestMapping` annotations are detected on the same element, a warning will
be logged, and only the first mapping will be used. This also applies to composed
`@RequestMapping` annotations such as `@GetMapping`, `@PostMapping`, etc.
The following example has type and method level mappings:
[tabs]
@@ -462,11 +468,6 @@ transparently for request mapping. Controller methods do not need to change.
A response wrapper, applied in `jakarta.servlet.http.HttpServlet`, ensures a `Content-Length`
header is set to the number of bytes written (without actually writing to the response).
`@GetMapping` (and `@RequestMapping(method=HttpMethod.GET)`) are implicitly mapped to
and support HTTP HEAD. An HTTP HEAD request is processed as if it were HTTP GET except
that, instead of writing the body, the number of bytes are counted and the `Content-Length`
header is set.
By default, HTTP OPTIONS is handled by setting the `Allow` response header to the list of HTTP
methods listed in all `@RequestMapping` methods that have matching URL patterns.
@@ -491,8 +492,14 @@ attributes with a narrower, more specific purpose.
`@GetMapping`, `@PostMapping`, `@PutMapping`, `@DeleteMapping`, and `@PatchMapping` are
examples of composed annotations. They are provided because, arguably, most
controller methods should be mapped to a specific HTTP method versus using `@RequestMapping`,
which, by default, matches to all HTTP methods. If you need an example of composed
annotations, look at how those are declared.
which, by default, matches to all HTTP methods. If you need an example of how to implement
a composed annotation, look at how those are declared.
NOTE: `@RequestMapping` cannot be used in conjunction with other `@RequestMapping`
annotations that are declared on the same element (class, interface, or method). If
multiple `@RequestMapping` annotations are detected on the same element, a warning will
be logged, and only the first mapping will be used. This also applies to composed
`@RequestMapping` annotations such as `@GetMapping`, `@PostMapping`, etc.
Spring MVC also supports custom request-mapping attributes with custom request-matching
logic. This is a more advanced option that requires subclassing
@@ -562,10 +569,17 @@ Kotlin::
== `@HttpExchange`
[.small]#xref:web/webflux/controller/ann-requestmapping.adoc#webflux-ann-httpexchange-annotation[See equivalent in the Reactive stack]#
As an alternative to `@RequestMapping`, you can also handle requests with `@HttpExchange`
methods. Such methods are declared on an
xref:integration/rest-clients.adoc#rest-http-interface[HTTP Interface] and can be used as
a client via `HttpServiceProxyFactory` or implemented by a server `@Controller`.
While the main purpose of `@HttpExchange` is to abstract HTTP client code with a
generated proxy, the
xref:integration/rest-clients.adoc#rest-http-interface[HTTP Interface] on which
such annotations are placed is a contract neutral to client vs server use.
In addition to simplifying client code, there are also cases where an HTTP Interface
may be a convenient way for servers to expose their API for client access. This leads
to increased coupling between client and server and is often not a good choice,
especially for public API's, but may be exactly the goal for an internal API.
It is an approach commonly used in Spring Cloud, and it is why `@HttpExchange` is
supported as an alternative to `@RequestMapping` for server side handling in
controller classes.
For example:
@@ -575,16 +589,23 @@ Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@RestController
@HttpExchange("/persons")
class PersonController {
interface PersonService {
@GetExchange("/{id}")
Person getPerson(@PathVariable Long id);
@PostExchange
void add(@RequestBody Person person);
}
@RestController
class PersonController implements PersonService {
public Person getPerson(@PathVariable Long id) {
// ...
}
@PostExchange
@ResponseStatus(HttpStatus.CREATED)
public void add(@RequestBody Person person) {
// ...
@@ -596,30 +617,38 @@ Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@RestController
@HttpExchange("/persons")
class PersonController {
interface PersonService {
@GetExchange("/{id}")
fun getPerson(@PathVariable id: Long): Person {
fun getPerson(@PathVariable id: Long): Person
@PostExchange
fun add(@RequestBody person: Person)
}
@RestController
class PersonController : PersonService {
override fun getPerson(@PathVariable id: Long): Person {
// ...
}
@PostExchange
@ResponseStatus(HttpStatus.CREATED)
fun add(@RequestBody person: Person) {
override fun add(@RequestBody person: Person) {
// ...
}
}
----
======
There some differences between `@HttpExchange` and `@RequestMapping` since the
former needs to remain suitable for client and server use. For example, while
`@RequestMapping` can be declared to handle any number of paths and each path can
be a pattern, `@HttpExchange` must be declared with a single, concrete path. There are
also differences in the supported method parameters. Generally, `@HttpExchange` supports
a subset of method parameters that `@RequestMapping` does, excluding any parameters that
are server side only. For details see the list of supported method parameters for
xref:integration/rest-clients.adoc#rest-http-interface-method-parameters[HTTP interface] and for
`@HttpExchange` and `@RequestMapping` have differences.
`@RequestMapping` can map to any number of requests by path patterns, HTTP methods,
and more, while `@HttpExchange` declares a single endpoint with a concrete HTTP method,
path, and content types.
For method parameters and returns values, generally, `@HttpExchange` supports a
subset of the method parameters that `@RequestMapping` does. Notably, it excludes any
server-side specific parameter types. For details, see the list for
xref:integration/rest-clients.adoc#rest-http-interface-method-parameters[@HttpExchange] and
xref:web/webmvc/mvc-controller/ann-methods/arguments.adoc[@RequestMapping].
@@ -8,23 +8,23 @@ Spring MVC has built-in xref:core/validation/validator.adoc[Validation] support
xref:core/validation/beanvalidation.adoc[Java Bean Validation].
The validation support works on two levels.
First, method parameters such as
First, resolvers for
xref:web/webmvc/mvc-controller/ann-methods/modelattrib-method-args.adoc[@ModelAttribute],
xref:web/webmvc/mvc-controller/ann-methods/requestbody.adoc[@RequestBody], and
xref:web/webmvc/mvc-controller/ann-methods/multipart-forms.adoc[@RequestPart] do perform
validation if annotated with Jakarta's `@Valid` or Spring's `@Validated` annotation, and
raise `MethodArgumentNotValidException` in case of validation errors. If you want to handle
the errors in the controller method instead, you can declare an `Errors` or `BindingResult`
method parameter immediately after the validated parameter.
xref:web/webmvc/mvc-controller/ann-methods/multipart-forms.adoc[@RequestPart] method
parameters perform validation if the parameter has Jakarta's `@Valid` or Spring's
`@Validated` annotation, and raise `MethodArgumentNotValidException` if necessary.
Alternatively, you can handle the errors in the controller method by adding an
`Errors` or `BindingResult` method parameter immediately after the validated one.
Second, if {bean-validation-site}[Java Bean Validation] is present _AND_ other method
parameters, e.g. `@RequestHeader`, `@RequestParam`, `@PathVariable` have `@Constraint`
annotations, then method validation is applied to all method arguments, raising
`HandlerMethodValidationException` in case of validation errors. You can still declare an
`Errors` or `BindingResult` after an `@Valid` method parameter, and handle validation
errors within the controller method, as long as there are no validation errors on other
method arguments. Method validation is also applied to the return value if the method
is annotated with `@Valid` or has other `@Constraint` annotations.
Second, if {bean-validation-site}[Java Bean Validation] is present _AND_ any method
parameter has `@Constraint` annotations, then method validation is applied instead,
raising `HandlerMethodValidationException` if necessary. For this case you can still add
an `Errors` or `BindingResult` method parameter to handle validation errors within the
controller method, but if other method arguments have validation errors then
`HandlerMethodValidationException` is raised instead. Method validation can apply
to the return value if the method is annotated with `@Valid` or with `@Constraint`
annotations.
You can configure a `Validator` globally through the
xref:web/webmvc/mvc-config/validation.adoc[WebMvc config], or locally through an
@@ -109,4 +109,4 @@ Kotlin::
}
})
----
======
======
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,7 +24,7 @@ import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
import static org.assertj.core.api.Assertions.assertThat;
public class SpellCheckServiceTests {
class SpellCheckServiceTests {
// tag::hintspredicates[]
@Test
+18 -18
View File
@@ -8,30 +8,30 @@ javaPlatform {
dependencies {
api(platform("com.fasterxml.jackson:jackson-bom:2.15.3"))
api(platform("io.micrometer:micrometer-bom:1.12.2"))
api(platform("io.netty:netty-bom:4.1.104.Final"))
api(platform("io.micrometer:micrometer-bom:1.12.3"))
api(platform("io.netty:netty-bom:4.1.107.Final"))
api(platform("io.netty:netty5-bom:5.0.0.Alpha5"))
api(platform("io.projectreactor:reactor-bom:2023.0.2"))
api(platform("io.projectreactor:reactor-bom:2023.0.3"))
api(platform("io.rsocket:rsocket-bom:1.1.3"))
api(platform("org.apache.groovy:groovy-bom:4.0.17"))
api(platform("org.apache.groovy:groovy-bom:4.0.18"))
api(platform("org.apache.logging.log4j:log4j-bom:2.21.1"))
api(platform("org.assertj:assertj-bom:3.25.1"))
api(platform("org.eclipse.jetty:jetty-bom:12.0.5"))
api(platform("org.eclipse.jetty.ee10:jetty-ee10-bom:12.0.5"))
api(platform("org.assertj:assertj-bom:3.25.3"))
api(platform("org.eclipse.jetty:jetty-bom:12.0.6"))
api(platform("org.eclipse.jetty.ee10:jetty-ee10-bom:12.0.6"))
api(platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.7.3"))
api(platform("org.jetbrains.kotlinx:kotlinx-serialization-bom:1.6.0"))
api(platform("org.junit:junit-bom:5.10.1"))
api(platform("org.mockito:mockito-bom:5.8.0"))
api(platform("org.junit:junit-bom:5.10.2"))
api(platform("org.mockito:mockito-bom:5.10.0"))
constraints {
api("com.fasterxml:aalto-xml:1.3.2")
api("com.fasterxml.woodstox:woodstox-core:6.5.1")
api("com.github.ben-manes.caffeine:caffeine:3.1.8")
api("com.github.librepdf:openpdf:1.3.36")
api("com.github.librepdf:openpdf:1.3.40")
api("com.google.code.findbugs:findbugs:3.0.1")
api("com.google.code.findbugs:jsr305:3.0.2")
api("com.google.code.gson:gson:2.10.1")
api("com.google.protobuf:protobuf-java-util:3.25.1")
api("com.google.protobuf:protobuf-java-util:3.25.2")
api("com.h2database:h2:2.2.224")
api("com.jayway.jsonpath:json-path:2.8.0")
api("com.rometools:rome:1.19.0")
@@ -46,7 +46,7 @@ dependencies {
api("com.thoughtworks.xstream:xstream:1.4.20")
api("commons-io:commons-io:2.15.0")
api("de.bechte.junit:junit-hierarchicalcontextrunner:4.12.2")
api("io.micrometer:context-propagation:1.1.0")
api("io.micrometer:context-propagation:1.1.1")
api("io.mockk:mockk:1.13.4")
api("io.projectreactor.netty:reactor-netty5-http:2.0.0-M3")
api("io.projectreactor.tools:blockhound:1.0.8.RELEASE")
@@ -55,9 +55,9 @@ dependencies {
api("io.r2dbc:r2dbc-spi:1.0.0.RELEASE")
api("io.reactivex.rxjava3:rxjava:3.1.8")
api("io.smallrye.reactive:mutiny:1.10.0")
api("io.undertow:undertow-core:2.3.10.Final")
api("io.undertow:undertow-servlet:2.3.10.Final")
api("io.undertow:undertow-websockets-jsr:2.3.10.Final")
api("io.undertow:undertow-core:2.3.11.Final")
api("io.undertow:undertow-servlet:2.3.11.Final")
api("io.undertow:undertow-websockets-jsr:2.3.11.Final")
api("io.vavr:vavr:0.10.4")
api("jakarta.activation:jakarta.activation-api:2.0.1")
api("jakarta.annotation:jakarta.annotation-api:2.0.0")
@@ -100,7 +100,7 @@ dependencies {
api("org.apache.derby:derby:10.16.1.1")
api("org.apache.derby:derbyclient:10.16.1.1")
api("org.apache.derby:derbytools:10.16.1.1")
api("org.apache.httpcomponents.client5:httpclient5:5.3")
api("org.apache.httpcomponents.client5:httpclient5:5.3.1")
api("org.apache.httpcomponents.core5:httpcore5-reactive:5.2.4")
api("org.apache.poi:poi-ooxml:5.2.5")
api("org.apache.tomcat.embed:tomcat-embed-core:10.1.18")
@@ -115,7 +115,7 @@ dependencies {
api("org.codehaus.jettison:jettison:1.5.4")
api("org.crac:crac:1.4.0")
api("org.dom4j:dom4j:2.1.4")
api("org.eclipse.jetty:jetty-reactive-httpclient:4.0.1")
api("org.eclipse.jetty:jetty-reactive-httpclient:4.0.2")
api("org.eclipse.persistence:org.eclipse.persistence.jpa:3.0.4")
api("org.eclipse:yasson:2.0.4")
api("org.ehcache:ehcache:3.10.8")
@@ -139,7 +139,7 @@ dependencies {
api("org.seleniumhq.selenium:htmlunit-driver:2.70.0")
api("org.seleniumhq.selenium:selenium-java:3.141.59")
api("org.skyscreamer:jsonassert:1.5.1")
api("org.slf4j:slf4j-api:2.0.11")
api("org.slf4j:slf4j-api:2.0.12")
api("org.testng:testng:7.9.0")
api("org.webjars:underscorejs:1.8.3")
api("org.webjars:webjars-locator-core:0.55")
+1 -1
View File
@@ -1,4 +1,4 @@
version=6.1.3
version=6.1.4
org.gradle.caching=true
org.gradle.jvmargs=-Xmx2048m
+3 -2
View File
@@ -8,8 +8,9 @@ apply plugin: 'me.champeau.jmh'
apply from: "$rootDir/gradle/publications.gradle"
dependencies {
jmh 'org.openjdk.jmh:jmh-core:1.36'
jmh 'org.openjdk.jmh:jmh-generator-annprocess:1.36'
jmh 'org.openjdk.jmh:jmh-core:1.37'
jmh 'org.openjdk.jmh:jmh-generator-annprocess:1.37'
jmh 'org.openjdk.jmh:jmh-generator-bytecode:1.37'
jmh 'net.sf.jopt-simple:jopt-simple'
}
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Vendored
+10 -10
View File
@@ -43,11 +43,11 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
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
@@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
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
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -75,7 +75,7 @@ class AopNamespaceHandlerScopeIntegrationTests {
}
@Test
void testRequestScoping() throws Exception {
void testRequestScoping() {
MockHttpServletRequest oldRequest = new MockHttpServletRequest();
MockHttpServletRequest newRequest = new MockHttpServletRequest();
@@ -103,7 +103,7 @@ class AopNamespaceHandlerScopeIntegrationTests {
}
@Test
void testSessionScoping() throws Exception {
void testSessionScoping() {
MockHttpSession oldSession = new MockHttpSession();
MockHttpSession newSession = new MockHttpSession();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,7 +16,6 @@
package org.springframework.aop.framework.autoproxy;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.List;
@@ -61,12 +60,12 @@ class AdvisorAutoProxyCreatorIntegrationTests {
/**
* Return a bean factory with attributes and EnterpriseServices configured.
*/
protected BeanFactory getBeanFactory() throws IOException {
protected BeanFactory getBeanFactory() {
return new ClassPathXmlApplicationContext(DEFAULT_CONTEXT, CLASS);
}
@Test
void testDefaultExclusionPrefix() throws Exception {
void testDefaultExclusionPrefix() {
DefaultAdvisorAutoProxyCreator aapc = (DefaultAdvisorAutoProxyCreator) getBeanFactory().getBean(ADVISOR_APC_BEAN_NAME);
assertThat(aapc.getAdvisorBeanNamePrefix()).isEqualTo((ADVISOR_APC_BEAN_NAME + DefaultAdvisorAutoProxyCreator.SEPARATOR));
assertThat(aapc.isUsePrefix()).isFalse();
@@ -76,21 +75,21 @@ class AdvisorAutoProxyCreatorIntegrationTests {
* If no pointcuts match (no attrs) there should be proxying.
*/
@Test
void testNoProxy() throws Exception {
void testNoProxy() {
BeanFactory bf = getBeanFactory();
Object o = bf.getBean("noSetters");
assertThat(AopUtils.isAopProxy(o)).isFalse();
}
@Test
void testTxIsProxied() throws Exception {
void testTxIsProxied() {
BeanFactory bf = getBeanFactory();
ITestBean test = (ITestBean) bf.getBean("test");
assertThat(AopUtils.isAopProxy(test)).isTrue();
}
@Test
void testRegexpApplied() throws Exception {
void testRegexpApplied() {
BeanFactory bf = getBeanFactory();
ITestBean test = (ITestBean) bf.getBean("test");
MethodCounter counter = (MethodCounter) bf.getBean("countingAdvice");
@@ -100,7 +99,7 @@ class AdvisorAutoProxyCreatorIntegrationTests {
}
@Test
void testTransactionAttributeOnMethod() throws Exception {
void testTransactionAttributeOnMethod() {
BeanFactory bf = getBeanFactory();
ITestBean test = (ITestBean) bf.getBean("test");
@@ -166,7 +165,7 @@ class AdvisorAutoProxyCreatorIntegrationTests {
}
@Test
void testProgrammaticRollback() throws Exception {
void testProgrammaticRollback() {
BeanFactory bf = getBeanFactory();
Object bean = bf.getBean(TXMANAGER_BEAN_NAME);
@@ -250,7 +249,7 @@ class OrderedTxCheckAdvisor extends StaticMethodMatcherPointcutAdvisor implement
}
@Override
public void afterPropertiesSet() throws Exception {
public void afterPropertiesSet() {
setAdvice(new TxCountingBeforeAdvice());
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -46,7 +46,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Brian Clozel
*/
@EnabledIfRuntimeHintsAgent
public class RuntimeHintsAgentTests {
class RuntimeHintsAgentTests {
private static final ClassLoader classLoader = ClassLoader.getSystemClassLoader();
@@ -58,7 +58,7 @@ public class RuntimeHintsAgentTests {
@BeforeAll
public static void classSetup() throws NoSuchMethodException {
static void classSetup() throws NoSuchMethodException {
defaultConstructor = String.class.getConstructor();
toStringMethod = String.class.getMethod("toString");
privateGreetMethod = PrivateClass.class.getDeclaredMethod("greet");
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -39,7 +39,7 @@ class ComponentBeanDefinitionParserTests {
@BeforeAll
void setUp() throws Exception {
void setUp() {
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(
new ClassPathResource("component-config.xml", ComponentBeanDefinitionParserTests.class));
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,7 +34,7 @@ public class ComponentFactoryBean implements FactoryBean<Component> {
}
@Override
public Component getObject() throws Exception {
public Component getObject() {
if (this.children != null && this.children.size() > 0) {
for (Component child : children) {
this.parent.addComponent(child);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -42,7 +42,6 @@ import static org.assertj.core.api.Assertions.assertThatException;
* @author Chris Beams
* @since 3.1
*/
@SuppressWarnings("resource")
class EnableCachingIntegrationTests {
@Test
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -87,7 +87,6 @@ import static org.springframework.core.env.EnvironmentSystemIntegrationTests.Con
* @author Sam Brannen
* @see org.springframework.context.support.EnvironmentIntegrationTests
*/
@SuppressWarnings("resource")
public class EnvironmentSystemIntegrationTests {
private final ConfigurableEnvironment prodEnv = new StandardEnvironment();
@@ -618,7 +617,7 @@ public class EnvironmentSystemIntegrationTests {
@Import({DevConfig.class, ProdConfig.class})
static class Config {
@Bean
public EnvironmentAwareBean envAwareBean() {
EnvironmentAwareBean envAwareBean() {
return new EnvironmentAwareBean();
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,6 +27,7 @@ import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.expression.TypeConverter;
import org.springframework.util.ClassUtils;
/**
* Copied from Spring Integration for purposes of reproducing
@@ -59,11 +60,9 @@ class BeanFactoryTypeConverter implements TypeConverter, BeanFactoryAware {
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (beanFactory instanceof ConfigurableBeanFactory) {
Object typeConverter = ((ConfigurableBeanFactory) beanFactory).getTypeConverter();
if (typeConverter instanceof SimpleTypeConverter) {
delegate = (SimpleTypeConverter) typeConverter;
}
if (beanFactory instanceof ConfigurableBeanFactory cbf &&
cbf.getTypeConverter() instanceof SimpleTypeConverter simpleTypeConverter) {
this.delegate = simpleTypeConverter;
}
}
@@ -86,7 +85,6 @@ class BeanFactoryTypeConverter implements TypeConverter, BeanFactoryAware {
if (conversionService.canConvert(sourceTypeDescriptor, targetTypeDescriptor)) {
return true;
}
// TODO: what does this mean? This method is not used in SpEL so probably ignorable?
Class<?> sourceType = sourceTypeDescriptor.getObjectType();
Class<?> targetType = targetTypeDescriptor.getObjectType();
return canConvert(sourceType, targetType);
@@ -94,7 +92,7 @@ class BeanFactoryTypeConverter implements TypeConverter, BeanFactoryAware {
@Override
public Object convertValue(Object value, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (targetType.getType() == Void.class || targetType.getType() == Void.TYPE) {
if (ClassUtils.isVoidType(targetType.getType())) {
return null;
}
if (conversionService.canConvert(sourceType, targetType)) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -51,7 +51,6 @@ import static org.springframework.core.testfixture.TestGroup.LONG_RUNNING;
* @author Juergen Hoeller
* @since 3.1
*/
@SuppressWarnings("resource")
@EnabledForTestGroups(LONG_RUNNING)
class ScheduledAndTransactionalAnnotationIntegrationTests {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -54,7 +54,6 @@ import static org.assertj.core.api.Assertions.assertThatException;
* @author Sam Brannen
* @since 3.1
*/
@SuppressWarnings("resource")
class EnableTransactionManagementIntegrationTests {
@Test
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,14 +27,13 @@ import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests proving that regardless the proxy strategy used (JDK interface-based vs. CGLIB
* subclass-based), discovery of advice-oriented annotations is consistent.
*
* <p>
* For example, Spring's @Transactional may be declared at the interface or class level,
* and whether interface or subclass proxies are used, the @Transactional annotation must
* be discovered in a consistent fashion.
*
* @author Chris Beams
*/
@SuppressWarnings("resource")
class ProxyAnnotationDiscoveryTests {
@Test
+2 -2
View File
@@ -1,6 +1,6 @@
plugins {
id "com.gradle.enterprise" version "3.14"
id "io.spring.ge.conventions" version "0.0.13"
id "com.gradle.enterprise" version "3.16.1"
id "io.spring.ge.conventions" version "0.0.15"
id "org.gradle.toolchains.foojay-resolver-convention" version "0.7.0"
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,7 +22,6 @@ import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -85,10 +84,7 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
private boolean preFiltered = false;
/** The AdvisorChainFactory to use. */
private AdvisorChainFactory advisorChainFactory;
/** Cache with Method as key and advisor chain List as value. */
private transient Map<MethodCacheKey, List<Object>> methodCache;
private AdvisorChainFactory advisorChainFactory = DefaultAdvisorChainFactory.INSTANCE;
/**
* Interfaces to be implemented by the proxy. Held in List to keep the order
@@ -110,6 +106,14 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
*/
private List<Advisor> advisorKey = this.advisors;
/** Cache with Method as key and advisor chain List as value. */
@Nullable
private transient Map<MethodCacheKey, List<Object>> methodCache;
/** Cache with shared interceptors which are not method-specific. */
@Nullable
private transient volatile List<Object> cachedInterceptors;
/**
* Optional field for {@link AopProxy} implementations to store metadata in.
* Used by {@link JdkDynamicAopProxy}.
@@ -124,8 +128,6 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
* No-arg constructor for use as a JavaBean.
*/
public AdvisedSupport() {
this.advisorChainFactory = DefaultAdvisorChainFactory.INSTANCE;
this.methodCache = new ConcurrentHashMap<>(32);
}
/**
@@ -133,19 +135,9 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
* @param interfaces the proxied interfaces
*/
public AdvisedSupport(Class<?>... interfaces) {
this();
setInterfaces(interfaces);
}
/**
* Internal constructor for {@link #getConfigurationOnlyCopy()}.
* @since 6.0.10
*/
private AdvisedSupport(AdvisorChainFactory advisorChainFactory, Map<MethodCacheKey, List<Object>> methodCache) {
this.advisorChainFactory = advisorChainFactory;
this.methodCache = methodCache;
}
/**
* Set the given object as target.
@@ -497,6 +489,18 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
* @return a List of MethodInterceptors (may also include InterceptorAndDynamicMethodMatchers)
*/
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
if (this.methodCache == null) {
// Shared cache since there are no method-specific advisors (see below).
List<Object> cachedInterceptors = this.cachedInterceptors;
if (cachedInterceptors == null) {
cachedInterceptors = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
this, method, targetClass);
this.cachedInterceptors = cachedInterceptors;
}
return cachedInterceptors;
}
// Method-specific cache for method-specific pointcuts
return this.methodCache.computeIfAbsent(new MethodCacheKey(method), k ->
this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(this, method, targetClass));
}
@@ -505,8 +509,18 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
* Invoked when advice has changed.
*/
protected void adviceChanged() {
this.methodCache.clear();
this.methodCache = null;
this.cachedInterceptors = null;
this.proxyMetadataCache = null;
// Initialize method cache if necessary; otherwise,
// cachedInterceptors is going to be shared (see above).
for (Advisor advisor : this.advisors) {
if (advisor instanceof PointcutAdvisor) {
this.methodCache = new ConcurrentHashMap<>();
break;
}
}
}
/**
@@ -545,21 +559,28 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
* replacing the {@link TargetSource}.
*/
AdvisedSupport getConfigurationOnlyCopy() {
AdvisedSupport copy = new AdvisedSupport(this.advisorChainFactory, this.methodCache);
AdvisedSupport copy = new AdvisedSupport();
copy.copyFrom(this);
copy.targetSource = EmptyTargetSource.forClass(getTargetClass(), getTargetSource().isStatic());
copy.preFiltered = this.preFiltered;
copy.advisorChainFactory = this.advisorChainFactory;
copy.interfaces = new ArrayList<>(this.interfaces);
copy.advisors = new ArrayList<>(this.advisors);
copy.advisorKey = new ArrayList<>(this.advisors.size());
for (Advisor advisor : this.advisors) {
copy.advisorKey.add(new AdvisorKeyEntry(advisor));
}
copy.methodCache = this.methodCache;
copy.cachedInterceptors = this.cachedInterceptors;
copy.proxyMetadataCache = this.proxyMetadataCache;
return copy;
}
void reduceToAdvisorKey() {
this.advisors = this.advisorKey;
this.methodCache = Collections.emptyMap();
this.methodCache = null;
this.cachedInterceptors = null;
this.proxyMetadataCache = null;
}
Object getAdvisorKey() {
@@ -596,8 +617,8 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
// Rely on default serialization; just initialize state after deserialization.
ois.defaultReadObject();
// Initialize transient fields.
this.methodCache = new ConcurrentHashMap<>(32);
// Initialize method cache if necessary.
adviceChanged();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -417,7 +417,7 @@ class CglibAopProxy implements AopProxy, Serializable {
returnValue = proxy;
}
Class<?> returnType = method.getReturnType();
if (returnValue == null && returnType != Void.TYPE && returnType.isPrimitive()) {
if (returnValue == null && returnType != void.class && returnType.isPrimitive()) {
throw new AopInvocationException(
"Null return value from advice does not match primitive return type for: " + method);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,8 +32,13 @@ import org.springframework.lang.Nullable;
*/
abstract class CoroutinesUtils {
static Object asFlow(Object publisher) {
return ReactiveFlowKt.asFlow((Publisher<?>) publisher);
static Object asFlow(@Nullable Object publisher) {
if (publisher instanceof Publisher<?> rsPublisher) {
return ReactiveFlowKt.asFlow(rsPublisher);
}
else {
throw new IllegalArgumentException("Not a Reactive Streams Publisher: " + publisher);
}
}
@Nullable
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -230,7 +230,7 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa
// a reference to itself in another returned object.
retVal = proxy;
}
else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
else if (retVal == null && returnType != void.class && returnType.isPrimitive()) {
throw new AopInvocationException(
"Null return value from advice does not match primitive return type for: " + method);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -101,7 +101,7 @@ public abstract class Pointcuts {
public boolean matches(Method method, Class<?> targetClass) {
return (method.getName().startsWith("set") &&
method.getParameterCount() == 1 &&
method.getReturnType() == Void.TYPE);
method.getReturnType() == void.class);
}
private Object readResolve() {
@@ -127,7 +127,7 @@ public abstract class Pointcuts {
public boolean matches(Method method, Class<?> targetClass) {
return (method.getName().startsWith("get") &&
method.getParameterCount() == 0 &&
method.getReturnType() != Void.TYPE);
method.getReturnType() != void.class);
}
private Object readResolve() {
@@ -51,7 +51,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
* @author Chris Beams
* @author Yanming Zhou
*/
public class AspectJExpressionPointcutTests {
class AspectJExpressionPointcutTests {
public static final String MATCH_ALL_METHODS = "execution(* *(..))";
@@ -78,7 +78,7 @@ public class AspectJExpressionPointcutTests {
@Test
public void testMatchExplicit() {
void testMatchExplicit() {
String expression = "execution(int org.springframework.beans.testfixture.beans.TestBean.getAge())";
Pointcut pointcut = getPointcut(expression);
@@ -96,7 +96,7 @@ public class AspectJExpressionPointcutTests {
}
@Test
public void testMatchWithTypePattern() throws Exception {
void testMatchWithTypePattern() {
String expression = "execution(* *..TestBean.*Age(..))";
Pointcut pointcut = getPointcut(expression);
@@ -115,12 +115,12 @@ public class AspectJExpressionPointcutTests {
@Test
public void testThis() throws SecurityException, NoSuchMethodException{
void testThis() throws SecurityException, NoSuchMethodException{
testThisOrTarget("this");
}
@Test
public void testTarget() throws SecurityException, NoSuchMethodException {
void testTarget() throws SecurityException, NoSuchMethodException {
testThisOrTarget("target");
}
@@ -144,12 +144,12 @@ public class AspectJExpressionPointcutTests {
}
@Test
public void testWithinRootPackage() throws SecurityException, NoSuchMethodException {
void testWithinRootPackage() throws SecurityException, NoSuchMethodException {
testWithinPackage(false);
}
@Test
public void testWithinRootAndSubpackages() throws SecurityException, NoSuchMethodException {
void testWithinRootAndSubpackages() throws SecurityException, NoSuchMethodException {
testWithinPackage(true);
}
@@ -173,7 +173,7 @@ public class AspectJExpressionPointcutTests {
}
@Test
public void testFriendlyErrorOnNoLocationClassMatching() {
void testFriendlyErrorOnNoLocationClassMatching() {
AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
assertThatIllegalStateException().isThrownBy(() ->
pc.matches(ITestBean.class))
@@ -181,7 +181,7 @@ public class AspectJExpressionPointcutTests {
}
@Test
public void testFriendlyErrorOnNoLocation2ArgMatching() {
void testFriendlyErrorOnNoLocation2ArgMatching() {
AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
assertThatIllegalStateException().isThrownBy(() ->
pc.matches(getAge, ITestBean.class))
@@ -189,7 +189,7 @@ public class AspectJExpressionPointcutTests {
}
@Test
public void testFriendlyErrorOnNoLocation3ArgMatching() {
void testFriendlyErrorOnNoLocation3ArgMatching() {
AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
assertThatIllegalStateException().isThrownBy(() ->
pc.matches(getAge, ITestBean.class, (Object[]) null))
@@ -198,7 +198,7 @@ public class AspectJExpressionPointcutTests {
@Test
public void testMatchWithArgs() throws Exception {
void testMatchWithArgs() throws Exception {
String expression = "execution(void org.springframework.beans.testfixture.beans.TestBean.setSomeNumber(Number)) && args(Double)";
Pointcut pointcut = getPointcut(expression);
@@ -217,7 +217,7 @@ public class AspectJExpressionPointcutTests {
}
@Test
public void testSimpleAdvice() {
void testSimpleAdvice() {
String expression = "execution(int org.springframework.beans.testfixture.beans.TestBean.getAge())";
CallCountingInterceptor interceptor = new CallCountingInterceptor();
TestBean testBean = getAdvisedProxy(expression, interceptor);
@@ -230,7 +230,7 @@ public class AspectJExpressionPointcutTests {
}
@Test
public void testDynamicMatchingProxy() {
void testDynamicMatchingProxy() {
String expression = "execution(void org.springframework.beans.testfixture.beans.TestBean.setSomeNumber(Number)) && args(Double)";
CallCountingInterceptor interceptor = new CallCountingInterceptor();
TestBean testBean = getAdvisedProxy(expression, interceptor);
@@ -244,7 +244,7 @@ public class AspectJExpressionPointcutTests {
}
@Test
public void testInvalidExpression() {
void testInvalidExpression() {
String expression = "execution(void org.springframework.beans.testfixture.beans.TestBean.setSomeNumber(Number) && args(Double)";
assertThatIllegalArgumentException().isThrownBy(getPointcut(expression)::getClassFilter); // call to getClassFilter forces resolution
}
@@ -274,7 +274,7 @@ public class AspectJExpressionPointcutTests {
}
@Test
public void testWithUnsupportedPointcutPrimitive() {
void testWithUnsupportedPointcutPrimitive() {
String expression = "call(int org.springframework.beans.testfixture.beans.TestBean.getAge())";
assertThatExceptionOfType(UnsupportedPointcutPrimitiveException.class)
.isThrownBy(() -> getPointcut(expression).getClassFilter()) // call to getClassFilter forces resolution...
@@ -282,14 +282,14 @@ public class AspectJExpressionPointcutTests {
}
@Test
public void testAndSubstitution() {
void testAndSubstitution() {
AspectJExpressionPointcut pc = getPointcut("execution(* *(..)) and args(String)");
String expr = pc.getPointcutExpression().getPointcutExpression();
assertThat(expr).isEqualTo("execution(* *(..)) && args(String)");
}
@Test
public void testMultipleAndSubstitutions() {
void testMultipleAndSubstitutions() {
AspectJExpressionPointcut pc = getPointcut("execution(* *(..)) and args(String) and this(Object)");
String expr = pc.getPointcutExpression().getPointcutExpression();
assertThat(expr).isEqualTo("execution(* *(..)) && args(String) && this(Object)");
@@ -302,7 +302,7 @@ public class AspectJExpressionPointcutTests {
}
@Test
public void testMatchGenericArgument() {
void testMatchGenericArgument() {
String expression = "execution(* set*(java.util.List<org.springframework.beans.testfixture.beans.TestBean>) )";
AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut();
ajexp.setExpression(expression);
@@ -321,7 +321,7 @@ public class AspectJExpressionPointcutTests {
}
@Test
public void testMatchVarargs() throws Exception {
void testMatchVarargs() throws Exception {
@SuppressWarnings("unused")
class MyTemplate {
@@ -347,19 +347,19 @@ public class AspectJExpressionPointcutTests {
}
@Test
public void testMatchAnnotationOnClassWithAtWithin() throws Exception {
void testMatchAnnotationOnClassWithAtWithin() throws Exception {
String expression = "@within(test.annotation.transaction.Tx)";
testMatchAnnotationOnClass(expression);
}
@Test
public void testMatchAnnotationOnClassWithoutBinding() throws Exception {
void testMatchAnnotationOnClassWithoutBinding() throws Exception {
String expression = "within(@test.annotation.transaction.Tx *)";
testMatchAnnotationOnClass(expression);
}
@Test
public void testMatchAnnotationOnClassWithSubpackageWildcard() throws Exception {
void testMatchAnnotationOnClassWithSubpackageWildcard() throws Exception {
String expression = "within(@(test.annotation..*) *)";
AspectJExpressionPointcut springAnnotatedPc = testMatchAnnotationOnClass(expression);
assertThat(springAnnotatedPc.matches(TestBean.class.getMethod("setName", String.class), TestBean.class)).isFalse();
@@ -371,7 +371,7 @@ public class AspectJExpressionPointcutTests {
}
@Test
public void testMatchAnnotationOnClassWithExactPackageWildcard() throws Exception {
void testMatchAnnotationOnClassWithExactPackageWildcard() throws Exception {
String expression = "within(@(test.annotation.transaction.*) *)";
testMatchAnnotationOnClass(expression);
}
@@ -389,7 +389,7 @@ public class AspectJExpressionPointcutTests {
}
@Test
public void testAnnotationOnMethodWithFQN() throws Exception {
void testAnnotationOnMethodWithFQN() throws Exception {
String expression = "@annotation(test.annotation.transaction.Tx)";
AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut();
ajexp.setExpression(expression);
@@ -403,7 +403,7 @@ public class AspectJExpressionPointcutTests {
}
@Test
public void testAnnotationOnCglibProxyMethod() throws Exception {
void testAnnotationOnCglibProxyMethod() throws Exception {
String expression = "@annotation(test.annotation.transaction.Tx)";
AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut();
ajexp.setExpression(expression);
@@ -414,9 +414,8 @@ public class AspectJExpressionPointcutTests {
assertThat(ajexp.matches(BeanA.class.getMethod("getAge"), proxy.getClass())).isTrue();
}
@Test
public void testNotAnnotationOnCglibProxyMethod() throws Exception {
void testNotAnnotationOnCglibProxyMethod() throws Exception {
String expression = "!@annotation(test.annotation.transaction.Tx)";
AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut();
ajexp.setExpression(expression);
@@ -428,7 +427,7 @@ public class AspectJExpressionPointcutTests {
}
@Test
public void testAnnotationOnDynamicProxyMethod() throws Exception {
void testAnnotationOnDynamicProxyMethod() throws Exception {
String expression = "@annotation(test.annotation.transaction.Tx)";
AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut();
ajexp.setExpression(expression);
@@ -440,7 +439,7 @@ public class AspectJExpressionPointcutTests {
}
@Test
public void testNotAnnotationOnDynamicProxyMethod() throws Exception {
void testNotAnnotationOnDynamicProxyMethod() throws Exception {
String expression = "!@annotation(test.annotation.transaction.Tx)";
AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut();
ajexp.setExpression(expression);
@@ -452,7 +451,7 @@ public class AspectJExpressionPointcutTests {
}
@Test
public void testAnnotationOnMethodWithWildcard() throws Exception {
void testAnnotationOnMethodWithWildcard() throws Exception {
String expression = "execution(@(test.annotation..*) * *(..))";
AspectJExpressionPointcut anySpringMethodAnnotation = new AspectJExpressionPointcut();
anySpringMethodAnnotation.setExpression(expression);
@@ -468,7 +467,7 @@ public class AspectJExpressionPointcutTests {
}
@Test
public void testAnnotationOnMethodArgumentsWithFQN() throws Exception {
void testAnnotationOnMethodArgumentsWithFQN() throws Exception {
String expression = "@args(*, test.annotation.EmptySpringAnnotation))";
AspectJExpressionPointcut takesSpringAnnotatedArgument2 = new AspectJExpressionPointcut();
takesSpringAnnotatedArgument2.setExpression(expression);
@@ -497,7 +496,7 @@ public class AspectJExpressionPointcutTests {
}
@Test
public void testAnnotationOnMethodArgumentsWithWildcards() throws Exception {
void testAnnotationOnMethodArgumentsWithWildcards() throws Exception {
String expression = "execution(* *(*, @(test..*) *))";
AspectJExpressionPointcut takesSpringAnnotatedArgument2 = new AspectJExpressionPointcut();
takesSpringAnnotatedArgument2.setExpression(expression);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,10 +28,10 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Ramnivas Laddad
* @author Chris Beams
*/
public class BeanNamePointcutMatchingTests {
class BeanNamePointcutMatchingTests {
@Test
public void testMatchingPointcuts() {
void testMatchingPointcuts() {
assertMatch("someName", "bean(someName)");
// Spring bean names are less restrictive compared to AspectJ names (methods, types etc.)
@@ -66,7 +66,7 @@ public class BeanNamePointcutMatchingTests {
}
@Test
public void testNonMatchingPointcuts() {
void testNonMatchingPointcuts() {
assertMisMatch("someName", "bean(someNamex)");
assertMisMatch("someName", "bean(someX*Name)");
@@ -87,7 +87,6 @@ public class BeanNamePointcutMatchingTests {
}
private static boolean matches(final String beanName, String pcExpression) {
@SuppressWarnings("serial")
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut() {
@Override
protected String getCurrentProxiedBeanName() {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -46,20 +46,20 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
* @author Ramnivas Laddad
* @since 2.0
*/
public class MethodInvocationProceedingJoinPointTests {
class MethodInvocationProceedingJoinPointTests {
@Test
public void testingBindingWithJoinPoint() {
void testingBindingWithJoinPoint() {
assertThatIllegalStateException().isThrownBy(AbstractAspectJAdvice::currentJoinPoint);
}
@Test
public void testingBindingWithProceedingJoinPoint() {
void testingBindingWithProceedingJoinPoint() {
assertThatIllegalStateException().isThrownBy(AbstractAspectJAdvice::currentJoinPoint);
}
@Test
public void testCanGetMethodSignatureFromJoinPoint() {
void testCanGetMethodSignatureFromJoinPoint() {
final Object raw = new TestBean();
// Will be set by advice during a method call
final int newAge = 23;
@@ -118,7 +118,7 @@ public class MethodInvocationProceedingJoinPointTests {
}
@Test
public void testCanGetSourceLocationFromJoinPoint() {
void testCanGetSourceLocationFromJoinPoint() {
final Object raw = new TestBean();
ProxyFactory pf = new ProxyFactory(raw);
pf.addAdvisor(ExposeInvocationInterceptor.ADVISOR);
@@ -135,7 +135,7 @@ public class MethodInvocationProceedingJoinPointTests {
}
@Test
public void testCanGetStaticPartFromJoinPoint() {
void testCanGetStaticPartFromJoinPoint() {
final Object raw = new TestBean();
ProxyFactory pf = new ProxyFactory(raw);
pf.addAdvisor(ExposeInvocationInterceptor.ADVISOR);
@@ -152,7 +152,7 @@ public class MethodInvocationProceedingJoinPointTests {
}
@Test
public void toShortAndLongStringFormedCorrectly() throws Exception {
void toShortAndLongStringFormedCorrectly() {
final Object raw = new TestBean();
ProxyFactory pf = new ProxyFactory(raw);
pf.addAdvisor(ExposeInvocationInterceptor.ADVISOR);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -40,17 +40,17 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
* @author Dave Syer
*/
public class TrickyAspectJPointcutExpressionTests {
class TrickyAspectJPointcutExpressionTests {
@Test
public void testManualProxyJavaWithUnconditionalPointcut() throws Exception {
void testManualProxyJavaWithUnconditionalPointcut() {
TestService target = new TestServiceImpl();
LogUserAdvice logAdvice = new LogUserAdvice();
testAdvice(new DefaultPointcutAdvisor(logAdvice), logAdvice, target, "TestServiceImpl");
}
@Test
public void testManualProxyJavaWithStaticPointcut() throws Exception {
void testManualProxyJavaWithStaticPointcut() {
TestService target = new TestServiceImpl();
LogUserAdvice logAdvice = new LogUserAdvice();
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
@@ -59,7 +59,7 @@ public class TrickyAspectJPointcutExpressionTests {
}
@Test
public void testManualProxyJavaWithDynamicPointcut() throws Exception {
void testManualProxyJavaWithDynamicPointcut() {
TestService target = new TestServiceImpl();
LogUserAdvice logAdvice = new LogUserAdvice();
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
@@ -68,7 +68,7 @@ public class TrickyAspectJPointcutExpressionTests {
}
@Test
public void testManualProxyJavaWithDynamicPointcutAndProxyTargetClass() throws Exception {
void testManualProxyJavaWithDynamicPointcutAndProxyTargetClass() {
TestService target = new TestServiceImpl();
LogUserAdvice logAdvice = new LogUserAdvice();
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
@@ -77,7 +77,7 @@ public class TrickyAspectJPointcutExpressionTests {
}
@Test
public void testManualProxyJavaWithStaticPointcutAndTwoClassLoaders() throws Exception {
void testManualProxyJavaWithStaticPointcutAndTwoClassLoaders() throws Exception {
LogUserAdvice logAdvice = new LogUserAdvice();
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
@@ -95,13 +95,12 @@ public class TrickyAspectJPointcutExpressionTests {
testAdvice(new DefaultPointcutAdvisor(pointcut, logAdvice), logAdvice, other, "TestServiceImpl");
}
private void testAdvice(Advisor advisor, LogUserAdvice logAdvice, TestService target, String message)
throws Exception {
private void testAdvice(Advisor advisor, LogUserAdvice logAdvice, TestService target, String message) {
testAdvice(advisor, logAdvice, target, message, false);
}
private void testAdvice(Advisor advisor, LogUserAdvice logAdvice, TestService target, String message,
boolean proxyTargetClass) throws Exception {
boolean proxyTargetClass) {
logAdvice.reset();
@@ -148,7 +147,7 @@ public class TrickyAspectJPointcutExpressionTests {
public interface TestService {
public String sayHello();
String sayHello();
}
@@ -162,14 +161,14 @@ public class TrickyAspectJPointcutExpressionTests {
}
public class LogUserAdvice implements MethodBeforeAdvice, ThrowsAdvice {
public static class LogUserAdvice implements MethodBeforeAdvice, ThrowsAdvice {
private int countBefore = 0;
private int countThrows = 0;
@Override
public void before(Method method, Object[] objects, @Nullable Object o) throws Throwable {
public void before(Method method, Object[] objects, @Nullable Object o) {
countBefore++;
}
@@ -51,7 +51,7 @@ class TypePatternClassFilterTests {
}
@Test
void invocationOfMatchesMethodBlowsUpWhenNoTypePatternHasBeenSet() throws Exception {
void invocationOfMatchesMethodBlowsUpWhenNoTypePatternHasBeenSet() {
assertThatIllegalStateException().isThrownBy(() -> new TypePatternClassFilter().matches(String.class));
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -362,7 +362,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
assertThat(lockable.locked()).as("Already locked").isTrue();
lockable.lock();
assertThat(lockable.locked()).as("Real target ignores locking").isTrue();
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> lockable.unlock());
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(lockable::unlock);
}
@Test
@@ -645,7 +645,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
static class NamedPointcutAspectWithFQN {
@SuppressWarnings("unused")
private ITestBean fieldThatShouldBeIgnoredBySpringAtAspectJProcessing = new TestBean();
private final ITestBean fieldThatShouldBeIgnoredBySpringAtAspectJProcessing = new TestBean();
@Around("org.springframework.aop.aspectj.annotation.AbstractAspectJAdvisorFactoryTests.CommonPointcuts.getAge()()")
int changeReturnValue(ProceedingJoinPoint pjp) {
@@ -762,7 +762,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
@Aspect
class DoublingAspect {
static class DoublingAspect {
@Around("execution(* getAge())")
public Object doubleAge(ProceedingJoinPoint pjp) throws Throwable {
@@ -771,7 +771,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
}
@Aspect
class IncrementingAspect extends DoublingAspect {
static class IncrementingAspect extends DoublingAspect {
@Around("execution(* getAge())")
public int incrementAge(ProceedingJoinPoint pjp) throws Throwable {
@@ -1080,7 +1080,7 @@ class PerThisAspect {
// Just to check that this doesn't cause problems with introduction processing
@SuppressWarnings("unused")
private ITestBean fieldThatShouldBeIgnoredBySpringAtAspectJProcessing = new TestBean();
private final ITestBean fieldThatShouldBeIgnoredBySpringAtAspectJProcessing = new TestBean();
@Around("execution(int *.getAge())")
int returnCountAsAge() {
@@ -119,7 +119,7 @@ class ArgumentBindingTests {
static class PointcutWithAnnotationArgument {
@Around("execution(* org.springframework..*.*(..)) && @annotation(transactional)")
public Object around(ProceedingJoinPoint pjp, Transactional transactional) throws Throwable {
public Object around(ProceedingJoinPoint pjp, Transactional transactional) {
throw new IllegalStateException("Invoked with @Transactional");
}
}
@@ -132,7 +132,7 @@ class ArgumentBindingTests {
public void pointcutWithArgs(String s) {}
@Around("pointcutWithArgs(aString)")
public Object doAround(ProceedingJoinPoint pjp, String aString) throws Throwable {
public Object doAround(ProceedingJoinPoint pjp, String aString) {
throw new IllegalArgumentException(aString);
}
}
@@ -142,7 +142,7 @@ class ArgumentBindingTests {
static class DynamicPointcutWithArgs {
@Around("execution(* *(..)) && args(java.lang.String)")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
public Object doAround(ProceedingJoinPoint pjp) {
throw new IllegalArgumentException(String.valueOf(pjp.getArgs()[0]));
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,9 +20,9 @@ import org.junit.jupiter.api.Test;
import org.springframework.aop.Pointcut;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.aspectj.AspectJExpressionPointcutTests;
import org.springframework.aop.aspectj.annotation.AbstractAspectJAdvisorFactoryTests.ExceptionThrowingAspect;
import org.springframework.aop.framework.AopConfigException;
import org.springframework.aop.testfixture.aspectj.CommonExpressions;
import org.springframework.aop.testfixture.aspectj.PerTargetAspect;
import org.springframework.beans.testfixture.beans.TestBean;
@@ -33,15 +33,15 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
* @author Rod Johnson
* @author Chris Beams
*/
public class AspectJPointcutAdvisorTests {
class AspectJPointcutAdvisorTests {
private final AspectJAdvisorFactory af = new ReflectiveAspectJAdvisorFactory();
@Test
public void testSingleton() throws SecurityException, NoSuchMethodException {
void testSingleton() throws SecurityException, NoSuchMethodException {
AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut();
ajexp.setExpression(AspectJExpressionPointcutTests.MATCH_ALL_METHODS);
ajexp.setExpression(CommonExpressions.MATCH_ALL_METHODS);
InstantiationModelAwarePointcutAdvisorImpl ajpa = new InstantiationModelAwarePointcutAdvisorImpl(
ajexp, TestBean.class.getMethod("getAge"), af,
@@ -53,9 +53,9 @@ public class AspectJPointcutAdvisorTests {
}
@Test
public void testPerTarget() throws SecurityException, NoSuchMethodException {
void testPerTarget() throws SecurityException, NoSuchMethodException {
AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut();
ajexp.setExpression(AspectJExpressionPointcutTests.MATCH_ALL_METHODS);
ajexp.setExpression(CommonExpressions.MATCH_ALL_METHODS);
InstantiationModelAwarePointcutAdvisorImpl ajpa = new InstantiationModelAwarePointcutAdvisorImpl(
ajexp, TestBean.class.getMethod("getAge"), af,
@@ -76,13 +76,13 @@ public class AspectJPointcutAdvisorTests {
}
@Test
public void testPerCflowTarget() {
void testPerCflowTarget() {
assertThatExceptionOfType(AopConfigException.class).isThrownBy(() ->
testIllegalInstantiationModel(AbstractAspectJAdvisorFactoryTests.PerCflowAspect.class));
}
@Test
public void testPerCflowBelowTarget() {
void testPerCflowBelowTarget() {
assertThatExceptionOfType(AopConfigException.class).isThrownBy(() ->
testIllegalInstantiationModel(AbstractAspectJAdvisorFactoryTests.PerCflowBelowAspect.class));
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -36,17 +36,17 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
* @author Juergen Hoeller
* @author Chris Beams
*/
public class AspectProxyFactoryTests {
class AspectProxyFactoryTests {
@Test
public void testWithNonAspect() {
void testWithNonAspect() {
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new TestBean());
assertThatIllegalArgumentException().isThrownBy(() ->
proxyFactory.addAspect(TestBean.class));
}
@Test
public void testWithSimpleAspect() throws Exception {
void testWithSimpleAspect() {
TestBean bean = new TestBean();
bean.setAge(2);
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(bean);
@@ -56,7 +56,7 @@ public class AspectProxyFactoryTests {
}
@Test
public void testWithPerThisAspect() throws Exception {
void testWithPerThisAspect() {
TestBean bean1 = new TestBean();
TestBean bean2 = new TestBean();
@@ -76,7 +76,7 @@ public class AspectProxyFactoryTests {
}
@Test
public void testWithInstanceWithNonAspect() throws Exception {
void testWithInstanceWithNonAspect() {
AspectJProxyFactory pf = new AspectJProxyFactory();
assertThatIllegalArgumentException().isThrownBy(() ->
pf.addAspect(new TestBean()));
@@ -84,7 +84,7 @@ public class AspectProxyFactoryTests {
@Test
@SuppressWarnings("unchecked")
public void testSerializable() throws Exception {
void testSerializable() throws Exception {
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new TestBean());
proxyFactory.addAspect(LoggingAspectOnVarargs.class);
ITestBean proxy = proxyFactory.getProxy();
@@ -94,7 +94,7 @@ public class AspectProxyFactoryTests {
}
@Test
public void testWithInstance() throws Exception {
void testWithInstance() throws Exception {
MultiplyReturnValue aspect = new MultiplyReturnValue();
int multiple = 3;
aspect.setMultiple(multiple);
@@ -113,14 +113,13 @@ public class AspectProxyFactoryTests {
}
@Test
public void testWithNonSingletonAspectInstance() throws Exception {
void testWithNonSingletonAspectInstance() {
AspectJProxyFactory pf = new AspectJProxyFactory();
assertThatIllegalArgumentException().isThrownBy(() -> pf.addAspect(new PerThisAspect()));
}
@Test // SPR-13328
@SuppressWarnings("unchecked")
public void testProxiedVarargsWithEnumArray() throws Exception {
public void testProxiedVarargsWithEnumArray() {
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new TestBean());
proxyFactory.addAspect(LoggingAspectOnVarargs.class);
ITestBean proxy = proxyFactory.getProxy();
@@ -128,8 +127,7 @@ public class AspectProxyFactoryTests {
}
@Test // SPR-13328
@SuppressWarnings("unchecked")
public void testUnproxiedVarargsWithEnumArray() throws Exception {
public void testUnproxiedVarargsWithEnumArray() {
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new TestBean());
proxyFactory.addAspect(LoggingAspectOnSetter.class);
ITestBean proxy = proxyFactory.getProxy();
@@ -174,13 +172,13 @@ public class AspectProxyFactoryTests {
public enum MyEnum implements MyInterface {
A, B;
A, B
}
public enum MyOtherEnum implements MyInterface {
C, D;
C, D
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,7 +37,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Rob Harrop
* @author Chris Beams
*/
public class AspectJNamespaceHandlerTests {
class AspectJNamespaceHandlerTests {
private ParserContext parserContext;
@@ -47,7 +47,7 @@ public class AspectJNamespaceHandlerTests {
@BeforeEach
public void setUp() throws Exception {
public void setUp() {
SourceExtractor sourceExtractor = new PassThroughSourceExtractor();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this.registry);
XmlReaderContext readerContext =
@@ -56,7 +56,7 @@ public class AspectJNamespaceHandlerTests {
}
@Test
public void testRegisterAutoProxyCreator() throws Exception {
void testRegisterAutoProxyCreator() {
AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(this.parserContext, null);
assertThat(registry.getBeanDefinitionCount()).as("Incorrect number of definitions registered").isEqualTo(1);
@@ -65,7 +65,7 @@ public class AspectJNamespaceHandlerTests {
}
@Test
public void testRegisterAspectJAutoProxyCreator() throws Exception {
void testRegisterAspectJAutoProxyCreator() {
AopNamespaceUtils.registerAspectJAutoProxyCreatorIfNecessary(this.parserContext, null);
assertThat(registry.getBeanDefinitionCount()).as("Incorrect number of definitions registered").isEqualTo(1);
@@ -77,7 +77,7 @@ public class AspectJNamespaceHandlerTests {
}
@Test
public void testRegisterAspectJAutoProxyCreatorWithExistingAutoProxyCreator() throws Exception {
void testRegisterAspectJAutoProxyCreatorWithExistingAutoProxyCreator() {
AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(this.parserContext, null);
assertThat(registry.getBeanDefinitionCount()).isEqualTo(1);
@@ -89,7 +89,7 @@ public class AspectJNamespaceHandlerTests {
}
@Test
public void testRegisterAutoProxyCreatorWhenAspectJAutoProxyCreatorAlreadyExists() throws Exception {
void testRegisterAutoProxyCreatorWhenAspectJAutoProxyCreatorAlreadyExists() {
AopNamespaceUtils.registerAspectJAutoProxyCreatorIfNecessary(this.parserContext, null);
assertThat(registry.getBeanDefinitionCount()).isEqualTo(1);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -40,7 +40,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Adrian Colyer
* @author Chris Beams
*/
public class AspectJPrecedenceComparatorTests {
class AspectJPrecedenceComparatorTests {
private static final int HIGH_PRECEDENCE_ADVISOR_ORDER = 100;
private static final int LOW_PRECEDENCE_ADVISOR_ORDER = 200;
@@ -56,7 +56,7 @@ public class AspectJPrecedenceComparatorTests {
@BeforeEach
public void setUp() throws Exception {
public void setUp() {
this.comparator = new AspectJPrecedenceComparator();
this.anyOldMethod = getClass().getMethods()[0];
this.anyOldPointcut = new AspectJExpressionPointcut();
@@ -65,7 +65,7 @@ public class AspectJPrecedenceComparatorTests {
@Test
public void testSameAspectNoAfterAdvice() {
void testSameAspectNoAfterAdvice() {
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 +76,7 @@ public class AspectJPrecedenceComparatorTests {
}
@Test
public void testSameAspectAfterAdvice() {
void testSameAspectAfterAdvice() {
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 +87,14 @@ public class AspectJPrecedenceComparatorTests {
}
@Test
public void testSameAspectOneOfEach() {
void testSameAspectOneOfEach() {
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
public void testSameAdvisorPrecedenceDifferentAspectNoAfterAdvice() {
void testSameAdvisorPrecedenceDifferentAspectNoAfterAdvice() {
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 +105,7 @@ public class AspectJPrecedenceComparatorTests {
}
@Test
public void testSameAdvisorPrecedenceDifferentAspectAfterAdvice() {
void testSameAdvisorPrecedenceDifferentAspectAfterAdvice() {
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 +116,7 @@ public class AspectJPrecedenceComparatorTests {
}
@Test
public void testHigherAdvisorPrecedenceNoAfterAdvice() {
void testHigherAdvisorPrecedenceNoAfterAdvice() {
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 +127,7 @@ public class AspectJPrecedenceComparatorTests {
}
@Test
public void testHigherAdvisorPrecedenceAfterAdvice() {
void testHigherAdvisorPrecedenceAfterAdvice() {
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 +138,7 @@ public class AspectJPrecedenceComparatorTests {
}
@Test
public void testLowerAdvisorPrecedenceNoAfterAdvice() {
void testLowerAdvisorPrecedenceNoAfterAdvice() {
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 +149,7 @@ public class AspectJPrecedenceComparatorTests {
}
@Test
public void testLowerAdvisorPrecedenceAfterAdvice() {
void testLowerAdvisorPrecedenceAfterAdvice() {
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);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,14 +25,14 @@ import org.springframework.util.StopWatch;
/**
* Benchmarks for introductions.
*
* <p>
* NOTE: No assertions!
*
* @author Rod Johnson
* @author Chris Beams
* @since 2.0
*/
public class IntroductionBenchmarkTests {
class IntroductionBenchmarkTests {
private static final int EXPECTED_COMPARE = 13;
@@ -53,7 +53,7 @@ public class IntroductionBenchmarkTests {
}
@Test
public void timeManyInvocations() {
void timeManyInvocations() {
StopWatch sw = new StopWatch();
TestBean target = new TestBean();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,14 +29,14 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
*
* @author Dave Syer
*/
public class NullPrimitiveTests {
class NullPrimitiveTests {
interface Foo {
int getValue();
}
@Test
public void testNullPrimitiveWithJdkProxy() {
void testNullPrimitiveWithJdkProxy() {
class SimpleFoo implements Foo {
@Override
@@ -51,8 +51,7 @@ public class NullPrimitiveTests {
Foo foo = (Foo) factory.getProxy();
assertThatExceptionOfType(AopInvocationException.class).isThrownBy(() ->
foo.getValue())
assertThatExceptionOfType(AopInvocationException.class).isThrownBy(foo::getValue)
.withMessageContaining("Foo.getValue()");
}
@@ -63,7 +62,7 @@ public class NullPrimitiveTests {
}
@Test
public void testNullPrimitiveWithCglibProxy() {
void testNullPrimitiveWithCglibProxy() {
Bar target = new Bar();
ProxyFactory factory = new ProxyFactory(target);
@@ -71,8 +70,7 @@ public class NullPrimitiveTests {
Bar bar = (Bar) factory.getProxy();
assertThatExceptionOfType(AopInvocationException.class).isThrownBy(() ->
bar.getValue())
assertThatExceptionOfType(AopInvocationException.class).isThrownBy(bar::getValue)
.withMessageContaining("Bar.getValue()");
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,13 +32,13 @@ import static org.springframework.core.testfixture.io.ResourceTestUtils.qualifie
* @author Chris Beams
* @since 03.09.2004
*/
public class PrototypeTargetTests {
class PrototypeTargetTests {
private static final Resource CONTEXT = qualifiedResource(PrototypeTargetTests.class, "context.xml");
@Test
public void testPrototypeProxyWithPrototypeTarget() {
void testPrototypeProxyWithPrototypeTarget() {
TestBeanImpl.constructionCount = 0;
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(CONTEXT);
@@ -52,7 +52,7 @@ public class PrototypeTargetTests {
}
@Test
public void testSingletonProxyWithPrototypeTarget() {
void testSingletonProxyWithPrototypeTarget() {
TestBeanImpl.constructionCount = 0;
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(CONTEXT);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -57,10 +57,10 @@ import static org.assertj.core.api.Assertions.assertThatRuntimeException;
* @author Chris Beams
* @since 14.05.2003
*/
public class ProxyFactoryTests {
class ProxyFactoryTests {
@Test
public void testIndexOfMethods() {
void testIndexOfMethods() {
TestBean target = new TestBean();
ProxyFactory pf = new ProxyFactory(target);
NopInterceptor nop = new NopInterceptor();
@@ -76,7 +76,7 @@ public class ProxyFactoryTests {
}
@Test
public void testRemoveAdvisorByReference() {
void testRemoveAdvisorByReference() {
TestBean target = new TestBean();
ProxyFactory pf = new ProxyFactory(target);
NopInterceptor nop = new NopInterceptor();
@@ -96,7 +96,7 @@ public class ProxyFactoryTests {
}
@Test
public void testRemoveAdvisorByIndex() {
void testRemoveAdvisorByIndex() {
TestBean target = new TestBean();
ProxyFactory pf = new ProxyFactory(target);
NopInterceptor nop = new NopInterceptor();
@@ -144,7 +144,7 @@ public class ProxyFactoryTests {
}
@Test
public void testReplaceAdvisor() {
void testReplaceAdvisor() {
TestBean target = new TestBean();
ProxyFactory pf = new ProxyFactory(target);
NopInterceptor nop = new NopInterceptor();
@@ -173,7 +173,7 @@ public class ProxyFactoryTests {
}
@Test
public void testAddRepeatedInterface() {
void testAddRepeatedInterface() {
TimeStamped tst = () -> {
throw new UnsupportedOperationException("getTimeStamp");
};
@@ -186,7 +186,7 @@ public class ProxyFactoryTests {
}
@Test
public void testGetsAllInterfaces() {
void testGetsAllInterfaces() {
// Extend to get new interface
class TestBeanSubclass extends TestBean implements Comparable<Object> {
@Override
@@ -220,10 +220,10 @@ public class ProxyFactoryTests {
}
@Test
public void testInterceptorInclusionMethods() {
void testInterceptorInclusionMethods() {
class MyInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
public Object invoke(MethodInvocation invocation) {
throw new UnsupportedOperationException();
}
}
@@ -244,9 +244,9 @@ public class ProxyFactoryTests {
}
@Test
public void testSealedInterfaceExclusion() {
void testSealedInterfaceExclusion() {
// String implements ConstantDesc on JDK 12+, sealed as of JDK 17
ProxyFactory factory = new ProxyFactory(new String());
ProxyFactory factory = new ProxyFactory("");
NopInterceptor di = new NopInterceptor();
factory.addAdvice(0, di);
Object proxy = factory.getProxy();
@@ -257,7 +257,7 @@ public class ProxyFactoryTests {
* Should see effect immediately on behavior.
*/
@Test
public void testCanAddAndRemoveAspectInterfacesOnSingleton() {
void testCanAddAndRemoveAspectInterfacesOnSingleton() {
ProxyFactory config = new ProxyFactory(new TestBean());
assertThat(config.getProxy()).as("Shouldn't implement TimeStamped before manipulation")
@@ -304,7 +304,7 @@ public class ProxyFactoryTests {
}
@Test
public void testProxyTargetClassWithInterfaceAsTarget() {
void testProxyTargetClassWithInterfaceAsTarget() {
ProxyFactory pf = new ProxyFactory();
pf.setTargetClass(ITestBean.class);
Object proxy = pf.getProxy();
@@ -320,7 +320,7 @@ public class ProxyFactoryTests {
}
@Test
public void testProxyTargetClassWithConcreteClassAsTarget() {
void testProxyTargetClassWithConcreteClassAsTarget() {
ProxyFactory pf = new ProxyFactory();
pf.setTargetClass(TestBean.class);
Object proxy = pf.getProxy();
@@ -347,19 +347,18 @@ public class ProxyFactoryTests {
}
@Test
public void testInterfaceProxiesCanBeOrderedThroughAnnotations() {
void testInterfaceProxiesCanBeOrderedThroughAnnotations() {
Object proxy1 = new ProxyFactory(new A()).getProxy();
Object proxy2 = new ProxyFactory(new B()).getProxy();
List<Object> list = new ArrayList<>(2);
list.add(proxy1);
list.add(proxy2);
AnnotationAwareOrderComparator.sort(list);
assertThat(list).element(0).isSameAs(proxy2);
assertThat(list).element(1).isSameAs(proxy1);
assertThat(list).containsExactly(proxy2, proxy1);
}
@Test
public void testTargetClassProxiesCanBeOrderedThroughAnnotations() {
void testTargetClassProxiesCanBeOrderedThroughAnnotations() {
ProxyFactory pf1 = new ProxyFactory(new A());
pf1.setProxyTargetClass(true);
ProxyFactory pf2 = new ProxyFactory(new B());
@@ -370,12 +369,11 @@ public class ProxyFactoryTests {
list.add(proxy1);
list.add(proxy2);
AnnotationAwareOrderComparator.sort(list);
assertThat(list).element(0).isSameAs(proxy2);
assertThat(list).element(1).isSameAs(proxy1);
assertThat(list).containsExactly(proxy2, proxy1);
}
@Test
public void testInterceptorWithoutJoinpoint() {
void testInterceptorWithoutJoinpoint() {
final TestBean target = new TestBean("tb");
ITestBean proxy = ProxyFactory.getProxy(ITestBean.class, (MethodInterceptor) invocation -> {
assertThat(invocation.getThis()).isNull();
@@ -385,7 +383,7 @@ public class ProxyFactoryTests {
}
@Test
public void testCharSequenceProxy() {
void testCharSequenceProxy() {
CharSequence target = "test";
ProxyFactory pf = new ProxyFactory(target);
ClassLoader cl = target.getClass().getClassLoader();
@@ -395,7 +393,7 @@ public class ProxyFactoryTests {
}
@Test
public void testDateProxy() {
void testDateProxy() {
Date target = new Date();
ProxyFactory pf = new ProxyFactory(target);
pf.setProxyTargetClass(true);
@@ -406,14 +404,14 @@ public class ProxyFactoryTests {
}
@Test
public void testJdbcSavepointProxy() throws SQLException {
void testJdbcSavepointProxy() throws SQLException {
Savepoint target = new Savepoint() {
@Override
public int getSavepointId() throws SQLException {
public int getSavepointId() {
return 1;
}
@Override
public String getSavepointName() throws SQLException {
public String getSavepointName() {
return "sp";
}
};
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,17 +37,17 @@ import static org.mockito.Mockito.mock;
* @author Chris Beams
* @author Juergen Hoeller
*/
public class ThrowsAdviceInterceptorTests {
class ThrowsAdviceInterceptorTests {
@Test
public void testNoHandlerMethods() {
void testNoHandlerMethods() {
// should require one handler method at least
assertThatExceptionOfType(AopConfigException.class).isThrownBy(() ->
new ThrowsAdviceInterceptor(new Object()));
}
@Test
public void testNotInvoked() throws Throwable {
void testNotInvoked() throws Throwable {
MyThrowsHandler th = new MyThrowsHandler();
ThrowsAdviceInterceptor ti = new ThrowsAdviceInterceptor(th);
Object ret = new Object();
@@ -58,7 +58,7 @@ public class ThrowsAdviceInterceptorTests {
}
@Test
public void testNoHandlerMethodForThrowable() throws Throwable {
void testNoHandlerMethodForThrowable() throws Throwable {
MyThrowsHandler th = new MyThrowsHandler();
ThrowsAdviceInterceptor ti = new ThrowsAdviceInterceptor(th);
assertThat(ti.getHandlerMethodCount()).isEqualTo(2);
@@ -70,7 +70,7 @@ public class ThrowsAdviceInterceptorTests {
}
@Test
public void testCorrectHandlerUsed() throws Throwable {
void testCorrectHandlerUsed() throws Throwable {
MyThrowsHandler th = new MyThrowsHandler();
ThrowsAdviceInterceptor ti = new ThrowsAdviceInterceptor(th);
FileNotFoundException ex = new FileNotFoundException();
@@ -84,7 +84,7 @@ public class ThrowsAdviceInterceptorTests {
}
@Test
public void testCorrectHandlerUsedForSubclass() throws Throwable {
void testCorrectHandlerUsedForSubclass() throws Throwable {
MyThrowsHandler th = new MyThrowsHandler();
ThrowsAdviceInterceptor ti = new ThrowsAdviceInterceptor(th);
// Extends RemoteException
@@ -97,10 +97,9 @@ public class ThrowsAdviceInterceptorTests {
}
@Test
public void testHandlerMethodThrowsException() throws Throwable {
void testHandlerMethodThrowsException() throws Throwable {
final Throwable t = new Throwable();
@SuppressWarnings("serial")
MyThrowsHandler th = new MyThrowsHandler() {
@Override
public void afterThrowing(RemoteException ex) throws Throwable {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,7 +34,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Chris Beams
* @since 06.04.2004
*/
public class ConcurrencyThrottleInterceptorTests {
class ConcurrencyThrottleInterceptorTests {
protected static final Log logger = LogFactory.getLog(ConcurrencyThrottleInterceptorTests.class);
@@ -44,7 +44,7 @@ public class ConcurrencyThrottleInterceptorTests {
@Test
public void testSerializable() throws Exception {
void testSerializable() throws Exception {
DerivedTestBean tb = new DerivedTestBean();
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setInterfaces(ITestBean.class);
@@ -63,12 +63,12 @@ public class ConcurrencyThrottleInterceptorTests {
}
@Test
public void testMultipleThreadsWithLimit1() {
void testMultipleThreadsWithLimit1() {
testMultipleThreads(1);
}
@Test
public void testMultipleThreadsWithLimit10() {
void testMultipleThreadsWithLimit10() {
testMultipleThreads(10);
}
@@ -111,8 +111,8 @@ public class ConcurrencyThrottleInterceptorTests {
private static class ConcurrencyThread extends Thread {
private ITestBean proxy;
private Throwable ex;
private final ITestBean proxy;
private final Throwable ex;
public ConcurrencyThread(ITestBean proxy, Throwable ex) {
this.proxy = proxy;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -144,7 +144,7 @@ class CustomizableTraceInterceptorTests {
void sunnyDayPathLogsCorrectlyWithPrettyMuchAllPlaceholdersMatching() throws Throwable {
MethodInvocation methodInvocation = mock();
given(methodInvocation.getMethod()).willReturn(String.class.getMethod("toString", new Class[0]));
given(methodInvocation.getMethod()).willReturn(String.class.getMethod("toString"));
given(methodInvocation.getThis()).willReturn(this);
given(methodInvocation.getArguments()).willReturn(new Object[]{"$ One \\$", 2L});
given(methodInvocation.proceed()).willReturn("Hello!");
@@ -177,7 +177,6 @@ class CustomizableTraceInterceptorTests {
* is properly configured in {@link CustomizableTraceInterceptor}.
*/
@Test
@SuppressWarnings("deprecation")
void supportedPlaceholderValues() {
assertThat(ALLOWED_PLACEHOLDERS).containsExactlyInAnyOrderElementsOf(getPlaceholderConstantValues());
}
@@ -35,10 +35,10 @@ import static org.mockito.Mockito.verify;
* @author Rick Evans
* @author Chris Beams
*/
public class DebugInterceptorTests {
class DebugInterceptorTests {
@Test
public void testSunnyDayPathLogsCorrectly() throws Throwable {
void testSunnyDayPathLogsCorrectly() throws Throwable {
MethodInvocation methodInvocation = mock();
Log log = mock();
@@ -52,7 +52,7 @@ public class DebugInterceptorTests {
}
@Test
public void testExceptionPathStillLogsCorrectly() throws Throwable {
void testExceptionPathStillLogsCorrectly() throws Throwable {
MethodInvocation methodInvocation = mock();
IllegalArgumentException exception = new IllegalArgumentException();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,9 +29,9 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Rod Johnson
* @author Chris Beams
*/
public class ExposeBeanNameAdvisorsTests {
class ExposeBeanNameAdvisorsTests {
private class RequiresBeanNameBoundTestBean extends TestBean {
private static class RequiresBeanNameBoundTestBean extends TestBean {
private final String beanName;
public RequiresBeanNameBoundTestBean(String beanName) {
@@ -46,7 +46,7 @@ public class ExposeBeanNameAdvisorsTests {
}
@Test
public void testNoIntroduction() {
void testNoIntroduction() {
String beanName = "foo";
TestBean target = new RequiresBeanNameBoundTestBean(beanName);
ProxyFactory pf = new ProxyFactory(target);
@@ -61,7 +61,7 @@ public class ExposeBeanNameAdvisorsTests {
}
@Test
public void testWithIntroduction() {
void testWithIntroduction() {
String beanName = "foo";
TestBean target = new RequiresBeanNameBoundTestBean(beanName);
ProxyFactory pf = new ProxyFactory(target);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,10 +31,10 @@ import static org.springframework.core.testfixture.io.ResourceTestUtils.qualifie
* @author Rod Johnson
* @author Chris Beams
*/
public class ExposeInvocationInterceptorTests {
class ExposeInvocationInterceptorTests {
@Test
public void testXmlConfig() {
void testXmlConfig() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(
qualifiedResource(ExposeInvocationInterceptorTests.class, "context.xml"));
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,10 +32,10 @@ import static org.mockito.Mockito.verify;
* @author Rick Evans
* @author Chris Beams
*/
public class PerformanceMonitorInterceptorTests {
class PerformanceMonitorInterceptorTests {
@Test
public void testSuffixAndPrefixAssignment() {
void testSuffixAndPrefixAssignment() {
PerformanceMonitorInterceptor interceptor = new PerformanceMonitorInterceptor();
assertThat(interceptor.getPrefix()).isNotNull();
@@ -49,9 +49,9 @@ public class PerformanceMonitorInterceptorTests {
}
@Test
public void testSunnyDayPathLogsPerformanceMetricsCorrectly() throws Throwable {
void testSunnyDayPathLogsPerformanceMetricsCorrectly() throws Throwable {
MethodInvocation mi = mock();
given(mi.getMethod()).willReturn(String.class.getMethod("toString", new Class[0]));
given(mi.getMethod()).willReturn(String.class.getMethod("toString"));
Log log = mock();
@@ -62,10 +62,10 @@ public class PerformanceMonitorInterceptorTests {
}
@Test
public void testExceptionPathStillLogsPerformanceMetricsCorrectly() throws Throwable {
void testExceptionPathStillLogsPerformanceMetricsCorrectly() throws Throwable {
MethodInvocation mi = mock();
given(mi.getMethod()).willReturn(String.class.getMethod("toString", new Class[0]));
given(mi.getMethod()).willReturn(String.class.getMethod("toString"));
given(mi.proceed()).willThrow(new IllegalArgumentException());
Log log = mock();
@@ -34,10 +34,10 @@ import static org.mockito.Mockito.verify;
* @author Rick Evans
* @author Chris Beams
*/
public class SimpleTraceInterceptorTests {
class SimpleTraceInterceptorTests {
@Test
public void testSunnyDayPathLogsCorrectly() throws Throwable {
void testSunnyDayPathLogsCorrectly() throws Throwable {
MethodInvocation mi = mock();
given(mi.getMethod()).willReturn(String.class.getMethod("toString"));
given(mi.getThis()).willReturn(this);
@@ -51,7 +51,7 @@ public class SimpleTraceInterceptorTests {
}
@Test
public void testExceptionPathStillLogsCorrectly() throws Throwable {
void testExceptionPathStillLogsCorrectly() throws Throwable {
MethodInvocation mi = mock();
given(mi.getMethod()).willReturn(String.class.getMethod("toString"));
given(mi.getThis()).willReturn(this);
@@ -29,31 +29,31 @@ import static org.mockito.Mockito.mock;
* @author Rick Evans
* @author Chris Beams
*/
public class DefaultScopedObjectTests {
class DefaultScopedObjectTests {
private static final String GOOD_BEAN_NAME = "foo";
@Test
public void testCtorWithNullBeanFactory() throws Exception {
void testCtorWithNullBeanFactory() {
assertThatIllegalArgumentException().isThrownBy(() ->
new DefaultScopedObject(null, GOOD_BEAN_NAME));
}
@Test
public void testCtorWithNullTargetBeanName() throws Exception {
void testCtorWithNullTargetBeanName() {
assertThatIllegalArgumentException().isThrownBy(() ->
testBadTargetBeanName(null));
}
@Test
public void testCtorWithEmptyTargetBeanName() throws Exception {
void testCtorWithEmptyTargetBeanName() {
assertThatIllegalArgumentException().isThrownBy(() ->
testBadTargetBeanName(""));
}
@Test
public void testCtorWithJustWhitespacedTargetBeanName() throws Exception {
void testCtorWithJustWhitespacedTargetBeanName() {
assertThatIllegalArgumentException().isThrownBy(() ->
testBadTargetBeanName(" "));
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,10 +31,10 @@ import static org.springframework.core.testfixture.io.ResourceTestUtils.qualifie
* @author Juergen Hoeller
* @author Chris Beams
*/
public class ScopedProxyAutowireTests {
class ScopedProxyAutowireTests {
@Test
public void testScopedProxyInheritsAutowireCandidateFalse() {
void testScopedProxyInheritsAutowireCandidateFalse() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(
qualifiedResource(ScopedProxyAutowireTests.class, "scopedAutowireFalse.xml"));
@@ -48,7 +48,7 @@ public class ScopedProxyAutowireTests {
}
@Test
public void testScopedProxyReplacesAutowireCandidateTrue() {
void testScopedProxyReplacesAutowireCandidateTrue() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(
qualifiedResource(ScopedProxyAutowireTests.class, "scopedAutowireTrue.xml"));
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -38,10 +38,10 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Chris Beams
* @author Sebastien Deleuze
*/
public class AopUtilsTests {
class AopUtilsTests {
@Test
public void testPointcutCanNeverApply() {
void testPointcutCanNeverApply() {
class TestPointcut extends StaticMethodMatcherPointcut {
@Override
public boolean matches(Method method, @Nullable Class<?> clazzy) {
@@ -54,13 +54,13 @@ public class AopUtilsTests {
}
@Test
public void testPointcutAlwaysApplies() {
void testPointcutAlwaysApplies() {
assertThat(AopUtils.canApply(new DefaultPointcutAdvisor(new NopInterceptor()), Object.class)).isTrue();
assertThat(AopUtils.canApply(new DefaultPointcutAdvisor(new NopInterceptor()), TestBean.class)).isTrue();
}
@Test
public void testPointcutAppliesToOneMethodOnObject() {
void testPointcutAppliesToOneMethodOnObject() {
class TestPointcut extends StaticMethodMatcherPointcut {
@Override
public boolean matches(Method method, @Nullable Class<?> clazz) {
@@ -80,7 +80,7 @@ public class AopUtilsTests {
* that's subverted the singleton construction limitation.
*/
@Test
public void testCanonicalFrameworkClassesStillCanonicalOnDeserialization() throws Exception {
void testCanonicalFrameworkClassesStillCanonicalOnDeserialization() throws Exception {
assertThat(SerializationTestUtils.serializeAndDeserialize(MethodMatcher.TRUE)).isSameAs(MethodMatcher.TRUE);
assertThat(SerializationTestUtils.serializeAndDeserialize(ClassFilter.TRUE)).isSameAs(ClassFilter.TRUE);
assertThat(SerializationTestUtils.serializeAndDeserialize(Pointcut.TRUE)).isSameAs(Pointcut.TRUE);
@@ -91,7 +91,7 @@ public class AopUtilsTests {
}
@Test
public void testInvokeJoinpointUsingReflection() throws Throwable {
void testInvokeJoinpointUsingReflection() throws Throwable {
String name = "foo";
TestBean testBean = new TestBean(name);
Method method = ReflectionUtils.findMethod(TestBean.class, "getName");
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -33,7 +33,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Rod Johnson
* @author Chris Beams
*/
public class ComposablePointcutTests {
class ComposablePointcutTests {
public static MethodMatcher GETTER_METHOD_MATCHER = new StaticMethodMatcher() {
@Override
@@ -56,23 +56,16 @@ public class ComposablePointcutTests {
}
};
public static MethodMatcher SETTER_METHOD_MATCHER = new StaticMethodMatcher() {
@Override
public boolean matches(Method m, @Nullable Class<?> targetClass) {
return m.getName().startsWith("set");
}
};
@Test
public void testMatchAll() throws NoSuchMethodException {
void testMatchAll() throws NoSuchMethodException {
Pointcut pc = new ComposablePointcut();
assertThat(pc.getClassFilter().matches(Object.class)).isTrue();
assertThat(pc.getMethodMatcher().matches(Object.class.getMethod("hashCode"), Exception.class)).isTrue();
}
@Test
public void testFilterByClass() throws NoSuchMethodException {
void testFilterByClass() {
ComposablePointcut pc = new ComposablePointcut();
assertThat(pc.getClassFilter().matches(Object.class)).isTrue();
@@ -92,7 +85,7 @@ public class ComposablePointcutTests {
}
@Test
public void testUnionMethodMatcher() {
void testUnionMethodMatcher() {
// Matches the getAge() method in any class
ComposablePointcut pc = new ComposablePointcut(ClassFilter.TRUE, GET_AGE_METHOD_MATCHER);
assertThat(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_ABSQUATULATE, TestBean.class)).isFalse();
@@ -115,7 +108,7 @@ public class ComposablePointcutTests {
}
@Test
public void testIntersectionMethodMatcher() {
void testIntersectionMethodMatcher() {
ComposablePointcut pc = new ComposablePointcut();
assertThat(pc.getMethodMatcher().matches(PointcutsTests.TEST_BEAN_ABSQUATULATE, TestBean.class)).isTrue();
assertThat(pc.getMethodMatcher().matches(PointcutsTests.TEST_BEAN_GET_AGE, TestBean.class)).isTrue();
@@ -132,7 +125,7 @@ public class ComposablePointcutTests {
}
@Test
public void testEqualsAndHashCode() throws Exception {
void testEqualsAndHashCode() {
ComposablePointcut pc1 = new ComposablePointcut();
ComposablePointcut pc2 = new ComposablePointcut();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -203,7 +203,7 @@ class ControlFlowPointcutTests {
// Will not be advised: not under MyComponent
assertThat(proxy.getAge()).isEqualTo(target.getAge());
assertThat(cflow.getEvaluations()).isEqualTo(1 * evaluationFactor);
assertThat(cflow.getEvaluations()).isEqualTo(evaluationFactor);
assertThat(nop.getCount()).isEqualTo(0);
// Will be advised: the proxy is invoked under MyComponent#getAge
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -47,14 +47,14 @@ import static org.mockito.Mockito.mock;
class DelegatingIntroductionInterceptorTests {
@Test
void testNullTarget() throws Exception {
void testNullTarget() {
// Shouldn't accept null target
assertThatIllegalArgumentException().isThrownBy(() ->
new DelegatingIntroductionInterceptor(null));
}
@Test
void testIntroductionInterceptorWithDelegation() throws Exception {
void testIntroductionInterceptorWithDelegation() {
TestBean raw = new TestBean();
assertThat(raw).isNotInstanceOf(TimeStamped.class);
ProxyFactory factory = new ProxyFactory(raw);
@@ -70,7 +70,7 @@ class DelegatingIntroductionInterceptorTests {
}
@Test
void testIntroductionInterceptorWithInterfaceHierarchy() throws Exception {
void testIntroductionInterceptorWithInterfaceHierarchy() {
TestBean raw = new TestBean();
assertThat(raw).isNotInstanceOf(SubTimeStamped.class);
ProxyFactory factory = new ProxyFactory(raw);
@@ -86,7 +86,7 @@ class DelegatingIntroductionInterceptorTests {
}
@Test
void testIntroductionInterceptorWithSuperInterface() throws Exception {
void testIntroductionInterceptorWithSuperInterface() {
TestBean raw = new TestBean();
assertThat(raw).isNotInstanceOf(TimeStamped.class);
ProxyFactory factory = new ProxyFactory(raw);
@@ -107,7 +107,7 @@ class DelegatingIntroductionInterceptorTests {
final long t = 1001L;
class Tester implements TimeStamped, ITester {
@Override
public void foo() throws Exception {
public void foo() {
}
@Override
public long getTimeStamp() {
@@ -138,7 +138,7 @@ class DelegatingIntroductionInterceptorTests {
@SuppressWarnings("serial")
class TestII extends DelegatingIntroductionInterceptor implements TimeStamped, ITester {
@Override
public void foo() throws Exception {
public void foo() {
}
@Override
public long getTimeStamp() {
@@ -177,9 +177,8 @@ class DelegatingIntroductionInterceptorTests {
assertThat(o).isNotInstanceOf(TimeStamped.class);
}
@SuppressWarnings("serial")
@Test
void testIntroductionInterceptorDoesntReplaceToString() throws Exception {
void testIntroductionInterceptorDoesNotReplaceToString() {
TestBean raw = new TestBean();
assertThat(raw).isNotInstanceOf(TimeStamped.class);
ProxyFactory factory = new ProxyFactory(raw);
@@ -246,7 +245,7 @@ class DelegatingIntroductionInterceptorTests {
// Test when target implements the interface: should get interceptor by preference.
@Test
void testIntroductionMasksTargetImplementation() throws Exception {
void testIntroductionMasksTargetImplementation() {
final long t = 1001L;
@SuppressWarnings("serial")
class TestII extends DelegatingIntroductionInterceptor implements TimeStamped {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,7 +34,7 @@ import static org.mockito.Mockito.mock;
* @author Juergen Hoeller
* @author Chris Beams
*/
public class MethodMatchersTests {
class MethodMatchersTests {
private static final Method TEST_METHOD = mock(Method.class);
@@ -56,19 +56,19 @@ public class MethodMatchersTests {
@Test
public void testDefaultMatchesAll() throws Exception {
void testDefaultMatchesAll() {
MethodMatcher defaultMm = MethodMatcher.TRUE;
assertThat(defaultMm.matches(EXCEPTION_GETMESSAGE, Exception.class)).isTrue();
assertThat(defaultMm.matches(ITESTBEAN_SETAGE, TestBean.class)).isTrue();
}
@Test
public void testMethodMatcherTrueSerializable() throws Exception {
void testMethodMatcherTrueSerializable() throws Exception {
assertThat(MethodMatcher.TRUE).isSameAs(SerializationTestUtils.serializeAndDeserialize(MethodMatcher.TRUE));
}
@Test
public void testSingle() throws Exception {
void testSingle() {
MethodMatcher defaultMm = MethodMatcher.TRUE;
assertThat(defaultMm.matches(EXCEPTION_GETMESSAGE, Exception.class)).isTrue();
assertThat(defaultMm.matches(ITESTBEAN_SETAGE, TestBean.class)).isTrue();
@@ -80,7 +80,7 @@ public class MethodMatchersTests {
@Test
public void testDynamicAndStaticMethodMatcherIntersection() throws Exception {
void testDynamicAndStaticMethodMatcherIntersection() {
MethodMatcher mm1 = MethodMatcher.TRUE;
MethodMatcher mm2 = new TestDynamicMethodMatcherWhichMatches();
MethodMatcher intersection = MethodMatchers.intersection(mm1, mm2);
@@ -95,7 +95,7 @@ public class MethodMatchersTests {
}
@Test
public void testStaticMethodMatcherUnion() throws Exception {
void testStaticMethodMatcherUnion() {
MethodMatcher getterMatcher = new StartsWithMatcher("get");
MethodMatcher setterMatcher = new StartsWithMatcher("set");
MethodMatcher union = MethodMatchers.union(getterMatcher, setterMatcher);
@@ -107,7 +107,7 @@ public class MethodMatchersTests {
}
@Test
public void testUnionEquals() {
void testUnionEquals() {
MethodMatcher first = MethodMatchers.union(MethodMatcher.TRUE, MethodMatcher.TRUE);
MethodMatcher second = new ComposablePointcut(MethodMatcher.TRUE).union(new ComposablePointcut(MethodMatcher.TRUE)).getMethodMatcher();
assertThat(first).isEqualTo(second);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,7 +31,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Rod Johnson
* @author Chris Beams
*/
public class PointcutsTests {
class PointcutsTests {
public static Method TEST_BEAN_SET_AGE;
public static Method TEST_BEAN_GET_AGE;
@@ -120,7 +120,7 @@ public class PointcutsTests {
@Test
public void testTrue() {
void testTrue() {
assertThat(Pointcuts.matches(Pointcut.TRUE, TEST_BEAN_SET_AGE, TestBean.class, 6)).isTrue();
assertThat(Pointcuts.matches(Pointcut.TRUE, TEST_BEAN_GET_AGE, TestBean.class)).isTrue();
assertThat(Pointcuts.matches(Pointcut.TRUE, TEST_BEAN_ABSQUATULATE, TestBean.class)).isTrue();
@@ -130,7 +130,7 @@ public class PointcutsTests {
}
@Test
public void testMatches() {
void testMatches() {
assertThat(Pointcuts.matches(allClassSetterPointcut, TEST_BEAN_SET_AGE, TestBean.class, 6)).isTrue();
assertThat(Pointcuts.matches(allClassSetterPointcut, TEST_BEAN_GET_AGE, TestBean.class)).isFalse();
assertThat(Pointcuts.matches(allClassSetterPointcut, TEST_BEAN_ABSQUATULATE, TestBean.class)).isFalse();
@@ -143,7 +143,7 @@ public class PointcutsTests {
* Should match all setters and getters on any class
*/
@Test
public void testUnionOfSettersAndGetters() {
void testUnionOfSettersAndGetters() {
Pointcut union = Pointcuts.union(allClassGetterPointcut, allClassSetterPointcut);
assertThat(Pointcuts.matches(union, TEST_BEAN_SET_AGE, TestBean.class, 6)).isTrue();
assertThat(Pointcuts.matches(union, TEST_BEAN_GET_AGE, TestBean.class)).isTrue();
@@ -151,7 +151,7 @@ public class PointcutsTests {
}
@Test
public void testUnionOfSpecificGetters() {
void testUnionOfSpecificGetters() {
Pointcut union = Pointcuts.union(allClassGetAgePointcut, allClassGetNamePointcut);
assertThat(Pointcuts.matches(union, TEST_BEAN_SET_AGE, TestBean.class, 6)).isFalse();
assertThat(Pointcuts.matches(union, TEST_BEAN_GET_AGE, TestBean.class)).isTrue();
@@ -175,7 +175,7 @@ public class PointcutsTests {
* Second one matches all getters in the MyTestBean class. TestBean getters shouldn't pass.
*/
@Test
public void testUnionOfAllSettersAndSubclassSetters() {
void testUnionOfAllSettersAndSubclassSetters() {
assertThat(Pointcuts.matches(myTestBeanSetterPointcut, TEST_BEAN_SET_AGE, TestBean.class, 6)).isFalse();
assertThat(Pointcuts.matches(myTestBeanSetterPointcut, TEST_BEAN_SET_AGE, MyTestBean.class, 6)).isTrue();
assertThat(Pointcuts.matches(myTestBeanSetterPointcut, TEST_BEAN_GET_AGE, TestBean.class)).isFalse();
@@ -193,7 +193,7 @@ public class PointcutsTests {
* it's the union of allClassGetAge and subclass getters
*/
@Test
public void testIntersectionOfSpecificGettersAndSubclassGetters() {
void testIntersectionOfSpecificGettersAndSubclassGetters() {
assertThat(Pointcuts.matches(allClassGetAgePointcut, TEST_BEAN_GET_AGE, TestBean.class)).isTrue();
assertThat(Pointcuts.matches(allClassGetAgePointcut, TEST_BEAN_GET_AGE, MyTestBean.class)).isTrue();
assertThat(Pointcuts.matches(myTestBeanGetterPointcut, TEST_BEAN_GET_NAME, TestBean.class)).isFalse();
@@ -239,7 +239,7 @@ public class PointcutsTests {
* The intersection of these two pointcuts leaves nothing.
*/
@Test
public void testSimpleIntersection() {
void testSimpleIntersection() {
Pointcut intersection = Pointcuts.intersection(allClassGetterPointcut, allClassSetterPointcut);
assertThat(Pointcuts.matches(intersection, TEST_BEAN_SET_AGE, TestBean.class, 6)).isFalse();
assertThat(Pointcuts.matches(intersection, TEST_BEAN_GET_AGE, TestBean.class)).isFalse();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -36,14 +36,14 @@ import static org.springframework.core.testfixture.io.ResourceTestUtils.qualifie
* @author Rod Johnson
* @author Chris Beams
*/
public class RegexpMethodPointcutAdvisorIntegrationTests {
class RegexpMethodPointcutAdvisorIntegrationTests {
private static final Resource CONTEXT =
qualifiedResource(RegexpMethodPointcutAdvisorIntegrationTests.class, "context.xml");
@Test
public void testSinglePattern() throws Throwable {
void testSinglePattern() throws Throwable {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(CONTEXT);
ITestBean advised = (ITestBean) bf.getBean("settersAdvised");
@@ -62,7 +62,7 @@ public class RegexpMethodPointcutAdvisorIntegrationTests {
}
@Test
public void testMultiplePatterns() throws Throwable {
void testMultiplePatterns() throws Throwable {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(CONTEXT);
// This is a CGLIB proxy, so we can proxy it to the target class
@@ -86,7 +86,7 @@ public class RegexpMethodPointcutAdvisorIntegrationTests {
}
@Test
public void testSerialization() throws Throwable {
void testSerialization() throws Throwable {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(CONTEXT);
// This is a CGLIB proxy, so we can proxy it to the target class
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,13 +30,13 @@ import static org.springframework.core.testfixture.io.ResourceTestUtils.qualifie
/**
* @author Stephane Nicoll
*/
public class CommonsPool2TargetSourceProxyTests {
class CommonsPool2TargetSourceProxyTests {
private static final Resource CONTEXT =
qualifiedResource(CommonsPool2TargetSourceProxyTests.class, "context.xml");
@Test
public void testProxy() throws Exception {
void testProxy() {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
reader.loadBeanDefinitions(CONTEXT);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -39,7 +39,7 @@ import static org.springframework.core.testfixture.io.ResourceTestUtils.qualifie
* @author Rod Johnson
* @author Chris Beams
*/
public class HotSwappableTargetSourceTests {
class HotSwappableTargetSourceTests {
/** Initial count value set in bean factory XML */
private static final int INITIAL_COUNT = 10;
@@ -68,7 +68,7 @@ public class HotSwappableTargetSourceTests {
* Check it works like a normal invoker
*/
@Test
public void testBasicFunctionality() {
void testBasicFunctionality() {
SideEffectBean proxied = (SideEffectBean) beanFactory.getBean("swappable");
assertThat(proxied.getCount()).isEqualTo(INITIAL_COUNT);
proxied.doWork();
@@ -80,7 +80,7 @@ public class HotSwappableTargetSourceTests {
}
@Test
public void testValidSwaps() {
void testValidSwaps() {
SideEffectBean target1 = (SideEffectBean) beanFactory.getBean("target1");
SideEffectBean target2 = (SideEffectBean) beanFactory.getBean("target2");
@@ -107,7 +107,7 @@ public class HotSwappableTargetSourceTests {
}
@Test
public void testRejectsSwapToNull() {
void testRejectsSwapToNull() {
HotSwappableTargetSource swapper = (HotSwappableTargetSource) beanFactory.getBean("swapper");
assertThatIllegalArgumentException().as("Shouldn't be able to swap to invalid value").isThrownBy(() ->
swapper.swap(null))
@@ -117,7 +117,7 @@ public class HotSwappableTargetSourceTests {
}
@Test
public void testSerialization() throws Exception {
void testSerialization() throws Exception {
SerializablePerson sp1 = new SerializablePerson();
sp1.setName("Tony");
SerializablePerson sp2 = new SerializablePerson();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,10 +28,10 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Juergen Hoeller
* @author Chris Beams
*/
public class LazyCreationTargetSourceTests {
class LazyCreationTargetSourceTests {
@Test
public void testCreateLazy() {
void testCreateLazy() {
TargetSource targetSource = new AbstractLazyCreationTargetSource() {
@Override
protected Object createObject() {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -36,10 +36,10 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Rod Johnson
* @author Chris Beams
*/
public class PrototypeBasedTargetSourceTests {
class PrototypeBasedTargetSourceTests {
@Test
public void testSerializability() throws Exception {
void testSerializability() throws Exception {
MutablePropertyValues tsPvs = new MutablePropertyValues();
tsPvs.add("targetBeanName", "person");
RootBeanDefinition tsBd = new RootBeanDefinition(TestTargetSource.class);
@@ -56,10 +56,8 @@ public class PrototypeBasedTargetSourceTests {
TestTargetSource cpts = (TestTargetSource) bf.getBean("ts");
TargetSource serialized = SerializationTestUtils.serializeAndDeserialize(cpts);
boolean condition = serialized instanceof SingletonTargetSource;
assertThat(condition).as("Changed to SingletonTargetSource on deserialization").isTrue();
SingletonTargetSource sts = (SingletonTargetSource) serialized;
assertThat(sts.getTarget()).isNotNull();
assertThat(serialized).isInstanceOfSatisfying(SingletonTargetSource.class,
sts -> assertThat(sts.getTarget()).isNotNull());
}
@@ -72,7 +70,7 @@ public class PrototypeBasedTargetSourceTests {
* state can't prevent serialization from working
*/
@SuppressWarnings({"unused", "serial"})
private TestBean thisFieldIsNotSerializable = new TestBean();
private final TestBean thisFieldIsNotSerializable = new TestBean();
@Override
public Object getTarget() {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,7 +30,7 @@ import static org.springframework.core.testfixture.io.ResourceTestUtils.qualifie
* @author Rod Johnson
* @author Chris Beams
*/
public class PrototypeTargetSourceTests {
class PrototypeTargetSourceTests {
/** Initial count value set in bean factory XML */
private static final int INITIAL_COUNT = 10;
@@ -52,7 +52,7 @@ public class PrototypeTargetSourceTests {
* With the singleton, there will be change.
*/
@Test
public void testPrototypeAndSingletonBehaveDifferently() {
void testPrototypeAndSingletonBehaveDifferently() {
SideEffectBean singleton = (SideEffectBean) beanFactory.getBean("singleton");
assertThat(singleton.getCount()).isEqualTo(INITIAL_COUNT);
singleton.doWork();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,7 +31,7 @@ import static org.springframework.core.testfixture.io.ResourceTestUtils.qualifie
* @author Rod Johnson
* @author Chris Beams
*/
public class ThreadLocalTargetSourceTests {
class ThreadLocalTargetSourceTests {
/** Initial count value set in bean factory XML */
private static final int INITIAL_COUNT = 10;
@@ -60,7 +60,7 @@ public class ThreadLocalTargetSourceTests {
* with one another.
*/
@Test
public void testUseDifferentManagedInstancesInSameThread() {
void testUseDifferentManagedInstancesInSameThread() {
SideEffectBean apartment = (SideEffectBean) beanFactory.getBean("apartment");
assertThat(apartment.getCount()).isEqualTo(INITIAL_COUNT);
apartment.doWork();
@@ -72,7 +72,7 @@ public class ThreadLocalTargetSourceTests {
}
@Test
public void testReuseInSameThread() {
void testReuseInSameThread() {
SideEffectBean apartment = (SideEffectBean) beanFactory.getBean("apartment");
assertThat(apartment.getCount()).isEqualTo(INITIAL_COUNT);
apartment.doWork();
@@ -86,7 +86,7 @@ public class ThreadLocalTargetSourceTests {
* Relies on introduction.
*/
@Test
public void testCanGetStatsViaMixin() {
void testCanGetStatsViaMixin() {
ThreadLocalTargetSourceStats stats = (ThreadLocalTargetSourceStats) beanFactory.getBean("apartment");
// +1 because creating target for stats call counts
assertThat(stats.getInvocationCount()).isEqualTo(1);
@@ -104,7 +104,7 @@ public class ThreadLocalTargetSourceTests {
}
@Test
public void testNewThreadHasOwnInstance() throws InterruptedException {
void testNewThreadHasOwnInstance() throws InterruptedException {
SideEffectBean apartment = (SideEffectBean) beanFactory.getBean("apartment");
assertThat(apartment.getCount()).isEqualTo(INITIAL_COUNT);
apartment.doWork();
@@ -144,7 +144,7 @@ public class ThreadLocalTargetSourceTests {
* Test for SPR-1442. Destroyed target should re-associated with thread and not throw NPE.
*/
@Test
public void testReuseDestroyedTarget() {
void testReuseDestroyedTarget() {
ThreadLocalTargetSource source = (ThreadLocalTargetSource)this.beanFactory.getBean("threadLocalTs");
// try first time
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,13 +27,13 @@ import static org.springframework.core.testfixture.TestGroup.LONG_RUNNING;
* @author Rob Harrop
* @author Chris Beams
*/
public class RefreshableTargetSourceTests {
class RefreshableTargetSourceTests {
/**
* Test what happens when checking for refresh but not refreshing object.
*/
@Test
public void testRefreshCheckWithNonRefresh() throws Exception {
void testRefreshCheckWithNonRefresh() throws Exception {
CountingRefreshableTargetSource ts = new CountingRefreshableTargetSource();
ts.setRefreshCheckDelay(0);
@@ -49,7 +49,7 @@ public class RefreshableTargetSourceTests {
* Test what happens when checking for refresh and refresh occurs.
*/
@Test
public void testRefreshCheckWithRefresh() throws Exception {
void testRefreshCheckWithRefresh() throws Exception {
CountingRefreshableTargetSource ts = new CountingRefreshableTargetSource(true);
ts.setRefreshCheckDelay(0);
@@ -65,7 +65,7 @@ public class RefreshableTargetSourceTests {
* Test what happens when no refresh occurs.
*/
@Test
public void testWithNoRefreshCheck() throws Exception {
void testWithNoRefreshCheck() {
CountingRefreshableTargetSource ts = new CountingRefreshableTargetSource(true);
ts.setRefreshCheckDelay(-1);

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