mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8f64480c9f |
@@ -21,7 +21,7 @@ jobs:
|
||||
toolchain: false
|
||||
- version: 21
|
||||
toolchain: true
|
||||
- version: 25
|
||||
- version: 24
|
||||
toolchain: true
|
||||
exclude:
|
||||
- os:
|
||||
|
||||
@@ -27,7 +27,7 @@ See the [Build from Source](https://github.com/spring-projects/spring-framework/
|
||||
|
||||
## Continuous Integration Builds
|
||||
|
||||
CI builds are defined with [GitHub Actions workflows](.github/workflows).
|
||||
Information regarding CI builds can be found in the [Spring Framework Concourse pipeline](ci/README.adoc) documentation.
|
||||
|
||||
## Stay in Touch
|
||||
|
||||
|
||||
+2
-2
@@ -86,7 +86,7 @@ configure([rootProject] + javaProjects) { project ->
|
||||
ext.javadocLinks = [
|
||||
"https://docs.oracle.com/en/java/javase/17/docs/api/",
|
||||
"https://jakarta.ee/specifications/platform/9/apidocs/",
|
||||
"https://docs.hibernate.org/orm/5.6/javadocs/",
|
||||
"https://docs.jboss.org/hibernate/orm/5.6/javadocs/",
|
||||
"https://www.quartz-scheduler.org/api/2.3.0/",
|
||||
"https://fasterxml.github.io/jackson-core/javadoc/2.14/",
|
||||
"https://fasterxml.github.io/jackson-databind/javadoc/2.14/",
|
||||
@@ -97,7 +97,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.14.1/api/",
|
||||
// "https://junit.org/junit5/docs/5.13.4/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/",
|
||||
|
||||
+18
-14
@@ -85,11 +85,11 @@ element. The following example shows how to use it:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
<bean id="collaborator" class="..." />
|
||||
<bean id="theTargetBean" class="..."/>
|
||||
|
||||
<bean id="client" class="...">
|
||||
<bean id="theClientBean" class="...">
|
||||
<property name="targetName">
|
||||
<idref bean="collaborator" />
|
||||
<idref bean="theTargetBean"/>
|
||||
</property>
|
||||
</bean>
|
||||
----
|
||||
@@ -99,24 +99,28 @@ following snippet:
|
||||
|
||||
[source,xml,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
<bean id="collaborator" class="..." />
|
||||
<bean id="theTargetBean" class="..." />
|
||||
|
||||
<bean id="client" class="...">
|
||||
<property name="targetName" value="collaborator" />
|
||||
<bean id="theClientBean" class="...">
|
||||
<property name="targetName" ref="theTargetBean"/>
|
||||
</bean>
|
||||
----
|
||||
|
||||
The first form is preferable to the second, because using the `idref` tag lets the
|
||||
container validate at deployment time that the referenced, named bean actually exists. In
|
||||
the second variation, no validation is performed on the value that is passed to the
|
||||
`targetName` property of the `client` bean. Typos are therefore only discovered (with most
|
||||
container validate at deployment time that the referenced, named bean actually
|
||||
exists. In the second variation, no validation is performed on the value that is passed
|
||||
to the `targetName` property of the `client` bean. Typos are only discovered (with most
|
||||
likely fatal results) when the `client` bean is actually instantiated. If the `client`
|
||||
bean is a xref:core/beans/factory-scopes.adoc[prototype] bean, this typo and the resulting
|
||||
exception may only be discovered long after the container is deployed.
|
||||
bean is a xref:core/beans/factory-scopes.adoc[prototype] bean, this typo and the resulting exception
|
||||
may only be discovered long after the container is deployed.
|
||||
|
||||
NOTE: A common place (at least in versions earlier than Spring 2.0) where the `<idref/>`
|
||||
element brings value is in the configuration of xref:core/aop-api/pfb.adoc#aop-pfb-1[AOP interceptors]
|
||||
in a `ProxyFactoryBean` bean definition. Using `<idref/>` elements when you specify the
|
||||
NOTE: The `local` attribute on the `idref` element is no longer supported in the 4.0 beans
|
||||
XSD, since it does not provide value over a regular `bean` reference any more. Change
|
||||
your existing `idref local` references to `idref bean` when upgrading to the 4.0 schema.
|
||||
|
||||
A common place (at least in versions earlier than Spring 2.0) where the `<idref/>` element
|
||||
brings value is in the configuration of xref:core/aop-api/pfb.adoc#aop-pfb-1[AOP interceptors] in a
|
||||
`ProxyFactoryBean` bean definition. Using `<idref/>` elements when you specify the
|
||||
interceptor names prevents you from misspelling an interceptor ID.
|
||||
|
||||
|
||||
|
||||
@@ -676,7 +676,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();
|
||||
----
|
||||
|
||||
|
||||
-11
@@ -77,17 +77,6 @@ exactly one candidate bean exists.
|
||||
|
||||
[TIP]
|
||||
====
|
||||
As stated in the documentation for Mockito, there are times when using `Mockito.when()` is
|
||||
inappropriate for stubbing a spy – for example, if calling a real method on a spy results
|
||||
in undesired side effects.
|
||||
|
||||
To avoid such undesired side effects, consider using
|
||||
`Mockito.doReturn(...).when(spy)...`, `Mockito.doThrow(...).when(spy)...`,
|
||||
`Mockito.doNothing().when(spy)...`, and similar methods.
|
||||
====
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
Only _singleton_ beans can be overridden. Any attempt to override a non-singleton bean
|
||||
will result in an exception.
|
||||
|
||||
|
||||
@@ -261,7 +261,7 @@ Kotlin::
|
||||
|
||||
This improves on the design of our xref:testing/mockmvc/htmlunit/mah.adoc#spring-mvc-test-server-htmlunit-mah-usage[HtmlUnit test]
|
||||
by leveraging the Page Object Pattern. As we mentioned in
|
||||
xref:testing/mockmvc/htmlunit/webdriver.adoc#mockmvc-server-htmlunit-webdriver-why[Why WebDriver and MockMvc?], we can use the Page Object Pattern
|
||||
xref:testing/mockmvc/htmlunit/webdriver.adoc#spring-mvc-test-server-htmlunit-webdriver-why[Why WebDriver and MockMvc?], we can use the Page Object Pattern
|
||||
with HtmlUnit, but it is much easier with WebDriver. Consider the following
|
||||
`CreateMessagePage` implementation:
|
||||
|
||||
|
||||
+28
-23
@@ -2,10 +2,10 @@
|
||||
= Application Events
|
||||
|
||||
The TestContext framework provides support for recording
|
||||
xref:core/beans/context-introduction.adoc#context-functionality-events[application events]
|
||||
published in the `ApplicationContext` so that assertions can be performed against those
|
||||
events within tests. All events published during the execution of a single test are made
|
||||
available via the `ApplicationEvents` API which allows you to process the events as a
|
||||
xref:core/beans/context-introduction.adoc#context-functionality-events[application events] published in the
|
||||
`ApplicationContext` so that assertions can be performed against those events within
|
||||
tests. All events published during the execution of a single test are made available via
|
||||
the `ApplicationEvents` API which allows you to process the events as a
|
||||
`java.util.Stream`.
|
||||
|
||||
To use `ApplicationEvents` in your tests, do the following.
|
||||
@@ -16,23 +16,16 @@ To use `ApplicationEvents` in your tests, do the following.
|
||||
that `ApplicationEventsTestExecutionListener` is registered by default and only needs
|
||||
to be manually registered if you have custom configuration via
|
||||
`@TestExecutionListeners` that does not include the default listeners.
|
||||
* When using the
|
||||
xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit-jupiter-extension[SpringExtension for JUnit Jupiter],
|
||||
declare a method parameter of type `ApplicationEvents` in a `@Test`, `@BeforeEach`, or
|
||||
`@AfterEach` method.
|
||||
** Since `ApplicationEvents` is scoped to the lifecycle of the current test method, this
|
||||
is the recommended approach.
|
||||
* Alternatively, you can annotate a field of type `ApplicationEvents` with `@Autowired`
|
||||
and use that instance of `ApplicationEvents` in your test and lifecycle methods.
|
||||
|
||||
NOTE: `ApplicationEvents` is registered with the `ApplicationContext` as a _resolvable
|
||||
dependency_ which is scoped to the lifecycle of the current test method. Consequently,
|
||||
`ApplicationEvents` cannot be accessed outside the lifecycle of a test method and cannot be
|
||||
`@Autowired` into the constructor of a test class.
|
||||
* Annotate a field of type `ApplicationEvents` with `@Autowired` and use that instance of
|
||||
`ApplicationEvents` in your test and lifecycle methods (such as `@BeforeEach` and
|
||||
`@AfterEach` methods in JUnit Jupiter).
|
||||
** When using the xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit-jupiter-extension[SpringExtension for JUnit Jupiter], you may declare a method
|
||||
parameter of type `ApplicationEvents` in a test or lifecycle method as an alternative
|
||||
to an `@Autowired` field in the test class.
|
||||
|
||||
The following test class uses the `SpringExtension` for JUnit Jupiter and
|
||||
{assertj-docs}[AssertJ] to assert the types of application events published while
|
||||
invoking a method in a Spring-managed component:
|
||||
{assertj-docs}[AssertJ] to assert the types of application events
|
||||
published while invoking a method in a Spring-managed component:
|
||||
|
||||
// Don't use "quotes" in the "subs" section because of the asterisks in /* ... */
|
||||
[tabs]
|
||||
@@ -45,10 +38,16 @@ Java::
|
||||
@RecordApplicationEvents // <1>
|
||||
class OrderServiceTests {
|
||||
|
||||
@Autowired
|
||||
OrderService orderService;
|
||||
|
||||
@Autowired
|
||||
ApplicationEvents events; // <2>
|
||||
|
||||
@Test
|
||||
void submitOrder(@Autowired OrderService service, ApplicationEvents events) { // <2>
|
||||
void submitOrder() {
|
||||
// Invoke method in OrderService that publishes an event
|
||||
service.submitOrder(new Order(/* ... */));
|
||||
orderService.submitOrder(new Order(/* ... */));
|
||||
// Verify that an OrderSubmitted event was published
|
||||
long numEvents = events.stream(OrderSubmitted.class).count(); // <3>
|
||||
assertThat(numEvents).isEqualTo(1);
|
||||
@@ -67,10 +66,16 @@ Kotlin::
|
||||
@RecordApplicationEvents // <1>
|
||||
class OrderServiceTests {
|
||||
|
||||
@Autowired
|
||||
lateinit var orderService: OrderService
|
||||
|
||||
@Autowired
|
||||
lateinit var events: ApplicationEvents // <2>
|
||||
|
||||
@Test
|
||||
fun submitOrder(@Autowired service: OrderService, events: ApplicationEvents) { // <2>
|
||||
fun submitOrder() {
|
||||
// Invoke method in OrderService that publishes an event
|
||||
service.submitOrder(Order(/* ... */))
|
||||
orderService.submitOrder(Order(/* ... */))
|
||||
// Verify that an OrderSubmitted event was published
|
||||
val numEvents = events.stream(OrderSubmitted::class).count() // <3>
|
||||
assertThat(numEvents).isEqualTo(1)
|
||||
|
||||
@@ -294,28 +294,7 @@ allPartsEvents.windowUntil(PartEvent::isLast)
|
||||
----
|
||||
======
|
||||
|
||||
NOTE: The body contents of the `PartEvent` objects must be completely consumed, relayed, or released to avoid memory leaks.
|
||||
|
||||
The following shows how to bind request parameters, including an optional `DataBinder` customization:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java]
|
||||
----
|
||||
Pet pet = request.bind(Pet.class, dataBinder -> dataBinder.setAllowedFields("name"));
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin]
|
||||
----
|
||||
val pet = request.bind(Pet::class.java, {dataBinder -> dataBinder.setAllowedFields("name")})
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
Note that the body contents of the `PartEvent` objects must be completely consumed, relayed, or released to avoid memory leaks.
|
||||
|
||||
[[webflux-fn-response]]
|
||||
=== ServerResponse
|
||||
|
||||
@@ -448,7 +448,7 @@ Java::
|
||||
----
|
||||
@GetMapping
|
||||
FragmentsRendering handle() {
|
||||
return FragmentsRendering.fragment("posts").fragment("comments").build();
|
||||
return FragmentsRendering.with("posts").fragment("comments").build();
|
||||
}
|
||||
----
|
||||
|
||||
@@ -458,7 +458,7 @@ Kotlin::
|
||||
----
|
||||
@GetMapping
|
||||
fun handle(): FragmentsRendering {
|
||||
return FragmentsRendering.fragment("posts").fragment("comments").build()
|
||||
return FragmentsRendering.with("posts").fragment("comments").build()
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
@@ -2,18 +2,18 @@
|
||||
= WebClient
|
||||
:page-section-summary-toc: 1
|
||||
|
||||
Spring WebFlux includes a client to perform HTTP requests. `WebClient` has a
|
||||
functional, fluent API based on Reactor (see xref:web/webflux-reactive-libraries.adoc[Reactive Libraries])
|
||||
Spring WebFlux includes a client to perform HTTP requests with. `WebClient` has a
|
||||
functional, fluent API based on Reactor, see xref:web-reactive.adoc#webflux-reactive-libraries[Reactive Libraries],
|
||||
which enables declarative composition of asynchronous logic without the need to deal with
|
||||
threads or concurrency. It is fully non-blocking, supports streaming, and relies on
|
||||
threads or concurrency. It is fully non-blocking, it supports streaming, and relies on
|
||||
the same xref:web/webflux/reactive-spring.adoc#webflux-codecs[codecs] that are also used to encode and
|
||||
decode request and response content on the server side.
|
||||
|
||||
`WebClient` needs an HTTP client library to perform requests. There is built-in
|
||||
`WebClient` needs an HTTP client library to perform requests with. There is built-in
|
||||
support for the following:
|
||||
|
||||
* {reactor-github-org}/reactor-netty[Reactor Netty]
|
||||
* {java-api}/java.net.http/java/net/http/HttpClient.html[JDK HttpClient]
|
||||
* https://github.com/jetty-project/jetty-reactive-httpclient[Jetty Reactive HttpClient]
|
||||
* https://hc.apache.org/index.html[Apache HttpComponents]
|
||||
* Others can be plugged in via `ClientHttpConnector`.
|
||||
* Others can be plugged via `ClientHttpConnector`.
|
||||
|
||||
@@ -310,7 +310,7 @@ Java::
|
||||
Flux<String> source = ... ;
|
||||
Mono<Void> output = session.send(source.map(session::textMessage)); <2>
|
||||
|
||||
return input.and(output); <3>
|
||||
return Mono.zip(input, output).then(); <3>
|
||||
}
|
||||
}
|
||||
----
|
||||
@@ -338,7 +338,7 @@ Kotlin::
|
||||
val source: Flux<String> = ...
|
||||
val output = session.send(source.map(session::textMessage)) // <2>
|
||||
|
||||
return input.and(output) // <3>
|
||||
return Mono.zip(input, output).then() // <3>
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
@@ -89,7 +89,39 @@ Kotlin::
|
||||
|
||||
You can map requests by using glob patterns and wildcards:
|
||||
|
||||
include::partial$web/uri-patterns.adoc[leveloffset=+1]
|
||||
[cols="2,3,5"]
|
||||
|===
|
||||
|Pattern |Description |Example
|
||||
|
||||
| `+?+`
|
||||
| Matches one character
|
||||
| `+"/pages/t?st.html"+` matches `+"/pages/test.html"+` and `+"/pages/t3st.html"+`
|
||||
|
||||
| `+*+`
|
||||
| Matches zero or more characters within a path segment
|
||||
| `+"/resources/*.png"+` matches `+"/resources/file.png"+`
|
||||
|
||||
`+"/projects/*/versions"+` matches `+"/projects/spring/versions"+` but does not match `+"/projects/spring/boot/versions"+`
|
||||
|
||||
| `+**+`
|
||||
| Matches zero or more path segments until the end of the path
|
||||
| `+"/resources/**"+` matches `+"/resources/file.png"+` and `+"/resources/images/file.png"+`
|
||||
|
||||
`+"/resources/**/file.png"+` is invalid as `+**+` is only allowed at the end of the path.
|
||||
|
||||
| `+{name}+`
|
||||
| Matches a path segment and captures it as a variable named "name"
|
||||
| `+"/projects/{project}/versions"+` matches `+"/projects/spring/versions"+` and captures `+project=spring+`
|
||||
|
||||
| `+{name:[a-z]+}+`
|
||||
| Matches the regexp `+"[a-z]+"+` as a path variable named "name"
|
||||
| `+"/projects/{project:[a-z]+}/versions"+` matches `+"/projects/spring/versions"+` but not `+"/projects/spring1/versions"+`
|
||||
|
||||
| `+{*path}+`
|
||||
| Matches zero or more path segments until the end of the path and captures it as a variable named "path"
|
||||
| `+"/resources/{*file}"+` matches `+"/resources/images/file.png"+` and captures `+file=/images/file.png+`
|
||||
|
||||
|===
|
||||
|
||||
Captured URI variables can be accessed with `@PathVariable`, as the following example shows:
|
||||
|
||||
|
||||
@@ -7,23 +7,19 @@ Spring WebFlux has built-in xref:core/validation/validator.adoc[Validation] for
|
||||
`@RequestMapping` methods, including xref:core/validation/beanvalidation.adoc[Java Bean Validation].
|
||||
Validation may be applied at one of two levels:
|
||||
|
||||
1. Java Bean Validation is applied individually to an
|
||||
xref:web/webflux/controller/ann-methods/modelattrib-method-args.adoc[@ModelAttribute],
|
||||
1. 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] method parameter
|
||||
annotated with `@jakarta.validation.Valid` or Spring's `@Validated` so long as
|
||||
it is a command object rather than a container such as `Map` or `Collection`, it does not
|
||||
have `Errors` or `BindingResult` immediately after in the method signature, and does not
|
||||
otherwise require method validation (see next). `WebExchangeBindException` is the
|
||||
exception raised when validating a method parameter individually.
|
||||
xref:web/webflux/controller/ann-methods/multipart-forms.adoc[@RequestPart] argument
|
||||
resolvers validate a method argument individually if the method parameter is annotated
|
||||
with Jakarta `@Valid` or Spring's `@Validated`, _AND_ there is no `Errors` or
|
||||
`BindingResult` parameter immediately after, _AND_ method validation is not needed (to be
|
||||
discussed next). The exception raised in this case is `WebExchangeBindException`.
|
||||
|
||||
2. Java Bean Validation is applied to the method when `@Constraint` annotations such as
|
||||
`@Min`, `@NotBlank` and others are declared directly on method parameters, or on the
|
||||
method for the return value, and it supersedes any validation that would be applied
|
||||
otherwise to a method parameter individually because method validation covers both
|
||||
method parameter constraints and nested constraints via `@Valid`.
|
||||
`HandlerMethodValidationException` is the exception raised validation is applied
|
||||
to the method.
|
||||
2. When `@Constraint` annotations such as `@Min`, `@NotBlank` and others are declared
|
||||
directly on method parameters, or on the method (for the return value), then method
|
||||
validation must be applied, and that supersedes validation at the method argument level
|
||||
because method validation covers both method parameter constraints and nested constraints
|
||||
via `@Valid`. The exception raised in this case is `HandlerMethodValidationException`.
|
||||
|
||||
Applications must handle both `WebExchangeBindException` and
|
||||
`HandlerMethodValidationException` as either may be raised depending on the controller
|
||||
|
||||
@@ -15,27 +15,27 @@ See xref:integration/rest-clients.adoc#rest-restclient[`RestClient`] for more de
|
||||
[[webmvc-webclient]]
|
||||
== `WebClient`
|
||||
|
||||
`WebClient` is a reactive client for making HTTP requests with a fluent API.
|
||||
`WebClient` is a reactive client to perform HTTP requests with a fluent API.
|
||||
|
||||
See xref:web/webflux-webclient.adoc[`WebClient`] for more details.
|
||||
See xref:web/webflux-webclient.adoc[WebClient] for more details.
|
||||
|
||||
|
||||
[[webmvc-resttemplate]]
|
||||
== `RestTemplate`
|
||||
|
||||
`RestTemplate` is a synchronous client for making HTTP requests. It is the original
|
||||
`RestTemplate` is a synchronous client to perform HTTP requests. It is the original
|
||||
Spring REST client and exposes a simple, template-method API over underlying HTTP client
|
||||
libraries.
|
||||
|
||||
See xref:integration/rest-clients.adoc#rest-resttemplate[`RestTemplate`] for details.
|
||||
See xref:integration/rest-clients.adoc[REST Endpoints] for details.
|
||||
|
||||
|
||||
[[webmvc-http-interface]]
|
||||
== HTTP Interface
|
||||
|
||||
The Spring Framework lets you define an HTTP service as a Java interface with HTTP
|
||||
The Spring Frameworks lets you define an HTTP service as a Java interface with HTTP
|
||||
exchange methods. You can then generate a proxy that implements this interface and
|
||||
performs the exchanges. This helps to simplify HTTP remote access and provides additional
|
||||
flexibility for choosing an API style such as synchronous or reactive.
|
||||
flexibility for to choose an API style such as synchronous or reactive.
|
||||
|
||||
See xref:integration/rest-clients.adoc#rest-http-interface[HTTP Interface] for details.
|
||||
See xref:integration/rest-clients.adoc#rest-http-interface[REST Endpoints] for details.
|
||||
|
||||
@@ -184,26 +184,6 @@ val map = request.params()
|
||||
----
|
||||
======
|
||||
|
||||
The following shows how to bind request parameters, including an optional `DataBinder` customization:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java]
|
||||
----
|
||||
Pet pet = request.bind(Pet.class, dataBinder -> dataBinder.setAllowedFields("name"));
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin]
|
||||
----
|
||||
val pet = request.bind(Pet::class.java, {dataBinder -> dataBinder.setAllowedFields("name")})
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
[[webmvc-fn-response]]
|
||||
=== ServerResponse
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ Java::
|
||||
----
|
||||
@GetMapping
|
||||
FragmentsRendering handle() {
|
||||
return FragmentsRendering.fragment("posts").fragment("comments").build();
|
||||
return FragmentsRendering.with("posts").fragment("comments").build();
|
||||
}
|
||||
----
|
||||
|
||||
@@ -58,7 +58,7 @@ Kotlin::
|
||||
----
|
||||
@GetMapping
|
||||
fun handle(): FragmentsRendering {
|
||||
return FragmentsRendering.fragment("posts").fragment("comments").build()
|
||||
return FragmentsRendering.with("posts").fragment("comments").build()
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
@@ -3,10 +3,7 @@
|
||||
|
||||
[.small]#xref:web/webflux/reactive-spring.adoc#webflux-filters[See equivalent in the Reactive stack]#
|
||||
|
||||
In the Servlet API, you can add a `jakarta.servlet.Filter` to apply interception-style logic
|
||||
before and after the rest of the processing chain of filters and the target `Servlet`.
|
||||
|
||||
The `spring-web` module has a number of built-in `Filter` implementations:
|
||||
The `spring-web` module provides some useful filters:
|
||||
|
||||
* xref:web/webmvc/filters.adoc#filters-http-put[Form Data]
|
||||
* xref:web/webmvc/filters.adoc#filters-forwarded-headers[Forwarded Headers]
|
||||
@@ -14,19 +11,9 @@ The `spring-web` module has a number of built-in `Filter` implementations:
|
||||
* xref:web/webmvc/filters.adoc#filters-cors[CORS]
|
||||
* xref:web/webmvc/filters.adoc#filters.url-handler[URL Handler]
|
||||
|
||||
There are also base class implementations for use in Spring applications:
|
||||
|
||||
* `GenericFilterBean` -- base class for a `Filter` configured as a Spring bean;
|
||||
integrates with the Spring `ApplicationContext` lifecycle.
|
||||
* `OncePerRequestFilter` -- extension of `GenericFilterBean` that supports a single
|
||||
invocation at the start of a request, i.e. during the `REQUEST` dispatch phase, and
|
||||
ignoring further handling via `FORWARD` dispatches. The filter also provides control
|
||||
over whether the `Filter` gets involved in `ASYNC` and `ERROR` dispatches.
|
||||
|
||||
Servlet filters can be configured in `web.xml` or via Servlet annotations.
|
||||
In a Spring Boot application , you can
|
||||
{spring-boot-docs}/how-to/webserver.html#howto.webserver.add-servlet-filter-listener.spring-bean[declare Filter's as beans]
|
||||
and Boot will have them configured.
|
||||
Servlet filters can be configured in the `web.xml` configuration file or using Servlet annotations.
|
||||
If you are using Spring Boot, you can
|
||||
{spring-boot-docs}/how-to/webserver.html#howto.webserver.add-servlet-filter-listener.spring-bean[declare them as beans and configure them as part of your application].
|
||||
|
||||
|
||||
[[filters-http-put]]
|
||||
|
||||
@@ -422,7 +422,7 @@ reactive types from the controller method.
|
||||
Reactive return values are handled as follows:
|
||||
|
||||
* A single-value promise is adapted to, similar to using `DeferredResult`. Examples
|
||||
include `CompletionStage` (JDK), `Mono` (Reactor), and `Single` (RxJava).
|
||||
include `CompletionStage` (JDK), Mono` (Reactor), and `Single` (RxJava).
|
||||
* A multi-value stream with a streaming media type (such as `application/x-ndjson`
|
||||
or `text/event-stream`) is adapted to, similar to using `ResponseBodyEmitter` or
|
||||
`SseEmitter`. Examples include `Flux` (Reactor) or `Observable` (RxJava).
|
||||
|
||||
+15
-2
@@ -103,9 +103,22 @@ Spring WebFlux. It was enabled for use in Spring MVC from version 5.3 and is ena
|
||||
default from version 6.0. See xref:web/webmvc/mvc-config/path-matching.adoc[MVC config] for
|
||||
customizations of path matching options.
|
||||
|
||||
You can map requests by using glob patterns and wildcards:
|
||||
`PathPattern` supports the same pattern syntax as `AntPathMatcher`. In addition, it also
|
||||
supports the capturing pattern, for example, `+{*spring}+`, for matching 0 or more path segments
|
||||
at the end of a path. `PathPattern` also restricts the use of `+**+` for matching multiple
|
||||
path segments such that it's only allowed at the end of a pattern. This eliminates many
|
||||
cases of ambiguity when choosing the best matching pattern for a given request.
|
||||
For full pattern syntax please refer to
|
||||
{spring-framework-api}/web/util/pattern/PathPattern.html[PathPattern] and
|
||||
{spring-framework-api}/util/AntPathMatcher.html[AntPathMatcher].
|
||||
|
||||
include::partial$web/uri-patterns.adoc[leveloffset=+1]
|
||||
Some example patterns:
|
||||
|
||||
* `+"/resources/ima?e.png"+` - match one character in a path segment
|
||||
* `+"/resources/*.png"+` - match zero or more characters in a path segment
|
||||
* `+"/resources/**"+` - match multiple path segments
|
||||
* `+"/projects/{project}/versions"+` - match a path segment and capture it as a variable
|
||||
* `++"/projects/{project:[a-z]+}/versions"++` - match and capture a variable with a regex
|
||||
|
||||
Captured URI variables can be accessed with `@PathVariable`. For example:
|
||||
|
||||
|
||||
@@ -7,26 +7,22 @@ Spring MVC has built-in xref:core/validation/validator.adoc[validation] for
|
||||
`@RequestMapping` methods, including xref:core/validation/beanvalidation.adoc[Java Bean Validation].
|
||||
Validation may be applied at one of two levels:
|
||||
|
||||
1. Java Bean Validation is applied individually to an
|
||||
xref:web/webmvc/mvc-controller/ann-methods/modelattrib-method-args.adoc[@ModelAttribute],
|
||||
1. 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] method parameter
|
||||
annotated with `@jakarta.validation.Valid` or Spring's `@Validated` so long as
|
||||
it is a command object rather than a container such as `Map` or `Collection`, it does not
|
||||
have `Errors` or `BindingResult` immediately after in the method signature, and does not
|
||||
otherwise require method validation (see next). `MethodArgumentNotValidException` is the
|
||||
exception raised when validating a method parameter individually.
|
||||
xref:web/webmvc/mvc-controller/ann-methods/multipart-forms.adoc[@RequestPart] argument
|
||||
resolvers validate a method argument individually if the method parameter is annotated
|
||||
with Jakarta `@Valid` or Spring's `@Validated`, _AND_ there is no `Errors` or
|
||||
`BindingResult` parameter immediately after, _AND_ method validation is not needed (to be
|
||||
discussed next). The exception raised in this case is `MethodArgumentNotValidException`.
|
||||
|
||||
2. Java Bean Validation is applied to the method when `@Constraint` annotations such as
|
||||
`@Min`, `@NotBlank` and others are declared directly on method parameters, or on the
|
||||
method for the return value, and it supersedes any validation that would be applied
|
||||
otherwise to a method parameter individually because method validation covers both
|
||||
method parameter constraints and nested constraints via `@Valid`.
|
||||
`HandlerMethodValidationException` is the exception raised validation is applied
|
||||
to the method.
|
||||
2. When `@Constraint` annotations such as `@Min`, `@NotBlank` and others are declared
|
||||
directly on method parameters, or on the method (for the return value), then method
|
||||
validation must be applied, and that supersedes validation at the method argument level
|
||||
because method validation covers both method parameter constraints and nested constraints
|
||||
via `@Valid`. The exception raised in this case is `HandlerMethodValidationException`.
|
||||
|
||||
Applications should handle both `MethodArgumentNotValidException` and
|
||||
`HandlerMethodValidationException` since either may be raised depending on the controller
|
||||
Applications must handle both `MethodArgumentNotValidException` and
|
||||
`HandlerMethodValidationException` as either may be raised depending on the controller
|
||||
method signature. The two exceptions, however are designed to be very similar, and can be
|
||||
handled with almost identical code. The main difference is that the former is for a single
|
||||
object while the latter is for a list of method parameters.
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
[cols="2,3,5"]
|
||||
|===
|
||||
|Pattern |Description |Example
|
||||
|
||||
| `spring`
|
||||
| Literal pattern
|
||||
| `+"/spring"+` matches `+"/spring"+`
|
||||
|
||||
| `+?+`
|
||||
| Matches one character
|
||||
| `+"/pages/t?st.html"+` matches `+"/pages/test.html"+` and `+"/pages/t3st.html"+`
|
||||
|
||||
| `+*+`
|
||||
| Matches zero or more characters within a path segment
|
||||
| `+"/resources/*.png"+` matches `+"/resources/file.png"+`
|
||||
|
||||
`+"/projects/*/versions"+` matches `+"/projects/spring/versions"+` but does not match `+"/projects/spring/boot/versions"+`.
|
||||
|
||||
`+"/projects/*"+` matches `+"/projects/spring"+` but does not match `+"/projects"+` as the path segment is not present.
|
||||
|
||||
| `+**+`
|
||||
| Matches zero or more path segments
|
||||
| `+"/resources/**"+` matches `+"/resources"+`, `+"/resources/file.png"+` and `+"/resources/images/file.png"+`
|
||||
|
||||
`+"/**/info"+` matches `+"/info"+`, `+"/spring/info"+` and `+"/spring/framework/info"+`
|
||||
|
||||
`+"/resources/**/file.png"+` is invalid as `+**+` is not allowed in the middle of the path.
|
||||
|
||||
`+"/**/spring/**"+` is not allowed, as only a single `+**+`/`+{*path}+` instance is allowed per pattern.
|
||||
|
||||
| `+{name}+`
|
||||
| Similar to `+*+`, but also captures the path segment as a variable named "name"
|
||||
| `+"/projects/{project}/versions"+` matches `+"/projects/spring/versions"+` and captures `+project=spring+`
|
||||
|
||||
`+"/projects/{project}/versions"+` does not match `+"/projects/spring/framework/versions"+` as it captures a single path segment.
|
||||
|
||||
| `{name:[a-z]+}`
|
||||
| Matches the regexp `"[a-z]+"` as a path variable named "name"
|
||||
| `"/projects/{project:[a-z]+}/versions"` matches `"/projects/spring/versions"` but not `"/projects/spring1/versions"`
|
||||
|
||||
| `+{*path}+`
|
||||
| Similar to `+**+`, but also captures the path segments as a variable named "path"
|
||||
| `+"/resources/{*file}"+` matches `+"/resources/images/file.png"+` and captures `+file=/images/file.png+`
|
||||
|
||||
`+"{*path}/resources"+` matches `+"/spring/framework/resources"+` and captures `+path=/spring/framework+`
|
||||
|
||||
`+"/resources/{*path}/file.png"+` is invalid as `{*path}` is not allowed in the middle of the path.
|
||||
|
||||
`+"/{*path}/spring/**"+` is not allowed, as only a single `+**+`/`+{*path}+` instance is allowed per pattern.
|
||||
|
||||
|===
|
||||
@@ -7,31 +7,31 @@ javaPlatform {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(platform("com.fasterxml.jackson:jackson-bom:2.18.5"))
|
||||
api(platform("io.micrometer:micrometer-bom:1.14.13"))
|
||||
api(platform("io.netty:netty-bom:4.1.128.Final"))
|
||||
api(platform("com.fasterxml.jackson:jackson-bom:2.18.4.1"))
|
||||
api(platform("io.micrometer:micrometer-bom:1.14.10"))
|
||||
api(platform("io.netty:netty-bom:4.1.124.Final"))
|
||||
api(platform("io.netty:netty5-bom:5.0.0.Alpha5"))
|
||||
api(platform("io.projectreactor:reactor-bom:2024.0.12"))
|
||||
api(platform("io.projectreactor:reactor-bom:2024.0.9"))
|
||||
api(platform("io.rsocket:rsocket-bom:1.1.5"))
|
||||
api(platform("org.apache.groovy:groovy-bom:4.0.29"))
|
||||
api(platform("org.apache.groovy:groovy-bom:4.0.28"))
|
||||
api(platform("org.apache.logging.log4j:log4j-bom:2.21.1"))
|
||||
api(platform("org.assertj:assertj-bom:3.27.6"))
|
||||
api(platform("org.eclipse.jetty:jetty-bom:12.0.30"))
|
||||
api(platform("org.eclipse.jetty.ee10:jetty-ee10-bom:12.0.30"))
|
||||
api(platform("org.assertj:assertj-bom:3.27.3"))
|
||||
api(platform("org.eclipse.jetty:jetty-bom:12.0.25"))
|
||||
api(platform("org.eclipse.jetty.ee10:jetty-ee10-bom:12.0.25"))
|
||||
api(platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.8.1"))
|
||||
api(platform("org.jetbrains.kotlinx:kotlinx-serialization-bom:1.6.3"))
|
||||
api(platform("org.junit:junit-bom:5.14.1"))
|
||||
api(platform("org.mockito:mockito-bom:5.20.0"))
|
||||
api(platform("org.junit:junit-bom:5.13.4"))
|
||||
api(platform("org.mockito:mockito-bom:5.18.0"))
|
||||
|
||||
constraints {
|
||||
api("com.fasterxml:aalto-xml:1.3.4")
|
||||
api("com.fasterxml:aalto-xml:1.3.2")
|
||||
api("com.fasterxml.woodstox:woodstox-core:6.7.0")
|
||||
api("com.github.ben-manes.caffeine:caffeine:3.2.3")
|
||||
api("com.github.ben-manes.caffeine:caffeine:3.2.2")
|
||||
api("com.github.librepdf:openpdf:1.3.43")
|
||||
api("com.google.code.findbugs:findbugs:3.0.1")
|
||||
api("com.google.code.findbugs:jsr305:3.0.2")
|
||||
api("com.google.code.gson:gson:2.13.2")
|
||||
api("com.google.protobuf:protobuf-java-util:4.32.1")
|
||||
api("com.google.code.gson:gson:2.13.1")
|
||||
api("com.google.protobuf:protobuf-java-util:4.31.1")
|
||||
api("com.h2database:h2:2.3.232")
|
||||
api("com.jayway.jsonpath:json-path:2.9.0")
|
||||
api("com.oracle.database.jdbc:ojdbc11:21.9.0.0")
|
||||
@@ -53,11 +53,11 @@ dependencies {
|
||||
api("io.r2dbc:r2dbc-h2:1.0.0.RELEASE")
|
||||
api("io.r2dbc:r2dbc-spi-test:1.0.0.RELEASE")
|
||||
api("io.r2dbc:r2dbc-spi:1.0.0.RELEASE")
|
||||
api("io.reactivex.rxjava3:rxjava:3.1.12")
|
||||
api("io.reactivex.rxjava3:rxjava:3.1.10")
|
||||
api("io.smallrye.reactive:mutiny:1.10.0")
|
||||
api("io.undertow:undertow-core:2.3.20.Final")
|
||||
api("io.undertow:undertow-servlet:2.3.20.Final")
|
||||
api("io.undertow:undertow-websockets-jsr:2.3.20.Final")
|
||||
api("io.undertow:undertow-core:2.3.18.Final")
|
||||
api("io.undertow:undertow-servlet:2.3.18.Final")
|
||||
api("io.undertow:undertow-websockets-jsr:2.3.18.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")
|
||||
@@ -90,17 +90,17 @@ dependencies {
|
||||
api("junit:junit:4.13.2")
|
||||
api("net.sf.jopt-simple:jopt-simple:5.0.4")
|
||||
api("org.apache-extras.beanshell:bsh:2.0b6")
|
||||
api("org.apache.activemq:activemq-broker:5.17.7")
|
||||
api("org.apache.activemq:activemq-kahadb-store:5.17.7")
|
||||
api("org.apache.activemq:activemq-stomp:5.17.7")
|
||||
api("org.apache.activemq:artemis-jakarta-client:2.42.0")
|
||||
api("org.apache.activemq:artemis-junit-5:2.42.0")
|
||||
api("org.apache.activemq:activemq-broker:5.17.6")
|
||||
api("org.apache.activemq:activemq-kahadb-store:5.17.6")
|
||||
api("org.apache.activemq:activemq-stomp:5.17.6")
|
||||
api("org.apache.activemq:artemis-jakarta-client:2.31.2")
|
||||
api("org.apache.activemq:artemis-junit-5:2.31.2")
|
||||
api("org.apache.commons:commons-pool2:2.9.0")
|
||||
api("org.apache.derby:derby:10.16.1.1")
|
||||
api("org.apache.derby:derbyclient:10.16.1.1")
|
||||
api("org.apache.derby:derbytools:10.16.1.1")
|
||||
api("org.apache.httpcomponents.client5:httpclient5:5.5")
|
||||
api("org.apache.httpcomponents.core5:httpcore5-reactive:5.3.5")
|
||||
api("org.apache.httpcomponents.core5:httpcore5-reactive:5.3.4")
|
||||
api("org.apache.poi:poi-ooxml:5.2.5")
|
||||
api("org.apache.tomcat.embed:tomcat-embed-core:10.1.28")
|
||||
api("org.apache.tomcat.embed:tomcat-embed-websocket:10.1.28")
|
||||
@@ -113,9 +113,9 @@ dependencies {
|
||||
api("org.bouncycastle:bcpkix-jdk18on:1.72")
|
||||
api("org.codehaus.jettison:jettison:1.5.4")
|
||||
api("org.crac:crac:1.4.0")
|
||||
api("org.dom4j:dom4j:2.2.0")
|
||||
api("org.easymock:easymock:5.6.0")
|
||||
api("org.eclipse.jetty:jetty-reactive-httpclient:4.0.12")
|
||||
api("org.dom4j:dom4j:2.1.4")
|
||||
api("org.easymock:easymock:5.5.0")
|
||||
api("org.eclipse.jetty:jetty-reactive-httpclient:4.0.9")
|
||||
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")
|
||||
@@ -129,7 +129,7 @@ dependencies {
|
||||
api("org.hibernate:hibernate-core-jakarta:5.6.15.Final")
|
||||
api("org.hibernate:hibernate-validator:7.0.5.Final")
|
||||
api("org.hsqldb:hsqldb:2.7.4")
|
||||
api("org.htmlunit:htmlunit:4.18.0")
|
||||
api("org.htmlunit:htmlunit:4.14.0")
|
||||
api("org.javamoney:moneta:1.4.4")
|
||||
api("org.jruby:jruby:9.4.13.0")
|
||||
api("org.junit.support:testng-engine:1.0.5")
|
||||
@@ -137,16 +137,16 @@ dependencies {
|
||||
api("org.ogce:xpp3:1.1.6")
|
||||
api("org.python:jython-standalone:2.7.4")
|
||||
api("org.quartz-scheduler:quartz:2.3.2")
|
||||
api("org.seleniumhq.selenium:htmlunit3-driver:4.38.0")
|
||||
api("org.seleniumhq.selenium:selenium-java:4.38.0")
|
||||
api("org.seleniumhq.selenium:htmlunit3-driver:4.34.0")
|
||||
api("org.seleniumhq.selenium:selenium-java:4.35.0")
|
||||
api("org.skyscreamer:jsonassert:1.5.3")
|
||||
api("org.slf4j:slf4j-api:2.0.17")
|
||||
api("org.testng:testng:7.11.0")
|
||||
api("org.webjars:underscorejs:1.8.3")
|
||||
api("org.webjars:webjars-locator-core:0.59")
|
||||
api("org.webjars:webjars-locator-lite:1.1.0")
|
||||
api("org.xmlunit:xmlunit-assertj:2.10.4")
|
||||
api("org.xmlunit:xmlunit-matchers:2.10.4")
|
||||
api("org.yaml:snakeyaml:2.5")
|
||||
api("org.xmlunit:xmlunit-assertj:2.10.3")
|
||||
api("org.xmlunit:xmlunit-matchers:2.10.3")
|
||||
api("org.yaml:snakeyaml:2.4")
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
version=6.2.13
|
||||
version=6.2.10
|
||||
|
||||
org.gradle.caching=true
|
||||
org.gradle.jvmargs=-Xmx2048m
|
||||
|
||||
@@ -62,7 +62,7 @@ public interface ProxyMethodInvocation extends MethodInvocation {
|
||||
MethodInvocation invocableClone(Object... arguments);
|
||||
|
||||
/**
|
||||
* Set the arguments to be used on subsequent invocations in any advice
|
||||
* Set the arguments to be used on subsequent invocations in the any advice
|
||||
* in this chain.
|
||||
* @param arguments the argument array
|
||||
*/
|
||||
|
||||
@@ -41,8 +41,8 @@ abstract class CoroutinesUtils {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
@Nullable
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
static Object awaitSingleOrNull(@Nullable Object value, Object continuation) {
|
||||
return MonoKt.awaitSingleOrNull(value instanceof Mono mono ? mono : Mono.justOrEmpty(value),
|
||||
(Continuation<Object>) continuation);
|
||||
|
||||
@@ -81,19 +81,13 @@ public abstract class ScopedProxyUtils {
|
||||
// Copy autowire settings from original bean definition.
|
||||
proxyDefinition.setAutowireCandidate(targetDefinition.isAutowireCandidate());
|
||||
proxyDefinition.setPrimary(targetDefinition.isPrimary());
|
||||
proxyDefinition.setFallback(targetDefinition.isFallback());
|
||||
if (targetDefinition instanceof AbstractBeanDefinition abd) {
|
||||
proxyDefinition.setDefaultCandidate(abd.isDefaultCandidate());
|
||||
proxyDefinition.copyQualifiersFrom(abd);
|
||||
}
|
||||
|
||||
// The target bean should be ignored in favor of the scoped proxy.
|
||||
targetDefinition.setAutowireCandidate(false);
|
||||
targetDefinition.setPrimary(false);
|
||||
targetDefinition.setFallback(false);
|
||||
if (targetDefinition instanceof AbstractBeanDefinition abd) {
|
||||
abd.setDefaultCandidate(false);
|
||||
}
|
||||
|
||||
// Register the target bean as separate bean in the factory.
|
||||
registry.registerBeanDefinition(targetBeanName, targetDefinition);
|
||||
|
||||
+3
-4
@@ -37,6 +37,7 @@ import static org.mockito.Mockito.verify;
|
||||
* Tests for {@link AsyncExecutionInterceptor}.
|
||||
*
|
||||
* @author Bao Ngo
|
||||
* @since 7.0
|
||||
*/
|
||||
class AsyncExecutionInterceptorTests {
|
||||
|
||||
@@ -61,13 +62,11 @@ class AsyncExecutionInterceptorTests {
|
||||
O run();
|
||||
}
|
||||
|
||||
|
||||
static class FutureRunner implements GenericRunner<Future<Void>> {
|
||||
|
||||
@Override
|
||||
public Future<Void> run() {
|
||||
return CompletableFuture.runAsync(() -> {});
|
||||
return CompletableFuture.runAsync(() -> {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,15 +18,6 @@ package org.springframework.aop.scope;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.AutowireCandidateQualifier;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.GenericBeanDefinition;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.support.SimpleBeanDefinitionRegistry;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
||||
@@ -34,7 +25,6 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
* Tests for {@link ScopedProxyUtils}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Juergen Hoeller
|
||||
* @since 5.1.10
|
||||
*/
|
||||
class ScopedProxyUtilsTests {
|
||||
@@ -63,79 +53,15 @@ class ScopedProxyUtilsTests {
|
||||
@Test
|
||||
void getOriginalBeanNameForNullTargetBean() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> ScopedProxyUtils.getOriginalBeanName(null))
|
||||
.withMessage("bean name 'null' does not refer to the target of a scoped proxy");
|
||||
.isThrownBy(() -> ScopedProxyUtils.getOriginalBeanName(null))
|
||||
.withMessage("bean name 'null' does not refer to the target of a scoped proxy");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getOriginalBeanNameForNonScopedTarget() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> ScopedProxyUtils.getOriginalBeanName("myBean"))
|
||||
.withMessage("bean name 'myBean' does not refer to the target of a scoped proxy");
|
||||
}
|
||||
|
||||
@Test
|
||||
void createScopedProxyTargetAppliesAutowireSettingsToProxyBeanDefinition() {
|
||||
AbstractBeanDefinition targetDefinition = new GenericBeanDefinition();
|
||||
// Opposite of defaults
|
||||
targetDefinition.setAutowireCandidate(false);
|
||||
targetDefinition.setDefaultCandidate(false);
|
||||
targetDefinition.setPrimary(true);
|
||||
targetDefinition.setFallback(true);
|
||||
|
||||
BeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
|
||||
BeanDefinitionHolder proxyHolder = ScopedProxyUtils.createScopedProxy(
|
||||
new BeanDefinitionHolder(targetDefinition, "myBean"), registry, false);
|
||||
AbstractBeanDefinition proxyBeanDefinition = (AbstractBeanDefinition) proxyHolder.getBeanDefinition();
|
||||
|
||||
assertThat(proxyBeanDefinition.isAutowireCandidate()).isFalse();
|
||||
assertThat(proxyBeanDefinition.isDefaultCandidate()).isFalse();
|
||||
assertThat(proxyBeanDefinition.isPrimary()).isTrue();
|
||||
assertThat(proxyBeanDefinition.isFallback()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void createScopedProxyTargetAppliesBeanAttributesToProxyBeanDefinition() {
|
||||
GenericBeanDefinition targetDefinition = new GenericBeanDefinition();
|
||||
// Opposite of defaults
|
||||
targetDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
targetDefinition.setSource("theSource");
|
||||
targetDefinition.addQualifier(new AutowireCandidateQualifier("myQualifier"));
|
||||
|
||||
BeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
|
||||
BeanDefinitionHolder proxyHolder = ScopedProxyUtils.createScopedProxy(
|
||||
new BeanDefinitionHolder(targetDefinition, "myBean"), registry, false);
|
||||
BeanDefinition proxyBeanDefinition = proxyHolder.getBeanDefinition();
|
||||
|
||||
assertThat(proxyBeanDefinition.getRole()).isEqualTo(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
assertThat(proxyBeanDefinition).isInstanceOf(RootBeanDefinition.class);
|
||||
assertThat(proxyBeanDefinition.getPropertyValues()).hasSize(2);
|
||||
assertThat(proxyBeanDefinition.getPropertyValues().get("proxyTargetClass")).isEqualTo(false);
|
||||
assertThat(proxyBeanDefinition.getPropertyValues().get("targetBeanName")).isEqualTo(
|
||||
ScopedProxyUtils.getTargetBeanName("myBean"));
|
||||
|
||||
RootBeanDefinition rootBeanDefinition = (RootBeanDefinition) proxyBeanDefinition;
|
||||
assertThat(rootBeanDefinition.getQualifiers()).hasSize(1);
|
||||
assertThat(rootBeanDefinition.hasQualifier("myQualifier")).isTrue();
|
||||
assertThat(rootBeanDefinition.getSource()).isEqualTo("theSource");
|
||||
}
|
||||
|
||||
@Test
|
||||
void createScopedProxyTargetCleansAutowireSettingsInTargetDefinition() {
|
||||
AbstractBeanDefinition targetDefinition = new GenericBeanDefinition();
|
||||
targetDefinition.setAutowireCandidate(true);
|
||||
targetDefinition.setDefaultCandidate(true);
|
||||
targetDefinition.setPrimary(true);
|
||||
targetDefinition.setFallback(true);
|
||||
|
||||
BeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
|
||||
ScopedProxyUtils.createScopedProxy(
|
||||
new BeanDefinitionHolder(targetDefinition, "myBean"), registry, false);
|
||||
|
||||
assertThat(targetDefinition.isAutowireCandidate()).isFalse();
|
||||
assertThat(targetDefinition.isDefaultCandidate()).isFalse();
|
||||
assertThat(targetDefinition.isPrimary()).isFalse();
|
||||
assertThat(targetDefinition.isFallback()).isFalse();
|
||||
.isThrownBy(() -> ScopedProxyUtils.getOriginalBeanName("myBean"))
|
||||
.withMessage("bean name 'myBean' does not refer to the target of a scoped proxy");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-28
@@ -19,7 +19,6 @@ package org.springframework.aop.framework
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.time.LocalDateTime
|
||||
|
||||
/**
|
||||
* Tests for Kotlin support in [CglibAopProxy].
|
||||
@@ -49,13 +48,6 @@ class CglibAopProxyKotlinTests {
|
||||
assertThatThrownBy { proxy.checkedException() }.isInstanceOf(CheckedException::class.java)
|
||||
}
|
||||
|
||||
@Test // gh-35487
|
||||
fun jvmDefault() {
|
||||
val proxyFactory = ProxyFactory()
|
||||
proxyFactory.setTarget(AddressRepo())
|
||||
proxyFactory.proxy
|
||||
}
|
||||
|
||||
|
||||
open class MyKotlinBean {
|
||||
|
||||
@@ -71,24 +63,4 @@ class CglibAopProxyKotlinTests {
|
||||
}
|
||||
|
||||
class CheckedException() : Exception()
|
||||
|
||||
open class AddressRepo(): CrudRepo<Address, Int>
|
||||
|
||||
interface CrudRepo<E : Any, ID : Any> {
|
||||
fun save(e: E): E {
|
||||
return e
|
||||
}
|
||||
fun delete(id: ID): Long {
|
||||
return 0L
|
||||
}
|
||||
}
|
||||
|
||||
data class Address(
|
||||
val id: Int = 0,
|
||||
val street: String,
|
||||
val version: Int = 0,
|
||||
val createdAt: LocalDateTime? = null,
|
||||
val updatedAt: LocalDateTime? = null,
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
+6
-5
@@ -566,7 +566,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
}
|
||||
|
||||
final List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
|
||||
Class<?> targetClass = ClassUtils.getUserClass(clazz);
|
||||
Class<?> targetClass = clazz;
|
||||
|
||||
do {
|
||||
final List<InjectionMetadata.InjectedElement> fieldElements = new ArrayList<>();
|
||||
@@ -586,11 +586,12 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
|
||||
final List<InjectionMetadata.InjectedElement> methodElements = new ArrayList<>();
|
||||
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
|
||||
if (method.isBridge()) {
|
||||
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
|
||||
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
|
||||
return;
|
||||
}
|
||||
MergedAnnotation<?> ann = findAutowiredAnnotation(method);
|
||||
if (ann != null && method.equals(BridgeMethodResolver.getMostSpecificMethod(method, clazz))) {
|
||||
MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);
|
||||
if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
|
||||
if (Modifier.isStatic(method.getModifiers())) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Autowired annotation is not supported on static methods: " + method);
|
||||
@@ -608,7 +609,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
}
|
||||
}
|
||||
boolean required = determineRequiredStatus(ann);
|
||||
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method, clazz);
|
||||
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
|
||||
methodElements.add(new AutowiredMethodElement(method, required, pd));
|
||||
}
|
||||
});
|
||||
|
||||
+1
-8
@@ -180,6 +180,7 @@ public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwa
|
||||
* {@code true} if a qualifier has been found and matched,
|
||||
* {@code null} if no qualifier has been found at all
|
||||
*/
|
||||
|
||||
@Nullable
|
||||
protected Boolean checkQualifiers(BeanDefinitionHolder bdHolder, Annotation[] annotationsToSearch) {
|
||||
boolean qualifierFound = false;
|
||||
@@ -374,14 +375,6 @@ public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwa
|
||||
return true;
|
||||
}
|
||||
}
|
||||
MethodParameter methodParam = descriptor.getMethodParameter();
|
||||
if (methodParam != null) {
|
||||
for (Annotation annotation : methodParam.getMethodAnnotations()) {
|
||||
if (isQualifier(annotation.annotationType())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+1
-20
@@ -29,7 +29,6 @@ import org.springframework.aot.generate.ValueCodeGenerator.Delegate;
|
||||
import org.springframework.aot.generate.ValueCodeGeneratorDelegates;
|
||||
import org.springframework.aot.generate.ValueCodeGeneratorDelegates.CollectionDelegate;
|
||||
import org.springframework.aot.generate.ValueCodeGeneratorDelegates.MapDelegate;
|
||||
import org.springframework.beans.factory.config.AutowiredPropertyMarker;
|
||||
import org.springframework.beans.factory.config.BeanReference;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.config.TypedStringValue;
|
||||
@@ -58,7 +57,6 @@ abstract class BeanDefinitionPropertyValueCodeGeneratorDelegates {
|
||||
* <li>{@link LinkedHashMap}</li>
|
||||
* <li>{@link BeanReference}</li>
|
||||
* <li>{@link TypedStringValue}</li>
|
||||
* <li>{@link AutowiredPropertyMarker}</li>
|
||||
* </ul>
|
||||
* When combined with {@linkplain ValueCodeGeneratorDelegates#INSTANCES the
|
||||
* delegates for common value types}, this should be added first as they have
|
||||
@@ -70,8 +68,7 @@ abstract class BeanDefinitionPropertyValueCodeGeneratorDelegates {
|
||||
new ManagedMapDelegate(),
|
||||
new LinkedHashMapDelegate(),
|
||||
new BeanReferenceDelegate(),
|
||||
new TypedStringValueDelegate(),
|
||||
new AutowiredPropertyMarkerDelegate()
|
||||
new TypedStringValueDelegate()
|
||||
);
|
||||
|
||||
|
||||
@@ -219,20 +216,4 @@ abstract class BeanDefinitionPropertyValueCodeGeneratorDelegates {
|
||||
return valueCodeGenerator.generateCode(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Delegate} for {@link AutowiredPropertyMarker} types.
|
||||
*/
|
||||
private static class AutowiredPropertyMarkerDelegate implements Delegate {
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public CodeBlock generateCode(ValueCodeGenerator valueCodeGenerator, Object value) {
|
||||
if (value instanceof AutowiredPropertyMarker) {
|
||||
return CodeBlock.of("$T.INSTANCE", AutowiredPropertyMarker.class);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-3
@@ -294,12 +294,9 @@ public class InstanceSupplierCodeGenerator {
|
||||
|
||||
this.generationContext.getRuntimeHints().reflection().registerMethod(factoryMethod, ExecutableMode.INVOKE);
|
||||
GeneratedMethod getInstanceMethod = generateGetInstanceSupplierMethod(method -> {
|
||||
CodeWarnings codeWarnings = new CodeWarnings();
|
||||
Class<?> suppliedType = ClassUtils.resolvePrimitiveIfNecessary(factoryMethod.getReturnType());
|
||||
codeWarnings.detectDeprecation(suppliedType, factoryMethod);
|
||||
method.addJavadoc("Get the bean instance supplier for '$L'.", beanName);
|
||||
method.addModifiers(PRIVATE_STATIC);
|
||||
codeWarnings.suppress(method);
|
||||
method.returns(ParameterizedTypeName.get(BeanInstanceSupplier.class, suppliedType));
|
||||
method.addStatement(generateInstanceSupplierForFactoryMethod(
|
||||
factoryMethod, suppliedType, targetClass, factoryMethod.getName()));
|
||||
|
||||
-13
@@ -152,18 +152,6 @@ public interface ConfigurableListableBeanFactory
|
||||
*/
|
||||
boolean isConfigurationFrozen();
|
||||
|
||||
/**
|
||||
* Mark current thread as main bootstrap thread for singleton instantiation,
|
||||
* with lenient bootstrap locking applying for background threads.
|
||||
* <p>Any such marker is to be removed at the end of the managed bootstrap in
|
||||
* {@link #preInstantiateSingletons()}.
|
||||
* @since 6.2.12
|
||||
* @see #setBootstrapExecutor
|
||||
* @see #preInstantiateSingletons()
|
||||
*/
|
||||
default void prepareSingletonBootstrap() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that all non-lazy-init singletons are instantiated, also considering
|
||||
* {@link org.springframework.beans.factory.FactoryBean FactoryBeans}.
|
||||
@@ -171,7 +159,6 @@ public interface ConfigurableListableBeanFactory
|
||||
* @throws BeansException if one of the singleton beans could not be created.
|
||||
* Note: This may have left the factory with some beans already initialized!
|
||||
* Call {@link #destroySingletons()} for full cleanup in this case.
|
||||
* @see #prepareSingletonBootstrap()
|
||||
* @see #destroySingletons()
|
||||
*/
|
||||
void preInstantiateSingletons() throws BeansException;
|
||||
|
||||
+2
-1
@@ -520,7 +520,8 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
* to check whether the bean with the given name matches the specified type. Allow
|
||||
* additional constraints to be applied to ensure that beans are not created early.
|
||||
* @param name the name of the bean to query
|
||||
* @param typeToMatch the type to match against (as a {@code ResolvableType})
|
||||
* @param typeToMatch the type to match against (as a
|
||||
* {@code ResolvableType})
|
||||
* @return {@code true} if the bean type matches, {@code false} if it
|
||||
* doesn't match or cannot be determined yet
|
||||
* @throws NoSuchBeanDefinitionException if there is no bean with the given name
|
||||
|
||||
+27
-51
@@ -58,7 +58,6 @@ import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.BeanNotOfRequiredTypeException;
|
||||
import org.springframework.beans.factory.CannotLoadBeanClassException;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InjectionPoint;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
|
||||
@@ -197,8 +196,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
/** Map from bean name to merged BeanDefinitionHolder. */
|
||||
private final Map<String, BeanDefinitionHolder> mergedBeanDefinitionHolders = new ConcurrentHashMap<>(256);
|
||||
|
||||
/** Map of bean definition names with a primary marker plus corresponding type. */
|
||||
private final Map<String, Class<?>> primaryBeanNamesWithType = new ConcurrentHashMap<>(16);
|
||||
/** Set of bean definition names with a primary marker. */
|
||||
private final Set<String> primaryBeanNames = ConcurrentHashMap.newKeySet(16);
|
||||
|
||||
/** Map of singleton and non-singleton bean names, keyed by dependency type. */
|
||||
private final Map<Class<?>, String[]> allBeanNamesByType = new ConcurrentHashMap<>(64);
|
||||
@@ -1038,7 +1037,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
protected void cacheMergedBeanDefinition(RootBeanDefinition mbd, String beanName) {
|
||||
super.cacheMergedBeanDefinition(mbd, beanName);
|
||||
if (mbd.isPrimary()) {
|
||||
this.primaryBeanNamesWithType.put(beanName, Void.class);
|
||||
this.primaryBeanNames.add(beanName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1102,11 +1101,6 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareSingletonBootstrap() {
|
||||
this.mainThreadPrefix = getThreadNamePrefix();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preInstantiateSingletons() throws BeansException {
|
||||
if (logger.isTraceEnabled()) {
|
||||
@@ -1118,12 +1112,11 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
|
||||
|
||||
// Trigger initialization of all non-lazy singleton beans...
|
||||
List<CompletableFuture<?>> futures = new ArrayList<>();
|
||||
|
||||
this.preInstantiationThread.set(PreInstantiation.MAIN);
|
||||
if (this.mainThreadPrefix == null) {
|
||||
this.mainThreadPrefix = getThreadNamePrefix();
|
||||
}
|
||||
this.mainThreadPrefix = getThreadNamePrefix();
|
||||
try {
|
||||
List<CompletableFuture<?>> futures = new ArrayList<>();
|
||||
for (String beanName : beanNames) {
|
||||
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
|
||||
if (!mbd.isAbstract() && mbd.isSingleton()) {
|
||||
@@ -1133,20 +1126,21 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!futures.isEmpty()) {
|
||||
try {
|
||||
CompletableFuture.allOf(futures.toArray(new CompletableFuture<?>[0])).join();
|
||||
}
|
||||
catch (CompletionException ex) {
|
||||
ReflectionUtils.rethrowRuntimeException(ex.getCause());
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
this.mainThreadPrefix = null;
|
||||
this.preInstantiationThread.remove();
|
||||
}
|
||||
|
||||
if (!futures.isEmpty()) {
|
||||
try {
|
||||
CompletableFuture.allOf(futures.toArray(new CompletableFuture<?>[0])).join();
|
||||
}
|
||||
catch (CompletionException ex) {
|
||||
ReflectionUtils.rethrowRuntimeException(ex.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger post-initialization callback for all applicable beans...
|
||||
for (String beanName : beanNames) {
|
||||
Object singletonInstance = getSingleton(beanName, false);
|
||||
@@ -1319,7 +1313,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
|
||||
// Cache a primary marker for the given bean.
|
||||
if (beanDefinition.isPrimary()) {
|
||||
this.primaryBeanNamesWithType.put(beanName, Void.class);
|
||||
this.primaryBeanNames.add(beanName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1411,7 +1405,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
destroySingleton(beanName);
|
||||
|
||||
// Remove a cached primary marker for the given bean.
|
||||
this.primaryBeanNamesWithType.remove(beanName);
|
||||
this.primaryBeanNames.remove(beanName);
|
||||
|
||||
// Notify all post-processors that the specified bean definition has been reset.
|
||||
for (MergedBeanDefinitionPostProcessor processor : getBeanPostProcessorCache().mergedDefinition) {
|
||||
@@ -1464,27 +1458,15 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
@Override
|
||||
protected void addSingleton(String beanName, Object singletonObject) {
|
||||
super.addSingleton(beanName, singletonObject);
|
||||
|
||||
Predicate<Class<?>> filter = (beanType -> beanType != Object.class && beanType.isInstance(singletonObject));
|
||||
this.allBeanNamesByType.keySet().removeIf(filter);
|
||||
this.singletonBeanNamesByType.keySet().removeIf(filter);
|
||||
|
||||
if (this.primaryBeanNamesWithType.containsKey(beanName) && singletonObject.getClass() != NullBean.class) {
|
||||
Class<?> beanType = (singletonObject instanceof FactoryBean<?> fb ?
|
||||
getTypeForFactoryBean(fb) : singletonObject.getClass());
|
||||
if (beanType != null) {
|
||||
this.primaryBeanNamesWithType.put(beanName, beanType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {
|
||||
super.registerSingleton(beanName, singletonObject);
|
||||
|
||||
updateManualSingletonNames(set -> set.add(beanName), set -> !this.beanDefinitionMap.containsKey(beanName));
|
||||
this.allBeanNamesByType.remove(Object.class);
|
||||
this.singletonBeanNamesByType.remove(Object.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1653,8 +1635,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
return doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
|
||||
}
|
||||
|
||||
@SuppressWarnings("NullAway") // Dataflow analysis limitation
|
||||
@Nullable
|
||||
@SuppressWarnings("NullAway")
|
||||
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
|
||||
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
|
||||
|
||||
@@ -1991,8 +1973,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();
|
||||
for (String candidate : candidateNames) {
|
||||
if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, fallbackDescriptor) &&
|
||||
(!multiple || matchesBeanName(candidate, descriptor.getDependencyName()) ||
|
||||
getAutowireCandidateResolver().hasQualifier(descriptor))) {
|
||||
(!multiple || getAutowireCandidateResolver().hasQualifier(descriptor))) {
|
||||
addCandidateEntry(result, candidate, descriptor, requiredType);
|
||||
}
|
||||
}
|
||||
@@ -2109,9 +2090,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
boolean candidateLocal = containsBeanDefinition(candidateBeanName);
|
||||
boolean primaryLocal = containsBeanDefinition(primaryBeanName);
|
||||
if (candidateLocal == primaryLocal) {
|
||||
String message = "more than one 'primary' bean found among candidates: " + candidates.keySet();
|
||||
logger.trace(message);
|
||||
throw new NoUniqueBeanDefinitionException(requiredType, candidates.size(), message);
|
||||
throw new NoUniqueBeanDefinitionException(requiredType, candidates.size(),
|
||||
"more than one 'primary' bean found among candidates: " + candidates.keySet());
|
||||
}
|
||||
else if (candidateLocal) {
|
||||
primaryBeanName = candidateBeanName;
|
||||
@@ -2263,12 +2243,12 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the given dependency name matches the bean name or the aliases
|
||||
* Determine whether the given candidate name matches the bean name or the aliases
|
||||
* stored in this bean definition.
|
||||
*/
|
||||
protected boolean matchesBeanName(String beanName, @Nullable String dependencyName) {
|
||||
return (dependencyName != null &&
|
||||
(dependencyName.equals(beanName) || ObjectUtils.containsElement(getAliases(beanName), dependencyName)));
|
||||
protected boolean matchesBeanName(String beanName, @Nullable String candidateName) {
|
||||
return (candidateName != null &&
|
||||
(candidateName.equals(beanName) || ObjectUtils.containsElement(getAliases(beanName), candidateName)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2288,12 +2268,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
* not matching the given bean name.
|
||||
*/
|
||||
private boolean hasPrimaryConflict(String beanName, Class<?> dependencyType) {
|
||||
for (Map.Entry<String, Class<?>> candidate : this.primaryBeanNamesWithType.entrySet()) {
|
||||
String candidateName = candidate.getKey();
|
||||
Class<?> candidateType = candidate.getValue();
|
||||
if (!candidateName.equals(beanName) && (candidateType != Void.class ?
|
||||
dependencyType.isAssignableFrom(candidateType) : // cached singleton class for primary bean
|
||||
isTypeMatch(candidateName, dependencyType))) { // not instantiated yet or not a singleton
|
||||
for (String candidate : this.primaryBeanNames) {
|
||||
if (isTypeMatch(candidate, dependencyType) && !candidate.equals(beanName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+3
-19
@@ -23,7 +23,6 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
@@ -419,29 +418,14 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
||||
String destroyMethodName = beanDefinition.resolvedDestroyMethodName;
|
||||
if (destroyMethodName == null) {
|
||||
destroyMethodName = beanDefinition.getDestroyMethodName();
|
||||
boolean autoCloseable = AutoCloseable.class.isAssignableFrom(target);
|
||||
boolean executorService = ExecutorService.class.isAssignableFrom(target);
|
||||
boolean autoCloseable = (AutoCloseable.class.isAssignableFrom(target));
|
||||
if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName) ||
|
||||
(destroyMethodName == null && (autoCloseable || executorService))) {
|
||||
(destroyMethodName == null && autoCloseable)) {
|
||||
// Only perform destroy method inference in case of the bean
|
||||
// not explicitly implementing the DisposableBean interface
|
||||
destroyMethodName = null;
|
||||
if (!(DisposableBean.class.isAssignableFrom(target))) {
|
||||
if (executorService) {
|
||||
destroyMethodName = SHUTDOWN_METHOD_NAME;
|
||||
try {
|
||||
// On JDK 19+, avoid the ExecutorService-level AutoCloseable default implementation
|
||||
// which awaits task termination for 1 day, even for delayed tasks such as cron jobs.
|
||||
// Custom close() implementations in ExecutorService subclasses are still accepted.
|
||||
if (target.getMethod(CLOSE_METHOD_NAME).getDeclaringClass() != ExecutorService.class) {
|
||||
destroyMethodName = CLOSE_METHOD_NAME;
|
||||
}
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
// Ignore - stick with shutdown()
|
||||
}
|
||||
}
|
||||
else if (autoCloseable) {
|
||||
if (autoCloseable) {
|
||||
destroyMethodName = CLOSE_METHOD_NAME;
|
||||
}
|
||||
else {
|
||||
|
||||
+31
-36
@@ -128,48 +128,43 @@ public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanReg
|
||||
locked = (lockFlag && this.singletonLock.tryLock());
|
||||
}
|
||||
try {
|
||||
// Defensively synchronize against non-thread-safe FactoryBean.getObject() implementations,
|
||||
// potentially to be called from a background thread while the main thread currently calls
|
||||
// the same getObject() method within the singleton lock.
|
||||
synchronized (factory) {
|
||||
Object object = this.factoryBeanObjectCache.get(beanName);
|
||||
if (object == null) {
|
||||
object = doGetObjectFromFactoryBean(factory, beanName);
|
||||
// Only post-process and store if not put there already during getObject() call above
|
||||
// (for example, because of circular reference processing triggered by custom getBean calls)
|
||||
Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
|
||||
if (alreadyThere != null) {
|
||||
object = alreadyThere;
|
||||
}
|
||||
else {
|
||||
if (shouldPostProcess) {
|
||||
Object object = this.factoryBeanObjectCache.get(beanName);
|
||||
if (object == null) {
|
||||
object = doGetObjectFromFactoryBean(factory, beanName);
|
||||
// Only post-process and store if not put there already during getObject() call above
|
||||
// (for example, because of circular reference processing triggered by custom getBean calls)
|
||||
Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
|
||||
if (alreadyThere != null) {
|
||||
object = alreadyThere;
|
||||
}
|
||||
else {
|
||||
if (shouldPostProcess) {
|
||||
if (locked) {
|
||||
if (isSingletonCurrentlyInCreation(beanName)) {
|
||||
// Temporarily return non-post-processed object, not storing it yet
|
||||
return object;
|
||||
}
|
||||
beforeSingletonCreation(beanName);
|
||||
}
|
||||
try {
|
||||
object = postProcessObjectFromFactoryBean(object, beanName);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new BeanCreationException(beanName,
|
||||
"Post-processing of FactoryBean's singleton object failed", ex);
|
||||
}
|
||||
finally {
|
||||
if (locked) {
|
||||
if (isSingletonCurrentlyInCreation(beanName)) {
|
||||
// Temporarily return non-post-processed object, not storing it yet
|
||||
return object;
|
||||
}
|
||||
beforeSingletonCreation(beanName);
|
||||
}
|
||||
try {
|
||||
object = postProcessObjectFromFactoryBean(object, beanName);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new BeanCreationException(beanName,
|
||||
"Post-processing of FactoryBean's singleton object failed", ex);
|
||||
}
|
||||
finally {
|
||||
if (locked) {
|
||||
afterSingletonCreation(beanName);
|
||||
}
|
||||
afterSingletonCreation(beanName);
|
||||
}
|
||||
}
|
||||
if (containsSingleton(beanName)) {
|
||||
this.factoryBeanObjectCache.put(beanName, object);
|
||||
}
|
||||
}
|
||||
if (containsSingleton(beanName)) {
|
||||
this.factoryBeanObjectCache.put(beanName, object);
|
||||
}
|
||||
}
|
||||
return object;
|
||||
}
|
||||
return object;
|
||||
}
|
||||
finally {
|
||||
if (locked) {
|
||||
|
||||
+1
-1
@@ -472,7 +472,7 @@ abstract class AbstractPropertyAccessorTests {
|
||||
assertThat(target.getAge()).as("age is OK").isEqualTo(age);
|
||||
assertThat(name).as("name is OK").isEqualTo(target.getName());
|
||||
accessor.setPropertyValues(new MutablePropertyValues());
|
||||
// Check it's unchanged
|
||||
// Check its unchanged
|
||||
assertThat(target.getAge()).as("age is OK").isEqualTo(age);
|
||||
assertThat(name).as("name is OK").isEqualTo(target.getName());
|
||||
}
|
||||
|
||||
+2
-7
@@ -87,7 +87,6 @@ import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.UrlResource;
|
||||
import org.springframework.core.testfixture.io.SerializationTestUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.StringValueResolver;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -1799,7 +1798,7 @@ class DefaultListableBeanFactoryTests {
|
||||
|
||||
assertThatExceptionOfType(NoUniqueBeanDefinitionException.class)
|
||||
.isThrownBy(() -> lbf.getBean(TestBean.class))
|
||||
.withMessageEndingWith("more than one 'primary' bean found among candidates: [bd1, bd2]");
|
||||
.withMessageContaining("more than one 'primary'");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -2123,7 +2122,7 @@ class DefaultListableBeanFactoryTests {
|
||||
|
||||
assertThatExceptionOfType(NoUniqueBeanDefinitionException.class)
|
||||
.isThrownBy(() -> lbf.getBean(ConstructorDependency.class, 42))
|
||||
.withMessageEndingWith("more than one 'primary' bean found among candidates: [bd1, bd2]");
|
||||
.withMessageContaining("more than one 'primary'");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -3224,10 +3223,6 @@ class DefaultListableBeanFactoryTests {
|
||||
assertThat(lbf.getBeanNamesForType(DerivedTestBean.class)).containsExactly("bd1");
|
||||
assertThat(lbf.getBeanNamesForType(NestedTestBean.class)).isSameAs(nestedBeanNames);
|
||||
assertThat(lbf.getBeanNamesForType(Object.class)).isSameAs(allBeanNames);
|
||||
|
||||
lbf.registerSingleton("bd3", new Object());
|
||||
assertThat(lbf.getBeanNamesForType(NestedTestBean.class)).isSameAs(nestedBeanNames);
|
||||
assertThat(lbf.getBeanNamesForType(Object.class)).containsExactly(StringUtils.addStringToArray(allBeanNames, "bd3"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
+82
-167
@@ -1275,6 +1275,88 @@ class AutowiredAnnotationBeanPostProcessorTests {
|
||||
assertThat(bean.getTestBean().get("testBean2")).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void fieldInjectionWithMap() {
|
||||
RootBeanDefinition bd = new RootBeanDefinition(MapFieldInjectionBean.class);
|
||||
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
||||
bf.registerBeanDefinition("annotatedBean", bd);
|
||||
TestBean tb1 = new TestBean("tb1");
|
||||
TestBean tb2 = new TestBean("tb2");
|
||||
bf.registerSingleton("testBean1", tb1);
|
||||
bf.registerSingleton("testBean2", tb2);
|
||||
bf.registerAlias("testBean1", "testBean");
|
||||
|
||||
MapFieldInjectionBean bean = bf.getBean("annotatedBean", MapFieldInjectionBean.class);
|
||||
assertThat(bean.getTestBeanMap()).hasSize(2);
|
||||
assertThat(bean.getTestBeanMap()).containsKey("testBean1");
|
||||
assertThat(bean.getTestBeanMap()).containsKey("testBean2");
|
||||
assertThat(bean.getTestBeanMap()).containsValue(tb1);
|
||||
assertThat(bean.getTestBeanMap()).containsValue(tb2);
|
||||
|
||||
bean = bf.getBean("annotatedBean", MapFieldInjectionBean.class);
|
||||
assertThat(bean.getTestBeanMap()).hasSize(2);
|
||||
assertThat(bean.getTestBeanMap()).containsKey("testBean1");
|
||||
assertThat(bean.getTestBeanMap()).containsKey("testBean2");
|
||||
assertThat(bean.getTestBeanMap()).containsValue(tb1);
|
||||
assertThat(bean.getTestBeanMap()).containsValue(tb2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void methodInjectionWithMap() {
|
||||
RootBeanDefinition bd = new RootBeanDefinition(MapMethodInjectionBean.class);
|
||||
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
||||
bf.registerBeanDefinition("annotatedBean", bd);
|
||||
TestBean tb = new TestBean();
|
||||
bf.registerSingleton("testBean", tb);
|
||||
|
||||
MapMethodInjectionBean bean = bf.getBean("annotatedBean", MapMethodInjectionBean.class);
|
||||
assertThat(bean.getTestBeanMap()).hasSize(1);
|
||||
assertThat(bean.getTestBeanMap()).containsKey("testBean");
|
||||
assertThat(bean.getTestBeanMap()).containsValue(tb);
|
||||
assertThat(bean.getTestBean()).isSameAs(tb);
|
||||
|
||||
bean = bf.getBean("annotatedBean", MapMethodInjectionBean.class);
|
||||
assertThat(bean.getTestBeanMap()).hasSize(1);
|
||||
assertThat(bean.getTestBeanMap()).containsKey("testBean");
|
||||
assertThat(bean.getTestBeanMap()).containsValue(tb);
|
||||
assertThat(bean.getTestBean()).isSameAs(tb);
|
||||
}
|
||||
|
||||
@Test
|
||||
void methodInjectionWithMapAndMultipleMatches() {
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(MapMethodInjectionBean.class));
|
||||
bf.registerBeanDefinition("testBean1", new RootBeanDefinition(TestBean.class));
|
||||
bf.registerBeanDefinition("testBean2", new RootBeanDefinition(TestBean.class));
|
||||
assertThatExceptionOfType(UnsatisfiedDependencyException.class).as("should have failed, more than one bean of type")
|
||||
.isThrownBy(() -> bf.getBean("annotatedBean"))
|
||||
.satisfies(methodParameterDeclaredOn(MapMethodInjectionBean.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void methodInjectionWithMapAndMultipleMatchesButOnlyOneAutowireCandidate() {
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(MapMethodInjectionBean.class));
|
||||
bf.registerBeanDefinition("testBean1", new RootBeanDefinition(TestBean.class));
|
||||
RootBeanDefinition rbd2 = new RootBeanDefinition(TestBean.class);
|
||||
rbd2.setAutowireCandidate(false);
|
||||
bf.registerBeanDefinition("testBean2", rbd2);
|
||||
|
||||
MapMethodInjectionBean bean = bf.getBean("annotatedBean", MapMethodInjectionBean.class);
|
||||
TestBean tb = bf.getBean("testBean1", TestBean.class);
|
||||
assertThat(bean.getTestBeanMap()).hasSize(1);
|
||||
assertThat(bean.getTestBeanMap()).containsKey("testBean1");
|
||||
assertThat(bean.getTestBeanMap()).containsValue(tb);
|
||||
assertThat(bean.getTestBean()).isSameAs(tb);
|
||||
}
|
||||
|
||||
@Test
|
||||
void methodInjectionWithMapAndNoMatches() {
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(MapMethodInjectionBean.class));
|
||||
|
||||
MapMethodInjectionBean bean = bf.getBean("annotatedBean", MapMethodInjectionBean.class);
|
||||
assertThat(bean.getTestBeanMap()).isNull();
|
||||
assertThat(bean.getTestBean()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructorInjectionWithTypedMapAsBean() {
|
||||
RootBeanDefinition bd = new RootBeanDefinition(MapConstructorInjectionBean.class);
|
||||
@@ -1327,19 +1409,6 @@ class AutowiredAnnotationBeanPostProcessorTests {
|
||||
|
||||
@Test
|
||||
void constructorInjectionWithPlainHashMapAsBean() {
|
||||
RootBeanDefinition bd = new RootBeanDefinition(NamedMapConstructorInjectionBean.class);
|
||||
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
||||
bf.registerBeanDefinition("annotatedBean", bd);
|
||||
bf.registerBeanDefinition("testBeanMap", new RootBeanDefinition(HashMap.class));
|
||||
|
||||
NamedMapConstructorInjectionBean bean = bf.getBean("annotatedBean", NamedMapConstructorInjectionBean.class);
|
||||
assertThat(bean.getTestBeanMap()).isSameAs(bf.getBean("testBeanMap"));
|
||||
bean = bf.getBean("annotatedBean", NamedMapConstructorInjectionBean.class);
|
||||
assertThat(bean.getTestBeanMap()).isSameAs(bf.getBean("testBeanMap"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructorInjectionWithQualifiedPlainHashMapAsBean() {
|
||||
RootBeanDefinition bd = new RootBeanDefinition(QualifiedMapConstructorInjectionBean.class);
|
||||
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
||||
bf.registerBeanDefinition("annotatedBean", bd);
|
||||
@@ -1428,114 +1497,6 @@ class AutowiredAnnotationBeanPostProcessorTests {
|
||||
assertThat(bean.getTestBeanSet()).contains(tb1, tb2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void fieldInjectionWithMap() {
|
||||
RootBeanDefinition bd = new RootBeanDefinition(MapFieldInjectionBean.class);
|
||||
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
||||
bf.registerBeanDefinition("annotatedBean", bd);
|
||||
TestBean tb1 = new TestBean("tb1");
|
||||
TestBean tb2 = new TestBean("tb2");
|
||||
bf.registerSingleton("testBean1", tb1);
|
||||
bf.registerSingleton("testBean2", tb2);
|
||||
bf.registerAlias("testBean1", "testBean");
|
||||
|
||||
MapFieldInjectionBean bean = bf.getBean("annotatedBean", MapFieldInjectionBean.class);
|
||||
assertThat(bean.getTestBeanMap()).hasSize(2);
|
||||
assertThat(bean.getTestBeanMap()).containsKey("testBean1");
|
||||
assertThat(bean.getTestBeanMap()).containsKey("testBean2");
|
||||
assertThat(bean.getTestBeanMap()).containsValue(tb1);
|
||||
assertThat(bean.getTestBeanMap()).containsValue(tb2);
|
||||
|
||||
bean = bf.getBean("annotatedBean", MapFieldInjectionBean.class);
|
||||
assertThat(bean.getTestBeanMap()).hasSize(2);
|
||||
assertThat(bean.getTestBeanMap()).containsKey("testBean1");
|
||||
assertThat(bean.getTestBeanMap()).containsKey("testBean2");
|
||||
assertThat(bean.getTestBeanMap()).containsValue(tb1);
|
||||
assertThat(bean.getTestBeanMap()).containsValue(tb2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void methodInjectionWithMap() {
|
||||
RootBeanDefinition bd = new RootBeanDefinition(MapMethodInjectionBean.class);
|
||||
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
||||
bf.registerBeanDefinition("annotatedBean", bd);
|
||||
TestBean tb = new TestBean();
|
||||
bf.registerSingleton("testBean", tb);
|
||||
|
||||
MapMethodInjectionBean bean = bf.getBean("annotatedBean", MapMethodInjectionBean.class);
|
||||
assertThat(bean.getTestBeanMap()).hasSize(1);
|
||||
assertThat(bean.getTestBeanMap()).containsKey("testBean");
|
||||
assertThat(bean.getTestBeanMap()).containsValue(tb);
|
||||
assertThat(bean.getTestBean()).isSameAs(tb);
|
||||
|
||||
bean = bf.getBean("annotatedBean", MapMethodInjectionBean.class);
|
||||
assertThat(bean.getTestBeanMap()).hasSize(1);
|
||||
assertThat(bean.getTestBeanMap()).containsKey("testBean");
|
||||
assertThat(bean.getTestBeanMap()).containsValue(tb);
|
||||
assertThat(bean.getTestBean()).isSameAs(tb);
|
||||
}
|
||||
|
||||
@Test
|
||||
void methodInjectionWithMapAndMultipleMatches() {
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(MapMethodInjectionBean.class));
|
||||
bf.registerBeanDefinition("testBean1", new RootBeanDefinition(TestBean.class));
|
||||
bf.registerBeanDefinition("testBean2", new RootBeanDefinition(TestBean.class));
|
||||
assertThatExceptionOfType(UnsatisfiedDependencyException.class).as("should have failed, more than one bean of type")
|
||||
.isThrownBy(() -> bf.getBean("annotatedBean"))
|
||||
.satisfies(methodParameterDeclaredOn(MapMethodInjectionBean.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void methodInjectionWithMapAndMultipleMatchesButOnlyOneAutowireCandidate() {
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(MapMethodInjectionBean.class));
|
||||
bf.registerBeanDefinition("testBean1", new RootBeanDefinition(TestBean.class));
|
||||
RootBeanDefinition rbd2 = new RootBeanDefinition(TestBean.class);
|
||||
rbd2.setAutowireCandidate(false);
|
||||
bf.registerBeanDefinition("testBean2", rbd2);
|
||||
|
||||
MapMethodInjectionBean bean = bf.getBean("annotatedBean", MapMethodInjectionBean.class);
|
||||
TestBean tb = bf.getBean("testBean1", TestBean.class);
|
||||
assertThat(bean.getTestBeanMap()).hasSize(1);
|
||||
assertThat(bean.getTestBeanMap()).containsKey("testBean1");
|
||||
assertThat(bean.getTestBeanMap()).containsValue(tb);
|
||||
assertThat(bean.getTestBean()).isSameAs(tb);
|
||||
}
|
||||
|
||||
@Test
|
||||
void methodInjectionWithMapAndNoMatches() {
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(MapMethodInjectionBean.class));
|
||||
|
||||
MapMethodInjectionBean bean = bf.getBean("annotatedBean", MapMethodInjectionBean.class);
|
||||
assertThat(bean.getTestBeanMap()).isNull();
|
||||
assertThat(bean.getTestBean()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void methodInjectionWithPlainHashMapAsBean() {
|
||||
RootBeanDefinition bd = new RootBeanDefinition(NamedMapMethodInjectionBean.class);
|
||||
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
||||
bf.registerBeanDefinition("annotatedBean", bd);
|
||||
bf.registerBeanDefinition("testBeanMap", new RootBeanDefinition(HashMap.class));
|
||||
|
||||
NamedMapMethodInjectionBean bean = bf.getBean("annotatedBean", NamedMapMethodInjectionBean.class);
|
||||
assertThat(bean.getTestBeanMap()).isSameAs(bf.getBean("testBeanMap"));
|
||||
bean = bf.getBean("annotatedBean", NamedMapMethodInjectionBean.class);
|
||||
assertThat(bean.getTestBeanMap()).isSameAs(bf.getBean("testBeanMap"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void methodInjectionWithQualifiedPlainHashMapAsBean() {
|
||||
RootBeanDefinition bd = new RootBeanDefinition(QualifiedMapMethodInjectionBean.class);
|
||||
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
||||
bf.registerBeanDefinition("annotatedBean", bd);
|
||||
bf.registerBeanDefinition("myTestBeanMap", new RootBeanDefinition(HashMap.class));
|
||||
|
||||
QualifiedMapMethodInjectionBean bean = bf.getBean("annotatedBean", QualifiedMapMethodInjectionBean.class);
|
||||
assertThat(bean.getTestBeanMap()).isSameAs(bf.getBean("myTestBeanMap"));
|
||||
bean = bf.getBean("annotatedBean", QualifiedMapMethodInjectionBean.class);
|
||||
assertThat(bean.getTestBeanMap()).isSameAs(bf.getBean("myTestBeanMap"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void selfReference() {
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(SelfInjectionBean.class));
|
||||
@@ -3297,21 +3258,6 @@ class AutowiredAnnotationBeanPostProcessorTests {
|
||||
}
|
||||
|
||||
|
||||
public static class NamedMapConstructorInjectionBean {
|
||||
|
||||
private Map<String, TestBean> testBeanMap;
|
||||
|
||||
@Autowired
|
||||
public NamedMapConstructorInjectionBean(Map<String, TestBean> testBeanMap) {
|
||||
this.testBeanMap = testBeanMap;
|
||||
}
|
||||
|
||||
public Map<String, TestBean> getTestBeanMap() {
|
||||
return this.testBeanMap;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class QualifiedMapConstructorInjectionBean {
|
||||
|
||||
private Map<String, TestBean> testBeanMap;
|
||||
@@ -3411,37 +3357,6 @@ class AutowiredAnnotationBeanPostProcessorTests {
|
||||
}
|
||||
|
||||
|
||||
public static class NamedMapMethodInjectionBean {
|
||||
|
||||
private Map<String, TestBean> testBeanMap;
|
||||
|
||||
@Autowired
|
||||
public void setTestBeanMap(Map<String, TestBean> testBeanMap) {
|
||||
this.testBeanMap = testBeanMap;
|
||||
}
|
||||
|
||||
public Map<String, TestBean> getTestBeanMap() {
|
||||
return this.testBeanMap;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class QualifiedMapMethodInjectionBean {
|
||||
|
||||
private Map<String, TestBean> testBeanMap;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("myTestBeanMap")
|
||||
public void setTestBeanMap(Map<String, TestBean> testBeanMap) {
|
||||
this.testBeanMap = testBeanMap;
|
||||
}
|
||||
|
||||
public Map<String, TestBean> getTestBeanMap() {
|
||||
return this.testBeanMap;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public static class ObjectFactoryFieldInjectionBean implements Serializable {
|
||||
|
||||
|
||||
-18
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.springframework.beans.factory.aot;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -35,7 +34,6 @@ import org.springframework.aot.generate.GenerationContext;
|
||||
import org.springframework.aot.generate.MethodReference;
|
||||
import org.springframework.aot.generate.MethodReference.ArgumentCodeGenerator;
|
||||
import org.springframework.aot.test.generate.TestGenerationContext;
|
||||
import org.springframework.beans.factory.config.AutowiredPropertyMarker;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
@@ -628,22 +626,6 @@ class BeanDefinitionMethodGeneratorTests {
|
||||
-> assertThat(customPropertyValue.value()).isEqualTo("test")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateBeanDefinitionMethodWhenHasAutowiredPropertyGeneratesMethod() {
|
||||
RootBeanDefinition beanDefinition = (RootBeanDefinition) BeanDefinitionBuilder
|
||||
.rootBeanDefinition(CustomBean.class).addAutowiredProperty("innerBean")
|
||||
.getBeanDefinition();
|
||||
RegisteredBean registeredBean = registerBean(beanDefinition);
|
||||
BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(
|
||||
this.methodGeneratorFactory, registeredBean, null,
|
||||
Collections.emptyList());
|
||||
MethodReference method = generator.generateBeanDefinitionMethod(
|
||||
this.generationContext, this.beanRegistrationsCode);
|
||||
compile(method, (actual, compiled) ->
|
||||
assertThat(actual.getPropertyValues().get("innerBean"))
|
||||
.isSameAs(AutowiredPropertyMarker.INSTANCE));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateBeanDefinitionMethodWhenHasAotContributionsAppliesContributions() {
|
||||
RegisteredBean registeredBean = registerBean(
|
||||
|
||||
-13
@@ -39,7 +39,6 @@ import org.springframework.aot.generate.GeneratedClass;
|
||||
import org.springframework.aot.generate.ValueCodeGenerator;
|
||||
import org.springframework.aot.generate.ValueCodeGeneratorDelegates;
|
||||
import org.springframework.aot.test.generate.TestGenerationContext;
|
||||
import org.springframework.beans.factory.config.AutowiredPropertyMarker;
|
||||
import org.springframework.beans.factory.config.BeanReference;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanNameReference;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
@@ -487,16 +486,4 @@ class BeanDefinitionPropertyValueCodeGeneratorDelegatesTests {
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
class AutowiredPropertyMarkerTests {
|
||||
|
||||
@Test
|
||||
void generateWhenAutowiredPropertyMarker() {
|
||||
compile(AutowiredPropertyMarker.INSTANCE, (instance, compiler) ->
|
||||
assertThat(instance).isInstanceOf(AutowiredPropertyMarker.class)
|
||||
.isSameAs(AutowiredPropertyMarker.INSTANCE));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-30
@@ -418,16 +418,6 @@ class InstanceSupplierCodeGeneratorTests {
|
||||
compileAndCheckWarnings(beanDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenTargetFactoryMethodIsProtectedAndReturnTypeIsDeprecated() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(DeprecatedBean.class)
|
||||
.setFactoryMethodOnBean("deprecatedReturnTypeProtected", "config").getBeanDefinition();
|
||||
beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||
.genericBeanDefinition(DeprecatedMemberConfiguration.class).getBeanDefinition());
|
||||
compileAndCheckWarnings(beanDefinition);
|
||||
}
|
||||
|
||||
private void compileAndCheckWarnings(BeanDefinition beanDefinition) {
|
||||
assertThatNoException().isThrownBy(() -> compile(TEST_COMPILER, beanDefinition,
|
||||
((instanceSupplier, compiled) -> {})));
|
||||
@@ -474,26 +464,6 @@ class InstanceSupplierCodeGeneratorTests {
|
||||
compileAndCheckWarnings(beanDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenTargetFactoryMethodReturnTypeIsDeprecatedForRemoval() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(DeprecatedForRemovalBean.class)
|
||||
.setFactoryMethodOnBean("deprecatedReturnType", "config").getBeanDefinition();
|
||||
beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||
.genericBeanDefinition(DeprecatedForRemovalMemberConfiguration.class).getBeanDefinition());
|
||||
compileAndCheckWarnings(beanDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenTargetFactoryMethodIsProtectedAndReturnTypeIsDeprecatedForRemoval() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(DeprecatedForRemovalBean.class)
|
||||
.setFactoryMethodOnBean("deprecatedReturnTypeProtected", "config").getBeanDefinition();
|
||||
beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||
.genericBeanDefinition(DeprecatedForRemovalMemberConfiguration.class).getBeanDefinition());
|
||||
compileAndCheckWarnings(beanDefinition);
|
||||
}
|
||||
|
||||
private void compileAndCheckWarnings(BeanDefinition beanDefinition) {
|
||||
assertThatNoException().isThrownBy(() -> compile(TEST_COMPILER, beanDefinition,
|
||||
((instanceSupplier, compiled) -> {})));
|
||||
|
||||
+1
-46
@@ -17,7 +17,6 @@
|
||||
package org.springframework.beans.factory.support;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -60,38 +59,13 @@ class RootBeanDefinitionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveDestroyMethodWithMatchingCandidateReplacedForCloseMethod() {
|
||||
void resolveDestroyMethodWithMatchingCandidateReplacedInferredVaue() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(BeanWithCloseMethod.class);
|
||||
beanDefinition.setDestroyMethodName(AbstractBeanDefinition.INFER_METHOD);
|
||||
beanDefinition.resolveDestroyMethodIfNecessary();
|
||||
assertThat(beanDefinition.getDestroyMethodNames()).containsExactly("close");
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveDestroyMethodWithMatchingCandidateReplacedForShutdownMethod() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(BeanWithShutdownMethod.class);
|
||||
beanDefinition.setDestroyMethodName(AbstractBeanDefinition.INFER_METHOD);
|
||||
beanDefinition.resolveDestroyMethodIfNecessary();
|
||||
assertThat(beanDefinition.getDestroyMethodNames()).containsExactly("shutdown");
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveDestroyMethodWithMatchingCandidateReplacedForExecutorService() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(BeanImplementingExecutorService.class);
|
||||
beanDefinition.setDestroyMethodName(AbstractBeanDefinition.INFER_METHOD);
|
||||
beanDefinition.resolveDestroyMethodIfNecessary();
|
||||
assertThat(beanDefinition.getDestroyMethodNames()).containsExactly("shutdown");
|
||||
// even on JDK 19+ where the ExecutorService interface declares a default AutoCloseable implementation
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveDestroyMethodWithMatchingCandidateReplacedForAutoCloseableExecutorService() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(BeanImplementingExecutorServiceAndAutoCloseable.class);
|
||||
beanDefinition.setDestroyMethodName(AbstractBeanDefinition.INFER_METHOD);
|
||||
beanDefinition.resolveDestroyMethodIfNecessary();
|
||||
assertThat(beanDefinition.getDestroyMethodNames()).containsExactly("close");
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveDestroyMethodWithNoCandidateSetDestroyMethodNameToNull() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(BeanWithNoDestroyMethod.class);
|
||||
@@ -116,25 +90,6 @@ class RootBeanDefinitionTests {
|
||||
}
|
||||
|
||||
|
||||
static class BeanWithShutdownMethod {
|
||||
|
||||
public void shutdown() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
abstract static class BeanImplementingExecutorService implements ExecutorService {
|
||||
}
|
||||
|
||||
|
||||
abstract static class BeanImplementingExecutorServiceAndAutoCloseable implements ExecutorService, AutoCloseable {
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static class BeanWithNoDestroyMethod {
|
||||
}
|
||||
|
||||
|
||||
+22
-33
@@ -28,7 +28,6 @@ import org.assertj.core.api.InstanceOfAssertFactories;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.config.DependencyDescriptor;
|
||||
import org.springframework.beans.factory.config.FieldRetrievingFactoryBean;
|
||||
import org.springframework.beans.factory.config.PropertiesFactoryBean;
|
||||
import org.springframework.beans.factory.parsing.ComponentDefinition;
|
||||
@@ -47,6 +46,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* @author Juergen Hoeller
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
class UtilNamespaceHandlerTests {
|
||||
|
||||
private DefaultListableBeanFactory beanFactory;
|
||||
@@ -55,7 +55,7 @@ class UtilNamespaceHandlerTests {
|
||||
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
void setUp() {
|
||||
this.beanFactory = new DefaultListableBeanFactory();
|
||||
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this.beanFactory);
|
||||
reader.setEventListener(this.listener);
|
||||
@@ -109,17 +109,17 @@ class UtilNamespaceHandlerTests {
|
||||
|
||||
@Test
|
||||
void testSimpleMap() {
|
||||
Map<?, ?> map = (Map<?, ?>) this.beanFactory.getBean("simpleMap");
|
||||
Map<?, ?> map = (Map) this.beanFactory.getBean("simpleMap");
|
||||
assertThat(map.get("foo")).isEqualTo("bar");
|
||||
Map<?, ?> map2 = (Map<?, ?>) this.beanFactory.getBean("simpleMap");
|
||||
Map<?, ?> map2 = (Map) this.beanFactory.getBean("simpleMap");
|
||||
assertThat(map).isSameAs(map2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testScopedMap() {
|
||||
Map<?, ?> map = (Map<?, ?>) this.beanFactory.getBean("scopedMap");
|
||||
Map<?, ?> map = (Map) this.beanFactory.getBean("scopedMap");
|
||||
assertThat(map.get("foo")).isEqualTo("bar");
|
||||
Map<?, ?> map2 = (Map<?, ?>) this.beanFactory.getBean("scopedMap");
|
||||
Map<?, ?> map2 = (Map) this.beanFactory.getBean("scopedMap");
|
||||
assertThat(map2.get("foo")).isEqualTo("bar");
|
||||
assertThat(map).isNotSameAs(map2);
|
||||
}
|
||||
@@ -164,23 +164,17 @@ class UtilNamespaceHandlerTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMapWithRef() throws Exception {
|
||||
Map<?, ?> map = (Map<?, ?>) this.beanFactory.getBean("mapWithRef");
|
||||
void testMapWithRef() {
|
||||
Map<?, ?> map = (Map) this.beanFactory.getBean("mapWithRef");
|
||||
assertThat(map).isInstanceOf(TreeMap.class);
|
||||
assertThat(map.get("bean")).isEqualTo(this.beanFactory.getBean("testBean"));
|
||||
assertThat(this.beanFactory.resolveDependency(
|
||||
new DependencyDescriptor(getClass().getDeclaredField("mapWithRef"), true), null))
|
||||
.isSameAs(map);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMapWithTypes() throws Exception {
|
||||
Map<?, ?> map = (Map<?, ?>) this.beanFactory.getBean("mapWithTypes");
|
||||
void testMapWithTypes() {
|
||||
Map<?, ?> map = (Map) this.beanFactory.getBean("mapWithTypes");
|
||||
assertThat(map).isInstanceOf(LinkedCaseInsensitiveMap.class);
|
||||
assertThat(map.get("bean")).isEqualTo(this.beanFactory.getBean("testBean"));
|
||||
assertThat(this.beanFactory.resolveDependency(
|
||||
new DependencyDescriptor(getClass().getDeclaredField("mapWithTypes"), true), null))
|
||||
.isSameAs(map);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -246,11 +240,11 @@ class UtilNamespaceHandlerTests {
|
||||
void testCircularCollections() {
|
||||
TestBean bean = (TestBean) this.beanFactory.getBean("circularCollectionsBean");
|
||||
|
||||
assertThat(bean.getSomeList()).singleElement().isSameAs(bean);
|
||||
assertThat(bean.getSomeSet()).singleElement().isSameAs(bean);
|
||||
assertThat(bean.getSomeList()).singleElement().isEqualTo(bean);
|
||||
assertThat(bean.getSomeSet()).singleElement().isEqualTo(bean);
|
||||
assertThat(bean.getSomeMap()).hasSize(1).allSatisfy((key, value) -> {
|
||||
assertThat(key).isEqualTo("foo");
|
||||
assertThat(value).isSameAs(bean);
|
||||
assertThat(value).isEqualTo(bean);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -261,17 +255,17 @@ class UtilNamespaceHandlerTests {
|
||||
|
||||
List<?> list = bean.getSomeList();
|
||||
assertThat(Proxy.isProxyClass(list.getClass())).isTrue();
|
||||
assertThat(list).singleElement().isSameAs(bean);
|
||||
assertThat(list).singleElement().isEqualTo(bean);
|
||||
|
||||
Set<?> set = bean.getSomeSet();
|
||||
assertThat(Proxy.isProxyClass(set.getClass())).isFalse();
|
||||
assertThat(set).singleElement().isSameAs(bean);
|
||||
assertThat(set).singleElement().isEqualTo(bean);
|
||||
|
||||
Map<?, ?> map = bean.getSomeMap();
|
||||
assertThat(Proxy.isProxyClass(map.getClass())).isFalse();
|
||||
assertThat(map).hasSize(1).allSatisfy((key, value) -> {
|
||||
assertThat(key).isEqualTo("foo");
|
||||
assertThat(value).isSameAs(bean);
|
||||
assertThat(value).isEqualTo(bean);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -282,17 +276,17 @@ class UtilNamespaceHandlerTests {
|
||||
|
||||
List<?> list = bean.getSomeList();
|
||||
assertThat(Proxy.isProxyClass(list.getClass())).isFalse();
|
||||
assertThat(list).singleElement().isSameAs(bean);
|
||||
assertThat(list).singleElement().isEqualTo(bean);
|
||||
|
||||
Set<?> set = bean.getSomeSet();
|
||||
assertThat(Proxy.isProxyClass(set.getClass())).isTrue();
|
||||
assertThat(set).singleElement().isSameAs(bean);
|
||||
assertThat(set).singleElement().isEqualTo(bean);
|
||||
|
||||
Map<?, ?> map = bean.getSomeMap();
|
||||
assertThat(Proxy.isProxyClass(map.getClass())).isFalse();
|
||||
assertThat(map).hasSize(1).allSatisfy((key, value) -> {
|
||||
assertThat(key).isEqualTo("foo");
|
||||
assertThat(value).isSameAs(bean);
|
||||
assertThat(value).isEqualTo(bean);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -303,17 +297,17 @@ class UtilNamespaceHandlerTests {
|
||||
|
||||
List<?> list = bean.getSomeList();
|
||||
assertThat(Proxy.isProxyClass(list.getClass())).isFalse();
|
||||
assertThat(list).singleElement().isSameAs(bean);
|
||||
assertThat(list).singleElement().isEqualTo(bean);
|
||||
|
||||
Set<?> set = bean.getSomeSet();
|
||||
assertThat(Proxy.isProxyClass(set.getClass())).isFalse();
|
||||
assertThat(set).singleElement().isSameAs(bean);
|
||||
assertThat(set).singleElement().isEqualTo(bean);
|
||||
|
||||
Map<?, ?> map = bean.getSomeMap();
|
||||
assertThat(Proxy.isProxyClass(map.getClass())).isTrue();
|
||||
assertThat(map).hasSize(1).allSatisfy((key, value) -> {
|
||||
assertThat(key).isEqualTo("foo");
|
||||
assertThat(value).isSameAs(bean);
|
||||
assertThat(value).isEqualTo(bean);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -378,9 +372,4 @@ class UtilNamespaceHandlerTests {
|
||||
assertThat(props).as("Incorrect property value").containsEntry("foo2", "local2");
|
||||
}
|
||||
|
||||
|
||||
// For DependencyDescriptor resolution
|
||||
private Map<String, TestBean> mapWithRef;
|
||||
private Map<String, TestBean> mapWithTypes;
|
||||
|
||||
}
|
||||
|
||||
-10
@@ -59,8 +59,6 @@ public class TestBean implements BeanNameAware, BeanFactoryAware, ITestBean, IOt
|
||||
|
||||
private boolean jedi;
|
||||
|
||||
private String favoriteCafé;
|
||||
|
||||
private ITestBean spouse;
|
||||
|
||||
private String touchy;
|
||||
@@ -211,14 +209,6 @@ public class TestBean implements BeanNameAware, BeanFactoryAware, ITestBean, IOt
|
||||
this.jedi = jedi;
|
||||
}
|
||||
|
||||
public String getFavoriteCafé() {
|
||||
return this.favoriteCafé;
|
||||
}
|
||||
|
||||
public void setFavoriteCafé(String favoriteCafé) {
|
||||
this.favoriteCafé = favoriteCafé;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITestBean getSpouse() {
|
||||
return this.spouse;
|
||||
|
||||
-10
@@ -33,14 +33,4 @@ public class DeprecatedForRemovalMemberConfiguration {
|
||||
return bean.toString();
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
public DeprecatedForRemovalBean deprecatedReturnType() {
|
||||
return new DeprecatedForRemovalBean();
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
DeprecatedForRemovalBean deprecatedReturnTypeProtected() {
|
||||
return new DeprecatedForRemovalBean();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-5
@@ -38,9 +38,4 @@ public class DeprecatedMemberConfiguration {
|
||||
return new DeprecatedBean();
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
DeprecatedBean deprecatedReturnTypeProtected() {
|
||||
return new DeprecatedBean();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-3
@@ -37,13 +37,12 @@ import javax.lang.model.element.TypeElement;
|
||||
|
||||
/**
|
||||
* Annotation {@link Processor} that writes a {@link CandidateComponentsMetadata}
|
||||
* file for Spring components.
|
||||
* file for spring components.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Juergen Hoeller
|
||||
* @since 5.0
|
||||
* @deprecated as of 6.1, in favor of the AOT engine and the forthcoming
|
||||
* support for an AOT-generated Spring components index
|
||||
* @deprecated as of 6.1, in favor of the AOT engine.
|
||||
*/
|
||||
@Deprecated(since = "6.1", forRemoval = true)
|
||||
public class CandidateComponentsIndexer implements Processor {
|
||||
|
||||
+2
-2
@@ -25,8 +25,8 @@ import javax.lang.model.element.ElementKind;
|
||||
|
||||
/**
|
||||
* A {@link StereotypesProvider} that extracts a stereotype for each
|
||||
* {@code jakarta.*} or {@code javax.*} annotation <i>present</i>
|
||||
* on a class or interface.
|
||||
* {@code jakarta.*} or {@code javax.*} annotation <i>present</i> on a class or
|
||||
* interface.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 5.0
|
||||
|
||||
-5
@@ -25,7 +25,6 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.factory.Aware;
|
||||
import org.springframework.core.MethodClassKey;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
@@ -98,10 +97,6 @@ public abstract class AbstractFallbackJCacheOperationSource implements JCacheOpe
|
||||
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
|
||||
return null;
|
||||
}
|
||||
// Skip methods declared on BeanFactoryAware and co.
|
||||
if (method.getDeclaringClass().isInterface() && Aware.class.isAssignableFrom(method.getDeclaringClass())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// The method may be on an interface, but we need metadata from the target class.
|
||||
// If the target class is null, the method will be unchanged.
|
||||
|
||||
+7
-7
@@ -23,14 +23,14 @@ import org.springframework.cache.interceptor.KeyGenerator;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Interface to be implemented for explicitly specifying how caches are resolved
|
||||
* and how keys are generated for annotation-driven cache management.
|
||||
* Interface to be implemented by @{@link org.springframework.context.annotation.Configuration
|
||||
* Configuration} classes annotated with @{@link EnableCaching} that wish or need to specify
|
||||
* explicitly how caches are resolved and how keys are generated for annotation-driven
|
||||
* cache management.
|
||||
*
|
||||
* <p>Typically implemented by @{@link org.springframework.context.annotation.Configuration
|
||||
* Configuration} classes annotated with @{@link EnableCaching}.
|
||||
* See @{@link EnableCaching} for general examples and context; see
|
||||
* {@link #cacheManager()}, {@link #cacheResolver()}, {@link #keyGenerator()},
|
||||
* and {@link #errorHandler()} for detailed instructions.
|
||||
* <p>See @{@link EnableCaching} for general examples and context; see
|
||||
* {@link #cacheManager()}, {@link #cacheResolver()}, {@link #keyGenerator()}, and
|
||||
* {@link #errorHandler()} for detailed instructions.
|
||||
*
|
||||
* <p><b>NOTE: A {@code CachingConfigurer} will get initialized early.</b>
|
||||
* Do not inject common dependencies into autowired fields directly; instead, consider
|
||||
|
||||
-5
@@ -27,7 +27,6 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.factory.Aware;
|
||||
import org.springframework.core.MethodClassKey;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
@@ -140,10 +139,6 @@ public abstract class AbstractFallbackCacheOperationSource implements CacheOpera
|
||||
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
|
||||
return null;
|
||||
}
|
||||
// Skip methods declared on BeanFactoryAware and co.
|
||||
if (method.getDeclaringClass().isInterface() && Aware.class.isAssignableFrom(method.getDeclaringClass())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// The method may be on an interface, but we need metadata from the target class.
|
||||
// If the target class is null, the method will be unchanged.
|
||||
|
||||
@@ -17,18 +17,19 @@
|
||||
package org.springframework.context.annotation;
|
||||
|
||||
/**
|
||||
* Enumeration used to determine whether JDK/CGLIB proxy-based or
|
||||
* Enumeration used to determine whether JDK proxy-based or
|
||||
* AspectJ weaving-based advice should be applied.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
* @see org.springframework.scheduling.annotation.AsyncConfigurationSelector#selectImports
|
||||
* @see org.springframework.scheduling.annotation.EnableAsync#mode()
|
||||
* @see org.springframework.scheduling.annotation.AsyncConfigurationSelector#selectImports
|
||||
* @see org.springframework.transaction.annotation.EnableTransactionManagement#mode()
|
||||
*/
|
||||
public enum AdviceMode {
|
||||
|
||||
/**
|
||||
* JDK/CGLIB proxy-based advice.
|
||||
* JDK proxy-based advice.
|
||||
*/
|
||||
PROXY,
|
||||
|
||||
|
||||
+7
-6
@@ -90,6 +90,7 @@ import org.springframework.util.ClassUtils;
|
||||
* @see ScannedGenericBeanDefinition
|
||||
* @see CandidateComponentsIndex
|
||||
*/
|
||||
@SuppressWarnings("removal") // components index
|
||||
public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {
|
||||
|
||||
static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
|
||||
@@ -451,9 +452,9 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
|
||||
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
|
||||
Set<BeanDefinition> candidates = new LinkedHashSet<>();
|
||||
try {
|
||||
String packageSearchPattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
|
||||
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
|
||||
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
|
||||
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPattern);
|
||||
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
|
||||
boolean traceEnabled = logger.isTraceEnabled();
|
||||
boolean debugEnabled = logger.isDebugEnabled();
|
||||
for (Resource resource : resources) {
|
||||
@@ -536,13 +537,13 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
|
||||
* @return whether the class qualifies as a candidate component
|
||||
*/
|
||||
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
|
||||
for (TypeFilter filter : this.excludeFilters) {
|
||||
if (filter.match(metadataReader, getMetadataReaderFactory())) {
|
||||
for (TypeFilter tf : this.excludeFilters) {
|
||||
if (tf.match(metadataReader, getMetadataReaderFactory())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (TypeFilter filter : this.includeFilters) {
|
||||
if (filter.match(metadataReader, getMetadataReaderFactory())) {
|
||||
for (TypeFilter tf : this.includeFilters) {
|
||||
if (tf.match(metadataReader, getMetadataReaderFactory())) {
|
||||
return isConditionMatch(metadataReader);
|
||||
}
|
||||
}
|
||||
|
||||
+17
-16
@@ -149,6 +149,8 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
private static final boolean jndiPresent = ClassUtils.isPresent(
|
||||
"javax.naming.InitialContext", CommonAnnotationBeanPostProcessor.class.getClassLoader());
|
||||
|
||||
private static final Set<Class<? extends Annotation>> resourceAnnotationTypes = CollectionUtils.newLinkedHashSet(3);
|
||||
|
||||
@Nullable
|
||||
private static final Class<? extends Annotation> jakartaResourceType;
|
||||
|
||||
@@ -158,8 +160,6 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
@Nullable
|
||||
private static final Class<? extends Annotation> ejbAnnotationType;
|
||||
|
||||
private static final Set<Class<? extends Annotation>> resourceAnnotationTypes = CollectionUtils.newLinkedHashSet(3);
|
||||
|
||||
static {
|
||||
jakartaResourceType = loadAnnotationType("jakarta.annotation.Resource");
|
||||
if (jakartaResourceType != null) {
|
||||
@@ -424,7 +424,7 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
}
|
||||
|
||||
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
|
||||
Class<?> targetClass = ClassUtils.getUserClass(clazz);
|
||||
Class<?> targetClass = clazz;
|
||||
|
||||
do {
|
||||
final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
|
||||
@@ -455,23 +455,24 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
});
|
||||
|
||||
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
|
||||
if (method.isBridge()) {
|
||||
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
|
||||
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
|
||||
return;
|
||||
}
|
||||
if (ejbAnnotationType != null && method.isAnnotationPresent(ejbAnnotationType)) {
|
||||
if (method.equals(BridgeMethodResolver.getMostSpecificMethod(method, clazz))) {
|
||||
if (ejbAnnotationType != null && bridgedMethod.isAnnotationPresent(ejbAnnotationType)) {
|
||||
if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
|
||||
if (Modifier.isStatic(method.getModifiers())) {
|
||||
throw new IllegalStateException("@EJB annotation is not supported on static methods");
|
||||
}
|
||||
if (method.getParameterCount() != 1) {
|
||||
throw new IllegalStateException("@EJB annotation requires a single-arg method: " + method);
|
||||
}
|
||||
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method, clazz);
|
||||
currElements.add(new EjbRefElement(method, method, pd));
|
||||
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
|
||||
currElements.add(new EjbRefElement(method, bridgedMethod, pd));
|
||||
}
|
||||
}
|
||||
else if (jakartaResourceType != null && method.isAnnotationPresent(jakartaResourceType)) {
|
||||
if (method.equals(BridgeMethodResolver.getMostSpecificMethod(method, clazz))) {
|
||||
else if (jakartaResourceType != null && bridgedMethod.isAnnotationPresent(jakartaResourceType)) {
|
||||
if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
|
||||
if (Modifier.isStatic(method.getModifiers())) {
|
||||
throw new IllegalStateException("@Resource annotation is not supported on static methods");
|
||||
}
|
||||
@@ -480,13 +481,13 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method);
|
||||
}
|
||||
if (!this.ignoredResourceTypes.contains(paramTypes[0].getName())) {
|
||||
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method, clazz);
|
||||
currElements.add(new ResourceElement(method, method, pd));
|
||||
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
|
||||
currElements.add(new ResourceElement(method, bridgedMethod, pd));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (javaxResourceType != null && method.isAnnotationPresent(javaxResourceType)) {
|
||||
if (method.equals(BridgeMethodResolver.getMostSpecificMethod(method, clazz))) {
|
||||
else if (javaxResourceType != null && bridgedMethod.isAnnotationPresent(javaxResourceType)) {
|
||||
if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
|
||||
if (Modifier.isStatic(method.getModifiers())) {
|
||||
throw new IllegalStateException("@Resource annotation is not supported on static methods");
|
||||
}
|
||||
@@ -495,8 +496,8 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method);
|
||||
}
|
||||
if (!this.ignoredResourceTypes.contains(paramTypes[0].getName())) {
|
||||
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method, clazz);
|
||||
currElements.add(new LegacyResourceElement(method, method, pd));
|
||||
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
|
||||
currElements.add(new LegacyResourceElement(method, bridgedMethod, pd));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-10
@@ -157,17 +157,9 @@ class ConfigurationClassBeanDefinitionReader {
|
||||
|
||||
ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(configBeanDef);
|
||||
configBeanDef.setScope(scopeMetadata.getScopeName());
|
||||
|
||||
String configBeanName;
|
||||
try {
|
||||
configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry);
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
throw new IllegalStateException("Failed to generate bean name for imported class '" +
|
||||
configClass.getMetadata().getClassName() + "'", ex);
|
||||
}
|
||||
|
||||
String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry);
|
||||
AnnotationConfigUtils.processCommonDefinitionAnnotations(configBeanDef, metadata);
|
||||
|
||||
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(configBeanDef, configBeanName);
|
||||
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
|
||||
this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition());
|
||||
|
||||
+2
-2
@@ -63,13 +63,13 @@ public interface AotApplicationContextInitializer<C extends ConfigurableApplicat
|
||||
|
||||
private static <C extends ConfigurableApplicationContext> void initialize(
|
||||
C applicationContext, String... initializerClassNames) {
|
||||
|
||||
Log logger = LogFactory.getLog(AotApplicationContextInitializer.class);
|
||||
ClassLoader classLoader = applicationContext.getClassLoader();
|
||||
logger.debug("Initializing ApplicationContext with AOT");
|
||||
for (String initializerClassName : initializerClassNames) {
|
||||
logger.trace(LogMessage.format("Applying %s", initializerClassName));
|
||||
instantiateInitializer(initializerClassName, classLoader).initialize(applicationContext);
|
||||
instantiateInitializer(initializerClassName, classLoader)
|
||||
.initialize(applicationContext);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -101,9 +101,10 @@ public @interface EventListener {
|
||||
|
||||
/**
|
||||
* The event classes that this listener handles.
|
||||
* <p>The annotated method may optionally accept a single parameter
|
||||
* of the given event class, or of a common base class or interface
|
||||
* for all given event classes.
|
||||
* <p>If this attribute is specified with a single value, the
|
||||
* annotated method may optionally accept a single parameter.
|
||||
* However, if this attribute is specified with multiple values,
|
||||
* the annotated method must <em>not</em> declare any parameters.
|
||||
*/
|
||||
@AliasFor("value")
|
||||
Class<?>[] classes() default {};
|
||||
|
||||
+4
-2
@@ -45,7 +45,9 @@ import org.springframework.util.MultiValueMap;
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 5.0
|
||||
* @deprecated as of 6.1, in favor of the AOT engine.
|
||||
*/
|
||||
@Deprecated(since = "6.1", forRemoval = true)
|
||||
public class CandidateComponentsIndex {
|
||||
|
||||
private static final AntPathMatcher pathMatcher = new AntPathMatcher(".");
|
||||
@@ -81,7 +83,7 @@ public class CandidateComponentsIndex {
|
||||
public Set<String> getCandidateTypes(String basePackage, String stereotype) {
|
||||
List<Entry> candidates = this.index.get(stereotype);
|
||||
if (candidates != null) {
|
||||
return candidates.stream()
|
||||
return candidates.parallelStream()
|
||||
.filter(t -> t.match(basePackage))
|
||||
.map(t -> t.type)
|
||||
.collect(Collectors.toSet());
|
||||
@@ -92,7 +94,7 @@ public class CandidateComponentsIndex {
|
||||
|
||||
private static class Entry {
|
||||
|
||||
final String type;
|
||||
private final String type;
|
||||
|
||||
private final String packageName;
|
||||
|
||||
|
||||
+3
@@ -38,7 +38,10 @@ import org.springframework.util.ConcurrentReferenceHashMap;
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 5.0
|
||||
* @deprecated as of 6.1, in favor of the AOT engine.
|
||||
*/
|
||||
@Deprecated(since = "6.1", forRemoval = true)
|
||||
@SuppressWarnings("removal")
|
||||
public final class CandidateComponentsIndexLoader {
|
||||
|
||||
/**
|
||||
|
||||
-3
@@ -936,9 +936,6 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
|
||||
// Mark current thread for singleton instantiation with applied bootstrap locking.
|
||||
beanFactory.prepareSingletonBootstrap();
|
||||
|
||||
// Initialize bootstrap executor for this context.
|
||||
if (beanFactory.containsBean(BOOTSTRAP_EXECUTOR_BEAN_NAME) &&
|
||||
beanFactory.isTypeMatch(BOOTSTRAP_EXECUTOR_BEAN_NAME, Executor.class)) {
|
||||
|
||||
+16
-18
@@ -25,18 +25,18 @@ import java.lang.annotation.Target;
|
||||
/**
|
||||
* Declares that a field or method parameter should be formatted as a number.
|
||||
*
|
||||
* <p>Supports formatting by style or custom pattern string. Can be applied to
|
||||
* any JDK {@code Number} types such as {@code Double} and {@code Long}.
|
||||
* <p>Supports formatting by style or custom pattern string. Can be applied
|
||||
* to any JDK {@code Number} type such as {@code Double} and {@code Long}.
|
||||
*
|
||||
* <p>For style-based formatting, set the {@link #style} attribute to the desired
|
||||
* {@link Style}. For custom formatting, set the {@link #pattern} attribute to the
|
||||
* desired number pattern, such as {@code "#,###.##"}.
|
||||
* <p>For style-based formatting, set the {@link #style} attribute to be the
|
||||
* desired {@link Style}. For custom formatting, set the {@link #pattern}
|
||||
* attribute to be the number pattern, such as {@code #, ###.##}.
|
||||
*
|
||||
* <p>Each attribute is mutually exclusive, so only set one attribute per
|
||||
* annotation (the one most convenient for your formatting needs). When the
|
||||
* {@link #pattern} attribute is specified, it takes precedence over the
|
||||
* {@link #style} attribute. When no annotation attributes are specified, the
|
||||
* default format applied is style-based for either number or currency,
|
||||
* annotation instance (the one most convenient one for your formatting needs).
|
||||
* When the {@link #pattern} attribute is specified, it takes precedence over
|
||||
* the {@link #style} attribute. When no annotation attributes are specified,
|
||||
* the default format applied is style-based for either number of currency,
|
||||
* depending on the annotated field or method parameter type.
|
||||
*
|
||||
* @author Keith Donald
|
||||
@@ -50,21 +50,19 @@ import java.lang.annotation.Target;
|
||||
public @interface NumberFormat {
|
||||
|
||||
/**
|
||||
* The style pattern to use to format the field or method parameter.
|
||||
* The style pattern to use to format the field.
|
||||
* <p>Defaults to {@link Style#DEFAULT} for general-purpose number formatting
|
||||
* for most annotated types, except for money types which default to currency
|
||||
* formatting.
|
||||
* <p>Set this attribute when you wish to format your field or method parameter
|
||||
* in accordance with a common style other than the default style.
|
||||
* formatting. Set this attribute when you wish to format your field in
|
||||
* accordance with a common style other than the default style.
|
||||
*/
|
||||
Style style() default Style.DEFAULT;
|
||||
|
||||
/**
|
||||
* The custom pattern to use to format the field or method parameter.
|
||||
* <p>Defaults to an empty String, indicating no custom pattern has been
|
||||
* specified.
|
||||
* <p>Set this attribute when you wish to format your field or method parameter
|
||||
* in accordance with a custom number pattern not represented by a style.
|
||||
* The custom pattern to use to format the field.
|
||||
* <p>Defaults to empty String, indicating no custom pattern String has been specified.
|
||||
* Set this attribute when you wish to format your field in accordance with a
|
||||
* custom number pattern not represented by a style.
|
||||
*/
|
||||
String pattern() default "";
|
||||
|
||||
|
||||
+1
@@ -87,6 +87,7 @@ public class AsyncAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAd
|
||||
private Class<? extends Annotation> asyncAnnotationType;
|
||||
|
||||
|
||||
|
||||
public AsyncAnnotationBeanPostProcessor() {
|
||||
setBeforeExistingAdvisors(true);
|
||||
}
|
||||
|
||||
+6
-11
@@ -22,18 +22,13 @@ import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Interface to be implemented for customizing the {@link Executor} instance used when
|
||||
* processing async method invocations or the {@link AsyncUncaughtExceptionHandler}
|
||||
* instance used to process exceptions thrown from async methods with a {@code void}
|
||||
* return type.
|
||||
* Interface to be implemented by @{@link org.springframework.context.annotation.Configuration
|
||||
* Configuration} classes annotated with @{@link EnableAsync} that wish to customize the
|
||||
* {@link Executor} instance used when processing async method invocations or the
|
||||
* {@link AsyncUncaughtExceptionHandler} instance used to process exception thrown from
|
||||
* async method with {@code void} return type.
|
||||
*
|
||||
* <p>Typically implemented by @{@link org.springframework.context.annotation.Configuration
|
||||
* Configuration} classes annotated with @{@link EnableAsync}.
|
||||
* See the @{@link EnableAsync} javadoc for usage examples.
|
||||
*
|
||||
* <p><b>NOTE: An {@code AsyncConfigurer} will get initialized early.</b>
|
||||
* Do not inject common dependencies into autowired fields directly; instead, consider
|
||||
* declaring a lazy {@link org.springframework.beans.factory.ObjectProvider} for those.
|
||||
* <p>See @{@link EnableAsync} for usage examples.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @author Stephane Nicoll
|
||||
|
||||
@@ -50,9 +50,7 @@ public class Task {
|
||||
|
||||
|
||||
/**
|
||||
* Return a {@link Runnable} that executes the underlying task.
|
||||
* <p>Note, this does not necessarily return the {@link Task#Task(Runnable) original runnable}
|
||||
* as it can be wrapped by the Framework for additional support.
|
||||
* Return the underlying task.
|
||||
*/
|
||||
public Runnable getRunnable() {
|
||||
return this.runnable;
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package org.springframework.validation;
|
||||
|
||||
import java.util.HexFormat;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
@@ -128,18 +126,8 @@ public class FieldError extends ObjectError {
|
||||
// We would preferably use ObjectUtils.nullSafeConciseToString(rejectedValue) here but
|
||||
// keep including the full nullSafeToString representation for backwards compatibility.
|
||||
return "Field error in object '" + getObjectName() + "' on field '" + this.field +
|
||||
"': rejected value [" + formatRejectedValue() + "]; " +
|
||||
"': rejected value [" + ObjectUtils.nullSafeToString(this.rejectedValue) + "]; " +
|
||||
resolvableToString();
|
||||
}
|
||||
|
||||
private String formatRejectedValue() {
|
||||
|
||||
// Special handling of byte[], to be moved into ObjectUtils in 7.0
|
||||
if (this.rejectedValue instanceof byte[] bytes && bytes.length != 0) {
|
||||
return "{" + HexFormat.of().formatHex(bytes) + "}";
|
||||
}
|
||||
|
||||
return ObjectUtils.nullSafeToString(this.rejectedValue);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
@@ -69,6 +69,8 @@ import org.springframework.validation.method.ParameterValidationResult;
|
||||
* at the type level of the containing target class, applying to all public service methods
|
||||
* of that class. By default, JSR-303 will validate against its default group only.
|
||||
*
|
||||
* <p>This functionality requires a Bean Validation 1.1+ provider.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
|
||||
+2
@@ -60,6 +60,8 @@ import org.springframework.validation.method.MethodValidationResult;
|
||||
* inline constraint annotations. Validation groups can be specified through {@code @Validated}
|
||||
* as well. By default, JSR-303 will validate against its default group only.
|
||||
*
|
||||
* <p>This functionality requires a Bean Validation 1.1+ provider.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
* @see MethodValidationInterceptor
|
||||
|
||||
+1
@@ -56,6 +56,7 @@ public class SpringConstraintValidatorFactory implements ConstraintValidatorFact
|
||||
return this.beanFactory.createBean(key);
|
||||
}
|
||||
|
||||
// Bean Validation 1.1 releaseInstance method
|
||||
@Override
|
||||
public void releaseInstance(ConstraintValidator<?, ?> instance) {
|
||||
this.beanFactory.destroyBean(instance);
|
||||
|
||||
+2
@@ -55,6 +55,8 @@ import org.springframework.validation.SmartValidator;
|
||||
* {@link CustomValidatorBean} and {@link LocalValidatorFactoryBean},
|
||||
* and as the primary implementation of the {@link SmartValidator} interface.
|
||||
*
|
||||
* <p>This adapter is fully compatible with Bean Validation 1.1 as well as 2.0.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @since 3.0
|
||||
|
||||
+6
-6
@@ -60,6 +60,12 @@ class CacheOperationExpressionEvaluatorTests {
|
||||
private final AnnotationCacheOperationSource source = new AnnotationCacheOperationSource();
|
||||
|
||||
|
||||
private Collection<CacheOperation> getOps(String name) {
|
||||
Method method = ReflectionUtils.findMethod(AnnotatedClass.class, name, Object.class, Object.class);
|
||||
return this.source.getCacheOperations(method, AnnotatedClass.class);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void testMultipleCachingSource() {
|
||||
Collection<CacheOperation> ops = getOps("multipleCaching");
|
||||
@@ -138,12 +144,6 @@ class CacheOperationExpressionEvaluatorTests {
|
||||
assertThat(value).isEqualTo(String.class.getName());
|
||||
}
|
||||
|
||||
|
||||
private Collection<CacheOperation> getOps(String name) {
|
||||
Method method = ReflectionUtils.findMethod(AnnotatedClass.class, name, Object.class, Object.class);
|
||||
return this.source.getCacheOperations(method, AnnotatedClass.class);
|
||||
}
|
||||
|
||||
private EvaluationContext createEvaluationContext(Object result) {
|
||||
return createEvaluationContext(result, null);
|
||||
}
|
||||
|
||||
Vendored
-4
@@ -104,7 +104,6 @@ class CachePutEvaluationTests {
|
||||
assertThat(this.cache.get(anotherValue + 100).get()).as("Wrong value for @CachePut key").isEqualTo(anotherValue);
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@EnableCaching
|
||||
static class Config implements CachingConfigurer {
|
||||
@@ -122,10 +121,8 @@ class CachePutEvaluationTests {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@CacheConfig("test")
|
||||
public static class SimpleService {
|
||||
|
||||
private AtomicLong counter = new AtomicLong();
|
||||
|
||||
/**
|
||||
@@ -147,5 +144,4 @@ class CachePutEvaluationTests {
|
||||
return this.counter.getAndIncrement();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-55
@@ -31,7 +31,6 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.testfixture.beans.TestBean;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.weaving.LoadTimeWeaverAware;
|
||||
import org.springframework.core.SpringProperties;
|
||||
import org.springframework.core.testfixture.EnabledForTestGroups;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
@@ -69,16 +68,6 @@ class BackgroundBootstrapTests {
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Timeout(10)
|
||||
@EnabledForTestGroups(LONG_RUNNING)
|
||||
void bootstrapWithLoadTimeWeaverAware() {
|
||||
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(LoadTimeWeaverAwareBeanConfig.class);
|
||||
ctx.getBean("testBean1", TestBean.class);
|
||||
ctx.getBean("testBean2", TestBean.class);
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Timeout(10)
|
||||
@EnabledForTestGroups(LONG_RUNNING)
|
||||
@@ -277,50 +266,6 @@ class BackgroundBootstrapTests {
|
||||
}
|
||||
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class LoadTimeWeaverAwareBeanConfig {
|
||||
|
||||
@Bean
|
||||
LoadTimeWeaverAware loadTimeWeaverAware(ObjectProvider<TestBean> testBean1) {
|
||||
Thread thread = new Thread(testBean1::getObject);
|
||||
thread.start();
|
||||
try {
|
||||
thread.join();
|
||||
}
|
||||
catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
return (loadTimeWeaver -> {});
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TestBean testBean1(TestBean testBean2) {
|
||||
return new TestBean(testBean2);
|
||||
}
|
||||
|
||||
@Bean @Lazy
|
||||
public FactoryBean<TestBean> testBean2() {
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
}
|
||||
catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
TestBean testBean = new TestBean();
|
||||
return new FactoryBean<>() {
|
||||
@Override
|
||||
public TestBean getObject() {
|
||||
return testBean;
|
||||
}
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return testBean.getClass();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class StrictLockingBeanConfig {
|
||||
|
||||
|
||||
+7
-13
@@ -61,7 +61,6 @@ class ClassPathBeanDefinitionScannerTests {
|
||||
GenericApplicationContext context = new GenericApplicationContext();
|
||||
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
|
||||
int beanCount = scanner.scan(BASE_PACKAGE);
|
||||
|
||||
assertThat(beanCount).isGreaterThanOrEqualTo(12);
|
||||
assertThat(context.containsBean("serviceInvocationCounter")).isTrue();
|
||||
assertThat(context.containsBean("fooServiceImpl")).isTrue();
|
||||
@@ -74,8 +73,8 @@ class ClassPathBeanDefinitionScannerTests {
|
||||
assertThat(context.containsBean(AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)).isTrue();
|
||||
assertThat(context.containsBean(AnnotationConfigUtils.EVENT_LISTENER_PROCESSOR_BEAN_NAME)).isTrue();
|
||||
assertThat(context.containsBean(AnnotationConfigUtils.EVENT_LISTENER_FACTORY_BEAN_NAME)).isTrue();
|
||||
|
||||
context.refresh();
|
||||
|
||||
FooServiceImpl fooService = context.getBean("fooServiceImpl", FooServiceImpl.class);
|
||||
assertThat(context.getDefaultListableBeanFactory().containsSingleton("myNamedComponent")).isTrue();
|
||||
assertThat(fooService.foo(123)).isEqualTo("bar");
|
||||
@@ -158,7 +157,6 @@ class ClassPathBeanDefinitionScannerTests {
|
||||
|
||||
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
|
||||
int beanCount = scanner.scan(BASE_PACKAGE);
|
||||
|
||||
assertThat(beanCount).isGreaterThanOrEqualTo(12);
|
||||
|
||||
ClassPathBeanDefinitionScanner scanner2 = new ClassPathBeanDefinitionScanner(context) {
|
||||
@@ -184,8 +182,8 @@ class ClassPathBeanDefinitionScannerTests {
|
||||
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
|
||||
scanner.setIncludeAnnotationConfig(false);
|
||||
int beanCount = scanner.scan(BASE_PACKAGE);
|
||||
|
||||
assertThat(beanCount).isGreaterThanOrEqualTo(7);
|
||||
|
||||
assertThat(context.containsBean("serviceInvocationCounter")).isTrue();
|
||||
assertThat(context.containsBean("fooServiceImpl")).isTrue();
|
||||
assertThat(context.containsBean("stubFooDao")).isTrue();
|
||||
@@ -484,14 +482,12 @@ class ClassPathBeanDefinitionScannerTests {
|
||||
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
|
||||
scanner.setBeanNameGenerator(new TestBeanNameGenerator());
|
||||
int beanCount = scanner.scan(BASE_PACKAGE);
|
||||
|
||||
assertThat(beanCount).isGreaterThanOrEqualTo(12);
|
||||
|
||||
context.refresh();
|
||||
|
||||
FooServiceImpl fooService = context.getBean("fooService", FooServiceImpl.class);
|
||||
StaticListableBeanFactory myBf = (StaticListableBeanFactory) context.getBean("myBf");
|
||||
MessageSource ms = (MessageSource) context.getBean("messageSource");
|
||||
|
||||
assertThat(fooService.isInitCalled()).isTrue();
|
||||
assertThat(fooService.foo(123)).isEqualTo("bar");
|
||||
assertThat(fooService.lookupFoo(123)).isEqualTo("bar");
|
||||
@@ -513,10 +509,9 @@ class ClassPathBeanDefinitionScannerTests {
|
||||
scanner.setIncludeAnnotationConfig(false);
|
||||
scanner.setBeanNameGenerator(new TestBeanNameGenerator());
|
||||
int beanCount = scanner.scan(BASE_PACKAGE);
|
||||
|
||||
assertThat(beanCount).isGreaterThanOrEqualTo(7);
|
||||
|
||||
context.refresh();
|
||||
|
||||
try {
|
||||
context.getBean("fooService");
|
||||
}
|
||||
@@ -550,10 +545,9 @@ class ClassPathBeanDefinitionScannerTests {
|
||||
scanner.setAutowireCandidatePatterns("*NoSuchDao");
|
||||
scanner.scan(BASE_PACKAGE);
|
||||
context.refresh();
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(() -> context.getBean("fooService"))
|
||||
.satisfies(ex ->
|
||||
assertThat(ex.getMostSpecificCause()).isInstanceOf(NoSuchBeanDefinitionException.class));
|
||||
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() ->
|
||||
context.getBean("fooService"))
|
||||
.satisfies(ex -> assertThat(ex.getMostSpecificCause()).isInstanceOf(NoSuchBeanDefinitionException.class));
|
||||
}
|
||||
|
||||
|
||||
|
||||
+28
-34
@@ -409,14 +409,11 @@ class ConfigurationClassPostProcessorTests {
|
||||
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(SingletonBeanConfig.class));
|
||||
beanFactory.setAllowBeanDefinitionOverriding(false);
|
||||
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||
|
||||
assertThatExceptionOfType(BeanDefinitionStoreException.class)
|
||||
.isThrownBy(() -> pp.postProcessBeanFactory(beanFactory))
|
||||
.withMessageContainingAll(
|
||||
"bar",
|
||||
"SingletonBeanConfig",
|
||||
TestBean.class.getName()
|
||||
);
|
||||
.withMessageContaining("bar")
|
||||
.withMessageContaining("SingletonBeanConfig")
|
||||
.withMessageContaining(TestBean.class.getName());
|
||||
}
|
||||
|
||||
@Test // gh-25430
|
||||
@@ -425,13 +422,10 @@ class ConfigurationClassPostProcessorTests {
|
||||
DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
|
||||
beanFactory.setAllowBeanDefinitionOverriding(false);
|
||||
context.register(FirstConfiguration.class, SecondConfiguration.class);
|
||||
|
||||
assertThatIllegalStateException().isThrownBy(context::refresh)
|
||||
.withMessageContainingAll(
|
||||
"alias 'taskExecutor'",
|
||||
"name 'applicationTaskExecutor'",
|
||||
"bean definition 'taskExecutor'"
|
||||
);
|
||||
.withMessageContaining("alias 'taskExecutor'")
|
||||
.withMessageContaining("name 'applicationTaskExecutor'")
|
||||
.withMessageContaining("bean definition 'taskExecutor'");
|
||||
context.close();
|
||||
}
|
||||
|
||||
@@ -984,7 +978,7 @@ class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void selfReferenceExclusionForFactoryMethodOnSameBean() {
|
||||
void testSelfReferenceExclusionForFactoryMethodOnSameBean() {
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(beanFactory);
|
||||
beanFactory.addBeanPostProcessor(bpp);
|
||||
@@ -998,7 +992,7 @@ class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void configWithDefaultMethods() {
|
||||
void testConfigWithDefaultMethods() {
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(beanFactory);
|
||||
beanFactory.addBeanPostProcessor(bpp);
|
||||
@@ -1012,7 +1006,7 @@ class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void configWithDefaultMethodsUsingAsm() {
|
||||
void testConfigWithDefaultMethodsUsingAsm() {
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(beanFactory);
|
||||
beanFactory.addBeanPostProcessor(bpp);
|
||||
@@ -1026,7 +1020,7 @@ class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void configWithFailingInit() { // gh-23343
|
||||
void testConfigWithFailingInit() { // gh-23343
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(beanFactory);
|
||||
beanFactory.addBeanPostProcessor(bpp);
|
||||
@@ -1040,7 +1034,7 @@ class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void circularDependency() {
|
||||
void testCircularDependency() {
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(beanFactory);
|
||||
beanFactory.addBeanPostProcessor(bpp);
|
||||
@@ -1054,42 +1048,42 @@ class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void circularDependencyWithApplicationContext() {
|
||||
void testCircularDependencyWithApplicationContext() {
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(() -> new AnnotationConfigApplicationContext(A.class, AStrich.class))
|
||||
.withMessageContaining("Circular reference");
|
||||
}
|
||||
|
||||
@Test
|
||||
void prototypeArgumentThroughBeanMethodCall() {
|
||||
void testPrototypeArgumentThroughBeanMethodCall() {
|
||||
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(BeanArgumentConfigWithPrototype.class);
|
||||
ctx.getBean(FooFactory.class).createFoo(new BarArgument());
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void singletonArgumentThroughBeanMethodCall() {
|
||||
void testSingletonArgumentThroughBeanMethodCall() {
|
||||
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(BeanArgumentConfigWithSingleton.class);
|
||||
ctx.getBean(FooFactory.class).createFoo(new BarArgument());
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullArgumentThroughBeanMethodCall() {
|
||||
void testNullArgumentThroughBeanMethodCall() {
|
||||
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(BeanArgumentConfigWithNull.class);
|
||||
ctx.getBean("aFoo");
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void injectionPointMatchForNarrowTargetReturnType() {
|
||||
void testInjectionPointMatchForNarrowTargetReturnType() {
|
||||
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(FooBarConfiguration.class);
|
||||
assertThat(ctx.getBean(FooImpl.class).bar).isSameAs(ctx.getBean(BarImpl.class));
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void varargOnBeanMethod() {
|
||||
void testVarargOnBeanMethod() {
|
||||
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(VarargConfiguration.class, TestBean.class);
|
||||
VarargConfiguration bean = ctx.getBean(VarargConfiguration.class);
|
||||
assertThat(bean.testBeans).isNotNull();
|
||||
@@ -1099,7 +1093,7 @@ class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void emptyVarargOnBeanMethod() {
|
||||
void testEmptyVarargOnBeanMethod() {
|
||||
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(VarargConfiguration.class);
|
||||
VarargConfiguration bean = ctx.getBean(VarargConfiguration.class);
|
||||
assertThat(bean.testBeans).isNotNull();
|
||||
@@ -1108,7 +1102,7 @@ class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void collectionArgumentOnBeanMethod() {
|
||||
void testCollectionArgumentOnBeanMethod() {
|
||||
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(CollectionArgumentConfiguration.class, TestBean.class);
|
||||
CollectionArgumentConfiguration bean = ctx.getBean(CollectionArgumentConfiguration.class);
|
||||
assertThat(bean.testBeans).containsExactly(ctx.getBean(TestBean.class));
|
||||
@@ -1116,7 +1110,7 @@ class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void emptyCollectionArgumentOnBeanMethod() {
|
||||
void testEmptyCollectionArgumentOnBeanMethod() {
|
||||
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(CollectionArgumentConfiguration.class);
|
||||
CollectionArgumentConfiguration bean = ctx.getBean(CollectionArgumentConfiguration.class);
|
||||
assertThat(bean.testBeans).isEmpty();
|
||||
@@ -1124,7 +1118,7 @@ class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void mapArgumentOnBeanMethod() {
|
||||
void testMapArgumentOnBeanMethod() {
|
||||
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(MapArgumentConfiguration.class, DummyRunnable.class);
|
||||
MapArgumentConfiguration bean = ctx.getBean(MapArgumentConfiguration.class);
|
||||
assertThat(bean.testBeans).hasSize(1).containsValue(ctx.getBean(Runnable.class));
|
||||
@@ -1132,7 +1126,7 @@ class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void emptyMapArgumentOnBeanMethod() {
|
||||
void testEmptyMapArgumentOnBeanMethod() {
|
||||
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(MapArgumentConfiguration.class);
|
||||
MapArgumentConfiguration bean = ctx.getBean(MapArgumentConfiguration.class);
|
||||
assertThat(bean.testBeans).isEmpty();
|
||||
@@ -1140,7 +1134,7 @@ class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void collectionInjectionFromSameConfigurationClass() {
|
||||
void testCollectionInjectionFromSameConfigurationClass() {
|
||||
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(CollectionInjectionConfiguration.class);
|
||||
CollectionInjectionConfiguration bean = ctx.getBean(CollectionInjectionConfiguration.class);
|
||||
assertThat(bean.testBeans).containsExactly(ctx.getBean(TestBean.class));
|
||||
@@ -1148,7 +1142,7 @@ class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void mapInjectionFromSameConfigurationClass() {
|
||||
void testMapInjectionFromSameConfigurationClass() {
|
||||
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(MapInjectionConfiguration.class);
|
||||
MapInjectionConfiguration bean = ctx.getBean(MapInjectionConfiguration.class);
|
||||
assertThat(bean.testBeans).containsOnly(Map.entry("testBean", ctx.getBean(Runnable.class)));
|
||||
@@ -1156,20 +1150,20 @@ class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void beanLookupFromSameConfigurationClass() {
|
||||
void testBeanLookupFromSameConfigurationClass() {
|
||||
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(BeanLookupConfiguration.class);
|
||||
assertThat(ctx.getBean(BeanLookupConfiguration.class).getTestBean()).isSameAs(ctx.getBean(TestBean.class));
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void nameClashBetweenConfigurationClassAndBean() {
|
||||
void testNameClashBetweenConfigurationClassAndBean() {
|
||||
assertThatExceptionOfType(BeanDefinitionStoreException.class)
|
||||
.isThrownBy(() -> new AnnotationConfigApplicationContext(MyTestBean.class));
|
||||
.isThrownBy(() -> new AnnotationConfigApplicationContext(MyTestBean.class).getBean("myTestBean", TestBean.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void beanDefinitionRegistryPostProcessorConfig() {
|
||||
void testBeanDefinitionRegistryPostProcessorConfig() {
|
||||
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(BeanDefinitionRegistryPostProcessorConfig.class);
|
||||
assertThat(ctx.getBean("myTestBean")).isInstanceOf(TestBean.class);
|
||||
ctx.close();
|
||||
|
||||
+2
-17
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.springframework.context.event;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
@@ -106,9 +105,8 @@ class AnnotationDrivenEventListenerTests {
|
||||
this.eventCollector.assertTotalEventsCount(1);
|
||||
|
||||
this.eventCollector.clear();
|
||||
TestEvent otherEvent = new TestEvent(this, Integer.valueOf(1));
|
||||
this.context.publishEvent(otherEvent);
|
||||
this.eventCollector.assertEvent(listener, otherEvent);
|
||||
this.context.publishEvent(event);
|
||||
this.eventCollector.assertEvent(listener, event);
|
||||
this.eventCollector.assertTotalEventsCount(1);
|
||||
|
||||
context.getBean(ApplicationEventMulticaster.class).removeApplicationListeners(l ->
|
||||
@@ -744,11 +742,6 @@ class AnnotationDrivenEventListenerTests {
|
||||
public void handleString(String content) {
|
||||
collectEvent(content);
|
||||
}
|
||||
|
||||
@EventListener({Boolean.class, Integer.class})
|
||||
public void handleBooleanOrInteger(Serializable content) {
|
||||
collectEvent(content);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1016,8 +1009,6 @@ class AnnotationDrivenEventListenerTests {
|
||||
|
||||
void handleString(String payload);
|
||||
|
||||
void handleBooleanOrInteger(Serializable content);
|
||||
|
||||
void handleTimestamp(Long timestamp);
|
||||
|
||||
void handleRatio(Double ratio);
|
||||
@@ -1040,12 +1031,6 @@ class AnnotationDrivenEventListenerTests {
|
||||
super.handleString(payload);
|
||||
}
|
||||
|
||||
@EventListener({Boolean.class, Integer.class})
|
||||
@Override
|
||||
public void handleBooleanOrInteger(Serializable content) {
|
||||
super.handleBooleanOrInteger(content);
|
||||
}
|
||||
|
||||
@ConditionalEvent("#root.event.timestamp > #p0")
|
||||
@Override
|
||||
public void handleTimestamp(Long timestamp) {
|
||||
|
||||
@@ -18,12 +18,11 @@ package org.springframework.context.event.test;
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class TestEvent extends IdentifiableApplicationEvent {
|
||||
|
||||
public final Object msg;
|
||||
public final String msg;
|
||||
|
||||
public TestEvent(Object source, String id, String msg) {
|
||||
super(source, id);
|
||||
@@ -35,11 +34,6 @@ public class TestEvent extends IdentifiableApplicationEvent {
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public TestEvent(Object source, Integer msg) {
|
||||
super(source);
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public TestEvent(Object source) {
|
||||
this(source, "test");
|
||||
}
|
||||
|
||||
+2
@@ -32,6 +32,8 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("removal")
|
||||
public class CandidateComponentsIndexLoaderTests {
|
||||
|
||||
@Test
|
||||
|
||||
+2
@@ -30,6 +30,8 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("removal")
|
||||
public class CandidateComponentsIndexTests {
|
||||
|
||||
@Test
|
||||
|
||||
+4
@@ -503,6 +503,10 @@ class SpringValidatorAdapterTests {
|
||||
|
||||
private static final String ID = "id";
|
||||
|
||||
@Override
|
||||
public void initialize(AnythingValid constraintAnnotation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(Object value, ConstraintValidatorContext context) {
|
||||
List<Field> fieldsErrors = new ArrayList<>();
|
||||
|
||||
+12
@@ -425,6 +425,10 @@ class ValidatorFactoryTests {
|
||||
@Autowired
|
||||
private Environment environment;
|
||||
|
||||
@Override
|
||||
public void initialize(NameAddressValid constraintAnnotation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(ValidPerson value, ConstraintValidatorContext context) {
|
||||
if (value.expectsAutowiredValidator) {
|
||||
@@ -491,6 +495,10 @@ class ValidatorFactoryTests {
|
||||
|
||||
public static class InnerValidator implements ConstraintValidator<InnerValid, InnerBean> {
|
||||
|
||||
@Override
|
||||
public void initialize(InnerValid constraintAnnotation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(InnerBean bean, ConstraintValidatorContext context) {
|
||||
context.disableDefaultConstraintViolation();
|
||||
@@ -534,6 +542,10 @@ class ValidatorFactoryTests {
|
||||
|
||||
public static class NotXListValidator implements ConstraintValidator<NotXList, List<String>> {
|
||||
|
||||
@Override
|
||||
public void initialize(NotXList constraintAnnotation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(List<String> list, ConstraintValidatorContext context) {
|
||||
context.disableDefaultConstraintViolation();
|
||||
|
||||
@@ -193,16 +193,12 @@ public class ClassReader {
|
||||
final byte[] classFileBuffer, final int classFileOffset, final boolean checkClassVersion) {
|
||||
this.classFileBuffer = classFileBuffer;
|
||||
this.b = classFileBuffer;
|
||||
|
||||
// Check the class' major_version. This field is after the magic and minor_version fields, which
|
||||
// use 4 and 2 bytes respectively.
|
||||
// SPRING PATCH: leniently try to parse newer class files as well
|
||||
// if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V26) {
|
||||
// throw new IllegalArgumentException(
|
||||
// "Unsupported class file major version " + readShort(classFileOffset + 6));
|
||||
// }
|
||||
// END OF PATCH
|
||||
|
||||
if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V25) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unsupported class file major version " + readShort(classFileOffset + 6));
|
||||
}
|
||||
// Create the constant pool arrays. The constant_pool_count field is after the magic,
|
||||
// minor_version and major_version fields, which use 4, 2 and 2 bytes respectively.
|
||||
int constantPoolCount = readUnsignedShort(classFileOffset + 8);
|
||||
|
||||
@@ -290,7 +290,6 @@ public interface Opcodes {
|
||||
int V23 = 0 << 16 | 67;
|
||||
int V24 = 0 << 16 | 68;
|
||||
int V25 = 0 << 16 | 69;
|
||||
int V26 = 0 << 16 | 70;
|
||||
|
||||
/**
|
||||
* Version flag indicating that the class is using 'preview' features.
|
||||
|
||||
@@ -1473,7 +1473,7 @@ final class SymbolTable {
|
||||
|
||||
/**
|
||||
* Another entry (and so on recursively) having the same hash code (modulo the size of {@link
|
||||
* SymbolTable#labelEntries}) as this one.
|
||||
* SymbolTable#labelEntries}}) as this one.
|
||||
*/
|
||||
LabelEntry next;
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -110,7 +110,7 @@ class BridgeMethodResolver {
|
||||
&& currentMethod != null) {
|
||||
Signature target = new Signature(name, desc);
|
||||
// If the target signature is the same as the current,
|
||||
// we shouldn't change our bridge because invokespecial
|
||||
// we shouldn't change our bridge becaues invokespecial
|
||||
// is the only way to make progress (otherwise we'll
|
||||
// get infinite recursion). This would typically
|
||||
// only happen when a bridge method is created to widen
|
||||
|
||||
@@ -1278,32 +1278,29 @@ public class Enhancer extends AbstractClassGenerator {
|
||||
Signature bridgeTarget = (Signature) bridgeToTarget.get(method.getSignature());
|
||||
if (bridgeTarget != null) {
|
||||
// checkcast each argument against the target's argument types
|
||||
Type[] argTypes = method.getSignature().getArgumentTypes();
|
||||
Type[] targetTypes = bridgeTarget.getArgumentTypes();
|
||||
for (int i = 0; i < targetTypes.length; i++) {
|
||||
for (int i = 0; i < bridgeTarget.getArgumentTypes().length; i++) {
|
||||
e.load_arg(i);
|
||||
Type argType = argTypes[i];
|
||||
Type target = targetTypes[i];
|
||||
if (!target.equals(argType)) {
|
||||
if (!TypeUtils.isPrimitive(target)) {
|
||||
e.box(argType);
|
||||
}
|
||||
Type target = bridgeTarget.getArgumentTypes()[i];
|
||||
if (!target.equals(method.getSignature().getArgumentTypes()[i])) {
|
||||
e.checkcast(target);
|
||||
}
|
||||
}
|
||||
|
||||
e.invoke_virtual_this(bridgeTarget);
|
||||
|
||||
// Not necessary to cast if the target & bridge have the same return type.
|
||||
Type retType = method.getSignature().getReturnType();
|
||||
Type target = bridgeTarget.getReturnType();
|
||||
if (!target.equals(retType)) {
|
||||
if (!TypeUtils.isPrimitive(target)) {
|
||||
e.unbox(retType);
|
||||
}
|
||||
else {
|
||||
e.checkcast(retType);
|
||||
}
|
||||
// Not necessary to cast if the target & bridge have
|
||||
// the same return type.
|
||||
// (This conveniently includes void and primitive types,
|
||||
// which would fail if casted. It's not possible to
|
||||
// covariant from boxed to unbox (or vice versa), so no having
|
||||
// to box/unbox for bridges).
|
||||
// TODO: It also isn't necessary to checkcast if the return is
|
||||
// assignable from the target. (This would happen if a subclass
|
||||
// used covariant returns to narrow the return type within a bridge
|
||||
// method.)
|
||||
if (!retType.equals(bridgeTarget.getReturnType())) {
|
||||
e.checkcast(retType);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -100,15 +100,15 @@ public final class BridgeMethodResolver {
|
||||
}
|
||||
|
||||
private static Method resolveBridgeMethod(Method bridgeMethod, Class<?> targetClass) {
|
||||
boolean localBridge = (targetClass == bridgeMethod.getDeclaringClass());
|
||||
Class<?> userClass = targetClass;
|
||||
if (!bridgeMethod.isBridge()) {
|
||||
if (!bridgeMethod.isBridge() && localBridge) {
|
||||
userClass = ClassUtils.getUserClass(targetClass);
|
||||
if (userClass == targetClass) {
|
||||
return bridgeMethod;
|
||||
}
|
||||
}
|
||||
|
||||
boolean localBridge = (targetClass == bridgeMethod.getDeclaringClass());
|
||||
Object cacheKey = (localBridge ? bridgeMethod : new MethodClassKey(bridgeMethod, targetClass));
|
||||
Method bridgedMethod = cache.get(cacheKey);
|
||||
if (bridgedMethod == null) {
|
||||
@@ -118,7 +118,7 @@ public final class BridgeMethodResolver {
|
||||
ReflectionUtils.doWithMethods(userClass, candidateMethods::add, filter);
|
||||
if (!candidateMethods.isEmpty()) {
|
||||
bridgedMethod = (candidateMethods.size() == 1 ? candidateMethods.get(0) :
|
||||
searchCandidates(candidateMethods, bridgeMethod, targetClass));
|
||||
searchCandidates(candidateMethods, bridgeMethod));
|
||||
}
|
||||
if (bridgedMethod == null) {
|
||||
// A bridge method was passed in but we couldn't find the bridged method.
|
||||
@@ -149,16 +149,14 @@ public final class BridgeMethodResolver {
|
||||
* @return the bridged method, or {@code null} if none found
|
||||
*/
|
||||
@Nullable
|
||||
private static Method searchCandidates(
|
||||
List<Method> candidateMethods, Method bridgeMethod, Class<?> targetClass) {
|
||||
|
||||
private static Method searchCandidates(List<Method> candidateMethods, Method bridgeMethod) {
|
||||
if (candidateMethods.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
Method previousMethod = null;
|
||||
boolean sameSig = true;
|
||||
for (Method candidateMethod : candidateMethods) {
|
||||
if (isBridgeMethodFor(bridgeMethod, candidateMethod, targetClass)) {
|
||||
if (isBridgeMethodFor(bridgeMethod, candidateMethod, bridgeMethod.getDeclaringClass())) {
|
||||
return candidateMethod;
|
||||
}
|
||||
else if (previousMethod != null) {
|
||||
@@ -174,12 +172,12 @@ public final class BridgeMethodResolver {
|
||||
* Determines whether the bridge {@link Method} is the bridge for the
|
||||
* supplied candidate {@link Method}.
|
||||
*/
|
||||
static boolean isBridgeMethodFor(Method bridgeMethod, Method candidateMethod, Class<?> targetClass) {
|
||||
if (isResolvedTypeMatch(candidateMethod, bridgeMethod, targetClass)) {
|
||||
static boolean isBridgeMethodFor(Method bridgeMethod, Method candidateMethod, Class<?> declaringClass) {
|
||||
if (isResolvedTypeMatch(candidateMethod, bridgeMethod, declaringClass)) {
|
||||
return true;
|
||||
}
|
||||
Method method = findGenericDeclaration(bridgeMethod);
|
||||
return (method != null && isResolvedTypeMatch(method, candidateMethod, targetClass));
|
||||
return (method != null && isResolvedTypeMatch(method, candidateMethod, declaringClass));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -188,25 +186,14 @@ public final class BridgeMethodResolver {
|
||||
* are equal after resolving all types against the declaringType, otherwise
|
||||
* returns {@code false}.
|
||||
*/
|
||||
private static boolean isResolvedTypeMatch(Method genericMethod, Method candidateMethod, Class<?> targetClass) {
|
||||
private static boolean isResolvedTypeMatch(Method genericMethod, Method candidateMethod, Class<?> declaringClass) {
|
||||
Type[] genericParameters = genericMethod.getGenericParameterTypes();
|
||||
if (genericParameters.length != candidateMethod.getParameterCount()) {
|
||||
return false;
|
||||
}
|
||||
Class<?> clazz = targetClass;
|
||||
while (clazz != null && clazz != Object.class && clazz != genericMethod.getDeclaringClass()) {
|
||||
if (checkResolvedTypeMatch(genericMethod, candidateMethod, clazz)) {
|
||||
return true;
|
||||
}
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean checkResolvedTypeMatch(Method genericMethod, Method candidateMethod, Class<?> clazz) {
|
||||
Class<?>[] candidateParameters = candidateMethod.getParameterTypes();
|
||||
for (int i = 0; i < candidateParameters.length; i++) {
|
||||
ResolvableType genericParameter = ResolvableType.forMethodParameter(genericMethod, i, clazz);
|
||||
ResolvableType genericParameter = ResolvableType.forMethodParameter(genericMethod, i, declaringClass);
|
||||
Class<?> candidateParameter = candidateParameters[i];
|
||||
if (candidateParameter.isArray()) {
|
||||
// An array type: compare the component type.
|
||||
@@ -286,9 +273,7 @@ public final class BridgeMethodResolver {
|
||||
* introduced in Java 6 to fix <a href="https://bugs.openjdk.org/browse/JDK-6342411">
|
||||
* JDK-6342411</a>.
|
||||
* @return whether signatures match as described
|
||||
* @deprecated as of 6.2.13: not necessary anymore due to {@link #getMostSpecificMethod}
|
||||
*/
|
||||
@Deprecated(since = "6.2.13", forRemoval = true)
|
||||
public static boolean isVisibilityBridgeMethodPair(Method bridgeMethod, Method bridgedMethod) {
|
||||
if (bridgeMethod == bridgedMethod) {
|
||||
// Same method: for common purposes, return true to proceed as if it was a visibility bridge.
|
||||
|
||||
@@ -18,7 +18,6 @@ package org.springframework.core.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -39,7 +38,6 @@ import org.springframework.util.StringUtils;
|
||||
* interface-declared parameter annotations from the concrete target method.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @since 6.1
|
||||
* @see #getMethodAnnotation(Class)
|
||||
* @see #getMethodParameters()
|
||||
@@ -183,7 +181,7 @@ public class AnnotatedMethod {
|
||||
clazz = null;
|
||||
}
|
||||
if (clazz != null) {
|
||||
for (Method candidate : clazz.getDeclaredMethods()) {
|
||||
for (Method candidate : clazz.getMethods()) {
|
||||
if (isOverrideFor(candidate)) {
|
||||
parameterAnnotations.add(candidate.getParameterAnnotations());
|
||||
}
|
||||
@@ -196,9 +194,8 @@ public class AnnotatedMethod {
|
||||
}
|
||||
|
||||
private boolean isOverrideFor(Method candidate) {
|
||||
if (Modifier.isPrivate(candidate.getModifiers()) ||
|
||||
!candidate.getName().equals(this.method.getName()) ||
|
||||
(candidate.getParameterCount() != this.method.getParameterCount())) {
|
||||
if (!candidate.getName().equals(this.method.getName()) ||
|
||||
candidate.getParameterCount() != this.method.getParameterCount()) {
|
||||
return false;
|
||||
}
|
||||
Class<?>[] paramTypes = this.method.getParameterTypes();
|
||||
@@ -207,7 +204,7 @@ public class AnnotatedMethod {
|
||||
}
|
||||
for (int i = 0; i < paramTypes.length; i++) {
|
||||
if (paramTypes[i] !=
|
||||
ResolvableType.forMethodParameter(candidate, i, this.method.getDeclaringClass()).toClass()) {
|
||||
ResolvableType.forMethodParameter(candidate, i, this.method.getDeclaringClass()).resolve()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -372,14 +372,14 @@ abstract class AnnotationsScanner {
|
||||
private static boolean hasSameGenericTypeParameters(
|
||||
Method rootMethod, Method candidateMethod, Class<?>[] rootParameterTypes) {
|
||||
|
||||
Class<?> rootDeclaringClass = rootMethod.getDeclaringClass();
|
||||
Class<?> sourceDeclaringClass = rootMethod.getDeclaringClass();
|
||||
Class<?> candidateDeclaringClass = candidateMethod.getDeclaringClass();
|
||||
if (!candidateDeclaringClass.isAssignableFrom(rootDeclaringClass)) {
|
||||
if (!candidateDeclaringClass.isAssignableFrom(sourceDeclaringClass)) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < rootParameterTypes.length; i++) {
|
||||
Class<?> resolvedParameterType = ResolvableType.forMethodParameter(
|
||||
candidateMethod, i, rootDeclaringClass).toClass();
|
||||
candidateMethod, i, sourceDeclaringClass).resolve();
|
||||
if (rootParameterTypes[i] != resolvedParameterType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
+8
-12
@@ -175,18 +175,14 @@ public abstract class AbstractCharSequenceDecoder<T extends CharSequence> extend
|
||||
public final T decode(DataBuffer dataBuffer, ResolvableType elementType,
|
||||
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
|
||||
|
||||
try {
|
||||
Charset charset = getCharset(mimeType);
|
||||
T value = decodeInternal(dataBuffer, charset);
|
||||
LogFormatUtils.traceDebug(logger, traceOn -> {
|
||||
String formatted = LogFormatUtils.formatValue(value, !traceOn);
|
||||
return Hints.getLogPrefix(hints) + "Decoded " + formatted;
|
||||
});
|
||||
return value;
|
||||
}
|
||||
finally {
|
||||
DataBufferUtils.release(dataBuffer);
|
||||
}
|
||||
Charset charset = getCharset(mimeType);
|
||||
T value = decodeInternal(dataBuffer, charset);
|
||||
DataBufferUtils.release(dataBuffer);
|
||||
LogFormatUtils.traceDebug(logger, traceOn -> {
|
||||
String formatted = LogFormatUtils.formatValue(value, !traceOn);
|
||||
return Hints.getLogPrefix(hints) + "Decoded " + formatted;
|
||||
});
|
||||
return value;
|
||||
}
|
||||
|
||||
private Charset getCharset(@Nullable MimeType mimeType) {
|
||||
|
||||
@@ -50,7 +50,7 @@ public abstract class Hints {
|
||||
|
||||
|
||||
/**
|
||||
* Create a map with a single hint via {@link Collections#singletonMap}.
|
||||
* Create a map wit a single hint via {@link Collections#singletonMap}.
|
||||
* @param hintName the hint name
|
||||
* @param value the hint value
|
||||
* @return the created map
|
||||
|
||||
@@ -117,10 +117,8 @@ public interface Resource extends InputStreamSource {
|
||||
|
||||
/**
|
||||
* Return a File handle for this resource.
|
||||
* <p>Note: This only works for files in the default file system.
|
||||
* @throws UnsupportedOperationException if the resource is a file but cannot be
|
||||
* exposed as a {@code java.io.File}; an alternative to {@code FileNotFoundException}
|
||||
* @throws java.io.FileNotFoundException if the resource cannot be resolved as a file
|
||||
* @throws java.io.FileNotFoundException if the resource cannot be resolved as
|
||||
* absolute file path, i.e. if the resource is not available in a file system
|
||||
* @throws IOException in case of general resolution/reading failures
|
||||
* @see #getInputStream()
|
||||
*/
|
||||
|
||||
@@ -36,9 +36,8 @@ import org.springframework.util.Assert;
|
||||
* <p>{@code DataBuffer}s has a separate {@linkplain #readPosition() read} and
|
||||
* {@linkplain #writePosition() write} position, as opposed to {@code ByteBuffer}'s
|
||||
* single {@linkplain ByteBuffer#position() position}. As such, the {@code DataBuffer}
|
||||
* does not require a {@linkplain ByteBuffer#flip() flip} to read after writing.
|
||||
* In general, the following invariant holds for the read and write positions,
|
||||
* and the capacity:
|
||||
* does not require a {@linkplain ByteBuffer#flip() flip} to read after writing. In general,
|
||||
* the following invariant holds for the read and write positions, and the capacity:
|
||||
*
|
||||
* <blockquote>
|
||||
* {@code 0} {@code <=}
|
||||
@@ -47,13 +46,12 @@ import org.springframework.util.Assert;
|
||||
* <i>capacity</i>
|
||||
* </blockquote>
|
||||
*
|
||||
* <p>The {@linkplain #capacity() capacity} of a {@code DataBuffer} is expanded on
|
||||
* demand, similar to {@code StringBuilder}.
|
||||
* <p>The {@linkplain #capacity() capacity} of a {@code DataBuffer} is expanded on demand,
|
||||
* similar to {@code StringBuilder}.
|
||||
*
|
||||
* <p>The main purpose of the {@code DataBuffer} abstraction is to provide a
|
||||
* convenient wrapper around {@link ByteBuffer} which is similar to Netty's
|
||||
* {@link io.netty.buffer.ByteBuf} but can also be used on non-Netty platforms
|
||||
* (i.e. Servlet containers).
|
||||
* <p>The main purpose of the {@code DataBuffer} abstraction is to provide a convenient wrapper
|
||||
* around {@link ByteBuffer} which is similar to Netty's {@link io.netty.buffer.ByteBuf} but
|
||||
* can also be used on non-Netty platforms (i.e. Servlet containers).
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Brian Clozel
|
||||
@@ -115,8 +113,8 @@ public interface DataBuffer {
|
||||
* the current capacity, it will be expanded.
|
||||
* @param capacity the new capacity
|
||||
* @return this buffer
|
||||
* @deprecated as of 6.0, in favor of {@link #ensureWritable(int)}
|
||||
* which has different semantics
|
||||
* @deprecated as of 6.0, in favor of {@link #ensureWritable(int)}, which
|
||||
* has different semantics
|
||||
*/
|
||||
@Deprecated(since = "6.0")
|
||||
DataBuffer capacity(int capacity);
|
||||
@@ -188,28 +186,6 @@ public interface DataBuffer {
|
||||
*/
|
||||
byte getByte(int index);
|
||||
|
||||
/**
|
||||
* Process a range of bytes from the current buffer using a
|
||||
* {@link ByteProcessor}.
|
||||
* @param index the index at which the processing will start
|
||||
* @param length the maximum number of bytes to be processed
|
||||
* @param processor the processor that consumes bytes
|
||||
* @return the position that was reached when processing was stopped,
|
||||
* or {@code -1} if the entire byte range was processed.
|
||||
* @throws IndexOutOfBoundsException when {@code index} is out of bounds
|
||||
* @since 6.2.12
|
||||
*/
|
||||
default int forEachByte(int index, int length, ByteProcessor processor) {
|
||||
Assert.isTrue(length >= 0, "Length must be >= 0");
|
||||
for (int position = index; position < index + length; position++) {
|
||||
byte b = getByte(position);
|
||||
if(!processor.process(b)) {
|
||||
return position;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a single byte from the current reading position from this data buffer.
|
||||
* @return the byte at this buffer's current reading position
|
||||
@@ -242,16 +218,16 @@ public interface DataBuffer {
|
||||
DataBuffer write(byte b);
|
||||
|
||||
/**
|
||||
* Write the given source into this buffer, starting at the current writing
|
||||
* position of this buffer.
|
||||
* Write the given source into this buffer, starting at the current writing position
|
||||
* of this buffer.
|
||||
* @param source the bytes to be written into this buffer
|
||||
* @return this buffer
|
||||
*/
|
||||
DataBuffer write(byte[] source);
|
||||
|
||||
/**
|
||||
* Write at most {@code length} bytes of the given source into this buffer,
|
||||
* starting at the current writing position of this buffer.
|
||||
* Write at most {@code length} bytes of the given source into this buffer, starting
|
||||
* at the current writing position of this buffer.
|
||||
* @param source the bytes to be written into this buffer
|
||||
* @param offset the index within {@code source} to start writing from
|
||||
* @param length the maximum number of bytes to be written from {@code source}
|
||||
@@ -260,8 +236,8 @@ public interface DataBuffer {
|
||||
DataBuffer write(byte[] source, int offset, int length);
|
||||
|
||||
/**
|
||||
* Write one or more {@code DataBuffer}s to this buffer, starting at the
|
||||
* current writing position. It is the responsibility of the caller to
|
||||
* Write one or more {@code DataBuffer}s to this buffer, starting at the current
|
||||
* writing position. It is the responsibility of the caller to
|
||||
* {@linkplain DataBufferUtils#release(DataBuffer) release} the given data buffers.
|
||||
* @param buffers the byte buffers to write into this buffer
|
||||
* @return this buffer
|
||||
@@ -269,8 +245,8 @@ public interface DataBuffer {
|
||||
DataBuffer write(DataBuffer... buffers);
|
||||
|
||||
/**
|
||||
* Write one or more {@link ByteBuffer} to this buffer, starting at the
|
||||
* current writing position.
|
||||
* Write one or more {@link ByteBuffer} to this buffer, starting at the current
|
||||
* writing position.
|
||||
* @param buffers the byte buffers to write into this buffer
|
||||
* @return this buffer
|
||||
*/
|
||||
@@ -323,37 +299,36 @@ public interface DataBuffer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code DataBuffer} whose contents is a shared subsequence
|
||||
* of this data buffer's content. Data between this data buffer and the
|
||||
* returned buffer is shared; though changes in the returned buffer's
|
||||
* position will not be reflected in the reading nor writing position
|
||||
* of this data buffer.
|
||||
* Create a new {@code DataBuffer} whose contents is a shared subsequence of this
|
||||
* data buffer's content. Data between this data buffer and the returned buffer is
|
||||
* shared; though changes in the returned buffer's position will not be reflected
|
||||
* in the reading nor writing position of this data buffer.
|
||||
* <p><strong>Note</strong> that this method will <strong>not</strong> call
|
||||
* {@link DataBufferUtils#retain(DataBuffer)} on the resulting slice:
|
||||
* the reference count will not be increased.
|
||||
* {@link DataBufferUtils#retain(DataBuffer)} on the resulting slice: the reference
|
||||
* count will not be increased.
|
||||
* @param index the index at which to start the slice
|
||||
* @param length the length of the slice
|
||||
* @return the specified slice of this data buffer
|
||||
* @deprecated as of 6.0, in favor of {@link #split(int)}
|
||||
* which has different semantics
|
||||
* @deprecated as of 6.0, in favor of {@link #split(int)}, which
|
||||
* has different semantics
|
||||
*/
|
||||
@Deprecated(since = "6.0")
|
||||
DataBuffer slice(int index, int length);
|
||||
|
||||
/**
|
||||
* Create a new {@code DataBuffer} whose contents is a shared, retained subsequence
|
||||
* of this data buffer's content. Data between this data buffer and the returned
|
||||
* buffer is shared; though changes in the returned buffer's position will not be
|
||||
* reflected in the reading nor writing position of this data buffer.
|
||||
* Create a new {@code DataBuffer} whose contents is a shared, retained subsequence of this
|
||||
* data buffer's content. Data between this data buffer and the returned buffer is
|
||||
* shared; though changes in the returned buffer's position will not be reflected
|
||||
* in the reading nor writing position of this data buffer.
|
||||
* <p><strong>Note</strong> that unlike {@link #slice(int, int)}, this method
|
||||
* <strong>will</strong> call {@link DataBufferUtils#retain(DataBuffer)}
|
||||
* (or equivalent) on the resulting slice.
|
||||
* <strong>will</strong> call {@link DataBufferUtils#retain(DataBuffer)} (or equivalent) on the
|
||||
* resulting slice.
|
||||
* @param index the index at which to start the slice
|
||||
* @param length the length of the slice
|
||||
* @return the specified, retained slice of this data buffer
|
||||
* @since 5.2
|
||||
* @deprecated as of 6.0, in favor of {@link #split(int)}
|
||||
* which has different semantics
|
||||
* @deprecated as of 6.0, in favor of {@link #split(int)}, which
|
||||
* has different semantics
|
||||
*/
|
||||
@Deprecated(since = "6.0")
|
||||
default DataBuffer retainedSlice(int index, int length) {
|
||||
@@ -362,10 +337,12 @@ public interface DataBuffer {
|
||||
|
||||
/**
|
||||
* Splits this data buffer into two at the given index.
|
||||
*
|
||||
* <p>Data that precedes the {@code index} will be returned in a new buffer,
|
||||
* while this buffer will contain data that follows after {@code index}.
|
||||
* Memory between the two buffers is shared, but independent and cannot
|
||||
* overlap (unlike {@link #slice(int, int) slice}).
|
||||
*
|
||||
* <p>The {@linkplain #readPosition() read} and
|
||||
* {@linkplain #writePosition() write} position of the returned buffer are
|
||||
* truncated to fit within the buffers {@linkplain #capacity() capacity} if
|
||||
@@ -385,22 +362,22 @@ public interface DataBuffer {
|
||||
* will not be reflected in the reading nor writing position of this data buffer.
|
||||
* @return this data buffer as a byte buffer
|
||||
* @deprecated as of 6.0, in favor of {@link #toByteBuffer(ByteBuffer)},
|
||||
* {@link #readableByteBuffers()} or {@link #writableByteBuffers()}
|
||||
* {@link #readableByteBuffers()}, or {@link #writableByteBuffers()}.
|
||||
*/
|
||||
@Deprecated(since = "6.0")
|
||||
ByteBuffer asByteBuffer();
|
||||
|
||||
/**
|
||||
* Expose a subsequence of this buffer's bytes as a {@link ByteBuffer}. Data
|
||||
* between this {@code DataBuffer} and the returned {@code ByteBuffer} is shared;
|
||||
* though changes in the returned buffer's {@linkplain ByteBuffer#position() position}
|
||||
* Expose a subsequence of this buffer's bytes as a {@link ByteBuffer}. Data between
|
||||
* this {@code DataBuffer} and the returned {@code ByteBuffer} is shared; though
|
||||
* changes in the returned buffer's {@linkplain ByteBuffer#position() position}
|
||||
* will not be reflected in the reading nor writing position of this data buffer.
|
||||
* @param index the index at which to start the byte buffer
|
||||
* @param length the length of the returned byte buffer
|
||||
* @return this data buffer as a byte buffer
|
||||
* @since 5.0.1
|
||||
* @deprecated as of 6.0, in favor of {@link #toByteBuffer(int, ByteBuffer, int, int)},
|
||||
* {@link #readableByteBuffers()} or {@link #writableByteBuffers()}
|
||||
* {@link #readableByteBuffers()}, or {@link #writableByteBuffers()}.
|
||||
*/
|
||||
@Deprecated(since = "6.0")
|
||||
ByteBuffer asByteBuffer(int index, int length);
|
||||
@@ -554,22 +531,4 @@ public interface DataBuffer {
|
||||
void close();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process a range of bytes one by one.
|
||||
* @since 6.2.12
|
||||
*/
|
||||
@FunctionalInterface
|
||||
interface ByteProcessor {
|
||||
|
||||
/**
|
||||
* Process the given {@code byte} and indicate whether processing
|
||||
* should continue further.
|
||||
* @param b a byte from the {@link DataBuffer}
|
||||
* @return {@code true} if processing should continue,
|
||||
* or {@code false} if processing should stop at this element
|
||||
*/
|
||||
boolean process(byte b);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-11
@@ -31,19 +31,8 @@ package org.springframework.core.io.buffer;
|
||||
public class DataBufferLimitException extends IllegalStateException {
|
||||
|
||||
|
||||
/**
|
||||
* Create an instance with the given message.
|
||||
*/
|
||||
public DataBufferLimitException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance with a message and a cause, e.g. {@link OutOfMemoryError}.
|
||||
* @since 6.2.12
|
||||
*/
|
||||
public DataBufferLimitException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.core.io.buffer;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
@@ -222,17 +223,17 @@ public abstract class DataBufferUtils {
|
||||
|
||||
try {
|
||||
if (resource.isFile()) {
|
||||
Path filePath = resource.getFile().toPath();
|
||||
File file = resource.getFile();
|
||||
return readAsynchronousFileChannel(
|
||||
() -> AsynchronousFileChannel.open(filePath, StandardOpenOption.READ),
|
||||
() -> AsynchronousFileChannel.open(file.toPath(), StandardOpenOption.READ),
|
||||
position, bufferFactory, bufferSize);
|
||||
}
|
||||
}
|
||||
catch (IOException | UnsupportedOperationException ignore) {
|
||||
catch (IOException ignore) {
|
||||
// fallback to resource.readableChannel(), below
|
||||
}
|
||||
Flux<DataBuffer> result = readByteChannel(resource::readableChannel, bufferFactory, bufferSize);
|
||||
return (position == 0 ? result : skipUntilByteCount(result, position));
|
||||
return position == 0 ? result : skipUntilByteCount(result, position);
|
||||
}
|
||||
|
||||
|
||||
@@ -832,9 +833,13 @@ public abstract class DataBufferUtils {
|
||||
|
||||
@Override
|
||||
public int match(DataBuffer dataBuffer) {
|
||||
int start = dataBuffer.readPosition();
|
||||
int end = dataBuffer.writePosition();
|
||||
return dataBuffer.forEachByte(start, end - start, b -> !this.match(b));
|
||||
for (int pos = dataBuffer.readPosition(); pos < dataBuffer.writePosition(); pos++) {
|
||||
byte b = dataBuffer.getByte(pos);
|
||||
if (match(b)) {
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -877,13 +882,14 @@ public abstract class DataBufferUtils {
|
||||
|
||||
@Override
|
||||
public int match(DataBuffer dataBuffer) {
|
||||
int start = dataBuffer.readPosition();
|
||||
int end = dataBuffer.writePosition();
|
||||
int matchPosition = dataBuffer.forEachByte(start, end - start, b -> !this.match(b));
|
||||
if (matchPosition != -1) {
|
||||
reset();
|
||||
for (int pos = dataBuffer.readPosition(); pos < dataBuffer.writePosition(); pos++) {
|
||||
byte b = dataBuffer.getByte(pos);
|
||||
if (match(b)) {
|
||||
reset();
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
return matchPosition;
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -55,6 +55,7 @@ public final class JettyDataBuffer implements PooledDataBuffer {
|
||||
this.bufferFactory = bufferFactory;
|
||||
this.delegate = delegate;
|
||||
this.chunk = chunk;
|
||||
this.chunk.retain();
|
||||
}
|
||||
|
||||
JettyDataBuffer(JettyDataBufferFactory bufferFactory, DefaultDataBuffer delegate) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user