mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
147 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8404537474 | |||
| c873a597c7 | |||
| 4dc93bc485 | |||
| 68f2b0ca59 | |||
| 1edc0d8002 | |||
| a6dc020dc4 | |||
| 07422d709e | |||
| 768fc7e341 | |||
| 8ecedb81b3 | |||
| f19433f2d8 | |||
| 0b02a5e073 | |||
| e2b24f3c12 | |||
| c375fb1f70 | |||
| ad3e5425d4 | |||
| c418118683 | |||
| fb3f30832c | |||
| a275d942d2 | |||
| 20afa3265a | |||
| df50c8db3e | |||
| 679b668bbb | |||
| 676daa990b | |||
| 7c7fa69558 | |||
| 9ad92b16b0 | |||
| 3b899fe7e2 | |||
| c91041b675 | |||
| a17cf742b2 | |||
| a102cd5f32 | |||
| d03b6aa1d6 | |||
| b32b4f3a59 | |||
| 502997d8e9 | |||
| fb4ad2f3ba | |||
| b3de1b8e95 | |||
| fb17e283d1 | |||
| 0b7a24fc14 | |||
| 75b540f25c | |||
| 8bf79cc9c4 | |||
| 7ff80bc09d | |||
| 8d6b0eb191 | |||
| 35c7e3960e | |||
| 8e8c3f5a7c | |||
| f50b230fb3 | |||
| 02ba06953f | |||
| df22ba39f8 | |||
| 826776f321 | |||
| 29f92c8a2b | |||
| 64e04b7bc2 | |||
| ad05b02ff5 | |||
| 430a24e6bc | |||
| b7b9f2cb6b | |||
| b76664e757 | |||
| ae13823851 | |||
| 58b4286216 | |||
| df079feea9 | |||
| 372282457f | |||
| 79df1da792 | |||
| c5771bc7c8 | |||
| 2365581265 | |||
| c1a8b9a14d | |||
| e945e7426e | |||
| f07b9fd217 | |||
| 80a20488fd | |||
| 1dc9dffc70 | |||
| 0226580773 | |||
| 3ef1b7d83c | |||
| 08bce69d3d | |||
| 56b60120fe | |||
| 1364a179a9 | |||
| 2161e865d7 | |||
| 0b2c2d04b2 | |||
| 07fe8eea83 | |||
| 5aac35b99e | |||
| 040ea0a97c | |||
| 3c05679a97 | |||
| d8729a7c67 | |||
| c95426a616 | |||
| 1e403d1606 | |||
| f1567fb21a | |||
| 60865eae4b | |||
| 0c39fff831 | |||
| e902f9551a | |||
| 0a20c8a44a | |||
| 3cb746c358 | |||
| 6526e79eea | |||
| b77d4d01c5 | |||
| 599ac58baa | |||
| 449174c7d4 | |||
| 9266e6d29e | |||
| 062d701ae1 | |||
| 7137b22e6b | |||
| acb786d359 | |||
| fa7300c1de | |||
| db17a97ce8 | |||
| 55f946c5a0 | |||
| 3181dca5ef | |||
| f86a69ebfb | |||
| 6d63890c56 | |||
| 489c89b912 | |||
| 39bc7566df | |||
| d3a249e34d | |||
| 81f1edbaf2 | |||
| 23ecb50137 | |||
| 3d33d2baa9 | |||
| 3745224646 | |||
| 3bf78d6f8c | |||
| 563b2a8505 | |||
| 9ccbeec947 | |||
| 29248dff15 | |||
| 65d450ab6d | |||
| 271f2dc665 | |||
| da323d3335 | |||
| 32f061a3e0 | |||
| 9b5cbc1334 | |||
| 40e378a5a6 | |||
| 8eb0a0b94e | |||
| 09cf489c6b | |||
| 9a5290ea27 | |||
| b3176208e2 | |||
| 1dfe737d0e | |||
| dc2f513619 | |||
| 0eb33d09ac | |||
| f67f98a1a7 | |||
| 6c42f374c8 | |||
| 4879b56bb9 | |||
| 2fd83aa764 | |||
| 93218a06ba | |||
| 714c3c59eb | |||
| adcdefce43 | |||
| 049a024dea | |||
| 20bbebb299 | |||
| 26f006509f | |||
| 089503aab7 | |||
| 74155e3d88 | |||
| 564f33d5ef | |||
| 83acd5b050 | |||
| 67798a7b52 | |||
| 2b981651e1 | |||
| 294cdba80c | |||
| 072a86149d | |||
| 8bb4c167e4 | |||
| dff7aa4d4b | |||
| c634acd9ff | |||
| ed5c19f53e | |||
| 03420f811b | |||
| 4565bcd757 | |||
| f22f439a68 | |||
| 5672284f53 | |||
| 367f381fea |
+3
-3
@@ -8,7 +8,7 @@ plugins {
|
||||
id 'com.github.ben-manes.versions' version '0.46.0'
|
||||
id 'com.github.johnrengelman.shadow' version '8.1.1' apply false
|
||||
id 'de.undercouch.download' version '5.4.0'
|
||||
id 'me.champeau.jmh' version '0.7.0' apply false
|
||||
id 'me.champeau.jmh' version '0.7.1' apply false
|
||||
}
|
||||
|
||||
ext {
|
||||
@@ -75,7 +75,7 @@ configure([rootProject] + javaProjects) { project ->
|
||||
}
|
||||
|
||||
checkstyle {
|
||||
toolVersion = "10.12.0"
|
||||
toolVersion = "10.12.1"
|
||||
configDirectory.set(rootProject.file("src/checkstyle"))
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ configure([rootProject] + javaProjects) { project ->
|
||||
"https://www.ibm.com/docs/api/v1/content/SSEQTP_8.5.5/com.ibm.websphere.javadoc.doc/web/apidocs/", // com.ibm.*
|
||||
"https://docs.jboss.org/jbossas/javadoc/4.0.5/connector/", // org.jboss.resource.*
|
||||
"https://docs.jboss.org/hibernate/orm/5.6/javadocs/",
|
||||
"https://www.eclipse.org/aspectj/doc/released/aspectj5rt-api/",
|
||||
"https://eclipse.dev/aspectj/doc/released/aspectj5rt-api",
|
||||
"https://www.quartz-scheduler.org/api/2.3.0/",
|
||||
"https://www.javadoc.io/doc/com.fasterxml.jackson.core/jackson-core/2.14.1/",
|
||||
"https://www.javadoc.io/doc/com.fasterxml.jackson.core/jackson-databind/2.14.1/",
|
||||
|
||||
@@ -154,7 +154,6 @@ Java::
|
||||
----
|
||||
package com.xyz;
|
||||
|
||||
@Aspect
|
||||
public class Pointcuts {
|
||||
|
||||
@Pointcut("execution(public * *(..))")
|
||||
@@ -179,7 +178,6 @@ Kotlin::
|
||||
----
|
||||
package com.xyz
|
||||
|
||||
@Aspect
|
||||
class Pointcuts {
|
||||
|
||||
@Pointcut("execution(public * *(..))")
|
||||
@@ -211,9 +209,9 @@ pointcut matching.
|
||||
|
||||
When working with enterprise applications, developers often have the need to refer to
|
||||
modules of the application and particular sets of operations from within several aspects.
|
||||
We recommend defining a dedicated aspect that captures commonly used _named pointcut_
|
||||
expressions for this purpose. Such an aspect typically resembles the following
|
||||
`CommonPointcuts` example (though what you name the aspect is up to you):
|
||||
We recommend defining a dedicated class that captures commonly used _named pointcut_
|
||||
expressions for this purpose. Such a class typically resembles the following
|
||||
`CommonPointcuts` example (though what you name the class is up to you):
|
||||
|
||||
[tabs]
|
||||
======
|
||||
@@ -223,10 +221,8 @@ Java::
|
||||
----
|
||||
package com.xyz;
|
||||
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
|
||||
@Aspect
|
||||
public class CommonPointcuts {
|
||||
|
||||
/**
|
||||
@@ -287,10 +283,8 @@ Kotlin::
|
||||
----
|
||||
package com.xyz
|
||||
|
||||
import org.aspectj.lang.annotation.Aspect
|
||||
import org.aspectj.lang.annotation.Pointcut
|
||||
|
||||
@Aspect
|
||||
class CommonPointcuts {
|
||||
|
||||
/**
|
||||
@@ -346,9 +340,9 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
You can refer to the pointcuts defined in such an aspect anywhere you need a pointcut
|
||||
expression by referencing the fully-qualified name of the `@Aspect` class combined with
|
||||
the `@Pointcut` method's name. For example, to make the service layer transactional, you
|
||||
You can refer to the pointcuts defined in such a class anywhere you need a pointcut
|
||||
expression by referencing the fully-qualified name of the class combined with the
|
||||
`@Pointcut` method's name. For example, to make the service layer transactional, you
|
||||
could write the following which references the
|
||||
`com.xyz.CommonPointcuts.businessService()` _named pointcut_:
|
||||
|
||||
|
||||
@@ -318,6 +318,51 @@ Java::
|
||||
----
|
||||
======
|
||||
|
||||
[[aot.bestpractices.jpa]]
|
||||
=== JPA
|
||||
|
||||
The JPA persistence unit has to be known upfront for certain optimizations to apply. Consider the following basic example:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
@Bean
|
||||
LocalContainerEntityManagerFactoryBean customDBEntityManagerFactory(DataSource dataSource) {
|
||||
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
|
||||
factoryBean.setDataSource(dataSource);
|
||||
factoryBean.setPackagesToScan("com.example.app");
|
||||
return factoryBean;
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
To make sure the scanning occurs ahead of time, a `PersistenceManagedTypes` bean must be declared and used by the
|
||||
factory bean definition, as shown by the following example:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
@Bean
|
||||
PersistenceManagedTypes persistenceManagedTypes(ResourceLoader resourceLoader) {
|
||||
return new PersistenceManagedTypesScanner(resourceLoader)
|
||||
.scan("com.example.app");
|
||||
}
|
||||
|
||||
@Bean
|
||||
LocalContainerEntityManagerFactoryBean customDBEntityManagerFactory(DataSource dataSource, PersistenceManagedTypes managedTypes) {
|
||||
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
|
||||
factoryBean.setDataSource(dataSource);
|
||||
factoryBean.setManagedTypes(managedTypes);
|
||||
return factoryBean;
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
[[aot.hints]]
|
||||
== Runtime Hints
|
||||
|
||||
@@ -151,10 +151,10 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
The last example we show here is for typical JDBC support. You could have the
|
||||
`DataSource` injected into an initialization method or a constructor, where you would create a
|
||||
`JdbcTemplate` and other data access support classes (such as `SimpleJdbcCall` and others) by using
|
||||
this `DataSource`. The following example autowires a `DataSource`:
|
||||
The last example we show here is for typical JDBC support. You could have the `DataSource`
|
||||
injected into an initialization method or a constructor, where you would create a `JdbcTemplate`
|
||||
and other data access support classes (such as `SimpleJdbcCall` and others) by using this
|
||||
`DataSource`. The following example autowires a `DataSource`:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
|
||||
@@ -9,13 +9,13 @@ to the database.
|
||||
[[jdbc-batch-classic]]
|
||||
== Basic Batch Operations with `JdbcTemplate`
|
||||
|
||||
You accomplish `JdbcTemplate` batch processing by implementing two methods of a special
|
||||
interface, `BatchPreparedStatementSetter`, and passing that implementation in as the second parameter
|
||||
You accomplish `JdbcTemplate` batch processing by implementing two methods of a special interface,
|
||||
`BatchPreparedStatementSetter`, and passing that implementation in as the second parameter
|
||||
in your `batchUpdate` method call. You can use the `getBatchSize` method to provide the size of
|
||||
the current batch. You can use the `setValues` method to set the values for the parameters of
|
||||
the prepared statement. This method is called the number of times that you
|
||||
specified in the `getBatchSize` call. The following example updates the `t_actor` table
|
||||
based on entries in a list, and the entire list is used as the batch:
|
||||
the prepared statement. This method is called the number of times that you specified in the
|
||||
`getBatchSize` call. The following example updates the `t_actor` table based on entries in a list,
|
||||
and the entire list is used as the batch:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
|
||||
@@ -10,7 +10,7 @@ This section covers:
|
||||
* xref:data-access/jdbc/connections.adoc#jdbc-SingleConnectionDataSource[Using `SingleConnectionDataSource`]
|
||||
* xref:data-access/jdbc/connections.adoc#jdbc-DriverManagerDataSource[Using `DriverManagerDataSource`]
|
||||
* xref:data-access/jdbc/connections.adoc#jdbc-TransactionAwareDataSourceProxy[Using `TransactionAwareDataSourceProxy`]
|
||||
* xref:data-access/jdbc/connections.adoc#jdbc-DataSourceTransactionManager[Using `DataSourceTransactionManager`]
|
||||
* xref:data-access/jdbc/connections.adoc#jdbc-DataSourceTransactionManager[Using `DataSourceTransactionManager` / `JdbcTransactionManager`]
|
||||
|
||||
|
||||
[[jdbc-datasource]]
|
||||
@@ -125,8 +125,12 @@ The following example shows C3P0 configuration:
|
||||
== Using `DataSourceUtils`
|
||||
|
||||
The `DataSourceUtils` class is a convenient and powerful helper class that provides
|
||||
`static` methods to obtain connections from JNDI and close connections if necessary. It
|
||||
supports thread-bound connections with, for example, `DataSourceTransactionManager`.
|
||||
`static` methods to obtain connections from JNDI and close connections if necessary.
|
||||
It supports a thread-bound JDBC `Connection` with `DataSourceTransactionManager` but
|
||||
also with `JtaTransactionManager` and `JpaTransactionManager`.
|
||||
|
||||
Note that `JdbcTemplate` implies `DataSourceUtils` connection access, using it
|
||||
behind every JDBC operation, implicitly participating in an ongoing transaction.
|
||||
|
||||
|
||||
[[jdbc-SmartDataSource]]
|
||||
@@ -165,7 +169,6 @@ In contrast to `DriverManagerDataSource`, it reuses the same connection all the
|
||||
avoiding excessive creation of physical connections.
|
||||
|
||||
|
||||
|
||||
[[jdbc-DriverManagerDataSource]]
|
||||
== Using `DriverManagerDataSource`
|
||||
|
||||
@@ -201,29 +204,44 @@ javadoc for more details.
|
||||
|
||||
|
||||
[[jdbc-DataSourceTransactionManager]]
|
||||
== Using `DataSourceTransactionManager`
|
||||
== Using `DataSourceTransactionManager` / `JdbcTransactionManager`
|
||||
|
||||
The `DataSourceTransactionManager` class is a `PlatformTransactionManager`
|
||||
implementation for single JDBC data sources. It binds a JDBC connection from the
|
||||
specified data source to the currently executing thread, potentially allowing for one
|
||||
thread connection per data source.
|
||||
implementation for a single JDBC `DataSource`. It binds a JDBC `Connection`
|
||||
from the specified `DataSource` to the currently executing thread, potentially
|
||||
allowing for one thread-bound `Connection` per `DataSource`.
|
||||
|
||||
Application code is required to retrieve the JDBC connection through
|
||||
`DataSourceUtils.getConnection(DataSource)` instead of Jakarta EE's standard
|
||||
Application code is required to retrieve the JDBC `Connection` through
|
||||
`DataSourceUtils.getConnection(DataSource)` instead of Java EE's standard
|
||||
`DataSource.getConnection`. It throws unchecked `org.springframework.dao` exceptions
|
||||
instead of checked `SQLExceptions`. All framework classes (such as `JdbcTemplate`) use this
|
||||
strategy implicitly. If not used with this transaction manager, the lookup strategy
|
||||
behaves exactly like the common one. Thus, it can be used in any case.
|
||||
instead of checked `SQLExceptions`. All framework classes (such as `JdbcTemplate`) use
|
||||
this strategy implicitly. If not used with a transaction manager, the lookup strategy
|
||||
behaves exactly like `DataSource.getConnection` and can therefore be used in any case.
|
||||
|
||||
The `DataSourceTransactionManager` class supports custom isolation levels and timeouts
|
||||
that get applied as appropriate JDBC statement query timeouts. To support the latter,
|
||||
application code must either use `JdbcTemplate` or call the
|
||||
`DataSourceUtils.applyTransactionTimeout(..)` method for each created statement.
|
||||
The `DataSourceTransactionManager` class supports savepoints (`PROPAGATION_NESTED`),
|
||||
custom isolation levels, and timeouts that get applied as appropriate JDBC statement
|
||||
query timeouts. To support the latter, application code must either use `JdbcTemplate` or
|
||||
call the `DataSourceUtils.applyTransactionTimeout(..)` method for each created statement.
|
||||
|
||||
You can use this implementation instead of `JtaTransactionManager` in the single-resource
|
||||
case, as it does not require the container to support JTA. Switching between
|
||||
both is just a matter of configuration, provided you stick to the required connection lookup
|
||||
pattern. JTA does not support custom isolation levels.
|
||||
You can use `DataSourceTransactionManager` instead of `JtaTransactionManager` in the
|
||||
single-resource case, as it does not require the container to support a JTA transaction
|
||||
coordinator. Switching between these transaction managers is just a matter of configuration,
|
||||
provided you stick to the required connection lookup pattern. Note that JTA does not support
|
||||
savepoints or custom isolation levels and has a different timeout mechanism but otherwise
|
||||
exposes similar behavior in terms of JDBC resources and JDBC commit/rollback management.
|
||||
|
||||
NOTE: As of 5.3, Spring provides an extended `JdbcTransactionManager` variant which adds
|
||||
exception translation capabilities on commit/rollback (aligned with `JdbcTemplate`).
|
||||
Where `DataSourceTransactionManager` will only ever throw `TransactionSystemException`
|
||||
(analogous to JTA), `JdbcTransactionManager` translates database locking failures etc to
|
||||
corresponding `DataAccessException` subclasses. Note that application code needs to be
|
||||
prepared for such exceptions, not exclusively expecting `TransactionSystemException`.
|
||||
In scenarios where that is the case, `JdbcTransactionManager` is the recommended choice.
|
||||
|
||||
In terms of exception behavior, `JdbcTransactionManager` is roughly equivalent to
|
||||
`JpaTransactionManager` and also to `R2dbcTransactionManager`, serving as an immediate
|
||||
companion/replacement for each other. `DataSourceTransactionManager` on the other hand
|
||||
is equivalent to `JtaTransactionManager` and can serve as a direct replacement there.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -718,12 +718,22 @@ See also xref:data-access/jdbc/core.adoc#jdbc-JdbcTemplate-idioms[`JdbcTemplate`
|
||||
between ``SQLException``s and Spring's own `org.springframework.dao.DataAccessException`,
|
||||
which is agnostic in regard to data access strategy. Implementations can be generic (for
|
||||
example, using SQLState codes for JDBC) or proprietary (for example, using Oracle error
|
||||
codes) for greater precision.
|
||||
codes) for greater precision. This exception translation mechanism is used behind the
|
||||
the common `JdbcTemplate` and `JdbcTransactionManager` entry points which do not
|
||||
propagate `SQLException` but rather `DataAccessException`.
|
||||
|
||||
NOTE: As of 6.0, the default exception translator is `SQLExceptionSubclassTranslator`,
|
||||
detecting JDBC 4 `SQLException` subclasses with a few extra checks, and with a fallback
|
||||
to `SQLState` introspection through `SQLStateSQLExceptionTranslator`. This is usually
|
||||
sufficient for common database access and does not require vendor-specific detection.
|
||||
For backwards compatibility, consider using `SQLErrorCodeSQLExceptionTranslator` as
|
||||
described below, potentially with custom error code mappings.
|
||||
|
||||
`SQLErrorCodeSQLExceptionTranslator` is the implementation of `SQLExceptionTranslator`
|
||||
that is used by default. This implementation uses specific vendor codes. It is more
|
||||
precise than the `SQLState` implementation. The error code translations are based on
|
||||
codes held in a JavaBean type class called `SQLErrorCodes`. This class is created and
|
||||
that is used by default when a file named `sql-error-codes.xml` is present in the root
|
||||
of the classpath. This implementation uses specific vendor codes. It is more precise than
|
||||
`SQLState` or `SQLException` subclass translation. The error code translations are based
|
||||
on codes held in a JavaBean type class called `SQLErrorCodes`. This class is created and
|
||||
populated by an `SQLErrorCodesFactory`, which (as the name suggests) is a factory for
|
||||
creating `SQLErrorCodes` based on the contents of a configuration file named
|
||||
`sql-error-codes.xml`. This file is populated with vendor codes and based on the
|
||||
@@ -744,8 +754,8 @@ The `SQLErrorCodeSQLExceptionTranslator` applies matching rules in the following
|
||||
translator. If this translation is not available, the next fallback translator is
|
||||
the `SQLStateSQLExceptionTranslator`.
|
||||
|
||||
NOTE: The `SQLErrorCodesFactory` is used by default to define `Error` codes and custom exception
|
||||
translations. They are looked up in a file named `sql-error-codes.xml` from the
|
||||
NOTE: The `SQLErrorCodesFactory` is used by default to define error codes and custom
|
||||
exception translations. They are looked up in a file named `sql-error-codes.xml` from the
|
||||
classpath, and the matching `SQLErrorCodes` instance is located based on the database
|
||||
name from the database metadata of the database in use.
|
||||
|
||||
@@ -784,12 +794,12 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
In the preceding example, the specific error code (`-12345`) is translated, while other errors are
|
||||
left to be translated by the default translator implementation. To use this custom
|
||||
translator, you must pass it to the `JdbcTemplate` through the method
|
||||
`setExceptionTranslator`, and you must use this `JdbcTemplate` for all of the data access
|
||||
processing where this translator is needed. The following example shows how you can use this custom
|
||||
translator:
|
||||
In the preceding example, the specific error code (`-12345`) is translated while
|
||||
other errors are left to be translated by the default translator implementation.
|
||||
To use this custom translator, you must pass it to the `JdbcTemplate` through the
|
||||
method `setExceptionTranslator`, and you must use this `JdbcTemplate` for all of the
|
||||
data access processing where this translator is needed. The following example shows
|
||||
how you can use this custom translator:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
@@ -800,7 +810,6 @@ Java::
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
public void setDataSource(DataSource dataSource) {
|
||||
|
||||
// create a JdbcTemplate and set data source
|
||||
this.jdbcTemplate = new JdbcTemplate();
|
||||
this.jdbcTemplate.setDataSource(dataSource);
|
||||
@@ -809,7 +818,6 @@ Java::
|
||||
CustomSQLErrorCodesTranslator tr = new CustomSQLErrorCodesTranslator();
|
||||
tr.setDataSource(dataSource);
|
||||
this.jdbcTemplate.setExceptionTranslator(tr);
|
||||
|
||||
}
|
||||
|
||||
public void updateShippingCharge(long orderId, long pct) {
|
||||
|
||||
@@ -3,30 +3,30 @@
|
||||
|
||||
The Spring Framework's JDBC abstraction framework consists of four different packages:
|
||||
|
||||
* `core`: The `org.springframework.jdbc.core` package contains the `JdbcTemplate` class and its
|
||||
various callback interfaces, plus a variety of related classes. A subpackage named
|
||||
`org.springframework.jdbc.core.simple` contains the `SimpleJdbcInsert` and
|
||||
* `core`: The `org.springframework.jdbc.core` package contains the `JdbcTemplate` class
|
||||
and its various callback interfaces, plus a variety of related classes. A subpackage
|
||||
named `org.springframework.jdbc.core.simple` contains the `SimpleJdbcInsert` and
|
||||
`SimpleJdbcCall` classes. Another subpackage named
|
||||
`org.springframework.jdbc.core.namedparam` contains the `NamedParameterJdbcTemplate`
|
||||
class and the related support classes. See xref:data-access/jdbc/core.adoc[Using the JDBC Core Classes to Control Basic JDBC Processing and Error Handling], xref:data-access/jdbc/advanced.adoc[JDBC Batch Operations], and
|
||||
xref:data-access/jdbc/simple.adoc[Simplifying JDBC Operations with the `SimpleJdbc` Classes].
|
||||
|
||||
* `datasource`: The `org.springframework.jdbc.datasource` package contains a utility class for easy
|
||||
`DataSource` access and various simple `DataSource` implementations that you can use for
|
||||
testing and running unmodified JDBC code outside of a Jakarta EE container. A subpackage
|
||||
named `org.springfamework.jdbc.datasource.embedded` provides support for creating
|
||||
* `datasource`: The `org.springframework.jdbc.datasource` package contains a utility class
|
||||
for easy `DataSource` access and various simple `DataSource` implementations that you can
|
||||
use for testing and running unmodified JDBC code outside of a Jakarta EE container. A subpackage
|
||||
named `org.springframework.jdbc.datasource.embedded` provides support for creating
|
||||
embedded databases by using Java database engines, such as HSQL, H2, and Derby. See
|
||||
xref:data-access/jdbc/connections.adoc[Controlling Database Connections] and xref:data-access/jdbc/embedded-database-support.adoc[Embedded Database Support].
|
||||
|
||||
* `object`: The `org.springframework.jdbc.object` package contains classes that represent RDBMS
|
||||
queries, updates, and stored procedures as thread-safe, reusable objects. See
|
||||
* `object`: The `org.springframework.jdbc.object` package contains classes that represent
|
||||
RDBMS queries, updates, and stored procedures as thread-safe, reusable objects. See
|
||||
xref:data-access/jdbc/object.adoc[Modeling JDBC Operations as Java Objects]. This approach is modeled by JDO, although objects returned by queries
|
||||
are naturally disconnected from the database. This higher-level of JDBC abstraction
|
||||
depends on the lower-level abstraction in the `org.springframework.jdbc.core` package.
|
||||
|
||||
* `support`: The `org.springframework.jdbc.support` package provides `SQLException` translation
|
||||
functionality and some utility classes. Exceptions thrown during JDBC processing are
|
||||
translated to exceptions defined in the `org.springframework.dao` package. This means
|
||||
* `support`: The `org.springframework.jdbc.support` package provides `SQLException`
|
||||
translation functionality and some utility classes. Exceptions thrown during JDBC processing
|
||||
are translated to exceptions defined in the `org.springframework.dao` package. This means
|
||||
that code using the Spring JDBC abstraction layer does not need to implement JDBC or
|
||||
RDBMS-specific error handling. All translated exceptions are unchecked, which gives you
|
||||
the option of catching the exceptions from which you can recover while letting other
|
||||
|
||||
@@ -495,7 +495,7 @@ features supported by Spring, usually in a vendor-specific manner:
|
||||
* Applying specific transaction semantics (such as custom isolation level or transaction
|
||||
timeout)
|
||||
* Retrieving the transactional JDBC `Connection` (for exposure to JDBC-based DAOs)
|
||||
* Advanced translation of `PersistenceExceptions` to Spring `DataAccessExceptions`
|
||||
* Advanced translation of `PersistenceException` to Spring's `DataAccessException`
|
||||
|
||||
This is particularly valuable for special transaction semantics and for advanced
|
||||
translation of exception. The default implementation (`DefaultJpaDialect`) does
|
||||
|
||||
@@ -718,19 +718,15 @@ javadoc for more details.
|
||||
=== Using `R2dbcTransactionManager`
|
||||
|
||||
The `R2dbcTransactionManager` class is a `ReactiveTransactionManager` implementation for
|
||||
single R2DBC data sources. It binds an R2DBC connection from the specified connection factory
|
||||
to the subscriber `Context`, potentially allowing for one subscriber connection for each
|
||||
connection factory.
|
||||
a single R2DBC `ConnectionFactory`. It binds an R2DBC `Connection` from the specified
|
||||
`ConnectionFactory` to the subscriber `Context`, potentially allowing for one subscriber
|
||||
`Connection` for each `ConnectionFactory`.
|
||||
|
||||
Application code is required to retrieve the R2DBC connection through
|
||||
Application code is required to retrieve the R2DBC `Connection` through
|
||||
`ConnectionFactoryUtils.getConnection(ConnectionFactory)`, instead of R2DBC's standard
|
||||
`ConnectionFactory.create()`.
|
||||
|
||||
All framework classes (such as `DatabaseClient`) use this strategy implicitly.
|
||||
If not used with this transaction manager, the lookup strategy behaves exactly like the common one.
|
||||
Thus, it can be used in any case.
|
||||
|
||||
The `R2dbcTransactionManager` class supports custom isolation levels that get applied to the connection.
|
||||
`ConnectionFactory.create()`. All framework classes (such as `DatabaseClient`) use this
|
||||
strategy implicitly. If not used with a transaction manager, the lookup strategy behaves
|
||||
exactly like `ConnectionFactory.create()` and can therefore be used in any case.
|
||||
|
||||
|
||||
|
||||
|
||||
+3
-4
@@ -124,7 +124,6 @@ In XML configuration, the `<tx:annotation-driven/>` tag provides similar conveni
|
||||
----
|
||||
<1> The line that makes the bean instance transactional.
|
||||
|
||||
|
||||
TIP: You can omit the `transaction-manager` attribute in the `<tx:annotation-driven/>`
|
||||
tag if the bean name of the `TransactionManager` that you want to wire in has the name
|
||||
`transactionManager`. If the `TransactionManager` bean that you want to dependency-inject
|
||||
@@ -522,17 +521,17 @@ The following listing shows the bean declarations:
|
||||
----
|
||||
<tx:annotation-driven/>
|
||||
|
||||
<bean id="transactionManager1" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
|
||||
<bean id="transactionManager1" class="org.springframework.jdbc.support.JdbcTransactionManager">
|
||||
...
|
||||
<qualifier value="order"/>
|
||||
</bean>
|
||||
|
||||
<bean id="transactionManager2" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
|
||||
<bean id="transactionManager2" class="org.springframework.jdbc.support.JdbcTransactionManager">
|
||||
...
|
||||
<qualifier value="account"/>
|
||||
</bean>
|
||||
|
||||
<bean id="transactionManager3" class="org.springframework.data.r2dbc.connectionfactory.R2dbcTransactionManager">
|
||||
<bean id="transactionManager3" class="org.springframework.data.r2dbc.connection.R2dbcTransactionManager">
|
||||
...
|
||||
<qualifier value="reactive-account"/>
|
||||
</bean>
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
= Observability Support
|
||||
|
||||
Micrometer defines an https://micrometer.io/docs/observation[Observation concept that enables both Metrics and Traces] in applications.
|
||||
Metrics support offers a way to create timers, gauges or counters for collecting statistics about the runtime behavior of your application.
|
||||
Metrics can help you to track error rates, usage patterns, performance and more.
|
||||
Metrics support offers a way to create timers, gauges, or counters for collecting statistics about the runtime behavior of your application.
|
||||
Metrics can help you to track error rates, usage patterns, performance, and more.
|
||||
Traces provide a holistic view of an entire system, crossing application boundaries; you can zoom in on particular user requests and follow their entire completion across applications.
|
||||
|
||||
Spring Framework instruments various parts of its own codebase to publish observations if an `ObservationRegistry` is configured.
|
||||
@@ -36,16 +36,16 @@ https://micrometer.io/docs/concepts#_naming_meters[to the format preferred by th
|
||||
[[observability.concepts]]
|
||||
== Micrometer Observation concepts
|
||||
|
||||
If you are not familiar with Micrometer Observation, here's a quick summary of the new concepts you should know about.
|
||||
If you are not familiar with Micrometer Observation, here's a quick summary of the concepts you should know about.
|
||||
|
||||
* `Observation` is the actual recording of something happening in your application. This is processed by `ObservationHandler` implementations to produce metrics or traces.
|
||||
* Each observation has a corresponding `ObservationContext` implementation; this type holds all the relevant information for extracting metadata for it.
|
||||
In the case of an HTTP server observation, the context implementation could hold the HTTP request, the HTTP response, any Exception thrown during processing...
|
||||
* Each `Observation` holds `KeyValues` metadata. In the case of a server HTTP observation, this could be the HTTP request method, the HTTP response status...
|
||||
In the case of an HTTP server observation, the context implementation could hold the HTTP request, the HTTP response, any exception thrown during processing, and so forth.
|
||||
* Each `Observation` holds `KeyValues` metadata. In the case of an HTTP server observation, this could be the HTTP request method, the HTTP response status, and so forth.
|
||||
This metadata is contributed by `ObservationConvention` implementations which should declare the type of `ObservationContext` they support.
|
||||
* `KeyValues` are said to be "low cardinality" if there is a low, bounded number of possible values for the `KeyValue` tuple (HTTP method is a good example).
|
||||
Low cardinality values are contributed to metrics only.
|
||||
High cardinality values are on the other hand unbounded (for example, HTTP request URIs) and are only contributed to Traces.
|
||||
Conversely, "high cardinality" values are unbounded (for example, HTTP request URIs) and are only contributed to traces.
|
||||
* An `ObservationDocumentation` documents all observations in a particular domain, listing the expected key names and their meaning.
|
||||
|
||||
|
||||
@@ -63,16 +63,16 @@ Each instrumented component will provide two extension points:
|
||||
=== Using custom Observation conventions
|
||||
|
||||
Let's take the example of the Spring MVC "http.server.requests" metrics instrumentation with the `ServerHttpObservationFilter`.
|
||||
This observation is using a `ServerRequestObservationConvention` with a `ServerRequestObservationContext`; custom conventions can be configured on the Servlet filter.
|
||||
This observation uses a `ServerRequestObservationConvention` with a `ServerRequestObservationContext`; custom conventions can be configured on the Servlet filter.
|
||||
If you would like to customize the metadata produced with the observation, you can extend the `DefaultServerRequestObservationConvention` for your requirements:
|
||||
|
||||
include-code::./ExtendedServerRequestObservationConvention[]
|
||||
|
||||
If you want full control, you can then implement the entire convention contract for the observation you're interested in:
|
||||
If you want full control, you can implement the entire convention contract for the observation you're interested in:
|
||||
|
||||
include-code::./CustomServerRequestObservationConvention[]
|
||||
|
||||
You can also achieve similar goals using a custom `ObservationFilter` - adding or removing key values for an observation.
|
||||
You can also achieve similar goals using a custom `ObservationFilter` – adding or removing key values for an observation.
|
||||
Filters do not replace the default convention and are used as a post-processing component.
|
||||
|
||||
include-code::./ServerRequestObservationFilter[]
|
||||
@@ -83,20 +83,24 @@ You can configure `ObservationFilter` instances on the `ObservationRegistry`.
|
||||
[[observability.http-server]]
|
||||
== HTTP Server instrumentation
|
||||
|
||||
HTTP server exchanges observations are created with the name `"http.server.requests"` for Servlet and Reactive applications.
|
||||
HTTP server exchange observations are created with the name `"http.server.requests"` for Servlet and Reactive applications.
|
||||
|
||||
[[observability.http-server.servlet]]
|
||||
=== Servlet applications
|
||||
|
||||
Applications need to configure the `org.springframework.web.filter.ServerHttpObservationFilter` Servlet filter in their application.
|
||||
It is using the `org.springframework.http.server.observation.DefaultServerRequestObservationConvention` by default, backed by the `ServerRequestObservationContext`.
|
||||
It uses the `org.springframework.http.server.observation.DefaultServerRequestObservationConvention` by default, backed by the `ServerRequestObservationContext`.
|
||||
|
||||
This will only record an observation as an error if the `Exception` has not been handled by the web Framework and has bubbled up to the Servlet filter.
|
||||
This will only record an observation as an error if the `Exception` has not been handled by the web framework and has bubbled up to the Servlet filter.
|
||||
Typically, all exceptions handled by Spring MVC's `@ExceptionHandler` and xref:web/webmvc/mvc-ann-rest-exceptions.adoc[`ProblemDetail` support] will not be recorded with the observation.
|
||||
You can, at any point during request processing, set the error field on the `ObservationContext` yourself:
|
||||
|
||||
include-code::./UserController[]
|
||||
|
||||
NOTE: Because the instrumentation is done at the Servlet Filter level, the observation scope only covers the filters ordered after this one as well as the handling of the request.
|
||||
Typically, Servlet container error handling is performed at a lower level and won't have any active observation or span.
|
||||
For this use case, a container-specific implementation is required, such as a `org.apache.catalina.Valve` for Tomcat; this is outside of the scope of this project.
|
||||
|
||||
By default, the following `KeyValues` are created:
|
||||
|
||||
.Low cardinality Keys
|
||||
@@ -122,9 +126,9 @@ By default, the following `KeyValues` are created:
|
||||
=== Reactive applications
|
||||
|
||||
Applications need to configure the `org.springframework.web.filter.reactive.ServerHttpObservationFilter` reactive `WebFilter` in their application.
|
||||
It is using the `org.springframework.http.server.reactive.observation.DefaultServerRequestObservationConvention` by default, backed by the `ServerRequestObservationContext`.
|
||||
It uses the `org.springframework.http.server.reactive.observation.DefaultServerRequestObservationConvention` by default, backed by the `ServerRequestObservationContext`.
|
||||
|
||||
This will only record an observation as an error if the `Exception` has not been handled by the web Framework and has bubbled up to the `WebFilter`.
|
||||
This will only record an observation as an error if the `Exception` has not been handled by the web framework and has bubbled up to the `WebFilter`.
|
||||
Typically, all exceptions handled by Spring WebFlux's `@ExceptionHandler` and xref:web/webflux/ann-rest-exceptions.adoc[`ProblemDetail` support] will not be recorded with the observation.
|
||||
You can, at any point during request processing, set the error field on the `ObservationContext` yourself:
|
||||
|
||||
@@ -153,9 +157,9 @@ By default, the following `KeyValues` are created:
|
||||
|
||||
|
||||
[[observability.http-client]]
|
||||
== HTTP Client instrumentation
|
||||
== HTTP Client Instrumentation
|
||||
|
||||
HTTP client exchanges observations are created with the name `"http.client.requests"` for blocking and reactive clients.
|
||||
HTTP client exchange observations are created with the name `"http.client.requests"` for blocking and reactive clients.
|
||||
Unlike their server counterparts, the instrumentation is implemented directly in the client so the only required step is to configure an `ObservationRegistry` on the client.
|
||||
|
||||
[[observability.http-client.resttemplate]]
|
||||
@@ -164,7 +168,7 @@ Unlike their server counterparts, the instrumentation is implemented directly in
|
||||
Applications must configure an `ObservationRegistry` on `RestTemplate` instances to enable the instrumentation; without that, observations are "no-ops".
|
||||
Spring Boot will auto-configure `RestTemplateBuilder` beans with the observation registry already set.
|
||||
|
||||
Instrumentation is using the `org.springframework.http.client.observation.ClientRequestObservationConvention` by default, backed by the `ClientRequestObservationContext`.
|
||||
Instrumentation uses the `org.springframework.http.client.observation.ClientRequestObservationConvention` by default, backed by the `ClientRequestObservationContext`.
|
||||
|
||||
.Low cardinality Keys
|
||||
[cols="a,a"]
|
||||
@@ -193,7 +197,7 @@ Instrumentation is using the `org.springframework.http.client.observation.Client
|
||||
Applications must configure an `ObservationRegistry` on the `WebClient` builder to enable the instrumentation; without that, observations are "no-ops".
|
||||
Spring Boot will auto-configure `WebClient.Builder` beans with the observation registry already set.
|
||||
|
||||
Instrumentation is using the `org.springframework.web.reactive.function.client.ClientRequestObservationConvention` by default, backed by the `ClientRequestObservationContext`.
|
||||
Instrumentation uses the `org.springframework.web.reactive.function.client.ClientRequestObservationConvention` by default, backed by the `ClientRequestObservationContext`.
|
||||
|
||||
.Low cardinality Keys
|
||||
[cols="a,a"]
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ Resource locations are typically XML configuration files or Groovy scripts locat
|
||||
classpath, while component classes are typically `@Configuration` classes. However,
|
||||
resource locations can also refer to files and scripts in the file system, and component
|
||||
classes can be `@Component` classes, `@Service` classes, and so on. See
|
||||
xref:testing/testcontext-framework/ctx-management/javaconfig.adoc#testcontext-ctx-management-javaconfig-component-classes[null] for further details.
|
||||
xref:testing/testcontext-framework/ctx-management/javaconfig.adoc#testcontext-ctx-management-javaconfig-component-classes[Component Classes] for further details.
|
||||
|
||||
The following example shows a `@ContextConfiguration` annotation that refers to an XML
|
||||
file:
|
||||
|
||||
@@ -189,6 +189,9 @@ lets applications use the Servlet API directly if they need to. Spring WebFlux
|
||||
relies on Servlet non-blocking I/O and uses the Servlet API behind a low-level
|
||||
adapter. It is not exposed for direct use.
|
||||
|
||||
NOTE: It is strongly advised not to map Servlet filters or directly manipulate the Servlet API in the context of a WebFlux application.
|
||||
For the reasons listed above, mixing blocking I/O and non-blocking I/O in the same context will cause runtime issues.
|
||||
|
||||
For Undertow, Spring WebFlux uses Undertow APIs directly without the Servlet API.
|
||||
|
||||
|
||||
@@ -197,9 +200,9 @@ For Undertow, Spring WebFlux uses Undertow APIs directly without the Servlet API
|
||||
== Performance
|
||||
|
||||
Performance has many characteristics and meanings. Reactive and non-blocking generally
|
||||
do not make applications run faster. They can, in some cases, (for example, if using the
|
||||
`WebClient` to run remote calls in parallel). On the whole, it requires more work to do
|
||||
things the non-blocking way and that can slightly increase the required processing time.
|
||||
do not make applications run faster. They can in some cases – for example, if using the
|
||||
`WebClient` to run remote calls in parallel. However, it requires more work to do
|
||||
things the non-blocking way, and that can slightly increase the required processing time.
|
||||
|
||||
The key expected benefit of reactive and non-blocking is the ability to scale with a small,
|
||||
fixed number of threads and less memory. That makes applications more resilient under load,
|
||||
@@ -221,10 +224,10 @@ block the current thread, (for example, for remote calls). For this reason, serv
|
||||
use a large thread pool to absorb potential blocking during request handling.
|
||||
|
||||
In Spring WebFlux (and non-blocking servers in general), it is assumed that applications
|
||||
do not block. Therefore, non-blocking servers use a small, fixed-size thread pool
|
||||
do not block. Therefore, non-blocking servers use a small, fixed-size thread pool
|
||||
(event loop workers) to handle requests.
|
||||
|
||||
TIP: "`To scale`" and "`small number of threads`" may sound contradictory but to never block the
|
||||
TIP: "`To scale`" and "`small number of threads`" may sound contradictory, but to never block the
|
||||
current thread (and rely on callbacks instead) means that you do not need extra threads, as
|
||||
there are no blocking calls to absorb.
|
||||
|
||||
@@ -250,7 +253,7 @@ application code within that pipeline is never invoked concurrently.
|
||||
|
||||
What threads should you expect to see on a server running with Spring WebFlux?
|
||||
|
||||
* On a "`vanilla`" Spring WebFlux server (for example, no data access nor other optional
|
||||
* On a "`vanilla`" Spring WebFlux server (for example, no data access or other optional
|
||||
dependencies), you can expect one thread for the server and several others for request
|
||||
processing (typically as many as the number of CPU cores). Servlet containers, however,
|
||||
may start with more threads (for example, 10 on Tomcat), in support of both servlet (blocking) I/O
|
||||
|
||||
@@ -69,9 +69,10 @@ written to the response and computing an MD5 hash from it. The next time a clien
|
||||
it does the same, but it also compares the computed value against the `If-None-Match`
|
||||
request header and, if the two are equal, returns a 304 (NOT_MODIFIED).
|
||||
|
||||
This strategy saves network bandwidth but not CPU, as the full response must be computed
|
||||
for each request. Other strategies at the controller level, described earlier, can avoid
|
||||
the computation. See xref:web/webmvc/mvc-caching.adoc[HTTP Caching].
|
||||
This strategy saves network bandwidth but not CPU, as the full response must be computed for each request.
|
||||
State-changing HTTP methods and other HTTP conditional request headers such as `If-Match` and `If-Unmodified-Since` are outside the scope of this filter.
|
||||
Other strategies at the controller level can avoid the computation and have a broader support for HTTP conditional requests.
|
||||
See xref:web/webmvc/mvc-caching.adoc[HTTP Caching].
|
||||
|
||||
This filter has a `writeWeakETag` parameter that configures the filter to write weak ETags
|
||||
similar to the following: `W/"02a2d595e6ed9a0b24f027f2b63b134d6"` (as defined in
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
STOMP over WebSocket support is available in the `spring-messaging` and
|
||||
`spring-websocket` modules. Once you have those dependencies, you can expose a STOMP
|
||||
endpoint over WebSocket with xref:web/websocket/fallback.adoc[SockJS Fallback], as the following example shows:
|
||||
endpoint over WebSocket, as the following example shows:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@@ -16,7 +16,7 @@ endpoint over WebSocket with xref:web/websocket/fallback.adoc[SockJS Fallback],
|
||||
|
||||
@Override
|
||||
public void registerStompEndpoints(StompEndpointRegistry registry) {
|
||||
registry.addEndpoint("/portfolio").withSockJS(); // <1>
|
||||
registry.addEndpoint("/portfolio"); // <1>
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -49,9 +49,7 @@ The following example shows the XML configuration equivalent of the preceding ex
|
||||
https://www.springframework.org/schema/websocket/spring-websocket.xsd">
|
||||
|
||||
<websocket:message-broker application-destination-prefix="/app">
|
||||
<websocket:stomp-endpoint path="/portfolio">
|
||||
<websocket:sockjs/>
|
||||
</websocket:stomp-endpoint>
|
||||
<websocket:stomp-endpoint path="/portfolio" />
|
||||
<websocket:simple-broker prefix="/topic, /queue"/>
|
||||
</websocket:message-broker>
|
||||
|
||||
@@ -64,34 +62,27 @@ messaging (that is, many subscribers versus one consumer). When you use an exter
|
||||
check the STOMP page of the broker to understand what kind of STOMP destinations and
|
||||
prefixes it supports.
|
||||
|
||||
To connect from a browser, for SockJS, you can use the
|
||||
https://github.com/sockjs/sockjs-client[`sockjs-client`]. For STOMP, many applications have
|
||||
used the https://github.com/jmesnil/stomp-websocket[jmesnil/stomp-websocket] library
|
||||
(also known as stomp.js), which is feature-complete and has been used in production for
|
||||
years but is no longer maintained. At present the
|
||||
https://github.com/JSteunou/webstomp-client[JSteunou/webstomp-client] is the most
|
||||
actively maintained and evolving successor of that library. The following example code
|
||||
is based on it:
|
||||
To connect from a browser, for STOMP, you can use
|
||||
https://github.com/stomp-js/stompjs[`stomp-js/stompjs`] which is the most
|
||||
actively maintained JavaScript library.
|
||||
|
||||
The following example code is based on it:
|
||||
|
||||
[source,javascript,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
var socket = new SockJS("/spring-websocket-portfolio/portfolio");
|
||||
var stompClient = webstomp.over(socket);
|
||||
|
||||
stompClient.connect({}, function(frame) {
|
||||
}
|
||||
const stompClient = new StompJs.Client({
|
||||
brokerURL: 'ws://domain.com/portfolio',
|
||||
onConnect: () => {
|
||||
// ...
|
||||
}
|
||||
});
|
||||
----
|
||||
|
||||
Alternatively, if you connect through WebSocket (without SockJS), you can use the following code:
|
||||
|
||||
[source,javascript,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
var socket = new WebSocket("/spring-websocket-portfolio/portfolio");
|
||||
var stompClient = Stomp.over(socket);
|
||||
|
||||
stompClient.connect({}, function(frame) {
|
||||
}
|
||||
----
|
||||
Alternatively, if you connect through SockJS, you can enable the
|
||||
xref:web/websocket/fallback.adoc[SockJS Fallback] on server-side with
|
||||
`registry.addEndpoint("/portfolio").withSockJS()` and on JavaScript side,
|
||||
by following
|
||||
https://stomp-js.github.io/guide/stompjs/rx-stomp/using-stomp-with-sockjs.html[those instructions].
|
||||
|
||||
Note that `stompClient` in the preceding example does not need to specify `login`
|
||||
and `passcode` headers. Even if it did, they would be ignored (or, rather,
|
||||
|
||||
@@ -8,18 +8,18 @@ javaPlatform {
|
||||
|
||||
dependencies {
|
||||
api(platform("com.fasterxml.jackson:jackson-bom:2.14.3"))
|
||||
api(platform("io.micrometer:micrometer-bom:1.10.8"))
|
||||
api(platform("io.netty:netty-bom:4.1.93.Final"))
|
||||
api(platform("io.micrometer:micrometer-bom:1.10.9"))
|
||||
api(platform("io.netty:netty-bom:4.1.94.Final"))
|
||||
api(platform("io.netty:netty5-bom:5.0.0.Alpha5"))
|
||||
api(platform("io.projectreactor:reactor-bom:2022.0.8"))
|
||||
api(platform("io.projectreactor:reactor-bom:2022.0.9"))
|
||||
api(platform("io.rsocket:rsocket-bom:1.1.3"))
|
||||
api(platform("org.apache.groovy:groovy-bom:4.0.12"))
|
||||
api(platform("org.apache.groovy:groovy-bom:4.0.13"))
|
||||
api(platform("org.apache.logging.log4j:log4j-bom:2.20.0"))
|
||||
api(platform("org.eclipse.jetty:jetty-bom:11.0.15"))
|
||||
api(platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.6.4"))
|
||||
api(platform("org.jetbrains.kotlinx:kotlinx-serialization-bom:1.4.0"))
|
||||
api(platform("org.junit:junit-bom:5.9.3"))
|
||||
api(platform("org.mockito:mockito-bom:5.3.1"))
|
||||
api(platform("org.mockito:mockito-bom:5.4.0"))
|
||||
|
||||
constraints {
|
||||
api("com.fasterxml:aalto-xml:1.3.2")
|
||||
@@ -46,7 +46,7 @@ dependencies {
|
||||
api("commons-io:commons-io:2.11.0")
|
||||
api("de.bechte.junit:junit-hierarchicalcontextrunner:4.12.2")
|
||||
api("info.picocli:picocli:4.7.4")
|
||||
api("io.micrometer:context-propagation:1.0.3")
|
||||
api("io.micrometer:context-propagation:1.0.4")
|
||||
api("io.mockk:mockk:1.13.4")
|
||||
api("io.projectreactor.netty:reactor-netty5-http:2.0.0-M3")
|
||||
api("io.projectreactor.tools:blockhound:1.0.8.RELEASE")
|
||||
@@ -82,6 +82,7 @@ dependencies {
|
||||
api("jakarta.websocket:jakarta.websocket-api:2.1.0")
|
||||
api("jakarta.websocket:jakarta.websocket-client-api:2.1.0")
|
||||
api("jakarta.xml.bind:jakarta.xml.bind-api:3.0.1")
|
||||
api("javax.annotation:javax.annotation-api:1.3.2")
|
||||
api("javax.cache:cache-api:1.1.1")
|
||||
api("javax.money:money-api:1.1")
|
||||
api("jaxen:jaxen:1.2.0")
|
||||
@@ -99,10 +100,10 @@ dependencies {
|
||||
api("org.apache.httpcomponents.client5:httpclient5:5.2.1")
|
||||
api("org.apache.httpcomponents.core5:httpcore5-reactive:5.2.1")
|
||||
api("org.apache.poi:poi-ooxml:5.2.3")
|
||||
api("org.apache.tomcat.embed:tomcat-embed-core:10.1.9")
|
||||
api("org.apache.tomcat.embed:tomcat-embed-websocket:10.1.9")
|
||||
api("org.apache.tomcat:tomcat-util:10.1.8")
|
||||
api("org.apache.tomcat:tomcat-websocket:10.1.8")
|
||||
api("org.apache.tomcat.embed:tomcat-embed-core:10.1.10")
|
||||
api("org.apache.tomcat.embed:tomcat-embed-websocket:10.1.10")
|
||||
api("org.apache.tomcat:tomcat-util:10.1.10")
|
||||
api("org.apache.tomcat:tomcat-websocket:10.1.10")
|
||||
api("org.aspectj:aspectjrt:1.9.19")
|
||||
api("org.aspectj:aspectjtools:1.9.19")
|
||||
api("org.aspectj:aspectjweaver:1.9.19")
|
||||
@@ -126,7 +127,7 @@ dependencies {
|
||||
api("org.hamcrest:hamcrest:2.2")
|
||||
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.1")
|
||||
api("org.hsqldb:hsqldb:2.7.2")
|
||||
api("org.javamoney:moneta:1.4.2")
|
||||
api("org.jruby:jruby:9.4.3.0")
|
||||
api("org.junit.support:testng-engine:1.0.4")
|
||||
@@ -140,7 +141,7 @@ dependencies {
|
||||
api("org.slf4j:slf4j-api:2.0.7")
|
||||
api("org.testng:testng:7.8.0")
|
||||
api("org.webjars:underscorejs:1.8.3")
|
||||
api("org.webjars:webjars-locator-core:0.52")
|
||||
api("org.webjars:webjars-locator-core:0.53")
|
||||
api("org.xmlunit:xmlunit-assertj:2.9.1")
|
||||
api("org.xmlunit:xmlunit-matchers:2.9.1")
|
||||
api("org.yaml:snakeyaml:1.33")
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
version=6.0.10-SNAPSHOT
|
||||
version=6.0.11
|
||||
|
||||
org.gradle.caching=true
|
||||
org.gradle.jvmargs=-Xmx2048m
|
||||
|
||||
Vendored
BIN
Binary file not shown.
+2
-1
@@ -1,6 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
@@ -130,10 +130,13 @@ location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
|
||||
+8
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,11 +21,15 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.assertj.core.api.InstanceOfAssertFactories;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.expression.MethodExecutor;
|
||||
import org.springframework.expression.TypedValue;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class Spr7538Tests {
|
||||
|
||||
@@ -49,8 +53,9 @@ class Spr7538Tests {
|
||||
ReflectiveMethodResolver resolver = new ReflectiveMethodResolver();
|
||||
MethodExecutor executor = resolver.resolve(context, target, "checkCompleteness", argumentTypes);
|
||||
|
||||
Object result = executor.execute(context, target, arguments);
|
||||
System.out.println("Result: " + result);
|
||||
TypedValue typedValue = executor.execute(context, target, arguments);
|
||||
assertThat(typedValue.getValue()).asInstanceOf(InstanceOfAssertFactories.BOOLEAN).isTrue();
|
||||
assertThat(typedValue.getTypeDescriptor().getType()).isEqualTo(Boolean.class);
|
||||
}
|
||||
|
||||
static class AlwaysTrueReleaseStrategy {
|
||||
|
||||
@@ -10,4 +10,5 @@ dependencies {
|
||||
testImplementation(testFixtures(project(":spring-core")))
|
||||
testFixturesImplementation(testFixtures(project(":spring-beans")))
|
||||
testFixturesImplementation(testFixtures(project(":spring-core")))
|
||||
testFixturesImplementation("com.google.code.findbugs:jsr305")
|
||||
}
|
||||
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.aop.aspectj.annotation;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.aop.Advisor;
|
||||
import org.springframework.aop.aspectj.AbstractAspectJAdvice;
|
||||
import org.springframework.aot.generate.GenerationContext;
|
||||
import org.springframework.aot.hint.ExecutableMode;
|
||||
import org.springframework.aot.hint.ReflectionHints;
|
||||
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution;
|
||||
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor;
|
||||
import org.springframework.beans.factory.aot.BeanFactoryInitializationCode;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* {@link BeanFactoryInitializationAotProcessor} implementation responsible for registering
|
||||
* hints for AOP advices.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Stephane Nicoll
|
||||
* @since 6.0.11
|
||||
*/
|
||||
class AspectJBeanFactoryInitializationAotProcessor implements BeanFactoryInitializationAotProcessor {
|
||||
|
||||
private static final boolean aspectJPresent = ClassUtils.isPresent(
|
||||
"org.aspectj.lang.annotation.Pointcut", AspectJBeanFactoryInitializationAotProcessor.class.getClassLoader());
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) {
|
||||
if (aspectJPresent) {
|
||||
return AspectDelegate.processAheadOfTime(beanFactory);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inner class to avoid a hard dependency on AspectJ at runtime.
|
||||
*/
|
||||
private static class AspectDelegate {
|
||||
|
||||
@Nullable
|
||||
private static AspectContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) {
|
||||
BeanFactoryAspectJAdvisorsBuilder builder = new BeanFactoryAspectJAdvisorsBuilder(beanFactory);
|
||||
List<Advisor> advisors = builder.buildAspectJAdvisors();
|
||||
return advisors.isEmpty() ? null : new AspectContribution(advisors);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static class AspectContribution implements BeanFactoryInitializationAotContribution {
|
||||
|
||||
private final List<Advisor> advisors;
|
||||
|
||||
public AspectContribution(List<Advisor> advisors) {
|
||||
this.advisors = advisors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyTo(GenerationContext generationContext, BeanFactoryInitializationCode beanFactoryInitializationCode) {
|
||||
ReflectionHints reflectionHints = generationContext.getRuntimeHints().reflection();
|
||||
for (Advisor advisor : this.advisors) {
|
||||
if (advisor.getAdvice() instanceof AbstractAspectJAdvice aspectJAdvice) {
|
||||
reflectionHints.registerMethod(aspectJAdvice.getAspectJAdviceMethod(), ExecutableMode.INVOKE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,2 +1,5 @@
|
||||
org.springframework.beans.factory.aot.BeanRegistrationAotProcessor=\
|
||||
org.springframework.aop.scope.ScopedProxyBeanRegistrationAotProcessor
|
||||
|
||||
org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor= \
|
||||
org.springframework.aop.aspectj.annotation.AspectJBeanFactoryInitializationAotProcessor
|
||||
|
||||
+8
-5
@@ -637,17 +637,20 @@ abstract class AbstractAspectJAdvisorFactoryTests {
|
||||
}
|
||||
|
||||
|
||||
static class CommonPointcuts {
|
||||
|
||||
@Pointcut("execution(* getAge())")
|
||||
void getAge() {
|
||||
}
|
||||
}
|
||||
|
||||
@Aspect
|
||||
static class NamedPointcutAspectWithFQN {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private ITestBean fieldThatShouldBeIgnoredBySpringAtAspectJProcessing = new TestBean();
|
||||
|
||||
@Pointcut("execution(* getAge())")
|
||||
void getAge() {
|
||||
}
|
||||
|
||||
@Around("org.springframework.aop.aspectj.annotation.AbstractAspectJAdvisorFactoryTests.NamedPointcutAspectWithFQN.getAge()")
|
||||
@Around("org.springframework.aop.aspectj.annotation.AbstractAspectJAdvisorFactoryTests.CommonPointcuts.getAge()()")
|
||||
int changeReturnValue(ProceedingJoinPoint pjp) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.aop.aspectj.annotation;
|
||||
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aot.generate.GenerationContext;
|
||||
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
|
||||
import org.springframework.aot.test.generate.TestGenerationContext;
|
||||
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link AspectJBeanFactoryInitializationAotProcessor}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
class AspectJBeanFactoryInitializationAotProcessorTests {
|
||||
|
||||
private final GenerationContext generationContext = new TestGenerationContext();
|
||||
|
||||
@Test
|
||||
void shouldSkipEmptyClass() {
|
||||
assertThat(createContribution(EmptyClass.class)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldProcessAspect() {
|
||||
process(TestAspect.class);
|
||||
assertThat(RuntimeHintsPredicates.reflection().onMethod(TestAspect.class, "alterReturnValue").invoke())
|
||||
.accepts(this.generationContext.getRuntimeHints());
|
||||
}
|
||||
|
||||
private void process(Class<?> beanClass) {
|
||||
BeanFactoryInitializationAotContribution contribution = createContribution(beanClass);
|
||||
if (contribution != null) {
|
||||
contribution.applyTo(this.generationContext, mock());
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static BeanFactoryInitializationAotContribution createContribution(Class<?> beanClass) {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerBeanDefinition(beanClass.getName(), new RootBeanDefinition(beanClass));
|
||||
return new AspectJBeanFactoryInitializationAotProcessor().processAheadOfTime(beanFactory);
|
||||
}
|
||||
|
||||
|
||||
static class EmptyClass { }
|
||||
|
||||
@Aspect
|
||||
static class TestAspect {
|
||||
|
||||
@Around("pointcut()")
|
||||
public Object alterReturnValue(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||
joinPoint.proceed();
|
||||
return "A-from-aspect";
|
||||
}
|
||||
|
||||
@Pointcut("execution(* com.example.aspect.Test*.methodA(..))")
|
||||
private void pointcut() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+13
-10
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -19,9 +19,9 @@ package org.springframework.aop.config;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.springframework.core.testfixture.io.ResourceTestUtils.qualifiedResource;
|
||||
@@ -29,25 +29,28 @@ import static org.springframework.core.testfixture.io.ResourceTestUtils.qualifie
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
* @author Chris Beams
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
class AopNamespaceHandlerPointcutErrorTests {
|
||||
|
||||
@Test
|
||||
void duplicatePointcutConfig() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
assertThatExceptionOfType(BeanDefinitionStoreException.class).isThrownBy(() ->
|
||||
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(
|
||||
qualifiedResource(getClass(), "pointcutDuplication.xml")))
|
||||
.satisfies(ex -> ex.contains(BeanDefinitionParsingException.class));
|
||||
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(bf);
|
||||
ClassPathResource resource = qualifiedResource(getClass(), "pointcutDuplication.xml");
|
||||
|
||||
assertThatExceptionOfType(BeanDefinitionStoreException.class)
|
||||
.isThrownBy(() -> reader.loadBeanDefinitions(resource));
|
||||
}
|
||||
|
||||
@Test
|
||||
void missingPointcutConfig() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
assertThatExceptionOfType(BeanDefinitionStoreException.class).isThrownBy(() ->
|
||||
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(
|
||||
qualifiedResource(getClass(), "pointcutMissing.xml")))
|
||||
.satisfies(ex -> ex.contains(BeanDefinitionParsingException.class));
|
||||
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(bf);
|
||||
ClassPathResource resource = qualifiedResource(getClass(), "pointcutMissing.xml");
|
||||
|
||||
assertThatExceptionOfType(BeanDefinitionStoreException.class)
|
||||
.isThrownBy(() -> reader.loadBeanDefinitions(resource));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+4
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -82,6 +82,8 @@ public class IntroductionBenchmarkTests {
|
||||
}
|
||||
sw.stop();
|
||||
|
||||
System.out.println(sw.prettyPrint());
|
||||
// TODO Add reasonable assertions.
|
||||
// System.out.println(sw.prettyPrint());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,4 +14,5 @@ dependencies {
|
||||
testImplementation("jakarta.annotation:jakarta.annotation-api")
|
||||
testFixturesApi("org.junit.jupiter:junit-jupiter-api")
|
||||
testFixturesImplementation("org.assertj:assertj-core")
|
||||
testFixturesImplementation("com.google.code.findbugs:jsr305")
|
||||
}
|
||||
+24
-17
@@ -287,7 +287,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
RootBeanDefinition beanDefinition = registeredBean.getMergedBeanDefinition();
|
||||
InjectionMetadata metadata = findInjectionMetadata(beanName, beanClass, beanDefinition);
|
||||
Collection<AutowiredElement> autowiredElements = getAutowiredElements(metadata,
|
||||
registeredBean.getMergedBeanDefinition().getPropertyValues());
|
||||
beanDefinition.getPropertyValues());
|
||||
if (!ObjectUtils.isEmpty(autowiredElements)) {
|
||||
return new AotContribution(beanClass, autowiredElements, getAutowireCandidateResolver());
|
||||
}
|
||||
@@ -638,7 +638,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
* Resolve the specified cached method argument or field value.
|
||||
*/
|
||||
@Nullable
|
||||
private Object resolvedCachedArgument(@Nullable String beanName, @Nullable Object cachedArgument) {
|
||||
private Object resolveCachedArgument(@Nullable String beanName, @Nullable Object cachedArgument) {
|
||||
if (cachedArgument instanceof DependencyDescriptor descriptor) {
|
||||
Assert.state(this.beanFactory != null, "No BeanFactory available");
|
||||
return this.beanFactory.resolveDependency(descriptor, beanName, null, null);
|
||||
@@ -683,10 +683,12 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
Object value;
|
||||
if (this.cached) {
|
||||
try {
|
||||
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
|
||||
value = resolveCachedArgument(beanName, this.cachedFieldValue);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
// Unexpected removal of target bean for cached argument -> re-resolve
|
||||
catch (BeansException ex) {
|
||||
// Unexpected target bean mismatch for cached argument -> re-resolve
|
||||
this.cached = false;
|
||||
logger.debug("Failed to resolve cached argument", ex);
|
||||
value = resolveFieldValue(field, bean, beanName);
|
||||
}
|
||||
}
|
||||
@@ -715,9 +717,8 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
}
|
||||
synchronized (this) {
|
||||
if (!this.cached) {
|
||||
Object cachedFieldValue = null;
|
||||
if (value != null || this.required) {
|
||||
cachedFieldValue = desc;
|
||||
Object cachedFieldValue = desc;
|
||||
registerDependentBeans(beanName, autowiredBeanNames);
|
||||
if (value != null && autowiredBeanNames.size() == 1) {
|
||||
String autowiredBeanName = autowiredBeanNames.iterator().next();
|
||||
@@ -727,9 +728,13 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
desc, autowiredBeanName, field.getType());
|
||||
}
|
||||
}
|
||||
this.cachedFieldValue = cachedFieldValue;
|
||||
this.cached = true;
|
||||
}
|
||||
else {
|
||||
this.cachedFieldValue = null;
|
||||
// cached flag remains false
|
||||
}
|
||||
this.cachedFieldValue = cachedFieldValue;
|
||||
this.cached = true;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
@@ -760,10 +765,12 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
Object[] arguments;
|
||||
if (this.cached) {
|
||||
try {
|
||||
arguments = resolveCachedArguments(beanName);
|
||||
arguments = resolveCachedArguments(beanName, this.cachedMethodArguments);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
// Unexpected removal of target bean for cached argument -> re-resolve
|
||||
catch (BeansException ex) {
|
||||
// Unexpected target bean mismatch for cached argument -> re-resolve
|
||||
this.cached = false;
|
||||
logger.debug("Failed to resolve cached argument", ex);
|
||||
arguments = resolveMethodArguments(method, bean, beanName);
|
||||
}
|
||||
}
|
||||
@@ -782,14 +789,13 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Object[] resolveCachedArguments(@Nullable String beanName) {
|
||||
Object[] cachedMethodArguments = this.cachedMethodArguments;
|
||||
private Object[] resolveCachedArguments(@Nullable String beanName, @Nullable Object[] cachedMethodArguments) {
|
||||
if (cachedMethodArguments == null) {
|
||||
return null;
|
||||
}
|
||||
Object[] arguments = new Object[cachedMethodArguments.length];
|
||||
for (int i = 0; i < arguments.length; i++) {
|
||||
arguments[i] = resolvedCachedArgument(beanName, cachedMethodArguments[i]);
|
||||
arguments[i] = resolveCachedArgument(beanName, cachedMethodArguments[i]);
|
||||
}
|
||||
return arguments;
|
||||
}
|
||||
@@ -822,7 +828,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
synchronized (this) {
|
||||
if (!this.cached) {
|
||||
if (arguments != null) {
|
||||
DependencyDescriptor[] cachedMethodArguments = Arrays.copyOf(descriptors, arguments.length);
|
||||
DependencyDescriptor[] cachedMethodArguments = Arrays.copyOf(descriptors, argumentCount);
|
||||
registerDependentBeans(beanName, autowiredBeans);
|
||||
if (autowiredBeans.size() == argumentCount) {
|
||||
Iterator<String> it = autowiredBeans.iterator();
|
||||
@@ -837,11 +843,12 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
}
|
||||
}
|
||||
this.cachedMethodArguments = cachedMethodArguments;
|
||||
this.cached = true;
|
||||
}
|
||||
else {
|
||||
this.cachedMethodArguments = null;
|
||||
// cached flag remains false
|
||||
}
|
||||
this.cached = true;
|
||||
}
|
||||
}
|
||||
return arguments;
|
||||
|
||||
+133
-89
@@ -24,7 +24,6 @@ import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
@@ -79,6 +78,7 @@ import org.springframework.util.ReflectionUtils;
|
||||
* @author Juergen Hoeller
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
* @author Sam Brannen
|
||||
* @since 2.5
|
||||
* @see #setInitAnnotationType
|
||||
* @see #setDestroyAnnotationType
|
||||
@@ -90,7 +90,7 @@ public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareB
|
||||
private final transient LifecycleMetadata emptyLifecycleMetadata =
|
||||
new LifecycleMetadata(Object.class, Collections.emptyList(), Collections.emptyList()) {
|
||||
@Override
|
||||
public void checkConfigMembers(RootBeanDefinition beanDefinition) {
|
||||
public void checkInitDestroyMethods(RootBeanDefinition beanDefinition) {
|
||||
}
|
||||
@Override
|
||||
public void invokeInitMethods(Object target, String beanName) {
|
||||
@@ -107,11 +107,9 @@ public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareB
|
||||
|
||||
protected transient Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
@Nullable
|
||||
private Class<? extends Annotation> initAnnotationType;
|
||||
private final Set<Class<? extends Annotation>> initAnnotationTypes = new LinkedHashSet<>(2);
|
||||
|
||||
@Nullable
|
||||
private Class<? extends Annotation> destroyAnnotationType;
|
||||
private final Set<Class<? extends Annotation>> destroyAnnotationTypes = new LinkedHashSet<>(2);
|
||||
|
||||
private int order = Ordered.LOWEST_PRECEDENCE;
|
||||
|
||||
@@ -125,9 +123,23 @@ public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareB
|
||||
* <p>Any custom annotation can be used, since there are no required
|
||||
* annotation attributes. There is no default, although a typical choice
|
||||
* is the {@link jakarta.annotation.PostConstruct} annotation.
|
||||
* @see #addInitAnnotationType
|
||||
*/
|
||||
public void setInitAnnotationType(Class<? extends Annotation> initAnnotationType) {
|
||||
this.initAnnotationType = initAnnotationType;
|
||||
this.initAnnotationTypes.clear();
|
||||
this.initAnnotationTypes.add(initAnnotationType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an init annotation to check for, indicating initialization
|
||||
* methods to call after configuration of a bean.
|
||||
* @since 6.0.11
|
||||
* @see #setInitAnnotationType
|
||||
*/
|
||||
public void addInitAnnotationType(@Nullable Class<? extends Annotation> initAnnotationType) {
|
||||
if (initAnnotationType != null) {
|
||||
this.initAnnotationTypes.add(initAnnotationType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,9 +148,23 @@ public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareB
|
||||
* <p>Any custom annotation can be used, since there are no required
|
||||
* annotation attributes. There is no default, although a typical choice
|
||||
* is the {@link jakarta.annotation.PreDestroy} annotation.
|
||||
* @see #addDestroyAnnotationType
|
||||
*/
|
||||
public void setDestroyAnnotationType(Class<? extends Annotation> destroyAnnotationType) {
|
||||
this.destroyAnnotationType = destroyAnnotationType;
|
||||
this.destroyAnnotationTypes.clear();
|
||||
this.destroyAnnotationTypes.add(destroyAnnotationType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a destroy annotation to check for, indicating destruction
|
||||
* methods to call when the context is shutting down.
|
||||
* @since 6.0.11
|
||||
* @see #setDestroyAnnotationType
|
||||
*/
|
||||
public void addDestroyAnnotationType(@Nullable Class<? extends Annotation> destroyAnnotationType) {
|
||||
if (destroyAnnotationType != null) {
|
||||
this.destroyAnnotationTypes.add(destroyAnnotationType);
|
||||
}
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
@@ -152,8 +178,8 @@ public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareB
|
||||
|
||||
|
||||
@Override
|
||||
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
|
||||
findInjectionMetadata(beanDefinition, beanType);
|
||||
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanClass, String beanName) {
|
||||
findLifecycleMetadata(beanDefinition, beanClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -161,7 +187,7 @@ public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareB
|
||||
public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) {
|
||||
RootBeanDefinition beanDefinition = registeredBean.getMergedBeanDefinition();
|
||||
beanDefinition.resolveDestroyMethodIfNecessary();
|
||||
LifecycleMetadata metadata = findInjectionMetadata(beanDefinition, registeredBean.getBeanClass());
|
||||
LifecycleMetadata metadata = findLifecycleMetadata(beanDefinition, registeredBean.getBeanClass());
|
||||
if (!CollectionUtils.isEmpty(metadata.initMethods)) {
|
||||
String[] initMethodNames = safeMerge(beanDefinition.getInitMethodNames(), metadata.initMethods);
|
||||
beanDefinition.setInitMethodNames(initMethodNames);
|
||||
@@ -173,16 +199,16 @@ public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareB
|
||||
return null;
|
||||
}
|
||||
|
||||
private LifecycleMetadata findInjectionMetadata(RootBeanDefinition beanDefinition, Class<?> beanType) {
|
||||
LifecycleMetadata metadata = findLifecycleMetadata(beanType);
|
||||
metadata.checkConfigMembers(beanDefinition);
|
||||
private LifecycleMetadata findLifecycleMetadata(RootBeanDefinition beanDefinition, Class<?> beanClass) {
|
||||
LifecycleMetadata metadata = findLifecycleMetadata(beanClass);
|
||||
metadata.checkInitDestroyMethods(beanDefinition);
|
||||
return metadata;
|
||||
}
|
||||
|
||||
private String[] safeMerge(@Nullable String[] existingNames, Collection<LifecycleElement> detectedElements) {
|
||||
Stream<String> detectedNames = detectedElements.stream().map(LifecycleElement::getIdentifier);
|
||||
private static String[] safeMerge(@Nullable String[] existingNames, Collection<LifecycleMethod> detectedMethods) {
|
||||
Stream<String> detectedNames = detectedMethods.stream().map(LifecycleMethod::getIdentifier);
|
||||
Stream<String> mergedNames = (existingNames != null ?
|
||||
Stream.concat(Stream.of(existingNames), detectedNames) : detectedNames);
|
||||
Stream.concat(detectedNames, Stream.of(existingNames)) : detectedNames);
|
||||
return mergedNames.distinct().toArray(String[]::new);
|
||||
}
|
||||
|
||||
@@ -217,12 +243,14 @@ public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareB
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.warn(msg, ex.getTargetException());
|
||||
}
|
||||
else {
|
||||
else if (logger.isWarnEnabled()) {
|
||||
logger.warn(msg + ": " + ex.getTargetException());
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
logger.warn("Failed to invoke destroy method on bean with name '" + beanName + "'", ex);
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Failed to invoke destroy method on bean with name '" + beanName + "'", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,19 +260,19 @@ public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareB
|
||||
}
|
||||
|
||||
|
||||
private LifecycleMetadata findLifecycleMetadata(Class<?> clazz) {
|
||||
private LifecycleMetadata findLifecycleMetadata(Class<?> beanClass) {
|
||||
if (this.lifecycleMetadataCache == null) {
|
||||
// Happens after deserialization, during destruction...
|
||||
return buildLifecycleMetadata(clazz);
|
||||
return buildLifecycleMetadata(beanClass);
|
||||
}
|
||||
// Quick check on the concurrent map first, with minimal locking.
|
||||
LifecycleMetadata metadata = this.lifecycleMetadataCache.get(clazz);
|
||||
LifecycleMetadata metadata = this.lifecycleMetadataCache.get(beanClass);
|
||||
if (metadata == null) {
|
||||
synchronized (this.lifecycleMetadataCache) {
|
||||
metadata = this.lifecycleMetadataCache.get(clazz);
|
||||
metadata = this.lifecycleMetadataCache.get(beanClass);
|
||||
if (metadata == null) {
|
||||
metadata = buildLifecycleMetadata(clazz);
|
||||
this.lifecycleMetadataCache.put(clazz, metadata);
|
||||
metadata = buildLifecycleMetadata(beanClass);
|
||||
this.lifecycleMetadataCache.put(beanClass, metadata);
|
||||
}
|
||||
return metadata;
|
||||
}
|
||||
@@ -252,43 +280,47 @@ public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareB
|
||||
return metadata;
|
||||
}
|
||||
|
||||
private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) {
|
||||
if (!AnnotationUtils.isCandidateClass(clazz, Arrays.asList(this.initAnnotationType, this.destroyAnnotationType))) {
|
||||
private LifecycleMetadata buildLifecycleMetadata(final Class<?> beanClass) {
|
||||
if (!AnnotationUtils.isCandidateClass(beanClass, this.initAnnotationTypes) &&
|
||||
!AnnotationUtils.isCandidateClass(beanClass, this.destroyAnnotationTypes)) {
|
||||
return this.emptyLifecycleMetadata;
|
||||
}
|
||||
|
||||
List<LifecycleElement> initMethods = new ArrayList<>();
|
||||
List<LifecycleElement> destroyMethods = new ArrayList<>();
|
||||
Class<?> targetClass = clazz;
|
||||
List<LifecycleMethod> initMethods = new ArrayList<>();
|
||||
List<LifecycleMethod> destroyMethods = new ArrayList<>();
|
||||
Class<?> currentClass = beanClass;
|
||||
|
||||
do {
|
||||
final List<LifecycleElement> currInitMethods = new ArrayList<>();
|
||||
final List<LifecycleElement> currDestroyMethods = new ArrayList<>();
|
||||
final List<LifecycleMethod> currInitMethods = new ArrayList<>();
|
||||
final List<LifecycleMethod> currDestroyMethods = new ArrayList<>();
|
||||
|
||||
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
|
||||
if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {
|
||||
LifecycleElement element = new LifecycleElement(method);
|
||||
currInitMethods.add(element);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Found init method on class [" + clazz.getName() + "]: " + method);
|
||||
ReflectionUtils.doWithLocalMethods(currentClass, method -> {
|
||||
for (Class<? extends Annotation> initAnnotationType : this.initAnnotationTypes) {
|
||||
if (initAnnotationType != null && method.isAnnotationPresent(initAnnotationType)) {
|
||||
currInitMethods.add(new LifecycleMethod(method, beanClass));
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Found init method on class [" + beanClass.getName() + "]: " + method);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) {
|
||||
currDestroyMethods.add(new LifecycleElement(method));
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Found destroy method on class [" + clazz.getName() + "]: " + method);
|
||||
for (Class<? extends Annotation> destroyAnnotationType : this.destroyAnnotationTypes) {
|
||||
if (destroyAnnotationType != null && method.isAnnotationPresent(destroyAnnotationType)) {
|
||||
currDestroyMethods.add(new LifecycleMethod(method, beanClass));
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Found destroy method on class [" + beanClass.getName() + "]: " + method);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
initMethods.addAll(0, currInitMethods);
|
||||
destroyMethods.addAll(currDestroyMethods);
|
||||
targetClass = targetClass.getSuperclass();
|
||||
currentClass = currentClass.getSuperclass();
|
||||
}
|
||||
while (targetClass != null && targetClass != Object.class);
|
||||
while (currentClass != null && currentClass != Object.class);
|
||||
|
||||
return (initMethods.isEmpty() && destroyMethods.isEmpty() ? this.emptyLifecycleMetadata :
|
||||
new LifecycleMetadata(clazz, initMethods, destroyMethods));
|
||||
new LifecycleMetadata(beanClass, initMethods, destroyMethods));
|
||||
}
|
||||
|
||||
|
||||
@@ -310,46 +342,46 @@ public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareB
|
||||
*/
|
||||
private class LifecycleMetadata {
|
||||
|
||||
private final Class<?> targetClass;
|
||||
private final Class<?> beanClass;
|
||||
|
||||
private final Collection<LifecycleElement> initMethods;
|
||||
private final Collection<LifecycleMethod> initMethods;
|
||||
|
||||
private final Collection<LifecycleElement> destroyMethods;
|
||||
private final Collection<LifecycleMethod> destroyMethods;
|
||||
|
||||
@Nullable
|
||||
private volatile Set<LifecycleElement> checkedInitMethods;
|
||||
private volatile Set<LifecycleMethod> checkedInitMethods;
|
||||
|
||||
@Nullable
|
||||
private volatile Set<LifecycleElement> checkedDestroyMethods;
|
||||
private volatile Set<LifecycleMethod> checkedDestroyMethods;
|
||||
|
||||
public LifecycleMetadata(Class<?> targetClass, Collection<LifecycleElement> initMethods,
|
||||
Collection<LifecycleElement> destroyMethods) {
|
||||
public LifecycleMetadata(Class<?> beanClass, Collection<LifecycleMethod> initMethods,
|
||||
Collection<LifecycleMethod> destroyMethods) {
|
||||
|
||||
this.targetClass = targetClass;
|
||||
this.beanClass = beanClass;
|
||||
this.initMethods = initMethods;
|
||||
this.destroyMethods = destroyMethods;
|
||||
}
|
||||
|
||||
public void checkConfigMembers(RootBeanDefinition beanDefinition) {
|
||||
Set<LifecycleElement> checkedInitMethods = new LinkedHashSet<>(this.initMethods.size());
|
||||
for (LifecycleElement element : this.initMethods) {
|
||||
String methodIdentifier = element.getIdentifier();
|
||||
public void checkInitDestroyMethods(RootBeanDefinition beanDefinition) {
|
||||
Set<LifecycleMethod> checkedInitMethods = new LinkedHashSet<>(this.initMethods.size());
|
||||
for (LifecycleMethod lifecycleMethod : this.initMethods) {
|
||||
String methodIdentifier = lifecycleMethod.getIdentifier();
|
||||
if (!beanDefinition.isExternallyManagedInitMethod(methodIdentifier)) {
|
||||
beanDefinition.registerExternallyManagedInitMethod(methodIdentifier);
|
||||
checkedInitMethods.add(element);
|
||||
checkedInitMethods.add(lifecycleMethod);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Registered init method on class [" + this.targetClass.getName() + "]: " + methodIdentifier);
|
||||
logger.trace("Registered init method on class [" + this.beanClass.getName() + "]: " + methodIdentifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
Set<LifecycleElement> checkedDestroyMethods = new LinkedHashSet<>(this.destroyMethods.size());
|
||||
for (LifecycleElement element : this.destroyMethods) {
|
||||
String methodIdentifier = element.getIdentifier();
|
||||
Set<LifecycleMethod> checkedDestroyMethods = new LinkedHashSet<>(this.destroyMethods.size());
|
||||
for (LifecycleMethod lifecycleMethod : this.destroyMethods) {
|
||||
String methodIdentifier = lifecycleMethod.getIdentifier();
|
||||
if (!beanDefinition.isExternallyManagedDestroyMethod(methodIdentifier)) {
|
||||
beanDefinition.registerExternallyManagedDestroyMethod(methodIdentifier);
|
||||
checkedDestroyMethods.add(element);
|
||||
checkedDestroyMethods.add(lifecycleMethod);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Registered destroy method on class [" + this.targetClass.getName() + "]: " + methodIdentifier);
|
||||
logger.trace("Registered destroy method on class [" + this.beanClass.getName() + "]: " + methodIdentifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -358,36 +390,36 @@ public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareB
|
||||
}
|
||||
|
||||
public void invokeInitMethods(Object target, String beanName) throws Throwable {
|
||||
Collection<LifecycleElement> checkedInitMethods = this.checkedInitMethods;
|
||||
Collection<LifecycleElement> initMethodsToIterate =
|
||||
Collection<LifecycleMethod> checkedInitMethods = this.checkedInitMethods;
|
||||
Collection<LifecycleMethod> initMethodsToIterate =
|
||||
(checkedInitMethods != null ? checkedInitMethods : this.initMethods);
|
||||
if (!initMethodsToIterate.isEmpty()) {
|
||||
for (LifecycleElement element : initMethodsToIterate) {
|
||||
for (LifecycleMethod lifecycleMethod : initMethodsToIterate) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Invoking init method on bean '" + beanName + "': " + element.getMethod());
|
||||
logger.trace("Invoking init method on bean '" + beanName + "': " + lifecycleMethod.getMethod());
|
||||
}
|
||||
element.invoke(target);
|
||||
lifecycleMethod.invoke(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void invokeDestroyMethods(Object target, String beanName) throws Throwable {
|
||||
Collection<LifecycleElement> checkedDestroyMethods = this.checkedDestroyMethods;
|
||||
Collection<LifecycleElement> destroyMethodsToUse =
|
||||
Collection<LifecycleMethod> checkedDestroyMethods = this.checkedDestroyMethods;
|
||||
Collection<LifecycleMethod> destroyMethodsToUse =
|
||||
(checkedDestroyMethods != null ? checkedDestroyMethods : this.destroyMethods);
|
||||
if (!destroyMethodsToUse.isEmpty()) {
|
||||
for (LifecycleElement element : destroyMethodsToUse) {
|
||||
for (LifecycleMethod lifecycleMethod : destroyMethodsToUse) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Invoking destroy method on bean '" + beanName + "': " + element.getMethod());
|
||||
logger.trace("Invoking destroy method on bean '" + beanName + "': " + lifecycleMethod.getMethod());
|
||||
}
|
||||
element.invoke(target);
|
||||
lifecycleMethod.invoke(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasDestroyMethods() {
|
||||
Collection<LifecycleElement> checkedDestroyMethods = this.checkedDestroyMethods;
|
||||
Collection<LifecycleElement> destroyMethodsToUse =
|
||||
Collection<LifecycleMethod> checkedDestroyMethods = this.checkedDestroyMethods;
|
||||
Collection<LifecycleMethod> destroyMethodsToUse =
|
||||
(checkedDestroyMethods != null ? checkedDestroyMethods : this.destroyMethods);
|
||||
return !destroyMethodsToUse.isEmpty();
|
||||
}
|
||||
@@ -395,20 +427,20 @@ public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareB
|
||||
|
||||
|
||||
/**
|
||||
* Class representing injection information about an annotated method.
|
||||
* Class representing an annotated init or destroy method.
|
||||
*/
|
||||
private static class LifecycleElement {
|
||||
private static class LifecycleMethod {
|
||||
|
||||
private final Method method;
|
||||
|
||||
private final String identifier;
|
||||
|
||||
public LifecycleElement(Method method) {
|
||||
public LifecycleMethod(Method method, Class<?> beanClass) {
|
||||
if (method.getParameterCount() != 0) {
|
||||
throw new IllegalStateException("Lifecycle method annotation requires a no-arg method: " + method);
|
||||
throw new IllegalStateException("Lifecycle annotation requires a no-arg method: " + method);
|
||||
}
|
||||
this.method = method;
|
||||
this.identifier = (Modifier.isPrivate(method.getModifiers()) ?
|
||||
this.identifier = (isPrivateOrNotVisible(method, beanClass) ?
|
||||
ClassUtils.getQualifiedMethodName(method) : method.getName());
|
||||
}
|
||||
|
||||
@@ -422,24 +454,36 @@ public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareB
|
||||
|
||||
public void invoke(Object target) throws Throwable {
|
||||
ReflectionUtils.makeAccessible(this.method);
|
||||
this.method.invoke(target, (Object[]) null);
|
||||
this.method.invoke(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof LifecycleElement otherElement)) {
|
||||
return false;
|
||||
}
|
||||
return (this.identifier.equals(otherElement.identifier));
|
||||
return (this == other || (other instanceof LifecycleMethod that &&
|
||||
this.identifier.equals(that.identifier)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.identifier.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the supplied lifecycle {@link Method} is private or not
|
||||
* visible to the supplied bean {@link Class}.
|
||||
* @since 6.0.11
|
||||
*/
|
||||
private static boolean isPrivateOrNotVisible(Method method, Class<?> beanClass) {
|
||||
int modifiers = method.getModifiers();
|
||||
if (Modifier.isPrivate(modifiers)) {
|
||||
return true;
|
||||
}
|
||||
// Method is declared in a class that resides in a different package
|
||||
// than the bean class and the method is neither public nor protected?
|
||||
return (!method.getDeclaringClass().getPackageName().equals(beanClass.getPackageName()) &&
|
||||
!(Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+45
-38
@@ -56,24 +56,23 @@ import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Internal code generator to set {@link RootBeanDefinition} properties.
|
||||
* <p>
|
||||
* Generates code in the following form:<blockquote><pre class="code">
|
||||
*
|
||||
* <p>Generates code in the following form:<pre class="code">
|
||||
* beanDefinition.setPrimary(true);
|
||||
* beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
||||
* ...
|
||||
* </pre></blockquote>
|
||||
* <p>
|
||||
* The generated code expects the following variables to be available:
|
||||
* <p>
|
||||
* </pre>
|
||||
*
|
||||
* <p>The generated code expects the following variables to be available:
|
||||
* <ul>
|
||||
* <li>{@code beanDefinition} - The {@link RootBeanDefinition} to
|
||||
* configure.</li>
|
||||
* <li>{@code beanDefinition}: the {@link RootBeanDefinition} to configure</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* Note that this generator does <b>not</b> set the {@link InstanceSupplier}.
|
||||
*
|
||||
* <p>Note that this generator does <b>not</b> set the {@link InstanceSupplier}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Stephane Nicoll
|
||||
* @author Sam Brannen
|
||||
* @since 6.0
|
||||
*/
|
||||
class BeanDefinitionPropertiesCodeGenerator {
|
||||
@@ -127,8 +126,8 @@ class BeanDefinitionPropertiesCodeGenerator {
|
||||
return code.build();
|
||||
}
|
||||
|
||||
private void addInitDestroyMethods(Builder code,
|
||||
AbstractBeanDefinition beanDefinition, @Nullable String[] methodNames, String format) {
|
||||
private void addInitDestroyMethods(Builder code, AbstractBeanDefinition beanDefinition,
|
||||
@Nullable String[] methodNames, String format) {
|
||||
if (!ObjectUtils.isEmpty(methodNames)) {
|
||||
Class<?> beanType = ClassUtils.getUserClass(beanDefinition.getResolvableType().toClass());
|
||||
Arrays.stream(methodNames).forEach(methodName -> addInitDestroyHint(beanType, methodName));
|
||||
@@ -140,17 +139,33 @@ class BeanDefinitionPropertiesCodeGenerator {
|
||||
}
|
||||
|
||||
private void addInitDestroyHint(Class<?> beanUserClass, String methodName) {
|
||||
Method method = ReflectionUtils.findMethod(beanUserClass, methodName);
|
||||
Class<?> methodDeclaringClass = beanUserClass;
|
||||
|
||||
// Parse fully-qualified method name if necessary.
|
||||
int indexOfDot = methodName.lastIndexOf('.');
|
||||
if (indexOfDot > 0) {
|
||||
String className = methodName.substring(0, indexOfDot);
|
||||
methodName = methodName.substring(indexOfDot + 1);
|
||||
if (!beanUserClass.getName().equals(className)) {
|
||||
try {
|
||||
methodDeclaringClass = ClassUtils.forName(className, beanUserClass.getClassLoader());
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new IllegalStateException("Failed to load Class [" + className +
|
||||
"] from ClassLoader [" + beanUserClass.getClassLoader() + "]", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Method method = ReflectionUtils.findMethod(methodDeclaringClass, methodName);
|
||||
if (method != null) {
|
||||
this.hints.reflection().registerMethod(method, ExecutableMode.INVOKE);
|
||||
}
|
||||
}
|
||||
|
||||
private void addConstructorArgumentValues(CodeBlock.Builder code,
|
||||
BeanDefinition beanDefinition) {
|
||||
|
||||
Map<Integer, ValueHolder> argumentValues = beanDefinition
|
||||
.getConstructorArgumentValues().getIndexedArgumentValues();
|
||||
private void addConstructorArgumentValues(CodeBlock.Builder code, BeanDefinition beanDefinition) {
|
||||
Map<Integer, ValueHolder> argumentValues =
|
||||
beanDefinition.getConstructorArgumentValues().getIndexedArgumentValues();
|
||||
if (!argumentValues.isEmpty()) {
|
||||
argumentValues.forEach((index, valueHolder) -> {
|
||||
CodeBlock valueCode = generateValue(valueHolder.getName(), valueHolder.getValue());
|
||||
@@ -161,9 +176,7 @@ class BeanDefinitionPropertiesCodeGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
private void addPropertyValues(CodeBlock.Builder code,
|
||||
RootBeanDefinition beanDefinition) {
|
||||
|
||||
private void addPropertyValues(CodeBlock.Builder code, RootBeanDefinition beanDefinition) {
|
||||
MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
|
||||
if (!propertyValues.isEmpty()) {
|
||||
for (PropertyValue propertyValue : propertyValues) {
|
||||
@@ -185,9 +198,7 @@ class BeanDefinitionPropertiesCodeGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
private void addQualifiers(CodeBlock.Builder code,
|
||||
RootBeanDefinition beanDefinition) {
|
||||
|
||||
private void addQualifiers(CodeBlock.Builder code, RootBeanDefinition beanDefinition) {
|
||||
Set<AutowireCandidateQualifier> qualifiers = beanDefinition.getQualifiers();
|
||||
if (!qualifiers.isEmpty()) {
|
||||
for (AutowireCandidateQualifier qualifier : qualifiers) {
|
||||
@@ -246,8 +257,8 @@ class BeanDefinitionPropertiesCodeGenerator {
|
||||
}
|
||||
|
||||
private boolean hasScope(String defaultValue, String actualValue) {
|
||||
return StringUtils.hasText(actualValue)
|
||||
&& !ConfigurableBeanFactory.SCOPE_SINGLETON.equals(actualValue);
|
||||
return StringUtils.hasText(actualValue) &&
|
||||
!ConfigurableBeanFactory.SCOPE_SINGLETON.equals(actualValue);
|
||||
}
|
||||
|
||||
private boolean hasDependsOn(String[] defaultValue, String[] actualValue) {
|
||||
@@ -259,16 +270,15 @@ class BeanDefinitionPropertiesCodeGenerator {
|
||||
}
|
||||
|
||||
private CodeBlock toStringVarArgs(String[] strings) {
|
||||
return Arrays.stream(strings).map(string -> CodeBlock.of("$S", string))
|
||||
.collect(CodeBlock.joining(","));
|
||||
return Arrays.stream(strings).map(string -> CodeBlock.of("$S", string)).collect(CodeBlock.joining(","));
|
||||
}
|
||||
|
||||
private Object toRole(int value) {
|
||||
return switch (value) {
|
||||
case BeanDefinition.ROLE_INFRASTRUCTURE -> CodeBlock.builder()
|
||||
.add("$T.ROLE_INFRASTRUCTURE", BeanDefinition.class).build();
|
||||
case BeanDefinition.ROLE_SUPPORT -> CodeBlock.builder()
|
||||
.add("$T.ROLE_SUPPORT", BeanDefinition.class).build();
|
||||
case BeanDefinition.ROLE_INFRASTRUCTURE ->
|
||||
CodeBlock.builder().add("$T.ROLE_INFRASTRUCTURE", BeanDefinition.class).build();
|
||||
case BeanDefinition.ROLE_SUPPORT ->
|
||||
CodeBlock.builder().add("$T.ROLE_SUPPORT", BeanDefinition.class).build();
|
||||
default -> value;
|
||||
};
|
||||
}
|
||||
@@ -278,16 +288,14 @@ class BeanDefinitionPropertiesCodeGenerator {
|
||||
Function<B, T> getter, String format) {
|
||||
|
||||
addStatementForValue(code, beanDefinition, getter,
|
||||
(defaultValue, actualValue) -> !Objects.equals(defaultValue, actualValue),
|
||||
format);
|
||||
(defaultValue, actualValue) -> !Objects.equals(defaultValue, actualValue), format);
|
||||
}
|
||||
|
||||
private <B extends BeanDefinition, T> void addStatementForValue(
|
||||
CodeBlock.Builder code, BeanDefinition beanDefinition,
|
||||
Function<B, T> getter, BiPredicate<T, T> filter, String format) {
|
||||
|
||||
addStatementForValue(code, beanDefinition, getter, filter, format,
|
||||
actualValue -> actualValue);
|
||||
addStatementForValue(code, beanDefinition, getter, filter, format, actualValue -> actualValue);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -299,8 +307,7 @@ class BeanDefinitionPropertiesCodeGenerator {
|
||||
T defaultValue = getter.apply((B) DEFAULT_BEAN_DEFINITION);
|
||||
T actualValue = getter.apply((B) beanDefinition);
|
||||
if (filter.test(defaultValue, actualValue)) {
|
||||
code.addStatement(format, BEAN_DEFINITION_VARIABLE,
|
||||
formatter.apply(actualValue));
|
||||
code.addStatement(format, BEAN_DEFINITION_VARIABLE, formatter.apply(actualValue));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
@@ -172,7 +172,9 @@ public final class BeanInstanceSupplier<T> extends AutowiredElementResolver impl
|
||||
* {@code generator} supplier to instantiate the underlying bean.
|
||||
* @param generator a {@link ThrowingSupplier} to instantiate the underlying bean
|
||||
* @return a new {@link BeanInstanceSupplier} instance with the specified generator
|
||||
* @deprecated in favor of {@link #withGenerator(ThrowingFunction)}
|
||||
*/
|
||||
@Deprecated(since = "6.0.11", forRemoval = true)
|
||||
public BeanInstanceSupplier<T> withGenerator(ThrowingSupplier<T> generator) {
|
||||
Assert.notNull(generator, "'generator' must not be null");
|
||||
return new BeanInstanceSupplier<>(this.lookup,
|
||||
|
||||
+34
-13
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,6 +18,7 @@ package org.springframework.beans.factory.aot;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Executable;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@@ -47,12 +48,6 @@ import org.springframework.util.ClassUtils;
|
||||
*/
|
||||
class DefaultBeanRegistrationCodeFragments implements BeanRegistrationCodeFragments {
|
||||
|
||||
/**
|
||||
* The variable name used to hold the bean type.
|
||||
*/
|
||||
private static final String BEAN_TYPE_VARIABLE = "beanType";
|
||||
|
||||
|
||||
private final BeanRegistrationsCode beanRegistrationsCode;
|
||||
|
||||
private final RegisteredBean registeredBean;
|
||||
@@ -118,19 +113,45 @@ class DefaultBeanRegistrationCodeFragments implements BeanRegistrationCodeFragme
|
||||
ResolvableType beanType, BeanRegistrationCode beanRegistrationCode) {
|
||||
|
||||
CodeBlock.Builder code = CodeBlock.builder();
|
||||
code.addStatement(generateBeanTypeCode(beanType));
|
||||
RootBeanDefinition mergedBeanDefinition = this.registeredBean.getMergedBeanDefinition();
|
||||
Class<?> beanClass = (mergedBeanDefinition.hasBeanClass()
|
||||
? ClassUtils.getUserClass(mergedBeanDefinition.getBeanClass()) : null);
|
||||
CodeBlock beanClassCode = generateBeanClassCode(
|
||||
beanRegistrationCode.getClassName().packageName(), beanClass);
|
||||
code.addStatement("$T $L = new $T($L)", RootBeanDefinition.class,
|
||||
BEAN_DEFINITION_VARIABLE, RootBeanDefinition.class, BEAN_TYPE_VARIABLE);
|
||||
BEAN_DEFINITION_VARIABLE, RootBeanDefinition.class, beanClassCode);
|
||||
if (targetTypeNecessary(beanType, beanClass)) {
|
||||
code.addStatement("$L.setTargetType($L)", BEAN_DEFINITION_VARIABLE,
|
||||
generateBeanTypeCode(beanType));
|
||||
}
|
||||
return code.build();
|
||||
}
|
||||
|
||||
private CodeBlock generateBeanClassCode(String targetPackage, @Nullable Class<?> beanClass) {
|
||||
if (beanClass != null) {
|
||||
if (Modifier.isPublic(beanClass.getModifiers()) || targetPackage.equals(beanClass.getPackageName())) {
|
||||
return CodeBlock.of("$T.class", beanClass);
|
||||
}
|
||||
else {
|
||||
return CodeBlock.of("$S", beanClass.getName());
|
||||
}
|
||||
}
|
||||
return CodeBlock.of("");
|
||||
}
|
||||
|
||||
private CodeBlock generateBeanTypeCode(ResolvableType beanType) {
|
||||
if (!beanType.hasGenerics()) {
|
||||
return CodeBlock.of("$T<?> $L = $T.class", Class.class, BEAN_TYPE_VARIABLE,
|
||||
ClassUtils.getUserClass(beanType.toClass()));
|
||||
return CodeBlock.of("$T.class", ClassUtils.getUserClass(beanType.toClass()));
|
||||
}
|
||||
return CodeBlock.of("$T $L = $L", ResolvableType.class, BEAN_TYPE_VARIABLE,
|
||||
ResolvableTypeCodeGenerator.generateCode(beanType));
|
||||
return ResolvableTypeCodeGenerator.generateCode(beanType);
|
||||
}
|
||||
|
||||
private boolean targetTypeNecessary(ResolvableType beanType, @Nullable Class<?> beanClass) {
|
||||
if (beanType.hasGenerics() || beanClass == null) {
|
||||
return true;
|
||||
}
|
||||
return (!beanType.toClass().equals(beanClass)
|
||||
|| this.registeredBean.getMergedBeanDefinition().getFactoryMethodName() != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+2
-1
@@ -220,7 +220,8 @@ class InstanceSupplierCodeGenerator {
|
||||
CodeBlock.Builder code = CodeBlock.builder();
|
||||
code.add("$T.<$T>forFactoryMethod($T.class, $S)", BeanInstanceSupplier.class,
|
||||
suppliedType, declaringClass, factoryMethod.getName());
|
||||
code.add(".withGenerator($T::$L)", declaringClass, factoryMethod.getName());
|
||||
code.add(".withGenerator(($L) -> $T.$L())", REGISTERED_BEAN_PARAMETER_NAME,
|
||||
declaringClass, factoryMethod.getName());
|
||||
return code.build();
|
||||
}
|
||||
|
||||
|
||||
+17
-12
@@ -1794,10 +1794,11 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
||||
}
|
||||
|
||||
/**
|
||||
* Give a bean a chance to react now all its properties are set,
|
||||
* Give a bean a chance to initialize itself after all its properties are set,
|
||||
* and a chance to know about its owning bean factory (this object).
|
||||
* This means checking whether the bean implements InitializingBean or defines
|
||||
* a custom init method, and invoking the necessary callback(s) if it does.
|
||||
* <p>This means checking whether the bean implements {@link InitializingBean}
|
||||
* or defines any custom init methods, and invoking the necessary callback(s)
|
||||
* if it does.
|
||||
* @param beanName the bean name in the factory (for debugging purposes)
|
||||
* @param bean the new bean instance we may need to initialize
|
||||
* @param mbd the merged bean definition that the bean was created with
|
||||
@@ -1832,26 +1833,30 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
||||
|
||||
/**
|
||||
* Invoke the specified custom init method on the given bean.
|
||||
* Called by invokeInitMethods.
|
||||
* <p>Can be overridden in subclasses for custom resolution of init
|
||||
* methods with arguments.
|
||||
* <p>Called by {@link #invokeInitMethods(String, Object, RootBeanDefinition)}.
|
||||
* <p>Can be overridden in subclasses for custom resolution of init methods
|
||||
* with arguments.
|
||||
* @see #invokeInitMethods
|
||||
*/
|
||||
protected void invokeCustomInitMethod(String beanName, Object bean, RootBeanDefinition mbd, String initMethodName)
|
||||
throws Throwable {
|
||||
|
||||
Class<?> beanClass = bean.getClass();
|
||||
MethodDescriptor descriptor = MethodDescriptor.create(beanName, beanClass, initMethodName);
|
||||
String methodName = descriptor.methodName();
|
||||
|
||||
Method initMethod = (mbd.isNonPublicAccessAllowed() ?
|
||||
BeanUtils.findMethod(bean.getClass(), initMethodName) :
|
||||
ClassUtils.getMethodIfAvailable(bean.getClass(), initMethodName));
|
||||
BeanUtils.findMethod(descriptor.declaringClass(), methodName) :
|
||||
ClassUtils.getMethodIfAvailable(beanClass, methodName));
|
||||
|
||||
if (initMethod == null) {
|
||||
if (mbd.isEnforceInitMethod()) {
|
||||
throw new BeanDefinitionValidationException("Could not find an init method named '" +
|
||||
initMethodName + "' on bean with name '" + beanName + "'");
|
||||
methodName + "' on bean with name '" + beanName + "'");
|
||||
}
|
||||
else {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("No default init method named '" + initMethodName +
|
||||
logger.trace("No default init method named '" + methodName +
|
||||
"' found on bean with name '" + beanName + "'");
|
||||
}
|
||||
// Ignore non-existent default lifecycle methods.
|
||||
@@ -1860,9 +1865,9 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
||||
}
|
||||
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Invoking init method '" + initMethodName + "' on bean with name '" + beanName + "'");
|
||||
logger.trace("Invoking init method '" + methodName + "' on bean with name '" + beanName + "'");
|
||||
}
|
||||
Method methodToInvoke = ClassUtils.getInterfaceMethodIfPossible(initMethod, bean.getClass());
|
||||
Method methodToInvoke = ClassUtils.getInterfaceMethodIfPossible(initMethod, beanClass);
|
||||
|
||||
try {
|
||||
ReflectionUtils.makeAccessible(methodToInvoke);
|
||||
|
||||
+3
-2
@@ -390,7 +390,8 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
|
||||
@Override
|
||||
@Nullable
|
||||
public String getBeanClassName() {
|
||||
return (this.beanClass instanceof Class<?> clazz ? clazz.getName() : (String) this.beanClass);
|
||||
Object beanClassObject = this.beanClass; // defensive access to volatile beanClass field
|
||||
return (beanClassObject instanceof Class<?> clazz ? clazz.getName() : (String) beanClassObject);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -423,7 +424,7 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
|
||||
* @see #resolveBeanClass(ClassLoader)
|
||||
*/
|
||||
public Class<?> getBeanClass() throws IllegalStateException {
|
||||
Object beanClassObject = this.beanClass;
|
||||
Object beanClassObject = this.beanClass; // defensive access to volatile beanClass field
|
||||
if (beanClassObject == null) {
|
||||
throw new IllegalStateException("No bean class specified on bean definition");
|
||||
}
|
||||
|
||||
+3
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -125,7 +125,8 @@ public final class BeanDefinitionBuilder {
|
||||
* @since 5.3.9
|
||||
*/
|
||||
public static <T> BeanDefinitionBuilder rootBeanDefinition(ResolvableType beanType, Supplier<T> instanceSupplier) {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(beanType);
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition();
|
||||
beanDefinition.setTargetType(beanType);
|
||||
beanDefinition.setInstanceSupplier(instanceSupplier);
|
||||
return new BeanDefinitionBuilder(beanDefinition);
|
||||
}
|
||||
|
||||
+1
-1
@@ -979,7 +979,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
for (String beanName : beanNames) {
|
||||
Object singletonInstance = getSingleton(beanName);
|
||||
if (singletonInstance instanceof SmartInitializingSingleton smartSingleton) {
|
||||
StartupStep smartInitialize = this.getApplicationStartup().start("spring.beans.smart-initialize")
|
||||
StartupStep smartInitialize = getApplicationStartup().start("spring.beans.smart-initialize")
|
||||
.tag("beanName", beanName);
|
||||
smartSingleton.afterSingletonsInstantiated();
|
||||
smartInitialize.end();
|
||||
|
||||
+5
-5
@@ -447,17 +447,17 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
|
||||
}
|
||||
String canonicalName = canonicalName(beanName);
|
||||
Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);
|
||||
if (dependentBeans == null) {
|
||||
if (dependentBeans == null || dependentBeans.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
if (dependentBeans.contains(dependentBeanName)) {
|
||||
return true;
|
||||
}
|
||||
if (alreadySeen == null) {
|
||||
alreadySeen = new HashSet<>();
|
||||
}
|
||||
alreadySeen.add(beanName);
|
||||
for (String transitiveDependency : dependentBeans) {
|
||||
if (alreadySeen == null) {
|
||||
alreadySeen = new HashSet<>();
|
||||
}
|
||||
alreadySeen.add(beanName);
|
||||
if (isDependent(transitiveDependency, dependentBeanName, alreadySeen)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
+7
-5
@@ -116,8 +116,7 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
||||
if (!this.invokeAutoCloseable) {
|
||||
this.destroyMethodNames = destroyMethodNames;
|
||||
List<Method> destroyMethods = new ArrayList<>(destroyMethodNames.length);
|
||||
for (int i = 0; i < destroyMethodNames.length; i++) {
|
||||
String destroyMethodName = destroyMethodNames[i];
|
||||
for (String destroyMethodName : destroyMethodNames) {
|
||||
Method destroyMethod = determineDestroyMethod(destroyMethodName);
|
||||
if (destroyMethod == null) {
|
||||
if (beanDefinition.isEnforceDestroyMethod()) {
|
||||
@@ -252,15 +251,18 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
||||
|
||||
|
||||
@Nullable
|
||||
private Method determineDestroyMethod(String name) {
|
||||
private Method determineDestroyMethod(String destroyMethodName) {
|
||||
try {
|
||||
Class<?> beanClass = this.bean.getClass();
|
||||
Method destroyMethod = findDestroyMethod(beanClass, name);
|
||||
MethodDescriptor descriptor = MethodDescriptor.create(this.beanName, beanClass, destroyMethodName);
|
||||
String methodName = descriptor.methodName();
|
||||
|
||||
Method destroyMethod = findDestroyMethod(descriptor.declaringClass(), methodName);
|
||||
if (destroyMethod != null) {
|
||||
return destroyMethod;
|
||||
}
|
||||
for (Class<?> beanInterface : beanClass.getInterfaces()) {
|
||||
destroyMethod = findDestroyMethod(beanInterface, name);
|
||||
destroyMethod = findDestroyMethod(beanInterface, methodName);
|
||||
if (destroyMethod != null) {
|
||||
return destroyMethod;
|
||||
}
|
||||
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.beans.factory.support;
|
||||
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Descriptor for a {@link java.lang.reflect.Method Method} which holds a
|
||||
* reference to the method's {@linkplain #declaringClass declaring class},
|
||||
* {@linkplain #methodName name}, and {@linkplain #parameterTypes parameter types}.
|
||||
*
|
||||
* @param declaringClass the method's declaring class
|
||||
* @param methodName the name of the method
|
||||
* @param parameterTypes the types of parameters accepted by the method
|
||||
* @author Sam Brannen
|
||||
* @since 6.0.11
|
||||
*/
|
||||
record MethodDescriptor(Class<?> declaringClass, String methodName, Class<?>... parameterTypes) {
|
||||
|
||||
/**
|
||||
* Create a {@link MethodDescriptor} for the supplied bean class and method name.
|
||||
* <p>The supplied {@code methodName} may be a {@linkplain Method#getName()
|
||||
* simple method name} or a
|
||||
* {@linkplain org.springframework.util.ClassUtils#getQualifiedMethodName(Method)
|
||||
* qualified method name}.
|
||||
* <p>If the method name is fully qualified, this utility will parse the
|
||||
* method name and its declaring class from the qualified method name and then
|
||||
* attempt to load the method's declaring class using the {@link ClassLoader}
|
||||
* of the supplied {@code beanClass}. Otherwise, the returned descriptor will
|
||||
* reference the supplied {@code beanClass} and {@code methodName}.
|
||||
* @param beanName the bean name in the factory (for debugging purposes)
|
||||
* @param beanClass the bean class
|
||||
* @param methodName the name of the method
|
||||
* @return a new {@code MethodDescriptor}; never {@code null}
|
||||
*/
|
||||
static MethodDescriptor create(String beanName, Class<?> beanClass, String methodName) {
|
||||
try {
|
||||
Class<?> declaringClass = beanClass;
|
||||
String methodNameToUse = methodName;
|
||||
|
||||
// Parse fully-qualified method name if necessary.
|
||||
int indexOfDot = methodName.lastIndexOf('.');
|
||||
if (indexOfDot > 0) {
|
||||
String className = methodName.substring(0, indexOfDot);
|
||||
methodNameToUse = methodName.substring(indexOfDot + 1);
|
||||
if (!beanClass.getName().equals(className)) {
|
||||
declaringClass = ClassUtils.forName(className, beanClass.getClassLoader());
|
||||
}
|
||||
}
|
||||
return new MethodDescriptor(declaringClass, methodNameToUse);
|
||||
}
|
||||
catch (Exception | LinkageError ex) {
|
||||
throw new BeanDefinitionValidationException(
|
||||
"Could not create MethodDescriptor for method '%s' on bean with name '%s': %s"
|
||||
.formatted(methodName, beanName, ex.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+25
-29
@@ -161,7 +161,9 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
|
||||
* @param beanType the type of bean to instantiate
|
||||
* @since 6.0
|
||||
* @see #setTargetType(ResolvableType)
|
||||
* @deprecated as of 6.0.11, in favor of an extra {@link #setTargetType(ResolvableType)} call
|
||||
*/
|
||||
@Deprecated(since = "6.0.11")
|
||||
public RootBeanDefinition(@Nullable ResolvableType beanType) {
|
||||
setTargetType(beanType);
|
||||
}
|
||||
@@ -495,14 +497,15 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
|
||||
|
||||
/**
|
||||
* Register an externally managed configuration initialization method —
|
||||
* for example, a method annotated with JSR-250's
|
||||
* {@link jakarta.annotation.PostConstruct} annotation.
|
||||
* <p>The supplied {@code initMethod} may be the
|
||||
* {@linkplain Method#getName() simple method name} for non-private methods or the
|
||||
* for example, a method annotated with JSR-250's {@code javax.annotation.PostConstruct}
|
||||
* or Jakarta's {@link jakarta.annotation.PostConstruct} annotation.
|
||||
* <p>The supplied {@code initMethod} may be a
|
||||
* {@linkplain Method#getName() simple method name} or a
|
||||
* {@linkplain org.springframework.util.ClassUtils#getQualifiedMethodName(Method)
|
||||
* qualified method name} for {@code private} methods. A qualified name is
|
||||
* necessary for {@code private} methods in order to disambiguate between
|
||||
* multiple private methods with the same name within a class hierarchy.
|
||||
* qualified method name} for package-private and {@code private} methods.
|
||||
* A qualified name is necessary for package-private and {@code private} methods
|
||||
* in order to disambiguate between multiple such methods with the same name
|
||||
* within a type hierarchy.
|
||||
*/
|
||||
public void registerExternallyManagedInitMethod(String initMethod) {
|
||||
synchronized (this.postProcessingLock) {
|
||||
@@ -541,23 +544,12 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
|
||||
if (isExternallyManagedInitMethod(initMethod)) {
|
||||
return true;
|
||||
}
|
||||
if (this.externallyManagedInitMethods != null) {
|
||||
for (String candidate : this.externallyManagedInitMethods) {
|
||||
int indexOfDot = candidate.lastIndexOf('.');
|
||||
if (indexOfDot >= 0) {
|
||||
String methodName = candidate.substring(indexOfDot + 1);
|
||||
if (methodName.equals(initMethod)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return hasAnyExternallyManagedMethod(this.externallyManagedInitMethods, initMethod);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all externally managed initialization methods (as an immutable Set).
|
||||
* Get all externally managed initialization methods (as an immutable Set).
|
||||
* <p>See {@link #registerExternallyManagedInitMethod} for details
|
||||
* regarding the format for the initialization methods in the returned set.
|
||||
* @since 5.3.11
|
||||
@@ -627,19 +619,23 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
|
||||
if (isExternallyManagedDestroyMethod(destroyMethod)) {
|
||||
return true;
|
||||
}
|
||||
if (this.externallyManagedDestroyMethods != null) {
|
||||
for (String candidate : this.externallyManagedDestroyMethods) {
|
||||
int indexOfDot = candidate.lastIndexOf('.');
|
||||
if (indexOfDot >= 0) {
|
||||
String methodName = candidate.substring(indexOfDot + 1);
|
||||
if (methodName.equals(destroyMethod)) {
|
||||
return true;
|
||||
}
|
||||
return hasAnyExternallyManagedMethod(this.externallyManagedDestroyMethods, destroyMethod);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasAnyExternallyManagedMethod(Set<String> candidates, String methodName) {
|
||||
if (candidates != null) {
|
||||
for (String candidate : candidates) {
|
||||
int indexOfDot = candidate.lastIndexOf('.');
|
||||
if (indexOfDot > 0) {
|
||||
String candidateMethodName = candidate.substring(indexOfDot + 1);
|
||||
if (candidateMethodName.equals(methodName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+6
-6
@@ -667,22 +667,22 @@ abstract class AbstractPropertyAccessorTests {
|
||||
accessor.setPropertyValue("myDouble", doubleValue);
|
||||
|
||||
assertThat(target.getMyPrimitiveByte()).isEqualTo(Byte.MAX_VALUE);
|
||||
assertThat(target.getMyByte().byteValue()).isEqualTo(Byte.MAX_VALUE);
|
||||
assertThat(target.getMyByte()).isEqualTo(Byte.MAX_VALUE);
|
||||
|
||||
assertThat(target.getMyPrimitiveShort()).isEqualTo(Short.MAX_VALUE);
|
||||
assertThat(target.getMyShort().shortValue()).isEqualTo(Short.MAX_VALUE);
|
||||
assertThat(target.getMyShort()).isEqualTo(Short.MAX_VALUE);
|
||||
|
||||
assertThat(target.getMyPrimitiveInt()).isEqualTo(Integer.MAX_VALUE);
|
||||
assertThat(target.getMyInteger().intValue()).isEqualTo(Integer.MAX_VALUE);
|
||||
assertThat(target.getMyInteger()).isEqualTo(Integer.MAX_VALUE);
|
||||
|
||||
assertThat(target.getMyPrimitiveLong()).isEqualTo(Long.MAX_VALUE);
|
||||
assertThat(target.getMyLong().longValue()).isEqualTo(Long.MAX_VALUE);
|
||||
assertThat(target.getMyLong()).isEqualTo(Long.MAX_VALUE);
|
||||
|
||||
assertThat((double) target.getMyPrimitiveFloat()).isCloseTo(Float.MAX_VALUE, within(0.001));
|
||||
assertThat((double) target.getMyFloat()).isCloseTo(Float.MAX_VALUE, within(0.001));
|
||||
|
||||
assertThat(target.getMyPrimitiveDouble()).isCloseTo(Double.MAX_VALUE, within(0.001));
|
||||
assertThat(target.getMyDouble().doubleValue()).isCloseTo(Double.MAX_VALUE, within(0.001));
|
||||
assertThat(target.getMyDouble()).isCloseTo(Double.MAX_VALUE, within(0.001));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1994,6 +1994,7 @@ abstract class AbstractPropertyAccessorTests {
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static class Spr13837Bean implements Spr13837 {
|
||||
|
||||
protected Integer something;
|
||||
@@ -2004,7 +2005,6 @@ abstract class AbstractPropertyAccessorTests {
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Spr13837Bean setSomething(final Integer something) {
|
||||
this.something = something;
|
||||
return this;
|
||||
|
||||
+2
-4
@@ -35,8 +35,7 @@ public abstract class AbstractPropertyValuesTests {
|
||||
assertThat(pvs.contains("forname")).as("Contains forname").isTrue();
|
||||
assertThat(pvs.contains("surname")).as("Contains surname").isTrue();
|
||||
assertThat(pvs.contains("age")).as("Contains age").isTrue();
|
||||
boolean condition1 = !pvs.contains("tory");
|
||||
assertThat(condition1).as("Doesn't contain tory").isTrue();
|
||||
assertThat(!pvs.contains("tory")).as("Doesn't contain tory").isTrue();
|
||||
|
||||
PropertyValue[] ps = pvs.getPropertyValues();
|
||||
Map<String, String> m = new HashMap<>();
|
||||
@@ -46,8 +45,7 @@ public abstract class AbstractPropertyValuesTests {
|
||||
for (PropertyValue element : ps) {
|
||||
Object val = m.get(element.getName());
|
||||
assertThat(val).as("Can't have unexpected value").isNotNull();
|
||||
boolean condition = val instanceof String;
|
||||
assertThat(condition).as("Val i string").isTrue();
|
||||
assertThat(val instanceof String).as("Val i string").isTrue();
|
||||
assertThat(val.equals(element.getValue())).as("val matches expected").isTrue();
|
||||
m.remove(element.getName());
|
||||
}
|
||||
|
||||
@@ -335,7 +335,7 @@ class BeanWrapperGenericsTests {
|
||||
|
||||
Object obj = gb.getMapOfListOfInteger().get("testKey").get(0);
|
||||
assertThat(obj).isInstanceOf(Integer.class);
|
||||
assertThat(((Integer) obj).intValue()).isEqualTo(1);
|
||||
assertThat(obj).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -351,7 +351,7 @@ class BeanWrapperGenericsTests {
|
||||
|
||||
Object obj = gb.getListOfMapOfInteger().get(0).get("testKey");
|
||||
assertThat(obj).isInstanceOf(Integer.class);
|
||||
assertThat(((Integer) obj).intValue()).isEqualTo(5);
|
||||
assertThat(obj).isEqualTo(5);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -366,7 +366,7 @@ class BeanWrapperGenericsTests {
|
||||
|
||||
Object obj = gb.getMapOfListOfListOfInteger().get("testKey").get(0).get(0);
|
||||
assertThat(obj).isInstanceOf(Integer.class);
|
||||
assertThat(((Integer) obj).intValue()).isEqualTo(1);
|
||||
assertThat(obj).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+5
-10
@@ -155,8 +155,7 @@ public class BeanFactoryUtilsTests {
|
||||
assertThat(beans.get("t1")).isEqualTo(t1);
|
||||
assertThat(beans.get("t2")).isEqualTo(t2);
|
||||
assertThat(beans.get("t3")).isEqualTo(t3.getObject());
|
||||
boolean condition = beans.get("t4") instanceof TestBean;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(beans.get("t4") instanceof TestBean).isTrue();
|
||||
|
||||
beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(lbf, DummyFactory.class, true, true);
|
||||
assertThat(beans).hasSize(2);
|
||||
@@ -192,8 +191,7 @@ public class BeanFactoryUtilsTests {
|
||||
assertThat(beans.get("t1")).isEqualTo(t1);
|
||||
assertThat(beans.get("t2")).isEqualTo(t2);
|
||||
assertThat(beans.get("t3")).isEqualTo(t3.getObject());
|
||||
boolean condition2 = beans.get("t4") instanceof TestBean;
|
||||
assertThat(condition2).isTrue();
|
||||
assertThat(beans.get("t4") instanceof TestBean).isTrue();
|
||||
// t3 and t4 are found here as of Spring 2.0, since they are pre-registered
|
||||
// singleton instances, while testFactory1 and testFactory are *not* found
|
||||
// because they are FactoryBean definitions that haven't been initialized yet.
|
||||
@@ -212,13 +210,11 @@ public class BeanFactoryUtilsTests {
|
||||
assertThat(beans.get("test3")).isEqualTo(test3);
|
||||
assertThat(beans.get("test")).isEqualTo(test);
|
||||
assertThat(beans.get("testFactory1")).isEqualTo(testFactory1);
|
||||
boolean condition1 = beans.get("testFactory2") instanceof TestBean;
|
||||
assertThat(condition1).isTrue();
|
||||
assertThat(beans.get("testFactory2") instanceof TestBean).isTrue();
|
||||
assertThat(beans.get("t1")).isEqualTo(t1);
|
||||
assertThat(beans.get("t2")).isEqualTo(t2);
|
||||
assertThat(beans.get("t3")).isEqualTo(t3.getObject());
|
||||
boolean condition = beans.get("t4") instanceof TestBean;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(beans.get("t4") instanceof TestBean).isTrue();
|
||||
|
||||
beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(this.listableBeanFactory, DummyFactory.class, true, true);
|
||||
assertThat(beans).hasSize(4);
|
||||
@@ -261,8 +257,7 @@ public class BeanFactoryUtilsTests {
|
||||
assertThat(beans.get("test3")).isEqualTo(test3);
|
||||
assertThat(beans.get("test")).isEqualTo(test);
|
||||
assertThat(beans.get("testFactory1")).isEqualTo(testFactory1);
|
||||
boolean condition = beans.get("testFactory2") instanceof TestBean;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(beans.get("testFactory2") instanceof TestBean).isTrue();
|
||||
|
||||
beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(this.listableBeanFactory, DummyFactory.class, true, true);
|
||||
assertThat(beans).hasSize(2);
|
||||
|
||||
+3
-5
@@ -3190,6 +3190,7 @@ class DefaultListableBeanFactoryTests {
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
private static class CustomTypeConverter implements TypeConverter {
|
||||
|
||||
private final NumberFormat numberFormat;
|
||||
@@ -3199,11 +3200,10 @@ class DefaultListableBeanFactoryTests {
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public Object convertIfNecessary(Object value, @Nullable Class requiredType) {
|
||||
if (value instanceof String && Float.class.isAssignableFrom(requiredType)) {
|
||||
if (value instanceof String text && Float.class.isAssignableFrom(requiredType)) {
|
||||
try {
|
||||
return this.numberFormat.parse((String) value).floatValue();
|
||||
return this.numberFormat.parse(text).floatValue();
|
||||
}
|
||||
catch (ParseException ex) {
|
||||
throw new TypeMismatchException(value, requiredType, ex);
|
||||
@@ -3218,13 +3218,11 @@ class DefaultListableBeanFactoryTests {
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public Object convertIfNecessary(Object value, @Nullable Class requiredType, @Nullable MethodParameter methodParam) {
|
||||
return convertIfNecessary(value, requiredType);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public Object convertIfNecessary(Object value, @Nullable Class requiredType, @Nullable Field field) {
|
||||
return convertIfNecessary(value, requiredType);
|
||||
}
|
||||
|
||||
+218
-146
File diff suppressed because it is too large
Load Diff
+82
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,6 +18,8 @@ package org.springframework.beans.factory.annotation;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RegisteredBean;
|
||||
@@ -29,12 +31,15 @@ import org.springframework.beans.testfixture.beans.factory.generator.lifecycle.I
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.lifecycle.MultiInitDestroyBean;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.SoftAssertions.assertSoftly;
|
||||
|
||||
/**
|
||||
* Tests for {@link InitDestroyAnnotationBeanPostProcessor}.
|
||||
*
|
||||
* @since 6.0
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
class InitDestroyAnnotationBeanPostProcessorTests {
|
||||
|
||||
@@ -65,8 +70,8 @@ class InitDestroyAnnotationBeanPostProcessorTests {
|
||||
beanDefinition.setDestroyMethodNames("customDestroyMethod");
|
||||
processAheadOfTime(beanDefinition);
|
||||
RootBeanDefinition mergedBeanDefinition = getMergedBeanDefinition();
|
||||
assertThat(mergedBeanDefinition.getInitMethodNames()).containsExactly("customInitMethod", "initMethod");
|
||||
assertThat(mergedBeanDefinition.getDestroyMethodNames()).containsExactly("customDestroyMethod", "destroyMethod");
|
||||
assertThat(mergedBeanDefinition.getInitMethodNames()).containsExactly("initMethod", "customInitMethod");
|
||||
assertThat(mergedBeanDefinition.getDestroyMethodNames()).containsExactly("destroyMethod", "customDestroyMethod");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -109,6 +114,35 @@ class InitDestroyAnnotationBeanPostProcessorTests {
|
||||
assertThat(mergedBeanDefinition.getDestroyMethodNames()).containsExactly("anotherDestroyMethod", "destroyMethod");
|
||||
}
|
||||
|
||||
@Test
|
||||
void processAheadOfTimeWithMultipleLevelsOfPublicAndPrivateInitAndDestroyMethods() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(CustomAnnotatedPrivateSameNameInitDestroyBean.class);
|
||||
// We explicitly define "afterPropertiesSet" as a "custom init method"
|
||||
// to ensure that it will be tracked as such even though it has the same
|
||||
// name as InitializingBean#afterPropertiesSet().
|
||||
beanDefinition.setInitMethodNames("afterPropertiesSet", "customInit");
|
||||
// We explicitly define "destroy" as a "custom destroy method"
|
||||
// to ensure that it will be tracked as such even though it has the same
|
||||
// name as DisposableBean#destroy().
|
||||
beanDefinition.setDestroyMethodNames("destroy", "customDestroy");
|
||||
processAheadOfTime(beanDefinition);
|
||||
RootBeanDefinition mergedBeanDefinition = getMergedBeanDefinition();
|
||||
assertSoftly(softly -> {
|
||||
softly.assertThat(mergedBeanDefinition.getInitMethodNames()).containsExactly(
|
||||
CustomAnnotatedPrivateInitDestroyBean.class.getName() + ".privateInit", // fully-qualified private method
|
||||
CustomAnnotatedPrivateSameNameInitDestroyBean.class.getName() + ".privateInit", // fully-qualified private method
|
||||
"afterPropertiesSet",
|
||||
"customInit"
|
||||
);
|
||||
softly.assertThat(mergedBeanDefinition.getDestroyMethodNames()).containsExactly(
|
||||
CustomAnnotatedPrivateSameNameInitDestroyBean.class.getName() + ".privateDestroy", // fully-qualified private method
|
||||
CustomAnnotatedPrivateInitDestroyBean.class.getName() + ".privateDestroy", // fully-qualified private method
|
||||
"destroy",
|
||||
"customDestroy"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
private void processAheadOfTime(RootBeanDefinition beanDefinition) {
|
||||
RegisteredBean registeredBean = registerBean(beanDefinition);
|
||||
assertThat(createAotBeanPostProcessor().processAheadOfTime(registeredBean)).isNull();
|
||||
@@ -133,4 +167,49 @@ class InitDestroyAnnotationBeanPostProcessorTests {
|
||||
|
||||
static class NoInitDestroyBean {}
|
||||
|
||||
static class CustomInitDestroyBean {
|
||||
|
||||
public void customInit() {
|
||||
}
|
||||
|
||||
public void customDestroy() {
|
||||
}
|
||||
}
|
||||
|
||||
static class CustomInitializingDisposableBean extends CustomInitDestroyBean
|
||||
implements InitializingBean, DisposableBean {
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
}
|
||||
}
|
||||
|
||||
static class CustomAnnotatedPrivateInitDestroyBean extends CustomInitializingDisposableBean {
|
||||
|
||||
@Init
|
||||
private void privateInit() {
|
||||
}
|
||||
|
||||
@Destroy
|
||||
private void privateDestroy() {
|
||||
}
|
||||
}
|
||||
|
||||
static class CustomAnnotatedPrivateSameNameInitDestroyBean extends CustomAnnotatedPrivateInitDestroyBean {
|
||||
|
||||
@Init
|
||||
@SuppressWarnings("unused")
|
||||
private void privateInit() {
|
||||
}
|
||||
|
||||
@Destroy
|
||||
@SuppressWarnings("unused")
|
||||
private void privateDestroy() {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-2
@@ -85,8 +85,7 @@ public class InjectAnnotationBeanPostProcessorTests {
|
||||
bf.getBean("testBean");
|
||||
}
|
||||
catch (BeanCreationException ex) {
|
||||
boolean condition = ex.getRootCause() instanceof IllegalStateException;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(ex.getRootCause() instanceof IllegalStateException).isTrue();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+88
-7
@@ -47,6 +47,10 @@ import org.springframework.beans.testfixture.beans.TestBean;
|
||||
import org.springframework.beans.testfixture.beans.factory.aot.InnerBeanConfiguration;
|
||||
import org.springframework.beans.testfixture.beans.factory.aot.MockBeanRegistrationsCode;
|
||||
import org.springframework.beans.testfixture.beans.factory.aot.SimpleBean;
|
||||
import org.springframework.beans.testfixture.beans.factory.aot.TestHierarchy;
|
||||
import org.springframework.beans.testfixture.beans.factory.aot.TestHierarchy.Implementation;
|
||||
import org.springframework.beans.testfixture.beans.factory.aot.TestHierarchy.One;
|
||||
import org.springframework.beans.testfixture.beans.factory.aot.TestHierarchy.Two;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.test.io.support.MockSpringFactoriesLoader;
|
||||
import org.springframework.core.test.tools.CompileWithForkedClassLoader;
|
||||
@@ -56,6 +60,7 @@ import org.springframework.core.test.tools.TestCompiler;
|
||||
import org.springframework.javapoet.CodeBlock;
|
||||
import org.springframework.javapoet.MethodSpec;
|
||||
import org.springframework.javapoet.ParameterizedTypeName;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
@@ -89,8 +94,10 @@ class BeanDefinitionMethodGeneratorTests {
|
||||
|
||||
|
||||
@Test
|
||||
void generateBeanDefinitionMethodGeneratesMethod() {
|
||||
RegisteredBean registeredBean = registerBean(new RootBeanDefinition(TestBean.class));
|
||||
void generateBeanDefinitionMethodWithOnlyTargetTypeDoesNotSetBeanClass() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition();
|
||||
beanDefinition.setTargetType(TestBean.class);
|
||||
RegisteredBean registeredBean = registerBean(beanDefinition);
|
||||
BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(
|
||||
this.methodGeneratorFactory, registeredBean, null,
|
||||
Collections.emptyList());
|
||||
@@ -99,7 +106,67 @@ class BeanDefinitionMethodGeneratorTests {
|
||||
compile(method, (actual, compiled) -> {
|
||||
SourceFile sourceFile = compiled.getSourceFile(".*BeanDefinitions");
|
||||
assertThat(sourceFile).contains("Get the bean definition for 'testBean'");
|
||||
assertThat(sourceFile).contains("beanType = TestBean.class");
|
||||
assertThat(sourceFile).contains("new RootBeanDefinition()");
|
||||
assertThat(sourceFile).contains("setTargetType(TestBean.class)");
|
||||
assertThat(sourceFile).contains("setInstanceSupplier(TestBean::new)");
|
||||
assertThat(actual).isInstanceOf(RootBeanDefinition.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateBeanDefinitionMethodSpecifiesBeanClassIfSet() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(TestBean.class);
|
||||
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) -> {
|
||||
SourceFile sourceFile = compiled.getSourceFile(".*BeanDefinitions");
|
||||
assertThat(sourceFile).contains("Get the bean definition for 'testBean'");
|
||||
assertThat(sourceFile).contains("new RootBeanDefinition(TestBean.class)");
|
||||
assertThat(sourceFile).doesNotContain("setTargetType(");
|
||||
assertThat(sourceFile).contains("setInstanceSupplier(TestBean::new)");
|
||||
assertThat(actual).isInstanceOf(RootBeanDefinition.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateBeanDefinitionMethodSpecifiesBeanClassAndTargetTypIfDifferent() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(One.class);
|
||||
beanDefinition.setTargetType(Implementation.class);
|
||||
beanDefinition.setResolvedFactoryMethod(ReflectionUtils.findMethod(TestHierarchy.class, "oneBean"));
|
||||
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) -> {
|
||||
SourceFile sourceFile = compiled.getSourceFile(".*BeanDefinitions");
|
||||
assertThat(sourceFile).contains("Get the bean definition for 'testBean'");
|
||||
assertThat(sourceFile).contains("new RootBeanDefinition(TestHierarchy.One.class)");
|
||||
assertThat(sourceFile).contains("setTargetType(TestHierarchy.Implementation.class)");
|
||||
assertThat(actual).isInstanceOf(RootBeanDefinition.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateBeanDefinitionMethodUSeBeanClassNameIfNotReachable() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(PackagePrivateTestBean.class);
|
||||
beanDefinition.setTargetType(TestBean.class);
|
||||
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) -> {
|
||||
SourceFile sourceFile = compiled.getSourceFile(".*BeanDefinitions");
|
||||
assertThat(sourceFile).contains("Get the bean definition for 'testBean'");
|
||||
assertThat(sourceFile).contains("new RootBeanDefinition(\"org.springframework.beans.factory.aot.PackagePrivateTestBean\"");
|
||||
assertThat(sourceFile).contains("setTargetType(TestBean.class)");
|
||||
assertThat(sourceFile).contains("setInstanceSupplier(TestBean::new)");
|
||||
assertThat(actual).isInstanceOf(RootBeanDefinition.class);
|
||||
});
|
||||
@@ -116,7 +183,6 @@ class BeanDefinitionMethodGeneratorTests {
|
||||
compile(method, (actual, compiled) -> {
|
||||
SourceFile sourceFile = compiled.getSourceFile(".*BeanDefinitions");
|
||||
assertThat(sourceFile).contains("Get the bean definition for 'testBean'");
|
||||
assertThat(sourceFile).contains("beanType = TestBean.class");
|
||||
assertThat(sourceFile).contains("setInstanceSupplier(TestBean::new)");
|
||||
assertThat(actual).isInstanceOf(RootBeanDefinition.class);
|
||||
});
|
||||
@@ -170,8 +236,9 @@ class BeanDefinitionMethodGeneratorTests {
|
||||
|
||||
@Test
|
||||
void generateBeanDefinitionMethodWhenHasGenericsGeneratesMethod() {
|
||||
RegisteredBean registeredBean = registerBean(new RootBeanDefinition(
|
||||
ResolvableType.forClassWithGenerics(GenericBean.class, Integer.class)));
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition();
|
||||
beanDefinition.setTargetType(ResolvableType.forClassWithGenerics(GenericBean.class, Integer.class));
|
||||
RegisteredBean registeredBean = registerBean(beanDefinition);
|
||||
BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(
|
||||
this.methodGeneratorFactory, registeredBean, null,
|
||||
Collections.emptyList());
|
||||
@@ -182,12 +249,26 @@ class BeanDefinitionMethodGeneratorTests {
|
||||
SourceFile sourceFile = compiled.getSourceFile(".*BeanDefinitions");
|
||||
assertThat(sourceFile).contains("Get the bean definition for 'testBean'");
|
||||
assertThat(sourceFile).contains(
|
||||
"beanType = ResolvableType.forClassWithGenerics(GenericBean.class, Integer.class)");
|
||||
"setTargetType(ResolvableType.forClassWithGenerics(GenericBean.class, Integer.class))");
|
||||
assertThat(sourceFile).contains("setInstanceSupplier(GenericBean::new)");
|
||||
assertThat(actual).isInstanceOf(RootBeanDefinition.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateBeanDefinitionMethodWhenHasExplicitResolvableType() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(One.class);
|
||||
beanDefinition.setResolvedFactoryMethod(ReflectionUtils.findMethod(TestHierarchy.class, "oneBean"));
|
||||
beanDefinition.setTargetType(Two.class);
|
||||
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.getResolvableType().resolve()).isEqualTo(Two.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateBeanDefinitionMethodWhenHasInstancePostProcessorGeneratesMethod() {
|
||||
RegisteredBean registeredBean = registerBean(new RootBeanDefinition(TestBean.class));
|
||||
|
||||
+99
-101
@@ -28,6 +28,8 @@ import java.util.function.Supplier;
|
||||
|
||||
import javax.lang.model.element.Modifier;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aot.generate.GeneratedClass;
|
||||
@@ -61,6 +63,8 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* @author Phillip Webb
|
||||
* @author Stephane Nicoll
|
||||
* @author Olga Maciaszek-Sharma
|
||||
* @author Sam Brannen
|
||||
* @since 6.0
|
||||
*/
|
||||
class BeanDefinitionPropertiesCodeGeneratorTests {
|
||||
|
||||
@@ -104,8 +108,7 @@ class BeanDefinitionPropertiesCodeGeneratorTests {
|
||||
@Test
|
||||
void setScopeWhenOther() {
|
||||
this.beanDefinition.setScope("prototype");
|
||||
compile((actual, compiled) -> assertThat(actual.getScope())
|
||||
.isEqualTo("prototype"));
|
||||
compile((actual, compiled) -> assertThat(actual.getScope()).isEqualTo("prototype"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -120,8 +123,7 @@ class BeanDefinitionPropertiesCodeGeneratorTests {
|
||||
@Test
|
||||
void setDependsOnWhenNotEmpty() {
|
||||
this.beanDefinition.setDependsOn("a", "b", "c");
|
||||
compile((actual, compiled) -> assertThat(actual.getDependsOn())
|
||||
.containsExactly("a", "b", "c"));
|
||||
compile((actual, compiled) -> assertThat(actual.getDependsOn()).containsExactly("a", "b", "c"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -154,8 +156,7 @@ class BeanDefinitionPropertiesCodeGeneratorTests {
|
||||
@Test
|
||||
void setAutowireCandidateWhenFalse() {
|
||||
this.beanDefinition.setAutowireCandidate(false);
|
||||
compile(
|
||||
(actual, compiled) -> assertThat(actual.isAutowireCandidate()).isFalse());
|
||||
compile((actual, compiled) -> assertThat(actual.isAutowireCandidate()).isFalse());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -179,8 +180,7 @@ class BeanDefinitionPropertiesCodeGeneratorTests {
|
||||
@Test
|
||||
void setSyntheticWhenTrue() {
|
||||
this.beanDefinition.setSynthetic(true);
|
||||
compile(
|
||||
(actual, compiled) -> assertThat(actual.isSynthetic()).isTrue());
|
||||
compile((actual, compiled) -> assertThat(actual.isSynthetic()).isTrue());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -196,8 +196,7 @@ class BeanDefinitionPropertiesCodeGeneratorTests {
|
||||
void setRoleWhenInfrastructure() {
|
||||
this.beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
compile((actual, compiled) -> {
|
||||
assertThat(compiled.getSourceFile())
|
||||
.contains("setRole(BeanDefinition.ROLE_INFRASTRUCTURE);");
|
||||
assertThat(compiled.getSourceFile()).contains("setRole(BeanDefinition.ROLE_INFRASTRUCTURE);");
|
||||
assertThat(actual.getRole()).isEqualTo(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
});
|
||||
}
|
||||
@@ -206,8 +205,7 @@ class BeanDefinitionPropertiesCodeGeneratorTests {
|
||||
void setRoleWhenSupport() {
|
||||
this.beanDefinition.setRole(BeanDefinition.ROLE_SUPPORT);
|
||||
compile((actual, compiled) -> {
|
||||
assertThat(compiled.getSourceFile())
|
||||
.contains("setRole(BeanDefinition.ROLE_SUPPORT);");
|
||||
assertThat(compiled.getSourceFile()).contains("setRole(BeanDefinition.ROLE_SUPPORT);");
|
||||
assertThat(actual.getRole()).isEqualTo(BeanDefinition.ROLE_SUPPORT);
|
||||
});
|
||||
}
|
||||
@@ -215,81 +213,16 @@ class BeanDefinitionPropertiesCodeGeneratorTests {
|
||||
@Test
|
||||
void setRoleWhenOther() {
|
||||
this.beanDefinition.setRole(999);
|
||||
compile(
|
||||
(actual, compiled) -> assertThat(actual.getRole()).isEqualTo(999));
|
||||
}
|
||||
|
||||
@Test
|
||||
void setInitMethodWhenSingleInitMethod() {
|
||||
this.beanDefinition.setTargetType(InitDestroyBean.class);
|
||||
this.beanDefinition.setInitMethodName("i1");
|
||||
compile((actual, compiled) -> assertThat(actual.getInitMethodNames())
|
||||
.containsExactly("i1"));
|
||||
String[] methodNames = { "i1" };
|
||||
assertHasMethodInvokeHints(InitDestroyBean.class, methodNames);
|
||||
}
|
||||
|
||||
@Test
|
||||
void setInitMethodWhenNoInitMethod() {
|
||||
this.beanDefinition.setTargetType(InitDestroyBean.class);
|
||||
compile((actual, compiled) -> assertThat(actual.getInitMethodNames()).isNull());
|
||||
}
|
||||
|
||||
@Test
|
||||
void setInitMethodWhenMultipleInitMethods() {
|
||||
this.beanDefinition.setTargetType(InitDestroyBean.class);
|
||||
this.beanDefinition.setInitMethodNames("i1", "i2");
|
||||
compile((actual, compiled) -> assertThat(actual.getInitMethodNames())
|
||||
.containsExactly("i1", "i2"));
|
||||
String[] methodNames = { "i1", "i2" };
|
||||
assertHasMethodInvokeHints(InitDestroyBean.class, methodNames);
|
||||
}
|
||||
|
||||
@Test
|
||||
void setDestroyMethodWhenDestroyInitMethod() {
|
||||
this.beanDefinition.setTargetType(InitDestroyBean.class);
|
||||
this.beanDefinition.setDestroyMethodName("d1");
|
||||
compile(
|
||||
(actual, compiled) -> assertThat(actual.getDestroyMethodNames())
|
||||
.containsExactly("d1"));
|
||||
String[] methodNames = { "d1" };
|
||||
assertHasMethodInvokeHints(InitDestroyBean.class, methodNames);
|
||||
}
|
||||
|
||||
@Test
|
||||
void setDestroyMethodWhenNoDestroyMethod() {
|
||||
this.beanDefinition.setTargetType(InitDestroyBean.class);
|
||||
compile((actual, compiled) -> assertThat(actual.getDestroyMethodNames()).isNull());
|
||||
}
|
||||
|
||||
@Test
|
||||
void setDestroyMethodWhenMultipleDestroyMethods() {
|
||||
this.beanDefinition.setTargetType(InitDestroyBean.class);
|
||||
this.beanDefinition.setDestroyMethodNames("d1", "d2");
|
||||
compile(
|
||||
(actual, compiled) -> assertThat(actual.getDestroyMethodNames())
|
||||
.containsExactly("d1", "d2"));
|
||||
String[] methodNames = { "d1", "d2" };
|
||||
assertHasMethodInvokeHints(InitDestroyBean.class, methodNames);
|
||||
}
|
||||
|
||||
private void assertHasMethodInvokeHints(Class<?> beanType, String... methodNames) {
|
||||
assertThat(methodNames).allMatch(methodName -> RuntimeHintsPredicates.reflection()
|
||||
.onMethod(beanType, methodName).invoke()
|
||||
.test(this.generationContext.getRuntimeHints()));
|
||||
compile((actual, compiled) -> assertThat(actual.getRole()).isEqualTo(999));
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructorArgumentValuesWhenValues() {
|
||||
this.beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0,
|
||||
String.class);
|
||||
this.beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(1,
|
||||
"test");
|
||||
this.beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(2,
|
||||
123);
|
||||
this.beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, String.class);
|
||||
this.beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(1, "test");
|
||||
this.beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(2, 123);
|
||||
compile((actual, compiled) -> {
|
||||
Map<Integer, ValueHolder> values = actual.getConstructorArgumentValues()
|
||||
.getIndexedArgumentValues();
|
||||
Map<Integer, ValueHolder> values = actual.getConstructorArgumentValues().getIndexedArgumentValues();
|
||||
assertThat(values.get(0).getValue()).isEqualTo(String.class);
|
||||
assertThat(values.get(1).getValue()).isEqualTo("test");
|
||||
assertThat(values.get(2).getValue()).isEqualTo(123);
|
||||
@@ -305,20 +238,17 @@ class BeanDefinitionPropertiesCodeGeneratorTests {
|
||||
assertThat(actual.getPropertyValues().get("test")).isEqualTo(String.class);
|
||||
assertThat(actual.getPropertyValues().get("spring")).isEqualTo("framework");
|
||||
});
|
||||
String[] methodNames = { "setTest", "setSpring" };
|
||||
assertHasMethodInvokeHints(PropertyValuesBean.class, methodNames);
|
||||
assertHasMethodInvokeHints(PropertyValuesBean.class, "setTest", "setSpring");
|
||||
}
|
||||
|
||||
@Test
|
||||
void propertyValuesWhenContainsBeanReference() {
|
||||
this.beanDefinition.getPropertyValues().add("myService",
|
||||
new RuntimeBeanNameReference("test"));
|
||||
this.beanDefinition.getPropertyValues().add("myService", new RuntimeBeanNameReference("test"));
|
||||
compile((actual, compiled) -> {
|
||||
assertThat(actual.getPropertyValues().contains("myService")).isTrue();
|
||||
assertThat(actual.getPropertyValues().get("myService"))
|
||||
.isInstanceOfSatisfying(RuntimeBeanReference.class,
|
||||
beanReference -> assertThat(beanReference.getBeanName())
|
||||
.isEqualTo("test"));
|
||||
beanReference -> assertThat(beanReference.getBeanName()).isEqualTo("test"));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -342,8 +272,7 @@ class BeanDefinitionPropertiesCodeGeneratorTests {
|
||||
compile((actual, compiled) -> {
|
||||
Object value = actual.getPropertyValues().get("value");
|
||||
assertThat(value).isInstanceOf(ManagedSet.class);
|
||||
assertThat(((Set<?>) value).iterator().next())
|
||||
.isInstanceOf(BeanReference.class);
|
||||
assertThat(((Set<?>) value).iterator().next()).isInstanceOf(BeanReference.class);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -369,8 +298,7 @@ class BeanDefinitionPropertiesCodeGeneratorTests {
|
||||
assertThat(actual.getPropertyValues().get("prefix")).isEqualTo("Hello");
|
||||
assertThat(actual.getPropertyValues().get("name")).isEqualTo("World");
|
||||
});
|
||||
String[] methodNames = { "setPrefix", "setName" };
|
||||
assertHasMethodInvokeHints(PropertyValuesFactoryBean.class, methodNames);
|
||||
assertHasMethodInvokeHints(PropertyValuesFactoryBean.class, "setPrefix", "setName" );
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -445,13 +373,82 @@ class BeanDefinitionPropertiesCodeGeneratorTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Nested
|
||||
class InitDestroyMethodTests {
|
||||
|
||||
private final String privateInitMethod = InitDestroyBean.class.getName() + ".privateInit";
|
||||
private final String privateDestroyMethod = InitDestroyBean.class.getName() + ".privateDestroy";
|
||||
|
||||
@BeforeEach
|
||||
void setTargetType() {
|
||||
beanDefinition.setTargetType(InitDestroyBean.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void noInitMethod() {
|
||||
compile((beanDef, compiled) -> assertThat(beanDef.getInitMethodNames()).isNull());
|
||||
}
|
||||
|
||||
@Test
|
||||
void singleInitMethod() {
|
||||
beanDefinition.setInitMethodName("init");
|
||||
compile((beanDef, compiled) -> assertThat(beanDef.getInitMethodNames()).containsExactly("init"));
|
||||
assertHasMethodInvokeHints(InitDestroyBean.class, "init");
|
||||
}
|
||||
|
||||
@Test
|
||||
void privateInitMethod() {
|
||||
beanDefinition.setInitMethodName(privateInitMethod);
|
||||
compile((beanDef, compiled) -> assertThat(beanDef.getInitMethodNames()).containsExactly(privateInitMethod));
|
||||
assertHasMethodInvokeHints(InitDestroyBean.class, "privateInit");
|
||||
}
|
||||
|
||||
@Test
|
||||
void multipleInitMethods() {
|
||||
beanDefinition.setInitMethodNames("init", privateInitMethod);
|
||||
compile((beanDef, compiled) -> assertThat(beanDef.getInitMethodNames()).containsExactly("init", privateInitMethod));
|
||||
assertHasMethodInvokeHints(InitDestroyBean.class, "init", "privateInit");
|
||||
}
|
||||
|
||||
@Test
|
||||
void noDestroyMethod() {
|
||||
compile((beanDef, compiled) -> assertThat(beanDef.getDestroyMethodNames()).isNull());
|
||||
}
|
||||
|
||||
@Test
|
||||
void singleDestroyMethod() {
|
||||
beanDefinition.setDestroyMethodName("destroy");
|
||||
compile((beanDef, compiled) -> assertThat(beanDef.getDestroyMethodNames()).containsExactly("destroy"));
|
||||
assertHasMethodInvokeHints(InitDestroyBean.class, "destroy");
|
||||
}
|
||||
|
||||
@Test
|
||||
void privateDestroyMethod() {
|
||||
beanDefinition.setDestroyMethodName(privateDestroyMethod);
|
||||
compile((beanDef, compiled) -> assertThat(beanDef.getDestroyMethodNames()).containsExactly(privateDestroyMethod));
|
||||
assertHasMethodInvokeHints(InitDestroyBean.class, "privateDestroy");
|
||||
}
|
||||
|
||||
@Test
|
||||
void multipleDestroyMethods() {
|
||||
beanDefinition.setDestroyMethodNames("destroy", privateDestroyMethod);
|
||||
compile((beanDef, compiled) -> assertThat(beanDef.getDestroyMethodNames()).containsExactly("destroy", privateDestroyMethod));
|
||||
assertHasMethodInvokeHints(InitDestroyBean.class, "destroy", "privateDestroy");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void assertHasMethodInvokeHints(Class<?> beanType, String... methodNames) {
|
||||
assertThat(methodNames).allMatch(methodName -> RuntimeHintsPredicates.reflection()
|
||||
.onMethod(beanType, methodName).invoke()
|
||||
.test(this.generationContext.getRuntimeHints()));
|
||||
}
|
||||
|
||||
private void compile(BiConsumer<RootBeanDefinition, Compiled> result) {
|
||||
compile(attribute -> true, result);
|
||||
}
|
||||
|
||||
private void compile(
|
||||
Predicate<String> attributeFilter,
|
||||
BiConsumer<RootBeanDefinition, Compiled> result) {
|
||||
private void compile(Predicate<String> attributeFilter, BiConsumer<RootBeanDefinition, Compiled> result) {
|
||||
DeferredTypeBuilder typeBuilder = new DeferredTypeBuilder();
|
||||
GeneratedClass generatedClass = this.generationContext.getGeneratedClasses().addForFeature("TestCode", typeBuilder);
|
||||
BeanDefinitionPropertiesCodeGenerator codeGenerator = new BeanDefinitionPropertiesCodeGenerator(
|
||||
@@ -471,24 +468,25 @@ class BeanDefinitionPropertiesCodeGeneratorTests {
|
||||
});
|
||||
this.generationContext.writeGeneratedContent();
|
||||
TestCompiler.forSystem().with(this.generationContext).compile(compiled -> {
|
||||
RootBeanDefinition suppliedBeanDefinition = (RootBeanDefinition) compiled
|
||||
.getInstance(Supplier.class).get();
|
||||
RootBeanDefinition suppliedBeanDefinition = (RootBeanDefinition) compiled.getInstance(Supplier.class).get();
|
||||
result.accept(suppliedBeanDefinition, compiled);
|
||||
});
|
||||
}
|
||||
|
||||
static class InitDestroyBean {
|
||||
|
||||
void i1() {
|
||||
void init() {
|
||||
}
|
||||
|
||||
void i2() {
|
||||
@SuppressWarnings("unused")
|
||||
private void privateInit() {
|
||||
}
|
||||
|
||||
void d1() {
|
||||
void destroy() {
|
||||
}
|
||||
|
||||
void d2() {
|
||||
@SuppressWarnings("unused")
|
||||
private void privateDestroy() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+4
@@ -186,6 +186,8 @@ class BeanInstanceSupplierTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Deprecated
|
||||
@SuppressWarnings("removal")
|
||||
void withGeneratorWhenSupplierIsNullThrowsException() {
|
||||
BeanInstanceSupplier<Object> resolver = BeanInstanceSupplier
|
||||
.forConstructor();
|
||||
@@ -245,6 +247,8 @@ class BeanInstanceSupplierTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Deprecated
|
||||
@SuppressWarnings("removal")
|
||||
void getWithGeneratorCallsSupplier() throws Exception {
|
||||
BeanRegistrar registrar = new BeanRegistrar(SingleArgConstructor.class);
|
||||
this.beanFactory.registerSingleton("one", "1");
|
||||
|
||||
+4
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -172,8 +172,9 @@ class DefaultBeanRegistrationCodeFragmentsTests {
|
||||
}
|
||||
|
||||
private RegisteredBean registerTestBean(ResolvableType beanType) {
|
||||
this.beanFactory.registerBeanDefinition("testBean",
|
||||
new RootBeanDefinition(beanType));
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition();
|
||||
beanDefinition.setTargetType(beanType);
|
||||
this.beanFactory.registerBeanDefinition("testBean", beanDefinition);
|
||||
return RegisteredBean.of(this.beanFactory, "testBean");
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -223,7 +223,7 @@ class InstanceSupplierCodeGeneratorTests {
|
||||
assertThat(bean).isInstanceOf(Integer.class);
|
||||
assertThat(bean).isEqualTo(42);
|
||||
assertThat(compiled.getSourceFile())
|
||||
.contains("SimpleConfiguration::integerBean");
|
||||
.contains("(registeredBean) -> SimpleConfiguration.integerBean()");
|
||||
});
|
||||
assertThat(getReflectionHints().getTypeHint(SimpleConfiguration.class))
|
||||
.satisfies(hasMethodWithMode(ExecutableMode.INTROSPECT));
|
||||
|
||||
+2
-4
@@ -66,8 +66,7 @@ public class CustomScopeConfigurerTests {
|
||||
CustomScopeConfigurer figurer = new CustomScopeConfigurer();
|
||||
figurer.setScopes(scopes);
|
||||
figurer.postProcessBeanFactory(factory);
|
||||
boolean condition = factory.getRegisteredScope(FOO_SCOPE) instanceof NoOpScope;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(factory.getRegisteredScope(FOO_SCOPE) instanceof NoOpScope).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -77,8 +76,7 @@ public class CustomScopeConfigurerTests {
|
||||
CustomScopeConfigurer figurer = new CustomScopeConfigurer();
|
||||
figurer.setScopes(scopes);
|
||||
figurer.postProcessBeanFactory(factory);
|
||||
boolean condition = factory.getRegisteredScope(FOO_SCOPE) instanceof NoOpScope;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(factory.getRegisteredScope(FOO_SCOPE) instanceof NoOpScope).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+8
-8
@@ -135,9 +135,9 @@ public class MethodInvokingFactoryBeanTests {
|
||||
mcfb.setTargetMethod("method1");
|
||||
mcfb.afterPropertiesSet();
|
||||
Integer i = (Integer) mcfb.getObject();
|
||||
assertThat(i.intValue()).isEqualTo(1);
|
||||
assertThat(i).isEqualTo(1);
|
||||
i = (Integer) mcfb.getObject();
|
||||
assertThat(i.intValue()).isEqualTo(1);
|
||||
assertThat(i).isEqualTo(1);
|
||||
|
||||
// non-singleton, non-static
|
||||
tc1 = new TestClass1();
|
||||
@@ -147,9 +147,9 @@ public class MethodInvokingFactoryBeanTests {
|
||||
mcfb.setSingleton(false);
|
||||
mcfb.afterPropertiesSet();
|
||||
i = (Integer) mcfb.getObject();
|
||||
assertThat(i.intValue()).isEqualTo(1);
|
||||
assertThat(i).isEqualTo(1);
|
||||
i = (Integer) mcfb.getObject();
|
||||
assertThat(i.intValue()).isEqualTo(2);
|
||||
assertThat(i).isEqualTo(2);
|
||||
|
||||
// singleton, static
|
||||
TestClass1._staticField1 = 0;
|
||||
@@ -158,9 +158,9 @@ public class MethodInvokingFactoryBeanTests {
|
||||
mcfb.setTargetMethod("staticMethod1");
|
||||
mcfb.afterPropertiesSet();
|
||||
i = (Integer) mcfb.getObject();
|
||||
assertThat(i.intValue()).isEqualTo(1);
|
||||
assertThat(i).isEqualTo(1);
|
||||
i = (Integer) mcfb.getObject();
|
||||
assertThat(i.intValue()).isEqualTo(1);
|
||||
assertThat(i).isEqualTo(1);
|
||||
|
||||
// non-singleton, static
|
||||
TestClass1._staticField1 = 0;
|
||||
@@ -169,9 +169,9 @@ public class MethodInvokingFactoryBeanTests {
|
||||
mcfb.setSingleton(false);
|
||||
mcfb.afterPropertiesSet();
|
||||
i = (Integer) mcfb.getObject();
|
||||
assertThat(i.intValue()).isEqualTo(1);
|
||||
assertThat(i).isEqualTo(1);
|
||||
i = (Integer) mcfb.getObject();
|
||||
assertThat(i.intValue()).isEqualTo(2);
|
||||
assertThat(i).isEqualTo(2);
|
||||
|
||||
// void return value
|
||||
mcfb = new MethodInvokingFactoryBean();
|
||||
|
||||
+5
-10
@@ -49,8 +49,7 @@ public class PropertyPathFactoryBeanTests {
|
||||
assertThat(xbf.getType("otb.spouse")).isEqualTo(ITestBean.class);
|
||||
Object result1 = xbf.getBean("otb.spouse");
|
||||
Object result2 = xbf.getBean("otb.spouse");
|
||||
boolean condition = result1 instanceof TestBean;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(result1 instanceof TestBean).isTrue();
|
||||
assertThat(result1).isSameAs(result2);
|
||||
assertThat(((TestBean) result1).getAge()).isEqualTo(99);
|
||||
}
|
||||
@@ -64,12 +63,9 @@ public class PropertyPathFactoryBeanTests {
|
||||
Object result1 = xbf.getBean("tb.spouse");
|
||||
Object result2 = xbf.getBean("propertyPath3");
|
||||
Object result3 = xbf.getBean("propertyPath3");
|
||||
boolean condition2 = result1 instanceof TestBean;
|
||||
assertThat(condition2).isTrue();
|
||||
boolean condition1 = result2 instanceof TestBean;
|
||||
assertThat(condition1).isTrue();
|
||||
boolean condition = result3 instanceof TestBean;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(result1 instanceof TestBean).isTrue();
|
||||
assertThat(result2 instanceof TestBean).isTrue();
|
||||
assertThat(result3 instanceof TestBean).isTrue();
|
||||
assertThat(((TestBean) result1).getAge()).isEqualTo(11);
|
||||
assertThat(((TestBean) result2).getAge()).isEqualTo(11);
|
||||
assertThat(((TestBean) result3).getAge()).isEqualTo(11);
|
||||
@@ -93,8 +89,7 @@ public class PropertyPathFactoryBeanTests {
|
||||
TestBean spouse = (TestBean) xbf.getBean("otb.spouse");
|
||||
TestBean tbWithInner = (TestBean) xbf.getBean("tbWithInner");
|
||||
assertThat(tbWithInner.getSpouse()).isSameAs(spouse);
|
||||
boolean condition = !tbWithInner.getFriends().isEmpty();
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(!tbWithInner.getFriends().isEmpty()).isTrue();
|
||||
assertThat(tbWithInner.getFriends().iterator().next()).isSameAs(spouse);
|
||||
}
|
||||
|
||||
|
||||
+4
-12
@@ -198,14 +198,10 @@ public class ServiceLocatorFactoryBeanTests {
|
||||
assertThat(testBean3).isNotSameAs(testBean2);
|
||||
assertThat(testBean4).isNotSameAs(testBean2);
|
||||
assertThat(testBean4).isNotSameAs(testBean3);
|
||||
boolean condition3 = testBean1 instanceof ExtendedTestService;
|
||||
assertThat(condition3).isFalse();
|
||||
boolean condition2 = testBean2 instanceof ExtendedTestService;
|
||||
assertThat(condition2).isFalse();
|
||||
boolean condition1 = testBean3 instanceof ExtendedTestService;
|
||||
assertThat(condition1).isFalse();
|
||||
boolean condition = testBean4 instanceof ExtendedTestService;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(testBean1 instanceof ExtendedTestService).isFalse();
|
||||
assertThat(testBean2 instanceof ExtendedTestService).isFalse();
|
||||
assertThat(testBean3 instanceof ExtendedTestService).isFalse();
|
||||
assertThat(testBean4 instanceof ExtendedTestService).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -264,17 +260,14 @@ public class ServiceLocatorFactoryBeanTests {
|
||||
|
||||
|
||||
public static class TestService {
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static class ExtendedTestService extends TestService {
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static class TestService2 {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -345,7 +338,6 @@ public class ServiceLocatorFactoryBeanTests {
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public static class ExceptionClassWithOnlyZeroArgCtor extends Exception {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+3
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -99,8 +99,7 @@ public class YamlMapFactoryBeanTests {
|
||||
assertThat(map).hasSize(1);
|
||||
assertThat(map.containsKey("foo")).isTrue();
|
||||
Object object = map.get("foo");
|
||||
boolean condition = object instanceof LinkedHashMap;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(object instanceof LinkedHashMap).isTrue();
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> sub = (Map<String, Object>) object;
|
||||
assertThat(sub.containsKey("key1.key2")).isTrue();
|
||||
@@ -115,8 +114,7 @@ public class YamlMapFactoryBeanTests {
|
||||
assertThat(map).hasSize(1);
|
||||
assertThat(map.containsKey("foo")).isTrue();
|
||||
Object object = map.get("foo");
|
||||
boolean condition = object instanceof LinkedHashMap;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(object instanceof LinkedHashMap).isTrue();
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> sub = (Map<String, Object>) object;
|
||||
assertThat(sub).hasSize(1);
|
||||
|
||||
+4
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -48,8 +48,7 @@ class ServiceLoaderTests {
|
||||
bd.getPropertyValues().add("serviceType", DocumentBuilderFactory.class.getName());
|
||||
bf.registerBeanDefinition("service", bd);
|
||||
ServiceLoader<?> serviceLoader = (ServiceLoader<?>) bf.getBean("service");
|
||||
boolean condition = serviceLoader.iterator().next() instanceof DocumentBuilderFactory;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(serviceLoader.iterator().next() instanceof DocumentBuilderFactory).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -58,8 +57,7 @@ class ServiceLoaderTests {
|
||||
RootBeanDefinition bd = new RootBeanDefinition(ServiceFactoryBean.class);
|
||||
bd.getPropertyValues().add("serviceType", DocumentBuilderFactory.class.getName());
|
||||
bf.registerBeanDefinition("service", bd);
|
||||
boolean condition = bf.getBean("service") instanceof DocumentBuilderFactory;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(bf.getBean("service") instanceof DocumentBuilderFactory).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -69,8 +67,7 @@ class ServiceLoaderTests {
|
||||
bd.getPropertyValues().add("serviceType", DocumentBuilderFactory.class.getName());
|
||||
bf.registerBeanDefinition("service", bd);
|
||||
List<?> serviceList = (List<?>) bf.getBean("service");
|
||||
boolean condition = serviceList.get(0) instanceof DocumentBuilderFactory;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(serviceList.get(0) instanceof DocumentBuilderFactory).isTrue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+18
-36
@@ -35,10 +35,8 @@ public class BeanDefinitionTests {
|
||||
bd.setLazyInit(true);
|
||||
bd.setScope("request");
|
||||
RootBeanDefinition otherBd = new RootBeanDefinition(TestBean.class);
|
||||
boolean condition1 = !bd.equals(otherBd);
|
||||
assertThat(condition1).isTrue();
|
||||
boolean condition = !otherBd.equals(bd);
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(!bd.equals(otherBd)).isTrue();
|
||||
assertThat(!otherBd.equals(bd)).isTrue();
|
||||
otherBd.setAbstract(true);
|
||||
otherBd.setLazyInit(true);
|
||||
otherBd.setScope("request");
|
||||
@@ -54,15 +52,11 @@ public class BeanDefinitionTests {
|
||||
bd.getPropertyValues().add("age", "99");
|
||||
RootBeanDefinition otherBd = new RootBeanDefinition(TestBean.class);
|
||||
otherBd.getPropertyValues().add("name", "myName");
|
||||
boolean condition3 = !bd.equals(otherBd);
|
||||
assertThat(condition3).isTrue();
|
||||
boolean condition2 = !otherBd.equals(bd);
|
||||
assertThat(condition2).isTrue();
|
||||
assertThat(!bd.equals(otherBd)).isTrue();
|
||||
assertThat(!otherBd.equals(bd)).isTrue();
|
||||
otherBd.getPropertyValues().add("age", "11");
|
||||
boolean condition1 = !bd.equals(otherBd);
|
||||
assertThat(condition1).isTrue();
|
||||
boolean condition = !otherBd.equals(bd);
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(!bd.equals(otherBd)).isTrue();
|
||||
assertThat(!otherBd.equals(bd)).isTrue();
|
||||
otherBd.getPropertyValues().add("age", "99");
|
||||
assertThat(bd.equals(otherBd)).isTrue();
|
||||
assertThat(otherBd.equals(bd)).isTrue();
|
||||
@@ -76,15 +70,11 @@ public class BeanDefinitionTests {
|
||||
bd.getConstructorArgumentValues().addIndexedArgumentValue(1, 5);
|
||||
RootBeanDefinition otherBd = new RootBeanDefinition(TestBean.class);
|
||||
otherBd.getConstructorArgumentValues().addGenericArgumentValue("test");
|
||||
boolean condition3 = !bd.equals(otherBd);
|
||||
assertThat(condition3).isTrue();
|
||||
boolean condition2 = !otherBd.equals(bd);
|
||||
assertThat(condition2).isTrue();
|
||||
assertThat(!bd.equals(otherBd)).isTrue();
|
||||
assertThat(!otherBd.equals(bd)).isTrue();
|
||||
otherBd.getConstructorArgumentValues().addIndexedArgumentValue(1, 9);
|
||||
boolean condition1 = !bd.equals(otherBd);
|
||||
assertThat(condition1).isTrue();
|
||||
boolean condition = !otherBd.equals(bd);
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(!bd.equals(otherBd)).isTrue();
|
||||
assertThat(!otherBd.equals(bd)).isTrue();
|
||||
otherBd.getConstructorArgumentValues().addIndexedArgumentValue(1, 5);
|
||||
assertThat(bd.equals(otherBd)).isTrue();
|
||||
assertThat(otherBd.equals(bd)).isTrue();
|
||||
@@ -99,15 +89,11 @@ public class BeanDefinitionTests {
|
||||
RootBeanDefinition otherBd = new RootBeanDefinition(TestBean.class);
|
||||
otherBd.getConstructorArgumentValues().addGenericArgumentValue("test", "int");
|
||||
otherBd.getConstructorArgumentValues().addIndexedArgumentValue(1, 5);
|
||||
boolean condition3 = !bd.equals(otherBd);
|
||||
assertThat(condition3).isTrue();
|
||||
boolean condition2 = !otherBd.equals(bd);
|
||||
assertThat(condition2).isTrue();
|
||||
assertThat(!bd.equals(otherBd)).isTrue();
|
||||
assertThat(!otherBd.equals(bd)).isTrue();
|
||||
otherBd.getConstructorArgumentValues().addIndexedArgumentValue(1, 5, "int");
|
||||
boolean condition1 = !bd.equals(otherBd);
|
||||
assertThat(condition1).isTrue();
|
||||
boolean condition = !otherBd.equals(bd);
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(!bd.equals(otherBd)).isTrue();
|
||||
assertThat(!otherBd.equals(bd)).isTrue();
|
||||
otherBd.getConstructorArgumentValues().addIndexedArgumentValue(1, 5, "long");
|
||||
assertThat(bd.equals(otherBd)).isTrue();
|
||||
assertThat(otherBd.equals(bd)).isTrue();
|
||||
@@ -125,10 +111,8 @@ public class BeanDefinitionTests {
|
||||
otherBd.setScope("request");
|
||||
otherBd.setAbstract(true);
|
||||
otherBd.setLazyInit(true);
|
||||
boolean condition1 = !bd.equals(otherBd);
|
||||
assertThat(condition1).isTrue();
|
||||
boolean condition = !otherBd.equals(bd);
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(!bd.equals(otherBd)).isTrue();
|
||||
assertThat(!otherBd.equals(bd)).isTrue();
|
||||
otherBd.setParentName("parent");
|
||||
assertThat(bd.equals(otherBd)).isTrue();
|
||||
assertThat(otherBd.equals(bd)).isTrue();
|
||||
@@ -153,10 +137,8 @@ public class BeanDefinitionTests {
|
||||
bd.setScope("request");
|
||||
BeanDefinitionHolder holder = new BeanDefinitionHolder(bd, "bd");
|
||||
RootBeanDefinition otherBd = new RootBeanDefinition(TestBean.class);
|
||||
boolean condition1 = !bd.equals(otherBd);
|
||||
assertThat(condition1).isTrue();
|
||||
boolean condition = !otherBd.equals(bd);
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(!bd.equals(otherBd)).isTrue();
|
||||
assertThat(!otherBd.equals(bd)).isTrue();
|
||||
otherBd.setAbstract(true);
|
||||
otherBd.setLazyInit(true);
|
||||
otherBd.setScope("request");
|
||||
|
||||
+4
-8
@@ -422,10 +422,8 @@ class BeanFactoryGenericsTests {
|
||||
bf.registerBeanDefinition("genericBean", rbd);
|
||||
GenericBean<?> gb = (GenericBean<?>) bf.getBean("genericBean");
|
||||
|
||||
boolean condition1 = gb.getCollectionMap().get(1) instanceof HashSet;
|
||||
assertThat(condition1).isTrue();
|
||||
boolean condition = gb.getCollectionMap().get(2) instanceof ArrayList;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(gb.getCollectionMap().get(1) instanceof HashSet).isTrue();
|
||||
assertThat(gb.getCollectionMap().get(2) instanceof ArrayList).isTrue();
|
||||
}
|
||||
|
||||
|
||||
@@ -577,10 +575,8 @@ class BeanFactoryGenericsTests {
|
||||
bf.registerBeanDefinition("genericBean", rbd);
|
||||
GenericBean<?> gb = (GenericBean<?>) bf.getBean("genericBean");
|
||||
|
||||
boolean condition1 = gb.getCollectionMap().get(1) instanceof HashSet;
|
||||
assertThat(condition1).isTrue();
|
||||
boolean condition = gb.getCollectionMap().get(2) instanceof ArrayList;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(gb.getCollectionMap().get(1) instanceof HashSet).isTrue();
|
||||
assertThat(gb.getCollectionMap().get(2) instanceof ArrayList).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+12
-20
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -28,12 +28,13 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* @author Chris Beams
|
||||
* @since 04.07.2006
|
||||
*/
|
||||
public class DefaultSingletonBeanRegistryTests {
|
||||
class DefaultSingletonBeanRegistryTests {
|
||||
|
||||
private final DefaultSingletonBeanRegistry beanRegistry = new DefaultSingletonBeanRegistry();
|
||||
|
||||
|
||||
@Test
|
||||
public void testSingletons() {
|
||||
DefaultSingletonBeanRegistry beanRegistry = new DefaultSingletonBeanRegistry();
|
||||
|
||||
void singletons() {
|
||||
TestBean tb = new TestBean();
|
||||
beanRegistry.registerSingleton("tb", tb);
|
||||
assertThat(beanRegistry.getSingleton("tb")).isSameAs(tb);
|
||||
@@ -44,20 +45,15 @@ public class DefaultSingletonBeanRegistryTests {
|
||||
assertThat(beanRegistry.getSingleton("tb")).isSameAs(tb);
|
||||
assertThat(beanRegistry.getSingleton("tb2")).isSameAs(tb2);
|
||||
assertThat(beanRegistry.getSingletonCount()).isEqualTo(2);
|
||||
String[] names = beanRegistry.getSingletonNames();
|
||||
assertThat(names).hasSize(2);
|
||||
assertThat(names[0]).isEqualTo("tb");
|
||||
assertThat(names[1]).isEqualTo("tb2");
|
||||
assertThat(beanRegistry.getSingletonNames()).containsExactly("tb", "tb2");
|
||||
|
||||
beanRegistry.destroySingletons();
|
||||
assertThat(beanRegistry.getSingletonCount()).isEqualTo(0);
|
||||
assertThat(beanRegistry.getSingletonCount()).isZero();
|
||||
assertThat(beanRegistry.getSingletonNames()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDisposableBean() {
|
||||
DefaultSingletonBeanRegistry beanRegistry = new DefaultSingletonBeanRegistry();
|
||||
|
||||
void disposableBean() {
|
||||
DerivedTestBean tb = new DerivedTestBean();
|
||||
beanRegistry.registerSingleton("tb", tb);
|
||||
beanRegistry.registerDisposableBean("tb", tb);
|
||||
@@ -65,21 +61,17 @@ public class DefaultSingletonBeanRegistryTests {
|
||||
|
||||
assertThat(beanRegistry.getSingleton("tb")).isSameAs(tb);
|
||||
assertThat(beanRegistry.getSingletonCount()).isEqualTo(1);
|
||||
String[] names = beanRegistry.getSingletonNames();
|
||||
assertThat(names).hasSize(1);
|
||||
assertThat(names[0]).isEqualTo("tb");
|
||||
assertThat(beanRegistry.getSingletonNames()).containsExactly("tb");
|
||||
assertThat(tb.wasDestroyed()).isFalse();
|
||||
|
||||
beanRegistry.destroySingletons();
|
||||
assertThat(beanRegistry.getSingletonCount()).isEqualTo(0);
|
||||
assertThat(beanRegistry.getSingletonCount()).isZero();
|
||||
assertThat(beanRegistry.getSingletonNames()).isEmpty();
|
||||
assertThat(tb.wasDestroyed()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDependentRegistration() {
|
||||
DefaultSingletonBeanRegistry beanRegistry = new DefaultSingletonBeanRegistry();
|
||||
|
||||
void dependentRegistration() {
|
||||
beanRegistry.registerDependentBean("a", "b");
|
||||
beanRegistry.registerDependentBean("b", "c");
|
||||
beanRegistry.registerDependentBean("c", "b");
|
||||
|
||||
+2
-4
@@ -337,16 +337,14 @@ public class FactoryMethodTests {
|
||||
// Check that listInstance is not considered a bean of type FactoryMethods.
|
||||
assertThat(List.class.isAssignableFrom(xbf.getType("listInstance"))).isTrue();
|
||||
String[] names = xbf.getBeanNamesForType(FactoryMethods.class);
|
||||
boolean condition1 = !Arrays.asList(names).contains("listInstance");
|
||||
assertThat(condition1).isTrue();
|
||||
assertThat(Arrays.asList(names).contains("listInstance")).isFalse();
|
||||
names = xbf.getBeanNamesForType(List.class);
|
||||
assertThat(Arrays.asList(names).contains("listInstance")).isTrue();
|
||||
|
||||
xbf.preInstantiateSingletons();
|
||||
assertThat(List.class.isAssignableFrom(xbf.getType("listInstance"))).isTrue();
|
||||
names = xbf.getBeanNamesForType(FactoryMethods.class);
|
||||
boolean condition = !Arrays.asList(names).contains("listInstance");
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(Arrays.asList(names).contains("listInstance")).isFalse();
|
||||
names = xbf.getBeanNamesForType(List.class);
|
||||
assertThat(Arrays.asList(names).contains("listInstance")).isTrue();
|
||||
List<?> list = (List<?>) xbf.getBean("listInstance");
|
||||
|
||||
+2
-2
@@ -65,13 +65,13 @@ public class UtilNamespaceHandlerTests {
|
||||
@Test
|
||||
void testConstant() {
|
||||
Integer min = (Integer) this.beanFactory.getBean("min");
|
||||
assertThat(min.intValue()).isEqualTo(Integer.MIN_VALUE);
|
||||
assertThat(min).isEqualTo(Integer.MIN_VALUE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testConstantWithDefaultName() {
|
||||
Integer max = (Integer) this.beanFactory.getBean("java.lang.Integer.MAX_VALUE");
|
||||
assertThat(max.intValue()).isEqualTo(Integer.MAX_VALUE);
|
||||
assertThat(max).isEqualTo(Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+44
-56
@@ -70,24 +70,21 @@ public class XmlBeanCollectionTests {
|
||||
ListFactoryBean listFactory = new ListFactoryBean();
|
||||
listFactory.setSourceList(new LinkedList());
|
||||
listFactory.afterPropertiesSet();
|
||||
boolean condition2 = listFactory.getObject() instanceof ArrayList;
|
||||
assertThat(condition2).isTrue();
|
||||
assertThat(listFactory.getObject() instanceof ArrayList).isTrue();
|
||||
|
||||
SetFactoryBean setFactory = new SetFactoryBean();
|
||||
setFactory.setSourceSet(new TreeSet());
|
||||
setFactory.afterPropertiesSet();
|
||||
boolean condition1 = setFactory.getObject() instanceof LinkedHashSet;
|
||||
assertThat(condition1).isTrue();
|
||||
assertThat(setFactory.getObject() instanceof LinkedHashSet).isTrue();
|
||||
|
||||
MapFactoryBean mapFactory = new MapFactoryBean();
|
||||
mapFactory.setSourceMap(new TreeMap());
|
||||
mapFactory.afterPropertiesSet();
|
||||
boolean condition = mapFactory.getObject() instanceof LinkedHashMap;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(mapFactory.getObject() instanceof LinkedHashMap).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefSubelement() throws Exception {
|
||||
public void testRefSubelement() {
|
||||
//assertTrue("5 beans in reftypes, not " + this.beanFactory.getBeanDefinitionCount(), this.beanFactory.getBeanDefinitionCount() == 5);
|
||||
TestBean jen = (TestBean) this.beanFactory.getBean("jenny");
|
||||
TestBean dave = (TestBean) this.beanFactory.getBean("david");
|
||||
@@ -95,25 +92,25 @@ public class XmlBeanCollectionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPropertyWithLiteralValueSubelement() throws Exception {
|
||||
public void testPropertyWithLiteralValueSubelement() {
|
||||
TestBean verbose = (TestBean) this.beanFactory.getBean("verbose");
|
||||
assertThat(verbose.getName()).isEqualTo("verbose");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPropertyWithIdRefLocalAttrSubelement() throws Exception {
|
||||
public void testPropertyWithIdRefLocalAttrSubelement() {
|
||||
TestBean verbose = (TestBean) this.beanFactory.getBean("verbose2");
|
||||
assertThat(verbose.getName()).isEqualTo("verbose");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPropertyWithIdRefBeanAttrSubelement() throws Exception {
|
||||
public void testPropertyWithIdRefBeanAttrSubelement() {
|
||||
TestBean verbose = (TestBean) this.beanFactory.getBean("verbose3");
|
||||
assertThat(verbose.getName()).isEqualTo("verbose");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefSubelementsBuildCollection() throws Exception {
|
||||
public void testRefSubelementsBuildCollection() {
|
||||
TestBean jen = (TestBean) this.beanFactory.getBean("jenny");
|
||||
TestBean dave = (TestBean) this.beanFactory.getBean("david");
|
||||
TestBean rod = (TestBean) this.beanFactory.getBean("rod");
|
||||
@@ -130,7 +127,7 @@ public class XmlBeanCollectionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefSubelementsBuildCollectionWithPrototypes() throws Exception {
|
||||
public void testRefSubelementsBuildCollectionWithPrototypes() {
|
||||
TestBean jen = (TestBean) this.beanFactory.getBean("pJenny");
|
||||
TestBean dave = (TestBean) this.beanFactory.getBean("pDavid");
|
||||
TestBean rod = (TestBean) this.beanFactory.getBean("pRod");
|
||||
@@ -153,7 +150,7 @@ public class XmlBeanCollectionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefSubelementsBuildCollectionFromSingleElement() throws Exception {
|
||||
public void testRefSubelementsBuildCollectionFromSingleElement() {
|
||||
TestBean loner = (TestBean) this.beanFactory.getBean("loner");
|
||||
TestBean dave = (TestBean) this.beanFactory.getBean("david");
|
||||
assertThat(loner.getFriends().size()).isEqualTo(1);
|
||||
@@ -161,7 +158,7 @@ public class XmlBeanCollectionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuildCollectionFromMixtureOfReferencesAndValues() throws Exception {
|
||||
public void testBuildCollectionFromMixtureOfReferencesAndValues() {
|
||||
MixedCollectionBean jumble = (MixedCollectionBean) this.beanFactory.getBean("jumble");
|
||||
assertThat(jumble.getJumble().size()).as("Expected 5 elements, not " + jumble.getJumble().size()).isEqualTo(5);
|
||||
List l = (List) jumble.getJumble();
|
||||
@@ -175,7 +172,7 @@ public class XmlBeanCollectionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidBeanNameReference() throws Exception {
|
||||
public void testInvalidBeanNameReference() {
|
||||
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() ->
|
||||
this.beanFactory.getBean("jumble2"))
|
||||
.withCauseInstanceOf(BeanDefinitionStoreException.class)
|
||||
@@ -183,13 +180,13 @@ public class XmlBeanCollectionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyMap() throws Exception {
|
||||
public void testEmptyMap() {
|
||||
HasMap hasMap = (HasMap) this.beanFactory.getBean("emptyMap");
|
||||
assertThat(hasMap.getMap().size()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMapWithLiteralsOnly() throws Exception {
|
||||
public void testMapWithLiteralsOnly() {
|
||||
HasMap hasMap = (HasMap) this.beanFactory.getBean("literalMap");
|
||||
assertThat(hasMap.getMap().size()).isEqualTo(3);
|
||||
assertThat(hasMap.getMap().get("foo").equals("bar")).isTrue();
|
||||
@@ -198,23 +195,21 @@ public class XmlBeanCollectionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMapWithLiteralsAndReferences() throws Exception {
|
||||
public void testMapWithLiteralsAndReferences() {
|
||||
HasMap hasMap = (HasMap) this.beanFactory.getBean("mixedMap");
|
||||
assertThat(hasMap.getMap().size()).isEqualTo(5);
|
||||
assertThat(hasMap.getMap().get("foo").equals(10)).isTrue();
|
||||
TestBean jenny = (TestBean) this.beanFactory.getBean("jenny");
|
||||
assertThat(hasMap.getMap().get("jenny")).isSameAs(jenny);
|
||||
assertThat(hasMap.getMap().get(5).equals("david")).isTrue();
|
||||
boolean condition1 = hasMap.getMap().get("bar") instanceof Long;
|
||||
assertThat(condition1).isTrue();
|
||||
assertThat(hasMap.getMap().get("bar") instanceof Long).isTrue();
|
||||
assertThat(hasMap.getMap().get("bar").equals(100L)).isTrue();
|
||||
boolean condition = hasMap.getMap().get("baz") instanceof Integer;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(hasMap.getMap().get("baz") instanceof Integer).isTrue();
|
||||
assertThat(hasMap.getMap().get("baz").equals(200)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMapWithLiteralsAndPrototypeReferences() throws Exception {
|
||||
public void testMapWithLiteralsAndPrototypeReferences() {
|
||||
TestBean jenny = (TestBean) this.beanFactory.getBean("pJenny");
|
||||
HasMap hasMap = (HasMap) this.beanFactory.getBean("pMixedMap");
|
||||
assertThat(hasMap.getMap().size()).isEqualTo(2);
|
||||
@@ -230,7 +225,7 @@ public class XmlBeanCollectionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMapWithLiteralsReferencesAndList() throws Exception {
|
||||
public void testMapWithLiteralsReferencesAndList() {
|
||||
HasMap hasMap = (HasMap) this.beanFactory.getBean("mixedMapWithList");
|
||||
assertThat(hasMap.getMap().size()).isEqualTo(4);
|
||||
assertThat(hasMap.getMap().get(null).equals("bar")).isTrue();
|
||||
@@ -267,13 +262,13 @@ public class XmlBeanCollectionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptySet() throws Exception {
|
||||
public void testEmptySet() {
|
||||
HasMap hasMap = (HasMap) this.beanFactory.getBean("emptySet");
|
||||
assertThat(hasMap.getSet().size()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPopulatedSet() throws Exception {
|
||||
public void testPopulatedSet() {
|
||||
HasMap hasMap = (HasMap) this.beanFactory.getBean("set");
|
||||
assertThat(hasMap.getSet().size()).isEqualTo(3);
|
||||
assertThat(hasMap.getSet().contains("bar")).isTrue();
|
||||
@@ -287,7 +282,7 @@ public class XmlBeanCollectionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPopulatedConcurrentSet() throws Exception {
|
||||
public void testPopulatedConcurrentSet() {
|
||||
HasMap hasMap = (HasMap) this.beanFactory.getBean("concurrentSet");
|
||||
assertThat(hasMap.getConcurrentSet().size()).isEqualTo(3);
|
||||
assertThat(hasMap.getConcurrentSet().contains("bar")).isTrue();
|
||||
@@ -297,7 +292,7 @@ public class XmlBeanCollectionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPopulatedIdentityMap() throws Exception {
|
||||
public void testPopulatedIdentityMap() {
|
||||
HasMap hasMap = (HasMap) this.beanFactory.getBean("identityMap");
|
||||
assertThat(hasMap.getIdentityMap().size()).isEqualTo(2);
|
||||
HashSet set = new HashSet(hasMap.getIdentityMap().keySet());
|
||||
@@ -306,14 +301,14 @@ public class XmlBeanCollectionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyProps() throws Exception {
|
||||
public void testEmptyProps() {
|
||||
HasMap hasMap = (HasMap) this.beanFactory.getBean("emptyProps");
|
||||
assertThat(hasMap.getProps().size()).isEqualTo(0);
|
||||
assertThat(Properties.class).isEqualTo(hasMap.getProps().getClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPopulatedProps() throws Exception {
|
||||
public void testPopulatedProps() {
|
||||
HasMap hasMap = (HasMap) this.beanFactory.getBean("props");
|
||||
assertThat(hasMap.getProps().size()).isEqualTo(2);
|
||||
assertThat(hasMap.getProps().get("foo").equals("bar")).isTrue();
|
||||
@@ -321,7 +316,7 @@ public class XmlBeanCollectionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testObjectArray() throws Exception {
|
||||
public void testObjectArray() {
|
||||
HasMap hasMap = (HasMap) this.beanFactory.getBean("objectArray");
|
||||
assertThat(hasMap.getObjectArray().length).isEqualTo(2);
|
||||
assertThat(hasMap.getObjectArray()[0].equals("one")).isTrue();
|
||||
@@ -329,7 +324,7 @@ public class XmlBeanCollectionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIntegerArray() throws Exception {
|
||||
public void testIntegerArray() {
|
||||
HasMap hasMap = (HasMap) this.beanFactory.getBean("integerArray");
|
||||
assertThat(hasMap.getIntegerArray().length).isEqualTo(3);
|
||||
assertThat(hasMap.getIntegerArray()[0]).isEqualTo(0);
|
||||
@@ -338,7 +333,7 @@ public class XmlBeanCollectionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClassArray() throws Exception {
|
||||
public void testClassArray() {
|
||||
HasMap hasMap = (HasMap) this.beanFactory.getBean("classArray");
|
||||
assertThat(hasMap.getClassArray().length).isEqualTo(2);
|
||||
assertThat(hasMap.getClassArray()[0].equals(String.class)).isTrue();
|
||||
@@ -346,7 +341,7 @@ public class XmlBeanCollectionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClassList() throws Exception {
|
||||
public void testClassList() {
|
||||
HasMap hasMap = (HasMap) this.beanFactory.getBean("classList");
|
||||
assertThat(hasMap.getClassList().size()).isEqualTo(2);
|
||||
assertThat(hasMap.getClassList().get(0).equals(String.class)).isTrue();
|
||||
@@ -354,7 +349,7 @@ public class XmlBeanCollectionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProps() throws Exception {
|
||||
public void testProps() {
|
||||
HasMap hasMap = (HasMap) this.beanFactory.getBean("props");
|
||||
assertThat(hasMap.getProps()).hasSize(2);
|
||||
assertThat(hasMap.getProps().getProperty("foo")).isEqualTo("bar");
|
||||
@@ -367,60 +362,54 @@ public class XmlBeanCollectionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListFactory() throws Exception {
|
||||
public void testListFactory() {
|
||||
List list = (List) this.beanFactory.getBean("listFactory");
|
||||
boolean condition = list instanceof LinkedList;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(list instanceof LinkedList).isTrue();
|
||||
assertThat(list.size()).isEqualTo(2);
|
||||
assertThat(list.get(0)).isEqualTo("bar");
|
||||
assertThat(list.get(1)).isEqualTo("jenny");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrototypeListFactory() throws Exception {
|
||||
public void testPrototypeListFactory() {
|
||||
List list = (List) this.beanFactory.getBean("pListFactory");
|
||||
boolean condition = list instanceof LinkedList;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(list instanceof LinkedList).isTrue();
|
||||
assertThat(list.size()).isEqualTo(2);
|
||||
assertThat(list.get(0)).isEqualTo("bar");
|
||||
assertThat(list.get(1)).isEqualTo("jenny");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetFactory() throws Exception {
|
||||
public void testSetFactory() {
|
||||
Set set = (Set) this.beanFactory.getBean("setFactory");
|
||||
boolean condition = set instanceof TreeSet;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(set instanceof TreeSet).isTrue();
|
||||
assertThat(set.size()).isEqualTo(2);
|
||||
assertThat(set.contains("bar")).isTrue();
|
||||
assertThat(set.contains("jenny")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrototypeSetFactory() throws Exception {
|
||||
public void testPrototypeSetFactory() {
|
||||
Set set = (Set) this.beanFactory.getBean("pSetFactory");
|
||||
boolean condition = set instanceof TreeSet;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(set instanceof TreeSet).isTrue();
|
||||
assertThat(set.size()).isEqualTo(2);
|
||||
assertThat(set.contains("bar")).isTrue();
|
||||
assertThat(set.contains("jenny")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMapFactory() throws Exception {
|
||||
public void testMapFactory() {
|
||||
Map map = (Map) this.beanFactory.getBean("mapFactory");
|
||||
boolean condition = map instanceof TreeMap;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(map instanceof TreeMap).isTrue();
|
||||
assertThat(map.size()).isEqualTo(2);
|
||||
assertThat(map.get("foo")).isEqualTo("bar");
|
||||
assertThat(map.get("jen")).isEqualTo("jenny");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrototypeMapFactory() throws Exception {
|
||||
public void testPrototypeMapFactory() {
|
||||
Map map = (Map) this.beanFactory.getBean("pMapFactory");
|
||||
boolean condition = map instanceof TreeMap;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(map instanceof TreeMap).isTrue();
|
||||
assertThat(map.size()).isEqualTo(2);
|
||||
assertThat(map.get("foo")).isEqualTo("bar");
|
||||
assertThat(map.get("jen")).isEqualTo("jenny");
|
||||
@@ -429,8 +418,7 @@ public class XmlBeanCollectionTests {
|
||||
@Test
|
||||
public void testChoiceBetweenSetAndMap() {
|
||||
MapAndSet sam = (MapAndSet) this.beanFactory.getBean("setAndMap");
|
||||
boolean condition = sam.getObject() instanceof Map;
|
||||
assertThat(condition).as("Didn't choose constructor with Map argument").isTrue();
|
||||
assertThat(sam.getObject() instanceof Map).as("Didn't choose constructor with Map argument").isTrue();
|
||||
Map map = (Map) sam.getObject();
|
||||
assertThat(map).hasSize(3);
|
||||
assertThat(map.get("key1")).isEqualTo("val1");
|
||||
@@ -439,7 +427,7 @@ public class XmlBeanCollectionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEnumSetFactory() throws Exception {
|
||||
public void testEnumSetFactory() {
|
||||
Set set = (Set) this.beanFactory.getBean("enumSetFactory");
|
||||
assertThat(set.size()).isEqualTo(2);
|
||||
assertThat(set.contains("ONE")).isTrue();
|
||||
|
||||
+8
-10
@@ -34,33 +34,32 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
public class CustomCollectionEditorTests {
|
||||
|
||||
@Test
|
||||
public void testCtorWithNullCollectionType() throws Exception {
|
||||
public void testCtorWithNullCollectionType() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
new CustomCollectionEditor(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public void testCtorWithNonCollectionType() throws Exception {
|
||||
public void testCtorWithNonCollectionType() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
new CustomCollectionEditor((Class) String.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithCollectionTypeThatDoesNotExposeAPublicNoArgCtor() throws Exception {
|
||||
public void testWithCollectionTypeThatDoesNotExposeAPublicNoArgCtor() {
|
||||
CustomCollectionEditor editor = new CustomCollectionEditor(CollectionTypeWithNoNoArgCtor.class);
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
editor.setValue("1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSunnyDaySetValue() throws Exception {
|
||||
public void testSunnyDaySetValue() {
|
||||
CustomCollectionEditor editor = new CustomCollectionEditor(ArrayList.class);
|
||||
editor.setValue(new int[] {0, 1, 2});
|
||||
Object value = editor.getValue();
|
||||
assertThat(value).isNotNull();
|
||||
boolean condition = value instanceof ArrayList;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(value instanceof ArrayList).isTrue();
|
||||
List<?> list = (List<?>) value;
|
||||
assertThat(list).as("There must be 3 elements in the converted collection").hasSize(3);
|
||||
assertThat(list.get(0)).isEqualTo(0);
|
||||
@@ -69,7 +68,7 @@ public class CustomCollectionEditorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWhenTargetTypeIsExactlyTheCollectionInterfaceUsesFallbackCollectionType() throws Exception {
|
||||
public void testWhenTargetTypeIsExactlyTheCollectionInterfaceUsesFallbackCollectionType() {
|
||||
CustomCollectionEditor editor = new CustomCollectionEditor(Collection.class);
|
||||
editor.setValue("0, 1, 2");
|
||||
Collection<?> value = (Collection<?>) editor.getValue();
|
||||
@@ -79,13 +78,12 @@ public class CustomCollectionEditorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSunnyDaySetAsTextYieldsSingleValue() throws Exception {
|
||||
public void testSunnyDaySetAsTextYieldsSingleValue() {
|
||||
CustomCollectionEditor editor = new CustomCollectionEditor(ArrayList.class);
|
||||
editor.setValue("0, 1, 2");
|
||||
Object value = editor.getValue();
|
||||
assertThat(value).isNotNull();
|
||||
boolean condition = value instanceof ArrayList;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(value instanceof ArrayList).isTrue();
|
||||
List<?> list = (List<?>) value;
|
||||
assertThat(list).as("There must be 1 element in the converted collection").hasSize(1);
|
||||
assertThat(list.get(0)).isEqualTo("0, 1, 2");
|
||||
|
||||
+27
-40
@@ -193,36 +193,31 @@ class CustomEditorTests {
|
||||
|
||||
bw.setPropertyValue("bool1", "false");
|
||||
assertThat(Boolean.FALSE.equals(bw.getPropertyValue("bool1"))).as("Correct bool1 value").isTrue();
|
||||
boolean condition4 = !tb.isBool1();
|
||||
assertThat(condition4).as("Correct bool1 value").isTrue();
|
||||
assertThat(!tb.isBool1()).as("Correct bool1 value").isTrue();
|
||||
|
||||
bw.setPropertyValue("bool1", " true ");
|
||||
assertThat(tb.isBool1()).as("Correct bool1 value").isTrue();
|
||||
|
||||
bw.setPropertyValue("bool1", " false ");
|
||||
boolean condition3 = !tb.isBool1();
|
||||
assertThat(condition3).as("Correct bool1 value").isTrue();
|
||||
assertThat(!tb.isBool1()).as("Correct bool1 value").isTrue();
|
||||
|
||||
bw.setPropertyValue("bool1", "on");
|
||||
assertThat(tb.isBool1()).as("Correct bool1 value").isTrue();
|
||||
|
||||
bw.setPropertyValue("bool1", "off");
|
||||
boolean condition2 = !tb.isBool1();
|
||||
assertThat(condition2).as("Correct bool1 value").isTrue();
|
||||
assertThat(!tb.isBool1()).as("Correct bool1 value").isTrue();
|
||||
|
||||
bw.setPropertyValue("bool1", "yes");
|
||||
assertThat(tb.isBool1()).as("Correct bool1 value").isTrue();
|
||||
|
||||
bw.setPropertyValue("bool1", "no");
|
||||
boolean condition1 = !tb.isBool1();
|
||||
assertThat(condition1).as("Correct bool1 value").isTrue();
|
||||
assertThat(!tb.isBool1()).as("Correct bool1 value").isTrue();
|
||||
|
||||
bw.setPropertyValue("bool1", "1");
|
||||
assertThat(tb.isBool1()).as("Correct bool1 value").isTrue();
|
||||
|
||||
bw.setPropertyValue("bool1", "0");
|
||||
boolean condition = !tb.isBool1();
|
||||
assertThat(condition).as("Correct bool1 value").isTrue();
|
||||
assertThat(!tb.isBool1()).as("Correct bool1 value").isTrue();
|
||||
|
||||
assertThatExceptionOfType(BeansException.class).isThrownBy(() ->
|
||||
bw.setPropertyValue("bool1", "argh"));
|
||||
@@ -239,29 +234,25 @@ class CustomEditorTests {
|
||||
|
||||
bw.setPropertyValue("bool2", "false");
|
||||
assertThat(Boolean.FALSE.equals(bw.getPropertyValue("bool2"))).as("Correct bool2 value").isTrue();
|
||||
boolean condition3 = !tb.getBool2();
|
||||
assertThat(condition3).as("Correct bool2 value").isTrue();
|
||||
assertThat(!tb.getBool2()).as("Correct bool2 value").isTrue();
|
||||
|
||||
bw.setPropertyValue("bool2", "on");
|
||||
assertThat(tb.getBool2().booleanValue()).as("Correct bool2 value").isTrue();
|
||||
|
||||
bw.setPropertyValue("bool2", "off");
|
||||
boolean condition2 = !tb.getBool2();
|
||||
assertThat(condition2).as("Correct bool2 value").isTrue();
|
||||
assertThat(!tb.getBool2()).as("Correct bool2 value").isTrue();
|
||||
|
||||
bw.setPropertyValue("bool2", "yes");
|
||||
assertThat(tb.getBool2().booleanValue()).as("Correct bool2 value").isTrue();
|
||||
|
||||
bw.setPropertyValue("bool2", "no");
|
||||
boolean condition1 = !tb.getBool2();
|
||||
assertThat(condition1).as("Correct bool2 value").isTrue();
|
||||
assertThat(!tb.getBool2()).as("Correct bool2 value").isTrue();
|
||||
|
||||
bw.setPropertyValue("bool2", "1");
|
||||
assertThat(tb.getBool2().booleanValue()).as("Correct bool2 value").isTrue();
|
||||
|
||||
bw.setPropertyValue("bool2", "0");
|
||||
boolean condition = !tb.getBool2();
|
||||
assertThat(condition).as("Correct bool2 value").isTrue();
|
||||
assertThat(!tb.getBool2()).as("Correct bool2 value").isTrue();
|
||||
|
||||
bw.setPropertyValue("bool2", "");
|
||||
assertThat(tb.getBool2()).as("Correct bool2 value").isNull();
|
||||
@@ -279,29 +270,25 @@ class CustomEditorTests {
|
||||
|
||||
bw.setPropertyValue("bool2", "false");
|
||||
assertThat(Boolean.FALSE.equals(bw.getPropertyValue("bool2"))).as("Correct bool2 value").isTrue();
|
||||
boolean condition3 = !tb.getBool2();
|
||||
assertThat(condition3).as("Correct bool2 value").isTrue();
|
||||
assertThat(!tb.getBool2()).as("Correct bool2 value").isTrue();
|
||||
|
||||
bw.setPropertyValue("bool2", "on");
|
||||
assertThat(tb.getBool2().booleanValue()).as("Correct bool2 value").isTrue();
|
||||
|
||||
bw.setPropertyValue("bool2", "off");
|
||||
boolean condition2 = !tb.getBool2();
|
||||
assertThat(condition2).as("Correct bool2 value").isTrue();
|
||||
assertThat(!tb.getBool2()).as("Correct bool2 value").isTrue();
|
||||
|
||||
bw.setPropertyValue("bool2", "yes");
|
||||
assertThat(tb.getBool2().booleanValue()).as("Correct bool2 value").isTrue();
|
||||
|
||||
bw.setPropertyValue("bool2", "no");
|
||||
boolean condition1 = !tb.getBool2();
|
||||
assertThat(condition1).as("Correct bool2 value").isTrue();
|
||||
assertThat(!tb.getBool2()).as("Correct bool2 value").isTrue();
|
||||
|
||||
bw.setPropertyValue("bool2", "1");
|
||||
assertThat(tb.getBool2().booleanValue()).as("Correct bool2 value").isTrue();
|
||||
|
||||
bw.setPropertyValue("bool2", "0");
|
||||
boolean condition = !tb.getBool2();
|
||||
assertThat(condition).as("Correct bool2 value").isTrue();
|
||||
assertThat(!tb.getBool2()).as("Correct bool2 value").isTrue();
|
||||
|
||||
bw.setPropertyValue("bool2", "");
|
||||
assertThat(bw.getPropertyValue("bool2")).as("Correct bool2 value").isNull();
|
||||
@@ -309,7 +296,7 @@ class CustomEditorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomBooleanEditorWithSpecialTrueAndFalseStrings() throws Exception {
|
||||
void testCustomBooleanEditorWithSpecialTrueAndFalseStrings() {
|
||||
String trueString = "pechorin";
|
||||
String falseString = "nash";
|
||||
|
||||
@@ -458,7 +445,7 @@ class CustomEditorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomNumberEditorWithFrenchBigDecimal() throws Exception {
|
||||
void testCustomNumberEditorWithFrenchBigDecimal() {
|
||||
NumberFormat nf = NumberFormat.getNumberInstance(Locale.FRENCH);
|
||||
NumberTestBean tb = new NumberTestBean();
|
||||
BeanWrapper bw = new BeanWrapperImpl(tb);
|
||||
@@ -542,14 +529,14 @@ class CustomEditorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCharacterEditorSetAsTextWithStringLongerThanOneCharacter() throws Exception {
|
||||
void testCharacterEditorSetAsTextWithStringLongerThanOneCharacter() {
|
||||
PropertyEditor charEditor = new CharacterEditor(false);
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
charEditor.setAsText("ColdWaterCanyon"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCharacterEditorGetAsTextReturnsEmptyStringIfValueIsNull() throws Exception {
|
||||
void testCharacterEditorGetAsTextReturnsEmptyStringIfValueIsNull() {
|
||||
PropertyEditor charEditor = new CharacterEditor(false);
|
||||
assertThat(charEditor.getAsText()).isEmpty();
|
||||
charEditor = new CharacterEditor(true);
|
||||
@@ -562,7 +549,7 @@ class CustomEditorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCharacterEditorSetAsTextWithNullNotAllowingEmptyAsNull() throws Exception {
|
||||
void testCharacterEditorSetAsTextWithNullNotAllowingEmptyAsNull() {
|
||||
PropertyEditor charEditor = new CharacterEditor(false);
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
charEditor.setAsText(null));
|
||||
@@ -584,7 +571,7 @@ class CustomEditorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testClassEditorWithNonExistentClass() throws Exception {
|
||||
void testClassEditorWithNonExistentClass() {
|
||||
PropertyEditor classEditor = new ClassEditor();
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
classEditor.setAsText("hairdresser.on.Fire"));
|
||||
@@ -602,7 +589,7 @@ class CustomEditorTests {
|
||||
* SPR_2165 - ClassEditor is inconsistent with multidimensional arrays
|
||||
*/
|
||||
@Test
|
||||
void testGetAsTextWithTwoDimensionalArray() throws Exception {
|
||||
void testGetAsTextWithTwoDimensionalArray() {
|
||||
String[][] chessboard = new String[8][8];
|
||||
ClassEditor editor = new ClassEditor();
|
||||
editor.setValue(chessboard.getClass());
|
||||
@@ -613,7 +600,7 @@ class CustomEditorTests {
|
||||
* SPR_2165 - ClassEditor is inconsistent with multidimensional arrays
|
||||
*/
|
||||
@Test
|
||||
void testGetAsTextWithRidiculousMultiDimensionalArray() throws Exception {
|
||||
void testGetAsTextWithRidiculousMultiDimensionalArray() {
|
||||
String[][][][][] ridiculousChessboard = new String[8][4][0][1][3];
|
||||
ClassEditor editor = new ClassEditor();
|
||||
editor.setValue(ridiculousChessboard.getClass());
|
||||
@@ -1413,7 +1400,7 @@ class CustomEditorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testClassArrayEditorSunnyDay() throws Exception {
|
||||
void testClassArrayEditorSunnyDay() {
|
||||
ClassArrayEditor classArrayEditor = new ClassArrayEditor();
|
||||
classArrayEditor.setAsText("java.lang.String,java.util.HashMap");
|
||||
Class<?>[] classes = (Class<?>[]) classArrayEditor.getValue();
|
||||
@@ -1426,7 +1413,7 @@ class CustomEditorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testClassArrayEditorSunnyDayWithArrayTypes() throws Exception {
|
||||
void testClassArrayEditorSunnyDayWithArrayTypes() {
|
||||
ClassArrayEditor classArrayEditor = new ClassArrayEditor();
|
||||
classArrayEditor.setAsText("java.lang.String[],java.util.Map[],int[],float[][][]");
|
||||
Class<?>[] classes = (Class<?>[]) classArrayEditor.getValue();
|
||||
@@ -1441,7 +1428,7 @@ class CustomEditorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testClassArrayEditorSetAsTextWithNull() throws Exception {
|
||||
void testClassArrayEditorSetAsTextWithNull() {
|
||||
ClassArrayEditor editor = new ClassArrayEditor();
|
||||
editor.setAsText(null);
|
||||
assertThat(editor.getValue()).isNull();
|
||||
@@ -1449,7 +1436,7 @@ class CustomEditorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testClassArrayEditorSetAsTextWithEmptyString() throws Exception {
|
||||
void testClassArrayEditorSetAsTextWithEmptyString() {
|
||||
ClassArrayEditor editor = new ClassArrayEditor();
|
||||
editor.setAsText("");
|
||||
assertThat(editor.getValue()).isNull();
|
||||
@@ -1457,7 +1444,7 @@ class CustomEditorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testClassArrayEditorSetAsTextWithWhitespaceString() throws Exception {
|
||||
void testClassArrayEditorSetAsTextWithWhitespaceString() {
|
||||
ClassArrayEditor editor = new ClassArrayEditor();
|
||||
editor.setAsText("\n");
|
||||
assertThat(editor.getValue()).isNull();
|
||||
@@ -1465,7 +1452,7 @@ class CustomEditorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCharsetEditor() throws Exception {
|
||||
void testCharsetEditor() {
|
||||
CharsetEditor editor = new CharsetEditor();
|
||||
String name = "UTF-8";
|
||||
editor.setAsText(name);
|
||||
|
||||
+13
-20
@@ -34,57 +34,51 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
public class FileEditorTests {
|
||||
|
||||
@Test
|
||||
public void testClasspathFileName() throws Exception {
|
||||
public void testClasspathFileName() {
|
||||
PropertyEditor fileEditor = new FileEditor();
|
||||
fileEditor.setAsText("classpath:" + ClassUtils.classPackageAsResourcePath(getClass()) + "/" +
|
||||
ClassUtils.getShortName(getClass()) + ".class");
|
||||
Object value = fileEditor.getValue();
|
||||
boolean condition = value instanceof File;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(value instanceof File).isTrue();
|
||||
File file = (File) value;
|
||||
assertThat(file).exists();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithNonExistentResource() throws Exception {
|
||||
public void testWithNonExistentResource() {
|
||||
PropertyEditor propertyEditor = new FileEditor();
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
propertyEditor.setAsText("classpath:no_way_this_file_is_found.doc"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithNonExistentFile() throws Exception {
|
||||
public void testWithNonExistentFile() {
|
||||
PropertyEditor fileEditor = new FileEditor();
|
||||
fileEditor.setAsText("file:no_way_this_file_is_found.doc");
|
||||
Object value = fileEditor.getValue();
|
||||
boolean condition1 = value instanceof File;
|
||||
assertThat(condition1).isTrue();
|
||||
assertThat(value instanceof File).isTrue();
|
||||
File file = (File) value;
|
||||
boolean condition = !file.exists();
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(file).doesNotExist();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAbsoluteFileName() throws Exception {
|
||||
public void testAbsoluteFileName() {
|
||||
PropertyEditor fileEditor = new FileEditor();
|
||||
fileEditor.setAsText("/no_way_this_file_is_found.doc");
|
||||
Object value = fileEditor.getValue();
|
||||
boolean condition1 = value instanceof File;
|
||||
assertThat(condition1).isTrue();
|
||||
assertThat(value instanceof File).isTrue();
|
||||
File file = (File) value;
|
||||
boolean condition = !file.exists();
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(file).doesNotExist();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnqualifiedFileNameFound() throws Exception {
|
||||
public void testUnqualifiedFileNameFound() {
|
||||
PropertyEditor fileEditor = new FileEditor();
|
||||
String fileName = ClassUtils.classPackageAsResourcePath(getClass()) + "/" +
|
||||
ClassUtils.getShortName(getClass()) + ".class";
|
||||
fileEditor.setAsText(fileName);
|
||||
Object value = fileEditor.getValue();
|
||||
boolean condition = value instanceof File;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(value instanceof File).isTrue();
|
||||
File file = (File) value;
|
||||
assertThat(file).exists();
|
||||
String absolutePath = file.getAbsolutePath().replace('\\', '/');
|
||||
@@ -92,14 +86,13 @@ public class FileEditorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnqualifiedFileNameNotFound() throws Exception {
|
||||
public void testUnqualifiedFileNameNotFound() {
|
||||
PropertyEditor fileEditor = new FileEditor();
|
||||
String fileName = ClassUtils.classPackageAsResourcePath(getClass()) + "/" +
|
||||
ClassUtils.getShortName(getClass()) + ".clazz";
|
||||
fileEditor.setAsText(fileName);
|
||||
Object value = fileEditor.getValue();
|
||||
boolean condition = value instanceof File;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(value instanceof File).isTrue();
|
||||
File file = (File) value;
|
||||
assertThat(file).doesNotExist();
|
||||
String absolutePath = file.getAbsolutePath().replace('\\', '/');
|
||||
|
||||
+6
-6
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.beans.propertyeditors;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -34,13 +35,13 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
public class InputStreamEditorTests {
|
||||
|
||||
@Test
|
||||
public void testCtorWithNullResourceEditor() throws Exception {
|
||||
public void testCtorWithNullResourceEditor() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
new InputStreamEditor(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSunnyDay() throws Exception {
|
||||
public void testSunnyDay() throws IOException {
|
||||
InputStream stream = null;
|
||||
try {
|
||||
String resource = "classpath:" + ClassUtils.classPackageAsResourcePath(getClass()) +
|
||||
@@ -49,8 +50,7 @@ public class InputStreamEditorTests {
|
||||
editor.setAsText(resource);
|
||||
Object value = editor.getValue();
|
||||
assertThat(value).isNotNull();
|
||||
boolean condition = value instanceof InputStream;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(value instanceof InputStream).isTrue();
|
||||
stream = (InputStream) value;
|
||||
assertThat(stream.available()).isGreaterThan(0);
|
||||
}
|
||||
@@ -62,14 +62,14 @@ public class InputStreamEditorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWhenResourceDoesNotExist() throws Exception {
|
||||
public void testWhenResourceDoesNotExist() {
|
||||
InputStreamEditor editor = new InputStreamEditor();
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
editor.setAsText("classpath:bingo!"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAsTextReturnsNullByDefault() throws Exception {
|
||||
public void testGetAsTextReturnsNullByDefault() {
|
||||
assertThat(new InputStreamEditor().getAsText()).isNull();
|
||||
String resource = "classpath:" + ClassUtils.classPackageAsResourcePath(getClass()) +
|
||||
"/" + ClassUtils.getShortName(getClass()) + ".class";
|
||||
|
||||
+2
-3
@@ -151,7 +151,7 @@ public class PropertiesEditorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void usingMapAsValueSource() throws Exception {
|
||||
public void usingMapAsValueSource() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("one", "1");
|
||||
map.put("two", "2");
|
||||
@@ -160,8 +160,7 @@ public class PropertiesEditorTests {
|
||||
pe.setValue(map);
|
||||
Object value = pe.getValue();
|
||||
assertThat(value).isNotNull();
|
||||
boolean condition = value instanceof Properties;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(value instanceof Properties).isTrue();
|
||||
Properties props = (Properties) value;
|
||||
assertThat(props).hasSize(3);
|
||||
assertThat(props.getProperty("one")).isEqualTo("1");
|
||||
|
||||
+7
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.beans.propertyeditors;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -34,13 +35,13 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
public class ReaderEditorTests {
|
||||
|
||||
@Test
|
||||
public void testCtorWithNullResourceEditor() throws Exception {
|
||||
public void testCtorWithNullResourceEditor() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
new ReaderEditor(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSunnyDay() throws Exception {
|
||||
public void testSunnyDay() throws IOException {
|
||||
Reader reader = null;
|
||||
try {
|
||||
String resource = "classpath:" + ClassUtils.classPackageAsResourcePath(getClass()) +
|
||||
@@ -49,8 +50,7 @@ public class ReaderEditorTests {
|
||||
editor.setAsText(resource);
|
||||
Object value = editor.getValue();
|
||||
assertThat(value).isNotNull();
|
||||
boolean condition = value instanceof Reader;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(value instanceof Reader).isTrue();
|
||||
reader = (Reader) value;
|
||||
assertThat(reader.ready()).isTrue();
|
||||
}
|
||||
@@ -62,7 +62,7 @@ public class ReaderEditorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWhenResourceDoesNotExist() throws Exception {
|
||||
public void testWhenResourceDoesNotExist() {
|
||||
String resource = "classpath:bingo!";
|
||||
ReaderEditor editor = new ReaderEditor();
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
@@ -70,7 +70,7 @@ public class ReaderEditorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAsTextReturnsNullByDefault() throws Exception {
|
||||
public void testGetAsTextReturnsNullByDefault() {
|
||||
assertThat(new ReaderEditor().getAsText()).isNull();
|
||||
String resource = "classpath:" + ClassUtils.classPackageAsResourcePath(getClass()) +
|
||||
"/" + ClassUtils.getShortName(getClass()) + ".class";
|
||||
|
||||
+15
-20
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -37,93 +37,88 @@ public class ResourceBundleEditorTests {
|
||||
|
||||
|
||||
@Test
|
||||
public void testSetAsTextWithJustBaseName() throws Exception {
|
||||
public void testSetAsTextWithJustBaseName() {
|
||||
ResourceBundleEditor editor = new ResourceBundleEditor();
|
||||
editor.setAsText(BASE_NAME);
|
||||
Object value = editor.getValue();
|
||||
assertThat(value).as("Returned ResourceBundle was null (must not be for valid setAsText(..) call).").isNotNull();
|
||||
boolean condition = value instanceof ResourceBundle;
|
||||
assertThat(condition).as("Returned object was not a ResourceBundle (must be for valid setAsText(..) call).").isTrue();
|
||||
assertThat(value instanceof ResourceBundle).as("Returned object was not a ResourceBundle (must be for valid setAsText(..) call).").isTrue();
|
||||
ResourceBundle bundle = (ResourceBundle) value;
|
||||
String string = bundle.getString(MESSAGE_KEY);
|
||||
assertThat(string).isEqualTo(MESSAGE_KEY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetAsTextWithBaseNameThatEndsInDefaultSeparator() throws Exception {
|
||||
public void testSetAsTextWithBaseNameThatEndsInDefaultSeparator() {
|
||||
ResourceBundleEditor editor = new ResourceBundleEditor();
|
||||
editor.setAsText(BASE_NAME + "_");
|
||||
Object value = editor.getValue();
|
||||
assertThat(value).as("Returned ResourceBundle was null (must not be for valid setAsText(..) call).").isNotNull();
|
||||
boolean condition = value instanceof ResourceBundle;
|
||||
assertThat(condition).as("Returned object was not a ResourceBundle (must be for valid setAsText(..) call).").isTrue();
|
||||
assertThat(value instanceof ResourceBundle).as("Returned object was not a ResourceBundle (must be for valid setAsText(..) call).").isTrue();
|
||||
ResourceBundle bundle = (ResourceBundle) value;
|
||||
String string = bundle.getString(MESSAGE_KEY);
|
||||
assertThat(string).isEqualTo(MESSAGE_KEY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetAsTextWithBaseNameAndLanguageCode() throws Exception {
|
||||
public void testSetAsTextWithBaseNameAndLanguageCode() {
|
||||
ResourceBundleEditor editor = new ResourceBundleEditor();
|
||||
editor.setAsText(BASE_NAME + "Lang" + "_en");
|
||||
Object value = editor.getValue();
|
||||
assertThat(value).as("Returned ResourceBundle was null (must not be for valid setAsText(..) call).").isNotNull();
|
||||
boolean condition = value instanceof ResourceBundle;
|
||||
assertThat(condition).as("Returned object was not a ResourceBundle (must be for valid setAsText(..) call).").isTrue();
|
||||
assertThat(value instanceof ResourceBundle).as("Returned object was not a ResourceBundle (must be for valid setAsText(..) call).").isTrue();
|
||||
ResourceBundle bundle = (ResourceBundle) value;
|
||||
String string = bundle.getString(MESSAGE_KEY);
|
||||
assertThat(string).isEqualTo("yob");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetAsTextWithBaseNameLanguageAndCountryCode() throws Exception {
|
||||
public void testSetAsTextWithBaseNameLanguageAndCountryCode() {
|
||||
ResourceBundleEditor editor = new ResourceBundleEditor();
|
||||
editor.setAsText(BASE_NAME + "LangCountry" + "_en_GB");
|
||||
Object value = editor.getValue();
|
||||
assertThat(value).as("Returned ResourceBundle was null (must not be for valid setAsText(..) call).").isNotNull();
|
||||
boolean condition = value instanceof ResourceBundle;
|
||||
assertThat(condition).as("Returned object was not a ResourceBundle (must be for valid setAsText(..) call).").isTrue();
|
||||
assertThat(value instanceof ResourceBundle).as("Returned object was not a ResourceBundle (must be for valid setAsText(..) call).").isTrue();
|
||||
ResourceBundle bundle = (ResourceBundle) value;
|
||||
String string = bundle.getString(MESSAGE_KEY);
|
||||
assertThat(string).isEqualTo("chav");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetAsTextWithTheKitchenSink() throws Exception {
|
||||
public void testSetAsTextWithTheKitchenSink() {
|
||||
ResourceBundleEditor editor = new ResourceBundleEditor();
|
||||
editor.setAsText(BASE_NAME + "LangCountryDialect" + "_en_GB_GLASGOW");
|
||||
Object value = editor.getValue();
|
||||
assertThat(value).as("Returned ResourceBundle was null (must not be for valid setAsText(..) call).").isNotNull();
|
||||
boolean condition = value instanceof ResourceBundle;
|
||||
assertThat(condition).as("Returned object was not a ResourceBundle (must be for valid setAsText(..) call).").isTrue();
|
||||
assertThat(value instanceof ResourceBundle).as("Returned object was not a ResourceBundle (must be for valid setAsText(..) call).").isTrue();
|
||||
ResourceBundle bundle = (ResourceBundle) value;
|
||||
String string = bundle.getString(MESSAGE_KEY);
|
||||
assertThat(string).isEqualTo("ned");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetAsTextWithNull() throws Exception {
|
||||
public void testSetAsTextWithNull() {
|
||||
ResourceBundleEditor editor = new ResourceBundleEditor();
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
editor.setAsText(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetAsTextWithEmptyString() throws Exception {
|
||||
public void testSetAsTextWithEmptyString() {
|
||||
ResourceBundleEditor editor = new ResourceBundleEditor();
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
editor.setAsText(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetAsTextWithWhiteSpaceString() throws Exception {
|
||||
public void testSetAsTextWithWhiteSpaceString() {
|
||||
ResourceBundleEditor editor = new ResourceBundleEditor();
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
editor.setAsText(" "));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetAsTextWithJustSeparatorString() throws Exception {
|
||||
public void testSetAsTextWithJustSeparatorString() {
|
||||
ResourceBundleEditor editor = new ResourceBundleEditor();
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
editor.setAsText("_"));
|
||||
|
||||
+21
-30
@@ -32,78 +32,72 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
public class URIEditorTests {
|
||||
|
||||
@Test
|
||||
public void standardURI() throws Exception {
|
||||
public void standardURI() {
|
||||
doTestURI("mailto:juergen.hoeller@interface21.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withNonExistentResource() throws Exception {
|
||||
public void withNonExistentResource() {
|
||||
doTestURI("gonna:/freak/in/the/morning/freak/in/the.evening");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void standardURL() throws Exception {
|
||||
public void standardURL() {
|
||||
doTestURI("https://www.springframework.org");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void standardURLWithFragment() throws Exception {
|
||||
public void standardURLWithFragment() {
|
||||
doTestURI("https://www.springframework.org#1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void standardURLWithWhitespace() throws Exception {
|
||||
public void standardURLWithWhitespace() {
|
||||
PropertyEditor uriEditor = new URIEditor();
|
||||
uriEditor.setAsText(" https://www.springframework.org ");
|
||||
Object value = uriEditor.getValue();
|
||||
boolean condition = value instanceof URI;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(value instanceof URI).isTrue();
|
||||
URI uri = (URI) value;
|
||||
assertThat(uri.toString()).isEqualTo("https://www.springframework.org");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void classpathURL() throws Exception {
|
||||
public void classpathURL() {
|
||||
PropertyEditor uriEditor = new URIEditor(getClass().getClassLoader());
|
||||
uriEditor.setAsText("classpath:" + ClassUtils.classPackageAsResourcePath(getClass()) +
|
||||
"/" + ClassUtils.getShortName(getClass()) + ".class");
|
||||
Object value = uriEditor.getValue();
|
||||
boolean condition1 = value instanceof URI;
|
||||
assertThat(condition1).isTrue();
|
||||
assertThat(value instanceof URI).isTrue();
|
||||
URI uri = (URI) value;
|
||||
assertThat(uriEditor.getAsText()).isEqualTo(uri.toString());
|
||||
boolean condition = !uri.getScheme().startsWith("classpath");
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(uri.getScheme()).doesNotStartWith("classpath");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void classpathURLWithWhitespace() throws Exception {
|
||||
public void classpathURLWithWhitespace() {
|
||||
PropertyEditor uriEditor = new URIEditor(getClass().getClassLoader());
|
||||
uriEditor.setAsText(" classpath:" + ClassUtils.classPackageAsResourcePath(getClass()) +
|
||||
"/" + ClassUtils.getShortName(getClass()) + ".class ");
|
||||
Object value = uriEditor.getValue();
|
||||
boolean condition1 = value instanceof URI;
|
||||
assertThat(condition1).isTrue();
|
||||
assertThat(value instanceof URI).isTrue();
|
||||
URI uri = (URI) value;
|
||||
assertThat(uriEditor.getAsText()).isEqualTo(uri.toString());
|
||||
boolean condition = !uri.getScheme().startsWith("classpath");
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(uri.getScheme()).doesNotStartWith("classpath");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void classpathURLAsIs() throws Exception {
|
||||
public void classpathURLAsIs() {
|
||||
PropertyEditor uriEditor = new URIEditor();
|
||||
uriEditor.setAsText("classpath:test.txt");
|
||||
Object value = uriEditor.getValue();
|
||||
boolean condition = value instanceof URI;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(value instanceof URI).isTrue();
|
||||
URI uri = (URI) value;
|
||||
assertThat(uriEditor.getAsText()).isEqualTo(uri.toString());
|
||||
assertThat(uri.getScheme()).startsWith("classpath");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setAsTextWithNull() throws Exception {
|
||||
public void setAsTextWithNull() {
|
||||
PropertyEditor uriEditor = new URIEditor();
|
||||
uriEditor.setAsText(null);
|
||||
assertThat(uriEditor.getValue()).isNull();
|
||||
@@ -111,30 +105,28 @@ public class URIEditorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAsTextReturnsEmptyStringIfValueNotSet() throws Exception {
|
||||
public void getAsTextReturnsEmptyStringIfValueNotSet() {
|
||||
PropertyEditor uriEditor = new URIEditor();
|
||||
assertThat(uriEditor.getAsText()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeURI() throws Exception {
|
||||
public void encodeURI() {
|
||||
PropertyEditor uriEditor = new URIEditor();
|
||||
uriEditor.setAsText("https://example.com/spaces and \u20AC");
|
||||
Object value = uriEditor.getValue();
|
||||
boolean condition = value instanceof URI;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(value instanceof URI).isTrue();
|
||||
URI uri = (URI) value;
|
||||
assertThat(uriEditor.getAsText()).isEqualTo(uri.toString());
|
||||
assertThat(uri.toASCIIString()).isEqualTo("https://example.com/spaces%20and%20%E2%82%AC");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeAlreadyEncodedURI() throws Exception {
|
||||
public void encodeAlreadyEncodedURI() {
|
||||
PropertyEditor uriEditor = new URIEditor(false);
|
||||
uriEditor.setAsText("https://example.com/spaces%20and%20%E2%82%AC");
|
||||
Object value = uriEditor.getValue();
|
||||
boolean condition = value instanceof URI;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(value instanceof URI).isTrue();
|
||||
URI uri = (URI) value;
|
||||
assertThat(uriEditor.getAsText()).isEqualTo(uri.toString());
|
||||
assertThat(uri.toASCIIString()).isEqualTo("https://example.com/spaces%20and%20%E2%82%AC");
|
||||
@@ -145,8 +137,7 @@ public class URIEditorTests {
|
||||
PropertyEditor uriEditor = new URIEditor();
|
||||
uriEditor.setAsText(uriSpec);
|
||||
Object value = uriEditor.getValue();
|
||||
boolean condition = value instanceof URI;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(value instanceof URI).isTrue();
|
||||
URI uri = (URI) value;
|
||||
assertThat(uri.toString()).isEqualTo(uriSpec);
|
||||
}
|
||||
|
||||
+11
-15
@@ -33,56 +33,52 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
public class URLEditorTests {
|
||||
|
||||
@Test
|
||||
public void testCtorWithNullResourceEditor() throws Exception {
|
||||
public void testCtorWithNullResourceEditor() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
new URLEditor(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardURI() throws Exception {
|
||||
public void testStandardURI() {
|
||||
PropertyEditor urlEditor = new URLEditor();
|
||||
urlEditor.setAsText("mailto:juergen.hoeller@interface21.com");
|
||||
Object value = urlEditor.getValue();
|
||||
boolean condition = value instanceof URL;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(value instanceof URL).isTrue();
|
||||
URL url = (URL) value;
|
||||
assertThat(urlEditor.getAsText()).isEqualTo(url.toExternalForm());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardURL() throws Exception {
|
||||
public void testStandardURL() {
|
||||
PropertyEditor urlEditor = new URLEditor();
|
||||
urlEditor.setAsText("https://www.springframework.org");
|
||||
Object value = urlEditor.getValue();
|
||||
boolean condition = value instanceof URL;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(value instanceof URL).isTrue();
|
||||
URL url = (URL) value;
|
||||
assertThat(urlEditor.getAsText()).isEqualTo(url.toExternalForm());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClasspathURL() throws Exception {
|
||||
public void testClasspathURL() {
|
||||
PropertyEditor urlEditor = new URLEditor();
|
||||
urlEditor.setAsText("classpath:" + ClassUtils.classPackageAsResourcePath(getClass()) +
|
||||
"/" + ClassUtils.getShortName(getClass()) + ".class");
|
||||
Object value = urlEditor.getValue();
|
||||
boolean condition1 = value instanceof URL;
|
||||
assertThat(condition1).isTrue();
|
||||
assertThat(value instanceof URL).isTrue();
|
||||
URL url = (URL) value;
|
||||
assertThat(urlEditor.getAsText()).isEqualTo(url.toExternalForm());
|
||||
boolean condition = !url.getProtocol().startsWith("classpath");
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(url.getProtocol()).doesNotStartWith("classpath");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithNonExistentResource() throws Exception {
|
||||
public void testWithNonExistentResource() {
|
||||
PropertyEditor urlEditor = new URLEditor();
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
urlEditor.setAsText("gonna:/freak/in/the/morning/freak/in/the.evening"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetAsTextWithNull() throws Exception {
|
||||
public void testSetAsTextWithNull() {
|
||||
PropertyEditor urlEditor = new URLEditor();
|
||||
urlEditor.setAsText(null);
|
||||
assertThat(urlEditor.getValue()).isNull();
|
||||
@@ -90,7 +86,7 @@ public class URLEditorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAsTextReturnsEmptyStringIfValueNotSet() throws Exception {
|
||||
public void testGetAsTextReturnsEmptyStringIfValueNotSet() {
|
||||
PropertyEditor urlEditor = new URLEditor();
|
||||
assertThat(urlEditor.getAsText()).isEmpty();
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,12 +20,12 @@ import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
/**
|
||||
* Kotlin tests for {@link BeanUtils}.
|
||||
* Kotlin tests for [BeanUtils].
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
@Suppress("unused", "UNUSED_PARAMETER")
|
||||
class KotlinBeanUtilsTests {
|
||||
class BeanUtilsKotlinTests {
|
||||
|
||||
@Test
|
||||
fun `Instantiate immutable class`() {
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -31,7 +31,7 @@ import org.springframework.beans.testfixture.beans.TestBean
|
||||
* @author Juergen Hoeller
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
class KotlinAutowiredTests {
|
||||
class AutowiredKotlinTests {
|
||||
|
||||
@Test
|
||||
fun `Autowiring with target`() {
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.beans.testfixture.beans.factory.aot;
|
||||
|
||||
/**
|
||||
* A hierarchy where the exposed type of a bean is a partial signature.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class TestHierarchy {
|
||||
|
||||
public interface One {
|
||||
}
|
||||
|
||||
public interface Two {
|
||||
}
|
||||
|
||||
public static class Implementation implements One, Two {
|
||||
}
|
||||
|
||||
public static One oneBean() {
|
||||
return new Implementation();
|
||||
}
|
||||
|
||||
}
|
||||
Vendored
+4
-6
@@ -189,13 +189,11 @@ public class CaffeineCacheManager implements CacheManager {
|
||||
@Override
|
||||
@Nullable
|
||||
public Cache getCache(String name) {
|
||||
if (this.dynamic) {
|
||||
Cache cache = this.cacheMap.get(name);
|
||||
return (cache != null) ? cache : this.cacheMap.computeIfAbsent(name, this::createCaffeineCache);
|
||||
}
|
||||
else {
|
||||
return this.cacheMap.get(name);
|
||||
Cache cache = this.cacheMap.get(name);
|
||||
if (cache == null && this.dynamic) {
|
||||
cache = this.cacheMap.computeIfAbsent(name, this::createCaffeineCache);
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ dependencies {
|
||||
optional("jakarta.inject:jakarta.inject-api")
|
||||
optional("jakarta.interceptor:jakarta.interceptor-api")
|
||||
optional("jakarta.validation:jakarta.validation-api")
|
||||
optional("javax.annotation:javax.annotation-api")
|
||||
optional("javax.money:money-api")
|
||||
optional("org.aspectj:aspectjweaver")
|
||||
optional("org.apache.groovy:groovy")
|
||||
@@ -31,11 +32,11 @@ dependencies {
|
||||
testImplementation(testFixtures(project(":spring-beans")))
|
||||
testImplementation(testFixtures(project(":spring-core")))
|
||||
testImplementation("io.projectreactor:reactor-core")
|
||||
testImplementation("jakarta.inject:jakarta.inject-tck")
|
||||
testImplementation("org.apache.groovy:groovy-jsr223")
|
||||
testImplementation("org.apache.groovy:groovy-xml")
|
||||
testImplementation("org.apache.commons:commons-pool2")
|
||||
testImplementation("org.awaitility:awaitility")
|
||||
testImplementation("jakarta.inject:jakarta.inject-tck")
|
||||
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core")
|
||||
testRuntimeOnly("jakarta.xml.bind:jakarta.xml.bind-api")
|
||||
testRuntimeOnly("org.glassfish:jakarta.el")
|
||||
|
||||
Vendored
+2
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -165,13 +165,7 @@ public class ConcurrentMapCacheManager implements CacheManager, BeanClassLoaderA
|
||||
public Cache getCache(String name) {
|
||||
Cache cache = this.cacheMap.get(name);
|
||||
if (cache == null && this.dynamic) {
|
||||
synchronized (this.cacheMap) {
|
||||
cache = this.cacheMap.get(name);
|
||||
if (cache == null) {
|
||||
cache = createConcurrentMapCache(name);
|
||||
this.cacheMap.put(name, cache);
|
||||
}
|
||||
}
|
||||
cache = this.cacheMap.computeIfAbsent(name, this::createConcurrentMapCache);
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperationSourcePointcut.java
Vendored
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+2
-23
@@ -23,7 +23,6 @@ import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
|
||||
import org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
@@ -89,12 +88,6 @@ public abstract class AnnotationConfigUtils {
|
||||
public static final String COMMON_ANNOTATION_PROCESSOR_BEAN_NAME =
|
||||
"org.springframework.context.annotation.internalCommonAnnotationProcessor";
|
||||
|
||||
/**
|
||||
* The bean name of the internally managed JSR-250 annotation processor.
|
||||
*/
|
||||
private static final String JSR250_ANNOTATION_PROCESSOR_BEAN_NAME =
|
||||
"org.springframework.context.annotation.internalJsr250AnnotationProcessor";
|
||||
|
||||
/**
|
||||
* The bean name of the internally managed JPA annotation processor.
|
||||
*/
|
||||
@@ -174,27 +167,13 @@ public abstract class AnnotationConfigUtils {
|
||||
}
|
||||
|
||||
// Check for Jakarta Annotations support, and if present add the CommonAnnotationBeanPostProcessor.
|
||||
if (jakartaAnnotationsPresent && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
|
||||
if ((jakartaAnnotationsPresent || jsr250Present) &&
|
||||
!registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
|
||||
RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
|
||||
def.setSource(source);
|
||||
beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
|
||||
}
|
||||
|
||||
// Check for JSR-250 support, and if present add an InitDestroyAnnotationBeanPostProcessor
|
||||
// for the javax variant of PostConstruct/PreDestroy.
|
||||
if (jsr250Present && !registry.containsBeanDefinition(JSR250_ANNOTATION_PROCESSOR_BEAN_NAME)) {
|
||||
try {
|
||||
RootBeanDefinition def = new RootBeanDefinition(InitDestroyAnnotationBeanPostProcessor.class);
|
||||
def.getPropertyValues().add("initAnnotationType", classLoader.loadClass("javax.annotation.PostConstruct"));
|
||||
def.getPropertyValues().add("destroyAnnotationType", classLoader.loadClass("javax.annotation.PreDestroy"));
|
||||
def.setSource(source);
|
||||
beanDefs.add(registerPostProcessor(registry, def, JSR250_ANNOTATION_PROCESSOR_BEAN_NAME));
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
// Failed to load javax variants of the annotation types -> ignore.
|
||||
}
|
||||
}
|
||||
|
||||
// Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.
|
||||
if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
|
||||
RootBeanDefinition def = new RootBeanDefinition();
|
||||
|
||||
+112
-23
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -33,11 +33,6 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.annotation.PreDestroy;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.ejb.EJB;
|
||||
|
||||
import org.springframework.aop.TargetSource;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
@@ -85,10 +80,15 @@ import org.springframework.util.StringValueResolver;
|
||||
* and default names as well. The target beans can be simple POJOs, with no special
|
||||
* requirements other than the type having to match.
|
||||
*
|
||||
* <p>This post-processor also supports the EJB 3 {@link jakarta.ejb.EJB} annotation,
|
||||
* <p>Additionally, the original {@code javax.annotation} variants of the annotations
|
||||
* dating back to the JSR-250 specification (Java EE 5-8, also included in JDK 6-8)
|
||||
* are still supported as well. Note that this is primarily for a smooth upgrade path,
|
||||
* not for adoption in new applications.
|
||||
*
|
||||
* <p>This post-processor also supports the EJB {@link jakarta.ejb.EJB} annotation,
|
||||
* analogous to {@link jakarta.annotation.Resource}, with the capability to
|
||||
* specify both a local bean name and a global JNDI name for fallback retrieval.
|
||||
* The target beans can be plain POJOs as well as EJB 3 Session Beans in this case.
|
||||
* The target beans can be plain POJOs as well as EJB Session Beans in this case.
|
||||
*
|
||||
* <p>For default usage, resolving resource names as Spring bean names,
|
||||
* simply define the following in your application context:
|
||||
@@ -113,8 +113,8 @@ import org.springframework.util.StringValueResolver;
|
||||
* by the "context:annotation-config" and "context:component-scan" XML tags.
|
||||
* Remove or turn off the default annotation configuration there if you intend
|
||||
* to specify a custom CommonAnnotationBeanPostProcessor bean definition!
|
||||
* <p><b>NOTE:</b> Annotation injection will be performed <i>before</i> XML injection; thus
|
||||
* the latter configuration will override the former for properties wired through
|
||||
* <p><b>NOTE:</b> Annotation injection will be performed <i>before</i> XML injection;
|
||||
* thus the latter configuration will override the former for properties wired through
|
||||
* both approaches.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
@@ -136,14 +136,28 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
private static final Set<Class<? extends Annotation>> resourceAnnotationTypes = new LinkedHashSet<>(4);
|
||||
|
||||
@Nullable
|
||||
private static final Class<? extends Annotation> ejbClass;
|
||||
private static final Class<? extends Annotation> jakartaResourceType;
|
||||
|
||||
@Nullable
|
||||
private static final Class<? extends Annotation> javaxResourceType;
|
||||
|
||||
@Nullable
|
||||
private static final Class<? extends Annotation> ejbAnnotationType;
|
||||
|
||||
static {
|
||||
resourceAnnotationTypes.add(Resource.class);
|
||||
jakartaResourceType = loadAnnotationType("jakarta.annotation.Resource");
|
||||
if (jakartaResourceType != null) {
|
||||
resourceAnnotationTypes.add(jakartaResourceType);
|
||||
}
|
||||
|
||||
ejbClass = loadAnnotationType("jakarta.ejb.EJB");
|
||||
if (ejbClass != null) {
|
||||
resourceAnnotationTypes.add(ejbClass);
|
||||
javaxResourceType = loadAnnotationType("javax.annotation.Resource");
|
||||
if (javaxResourceType != null) {
|
||||
resourceAnnotationTypes.add(javaxResourceType);
|
||||
}
|
||||
|
||||
ejbAnnotationType = loadAnnotationType("jakarta.ejb.EJB");
|
||||
if (ejbAnnotationType != null) {
|
||||
resourceAnnotationTypes.add(ejbAnnotationType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,8 +191,14 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
*/
|
||||
public CommonAnnotationBeanPostProcessor() {
|
||||
setOrder(Ordered.LOWEST_PRECEDENCE - 3);
|
||||
setInitAnnotationType(PostConstruct.class);
|
||||
setDestroyAnnotationType(PreDestroy.class);
|
||||
|
||||
// Jakarta EE 9 set of annotations in jakarta.annotation package
|
||||
addInitAnnotationType(loadAnnotationType("jakarta.annotation.PostConstruct"));
|
||||
addDestroyAnnotationType(loadAnnotationType("jakarta.annotation.PreDestroy"));
|
||||
|
||||
// Tolerate legacy JSR-250 annotations in javax.annotation package
|
||||
addInitAnnotationType(loadAnnotationType("javax.annotation.PostConstruct"));
|
||||
addDestroyAnnotationType(loadAnnotationType("javax.annotation.PreDestroy"));
|
||||
|
||||
// java.naming module present on JDK 9+?
|
||||
if (jndiPresent) {
|
||||
@@ -338,13 +358,13 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
|
||||
|
||||
ReflectionUtils.doWithLocalFields(targetClass, field -> {
|
||||
if (ejbClass != null && field.isAnnotationPresent(ejbClass)) {
|
||||
if (ejbAnnotationType != null && field.isAnnotationPresent(ejbAnnotationType)) {
|
||||
if (Modifier.isStatic(field.getModifiers())) {
|
||||
throw new IllegalStateException("@EJB annotation is not supported on static fields");
|
||||
}
|
||||
currElements.add(new EjbRefElement(field, field, null));
|
||||
}
|
||||
else if (field.isAnnotationPresent(Resource.class)) {
|
||||
else if (jakartaResourceType != null && field.isAnnotationPresent(jakartaResourceType)) {
|
||||
if (Modifier.isStatic(field.getModifiers())) {
|
||||
throw new IllegalStateException("@Resource annotation is not supported on static fields");
|
||||
}
|
||||
@@ -352,6 +372,14 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
currElements.add(new ResourceElement(field, field, null));
|
||||
}
|
||||
}
|
||||
else if (javaxResourceType != null && field.isAnnotationPresent(javaxResourceType)) {
|
||||
if (Modifier.isStatic(field.getModifiers())) {
|
||||
throw new IllegalStateException("@Resource annotation is not supported on static fields");
|
||||
}
|
||||
if (!this.ignoredResourceTypes.contains(field.getType().getName())) {
|
||||
currElements.add(new LegacyResourceElement(field, field, null));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
|
||||
@@ -360,7 +388,7 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
return;
|
||||
}
|
||||
if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
|
||||
if (ejbClass != null && bridgedMethod.isAnnotationPresent(ejbClass)) {
|
||||
if (ejbAnnotationType != null && bridgedMethod.isAnnotationPresent(ejbAnnotationType)) {
|
||||
if (Modifier.isStatic(method.getModifiers())) {
|
||||
throw new IllegalStateException("@EJB annotation is not supported on static methods");
|
||||
}
|
||||
@@ -370,7 +398,7 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
|
||||
currElements.add(new EjbRefElement(method, bridgedMethod, pd));
|
||||
}
|
||||
else if (bridgedMethod.isAnnotationPresent(Resource.class)) {
|
||||
else if (jakartaResourceType != null && bridgedMethod.isAnnotationPresent(jakartaResourceType)) {
|
||||
if (Modifier.isStatic(method.getModifiers())) {
|
||||
throw new IllegalStateException("@Resource annotation is not supported on static methods");
|
||||
}
|
||||
@@ -383,6 +411,19 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
currElements.add(new ResourceElement(method, bridgedMethod, pd));
|
||||
}
|
||||
}
|
||||
else if (javaxResourceType != null && bridgedMethod.isAnnotationPresent(javaxResourceType)) {
|
||||
if (Modifier.isStatic(method.getModifiers())) {
|
||||
throw new IllegalStateException("@Resource annotation is not supported on static methods");
|
||||
}
|
||||
Class<?>[] paramTypes = method.getParameterTypes();
|
||||
if (paramTypes.length != 1) {
|
||||
throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method);
|
||||
}
|
||||
if (!this.ignoredResourceTypes.contains(paramTypes[0].getName())) {
|
||||
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
|
||||
currElements.add(new LegacyResourceElement(method, bridgedMethod, pd));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -584,7 +625,55 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
|
||||
public ResourceElement(Member member, AnnotatedElement ae, @Nullable PropertyDescriptor pd) {
|
||||
super(member, pd);
|
||||
Resource resource = ae.getAnnotation(Resource.class);
|
||||
jakarta.annotation.Resource resource = ae.getAnnotation(jakarta.annotation.Resource.class);
|
||||
String resourceName = resource.name();
|
||||
Class<?> resourceType = resource.type();
|
||||
this.isDefaultName = !StringUtils.hasLength(resourceName);
|
||||
if (this.isDefaultName) {
|
||||
resourceName = this.member.getName();
|
||||
if (this.member instanceof Method && resourceName.startsWith("set") && resourceName.length() > 3) {
|
||||
resourceName = StringUtils.uncapitalizeAsProperty(resourceName.substring(3));
|
||||
}
|
||||
}
|
||||
else if (embeddedValueResolver != null) {
|
||||
resourceName = embeddedValueResolver.resolveStringValue(resourceName);
|
||||
}
|
||||
if (Object.class != resourceType) {
|
||||
checkResourceType(resourceType);
|
||||
}
|
||||
else {
|
||||
// No resource type specified... check field/method.
|
||||
resourceType = getResourceType();
|
||||
}
|
||||
this.name = (resourceName != null ? resourceName : "");
|
||||
this.lookupType = resourceType;
|
||||
String lookupValue = resource.lookup();
|
||||
this.mappedName = (StringUtils.hasLength(lookupValue) ? lookupValue : resource.mappedName());
|
||||
Lazy lazy = ae.getAnnotation(Lazy.class);
|
||||
this.lazyLookup = (lazy != null && lazy.value());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
|
||||
return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) :
|
||||
getResource(this, requestingBeanName));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Class representing injection information about an annotated field
|
||||
* or setter method, supporting the @Resource annotation.
|
||||
*/
|
||||
private class LegacyResourceElement extends LookupElement {
|
||||
|
||||
private final boolean lazyLookup;
|
||||
|
||||
public LegacyResourceElement(Member member, AnnotatedElement ae, @Nullable PropertyDescriptor pd) {
|
||||
super(member, pd);
|
||||
javax.annotation.Resource resource = ae.getAnnotation(javax.annotation.Resource.class);
|
||||
String resourceName = resource.name();
|
||||
Class<?> resourceType = resource.type();
|
||||
this.isDefaultName = !StringUtils.hasLength(resourceName);
|
||||
@@ -630,7 +719,7 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
|
||||
public EjbRefElement(Member member, AnnotatedElement ae, @Nullable PropertyDescriptor pd) {
|
||||
super(member, pd);
|
||||
EJB resource = ae.getAnnotation(EJB.class);
|
||||
jakarta.ejb.EJB resource = ae.getAnnotation(jakarta.ejb.EJB.class);
|
||||
String resourceBeanName = resource.beanName();
|
||||
String resourceName = resource.name();
|
||||
this.isDefaultName = !StringUtils.hasLength(resourceName);
|
||||
|
||||
+6
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,8 +21,8 @@ import org.springframework.instrument.classloading.LoadTimeWeaver;
|
||||
/**
|
||||
* Interface to be implemented by
|
||||
* {@link org.springframework.context.annotation.Configuration @Configuration}
|
||||
* classes annotated with {@link EnableLoadTimeWeaving @EnableLoadTimeWeaving} that wish to
|
||||
* customize the {@link LoadTimeWeaver} instance to be used.
|
||||
* classes annotated with {@link EnableLoadTimeWeaving @EnableLoadTimeWeaving}
|
||||
* that wish to customize the {@link LoadTimeWeaver} instance to be used.
|
||||
*
|
||||
* <p>See {@link org.springframework.scheduling.annotation.EnableAsync @EnableAsync}
|
||||
* for usage examples and information on how a default {@code LoadTimeWeaver}
|
||||
@@ -36,9 +36,9 @@ import org.springframework.instrument.classloading.LoadTimeWeaver;
|
||||
public interface LoadTimeWeavingConfigurer {
|
||||
|
||||
/**
|
||||
* Create, configure and return the {@code LoadTimeWeaver} instance to be used. Note
|
||||
* that it is unnecessary to annotate this method with {@code @Bean}, because the
|
||||
* object returned will automatically be registered as a bean by
|
||||
* Create, configure and return the {@code LoadTimeWeaver} instance to be used.
|
||||
* Note that it is unnecessary to annotate this method with {@code @Bean}
|
||||
* because the object returned will automatically be registered as a bean by
|
||||
* {@link LoadTimeWeavingConfiguration#loadTimeWeaver()}
|
||||
*/
|
||||
LoadTimeWeaver getLoadTimeWeaver();
|
||||
|
||||
+8
-3
@@ -168,12 +168,17 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
|
||||
@Override
|
||||
public boolean supportsEventType(ResolvableType eventType) {
|
||||
for (ResolvableType declaredEventType : this.declaredEventTypes) {
|
||||
if (declaredEventType.isAssignableFrom(eventType)) {
|
||||
if (eventType.hasUnresolvableGenerics() ?
|
||||
declaredEventType.toClass().isAssignableFrom(eventType.toClass()) :
|
||||
declaredEventType.isAssignableFrom(eventType)) {
|
||||
return true;
|
||||
}
|
||||
if (PayloadApplicationEvent.class.isAssignableFrom(eventType.toClass())) {
|
||||
if (eventType.hasUnresolvableGenerics()) {
|
||||
return true;
|
||||
}
|
||||
ResolvableType payloadType = eventType.as(PayloadApplicationEvent.class).getGeneric();
|
||||
if (declaredEventType.isAssignableFrom(payloadType) || eventType.hasUnresolvableGenerics()) {
|
||||
if (declaredEventType.isAssignableFrom(payloadType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -366,7 +371,7 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
|
||||
* Return the target bean instance to use.
|
||||
*/
|
||||
protected Object getTargetBean() {
|
||||
Assert.notNull(this.applicationContext, "ApplicationContext must no be null");
|
||||
Assert.notNull(this.applicationContext, "ApplicationContext must not be null");
|
||||
return this.applicationContext.getBean(this.beanName);
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -772,7 +772,8 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
|
||||
// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
|
||||
// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
|
||||
if (!NativeDetector.inNativeImage() && beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
|
||||
if (!NativeDetector.inNativeImage() && beanFactory.getTempClassLoader() == null &&
|
||||
beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
|
||||
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
|
||||
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
|
||||
}
|
||||
|
||||
+25
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,6 +21,8 @@ import java.security.ProtectionDomain;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.DecoratingClassLoader;
|
||||
import org.springframework.core.OverridingClassLoader;
|
||||
import org.springframework.core.SmartClassLoader;
|
||||
@@ -45,15 +47,26 @@ class ContextTypeMatchClassLoader extends DecoratingClassLoader implements Smart
|
||||
}
|
||||
|
||||
|
||||
private static Method findLoadedClassMethod;
|
||||
@Nullable
|
||||
private static final Method findLoadedClassMethod;
|
||||
|
||||
static {
|
||||
// Try to enable findLoadedClass optimization which allows us to selectively
|
||||
// override classes that have not been loaded yet. If not accessible, we will
|
||||
// always override requested classes, even when the classes have been loaded
|
||||
// by the parent ClassLoader already and cannot be transformed anymore anyway.
|
||||
Method method = null;
|
||||
try {
|
||||
findLoadedClassMethod = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class);
|
||||
method = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class);
|
||||
ReflectionUtils.makeAccessible(method);
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
throw new IllegalStateException("Invalid [java.lang.ClassLoader] class: no 'findLoadedClass' method defined!");
|
||||
catch (Throwable ex) {
|
||||
// Typically a JDK 9+ InaccessibleObjectException...
|
||||
// Avoid through JVM startup with --add-opens=java.base/java.lang=ALL-UNNAMED
|
||||
LogFactory.getLog(ContextTypeMatchClassLoader.class).debug(
|
||||
"ClassLoader.findLoadedClass not accessible -> will always override requested class", ex);
|
||||
}
|
||||
findLoadedClassMethod = method;
|
||||
}
|
||||
|
||||
|
||||
@@ -96,13 +109,14 @@ class ContextTypeMatchClassLoader extends DecoratingClassLoader implements Smart
|
||||
if (isExcluded(className) || ContextTypeMatchClassLoader.this.isExcluded(className)) {
|
||||
return false;
|
||||
}
|
||||
ReflectionUtils.makeAccessible(findLoadedClassMethod);
|
||||
ClassLoader parent = getParent();
|
||||
while (parent != null) {
|
||||
if (ReflectionUtils.invokeMethod(findLoadedClassMethod, parent, className) != null) {
|
||||
return false;
|
||||
if (findLoadedClassMethod != null) {
|
||||
ClassLoader parent = getParent();
|
||||
while (parent != null) {
|
||||
if (ReflectionUtils.invokeMethod(findLoadedClassMethod, parent, className) != null) {
|
||||
return false;
|
||||
}
|
||||
parent = parent.getParent();
|
||||
}
|
||||
parent = parent.getParent();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
+5
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -64,9 +64,10 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
|
||||
|
||||
|
||||
/**
|
||||
* Specify the maximum time allotted in milliseconds for the shutdown of
|
||||
* any phase (group of SmartLifecycle beans with the same 'phase' value).
|
||||
* <p>The default value is 30 seconds.
|
||||
* Specify the maximum time allotted in milliseconds for the shutdown of any
|
||||
* phase (group of {@link SmartLifecycle} beans with the same 'phase' value).
|
||||
* <p>The default value is 30000 milliseconds (30 seconds).
|
||||
* @see SmartLifecycle#getPhase()
|
||||
*/
|
||||
public void setTimeoutPerShutdownPhase(long timeoutPerShutdownPhase) {
|
||||
this.timeoutPerShutdownPhase = timeoutPerShutdownPhase;
|
||||
|
||||
@@ -53,7 +53,6 @@ import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.beans.factory.SmartInitializingSingleton;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.core.Constants;
|
||||
import org.springframework.jmx.export.assembler.AutodetectCapableMBeanInfoAssembler;
|
||||
import org.springframework.jmx.export.assembler.MBeanInfoAssembler;
|
||||
import org.springframework.jmx.export.assembler.SimpleReflectiveMBeanInfoAssembler;
|
||||
@@ -92,6 +91,7 @@ import org.springframework.util.ObjectUtils;
|
||||
* @author Rick Evans
|
||||
* @author Mark Fisher
|
||||
* @author Stephane Nicoll
|
||||
* @author Sam Brannen
|
||||
* @since 1.2
|
||||
* @see #setBeans
|
||||
* @see #setAutodetect
|
||||
@@ -134,20 +134,25 @@ public class MBeanExporter extends MBeanRegistrationSupport implements MBeanExpo
|
||||
/** Constant for the JMX {@code mr_type} "ObjectReference". */
|
||||
private static final String MR_TYPE_OBJECT_REFERENCE = "ObjectReference";
|
||||
|
||||
/** Prefix for the autodetect constants defined in this class. */
|
||||
private static final String CONSTANT_PREFIX_AUTODETECT = "AUTODETECT_";
|
||||
/**
|
||||
* Map of constant names to constant values for the autodetect constants defined
|
||||
* in this class.
|
||||
*/
|
||||
private static final Map<String, Integer> constants = Map.of(
|
||||
"AUTODETECT_NONE", AUTODETECT_NONE,
|
||||
"AUTODETECT_MBEAN", AUTODETECT_MBEAN,
|
||||
"AUTODETECT_ASSEMBLER", AUTODETECT_ASSEMBLER,
|
||||
"AUTODETECT_ALL", AUTODETECT_ALL
|
||||
);
|
||||
|
||||
|
||||
/** Constants instance for this class. */
|
||||
private static final Constants constants = new Constants(MBeanExporter.class);
|
||||
|
||||
/** The beans to be exposed as JMX managed resources, with JMX names as keys. */
|
||||
@Nullable
|
||||
private Map<String, Object> beans;
|
||||
|
||||
/** The autodetect mode to use for this MBeanExporter. */
|
||||
@Nullable
|
||||
private Integer autodetectMode;
|
||||
Integer autodetectMode;
|
||||
|
||||
/** Whether to eagerly initialize candidate beans when autodetecting MBeans. */
|
||||
private boolean allowEagerInit = false;
|
||||
@@ -222,23 +227,6 @@ public class MBeanExporter extends MBeanRegistrationSupport implements MBeanExpo
|
||||
this.autodetectMode = (autodetect ? AUTODETECT_ALL : AUTODETECT_NONE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the autodetection mode to use.
|
||||
* @throws IllegalArgumentException if the supplied value is not
|
||||
* one of the {@code AUTODETECT_} constants
|
||||
* @see #setAutodetectModeName(String)
|
||||
* @see #AUTODETECT_ALL
|
||||
* @see #AUTODETECT_ASSEMBLER
|
||||
* @see #AUTODETECT_MBEAN
|
||||
* @see #AUTODETECT_NONE
|
||||
*/
|
||||
public void setAutodetectMode(int autodetectMode) {
|
||||
if (!constants.getValues(CONSTANT_PREFIX_AUTODETECT).contains(autodetectMode)) {
|
||||
throw new IllegalArgumentException("Only values of autodetect constants allowed");
|
||||
}
|
||||
this.autodetectMode = autodetectMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the autodetection mode to use by name.
|
||||
* @throws IllegalArgumentException if the supplied value is not resolvable
|
||||
@@ -250,10 +238,26 @@ public class MBeanExporter extends MBeanRegistrationSupport implements MBeanExpo
|
||||
* @see #AUTODETECT_NONE
|
||||
*/
|
||||
public void setAutodetectModeName(String constantName) {
|
||||
if (!constantName.startsWith(CONSTANT_PREFIX_AUTODETECT)) {
|
||||
throw new IllegalArgumentException("Only autodetect constants allowed");
|
||||
}
|
||||
this.autodetectMode = (Integer) constants.asNumber(constantName);
|
||||
Assert.hasText(constantName, "'constantName' must not be null or blank");
|
||||
Integer mode = constants.get(constantName);
|
||||
Assert.notNull(mode, "Only autodetect constants allowed");
|
||||
this.autodetectMode = mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the autodetection mode to use.
|
||||
* @throws IllegalArgumentException if the supplied value is not
|
||||
* one of the {@code AUTODETECT_} constants
|
||||
* @see #setAutodetectModeName(String)
|
||||
* @see #AUTODETECT_ALL
|
||||
* @see #AUTODETECT_ASSEMBLER
|
||||
* @see #AUTODETECT_MBEAN
|
||||
* @see #AUTODETECT_NONE
|
||||
*/
|
||||
public void setAutodetectMode(int autodetectMode) {
|
||||
Assert.isTrue(constants.containsValue(autodetectMode),
|
||||
"Only values of autodetect constants allowed");
|
||||
this.autodetectMode = autodetectMode;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+13
-6
@@ -70,6 +70,9 @@ import org.springframework.util.ErrorHandler;
|
||||
*/
|
||||
public class ConcurrentTaskScheduler extends ConcurrentTaskExecutor implements TaskScheduler {
|
||||
|
||||
private static final TimeUnit NANO = TimeUnit.NANOSECONDS;
|
||||
|
||||
|
||||
@Nullable
|
||||
private static Class<?> managedScheduledExecutorServiceClass;
|
||||
|
||||
@@ -209,9 +212,9 @@ public class ConcurrentTaskScheduler extends ConcurrentTaskExecutor implements T
|
||||
|
||||
@Override
|
||||
public ScheduledFuture<?> schedule(Runnable task, Instant startTime) {
|
||||
Duration initialDelay = Duration.between(this.clock.instant(), startTime);
|
||||
Duration delay = Duration.between(this.clock.instant(), startTime);
|
||||
try {
|
||||
return this.scheduledExecutor.schedule(decorateTask(task, false), initialDelay.toNanos(), TimeUnit.NANOSECONDS);
|
||||
return this.scheduledExecutor.schedule(decorateTask(task, false), NANO.convert(delay), NANO);
|
||||
}
|
||||
catch (RejectedExecutionException ex) {
|
||||
throw new TaskRejectedException("Executor [" + this.scheduledExecutor + "] did not accept task: " + task, ex);
|
||||
@@ -222,7 +225,8 @@ public class ConcurrentTaskScheduler extends ConcurrentTaskExecutor implements T
|
||||
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Instant startTime, Duration period) {
|
||||
Duration initialDelay = Duration.between(this.clock.instant(), startTime);
|
||||
try {
|
||||
return this.scheduledExecutor.scheduleAtFixedRate(decorateTask(task, true), initialDelay.toNanos(), period.toNanos(), TimeUnit.NANOSECONDS);
|
||||
return this.scheduledExecutor.scheduleAtFixedRate(decorateTask(task, true),
|
||||
NANO.convert(initialDelay), NANO.convert(period), NANO);
|
||||
}
|
||||
catch (RejectedExecutionException ex) {
|
||||
throw new TaskRejectedException("Executor [" + this.scheduledExecutor + "] did not accept task: " + task, ex);
|
||||
@@ -232,7 +236,8 @@ public class ConcurrentTaskScheduler extends ConcurrentTaskExecutor implements T
|
||||
@Override
|
||||
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Duration period) {
|
||||
try {
|
||||
return this.scheduledExecutor.scheduleAtFixedRate(decorateTask(task, true), 0, period.toNanos(), TimeUnit.NANOSECONDS);
|
||||
return this.scheduledExecutor.scheduleAtFixedRate(decorateTask(task, true),
|
||||
0, NANO.convert(period), NANO);
|
||||
}
|
||||
catch (RejectedExecutionException ex) {
|
||||
throw new TaskRejectedException("Executor [" + this.scheduledExecutor + "] did not accept task: " + task, ex);
|
||||
@@ -243,7 +248,8 @@ public class ConcurrentTaskScheduler extends ConcurrentTaskExecutor implements T
|
||||
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Instant startTime, Duration delay) {
|
||||
Duration initialDelay = Duration.between(this.clock.instant(), startTime);
|
||||
try {
|
||||
return this.scheduledExecutor.scheduleWithFixedDelay(decorateTask(task, true), initialDelay.toNanos(), delay.toNanos(), TimeUnit.NANOSECONDS);
|
||||
return this.scheduledExecutor.scheduleWithFixedDelay(decorateTask(task, true),
|
||||
NANO.convert(initialDelay), NANO.convert(delay), NANO);
|
||||
}
|
||||
catch (RejectedExecutionException ex) {
|
||||
throw new TaskRejectedException("Executor [" + this.scheduledExecutor + "] did not accept task: " + task, ex);
|
||||
@@ -253,7 +259,8 @@ public class ConcurrentTaskScheduler extends ConcurrentTaskExecutor implements T
|
||||
@Override
|
||||
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Duration delay) {
|
||||
try {
|
||||
return this.scheduledExecutor.scheduleWithFixedDelay(decorateTask(task, true), 0, delay.toNanos(), TimeUnit.NANOSECONDS);
|
||||
return this.scheduledExecutor.scheduleWithFixedDelay(decorateTask(task, true),
|
||||
0, NANO.convert(delay), NANO);
|
||||
}
|
||||
catch (RejectedExecutionException ex) {
|
||||
throw new TaskRejectedException("Executor [" + this.scheduledExecutor + "] did not accept task: " + task, ex);
|
||||
|
||||
+8
-4
@@ -108,9 +108,9 @@ public abstract class ExecutorConfigurationSupport extends CustomizableThreadFac
|
||||
/**
|
||||
* Set whether to wait for scheduled tasks to complete on shutdown,
|
||||
* not interrupting running tasks and executing all tasks in the queue.
|
||||
* <p>Default is "false", shutting down immediately through interrupting
|
||||
* ongoing tasks and clearing the queue. Switch this flag to "true" if you
|
||||
* prefer fully completed tasks at the expense of a longer shutdown phase.
|
||||
* <p>Default is {@code false}, shutting down immediately through interrupting
|
||||
* ongoing tasks and clearing the queue. Switch this flag to {@code true} if
|
||||
* you prefer fully completed tasks at the expense of a longer shutdown phase.
|
||||
* <p>Note that Spring's container shutdown continues while ongoing tasks
|
||||
* are being completed. If you want this executor to block and wait for the
|
||||
* termination of tasks before the rest of the container continues to shut
|
||||
@@ -211,9 +211,13 @@ public abstract class ExecutorConfigurationSupport extends CustomizableThreadFac
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a shutdown on the underlying ExecutorService.
|
||||
* Perform a full shutdown on the underlying ExecutorService,
|
||||
* according to the corresponding configuration settings.
|
||||
* @see #setWaitForTasksToCompleteOnShutdown
|
||||
* @see #setAwaitTerminationMillis
|
||||
* @see java.util.concurrent.ExecutorService#shutdown()
|
||||
* @see java.util.concurrent.ExecutorService#shutdownNow()
|
||||
* @see java.util.concurrent.ExecutorService#awaitTermination
|
||||
*/
|
||||
public void shutdown() {
|
||||
if (logger.isDebugEnabled()) {
|
||||
|
||||
+2
-2
@@ -79,8 +79,8 @@ class ReschedulingRunnable extends DelegatingErrorHandlingRunnable implements Sc
|
||||
if (this.scheduledExecutionTime == null) {
|
||||
return null;
|
||||
}
|
||||
Duration initialDelay = Duration.between(this.triggerContext.getClock().instant(), this.scheduledExecutionTime);
|
||||
this.currentFuture = this.executor.schedule(this, initialDelay.toNanos(), TimeUnit.NANOSECONDS);
|
||||
Duration delay = Duration.between(this.triggerContext.getClock().instant(), this.scheduledExecutionTime);
|
||||
this.currentFuture = this.executor.schedule(this, delay.toNanos(), TimeUnit.NANOSECONDS);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
+13
-6
@@ -63,6 +63,9 @@ import org.springframework.util.concurrent.ListenableFutureTask;
|
||||
public class ThreadPoolTaskScheduler extends ExecutorConfigurationSupport
|
||||
implements AsyncListenableTaskExecutor, SchedulingTaskExecutor, TaskScheduler {
|
||||
|
||||
private static final TimeUnit NANO = TimeUnit.NANOSECONDS;
|
||||
|
||||
|
||||
private volatile int poolSize = 1;
|
||||
|
||||
private volatile boolean removeOnCancelPolicy;
|
||||
@@ -380,9 +383,9 @@ public class ThreadPoolTaskScheduler extends ExecutorConfigurationSupport
|
||||
@Override
|
||||
public ScheduledFuture<?> schedule(Runnable task, Instant startTime) {
|
||||
ScheduledExecutorService executor = getScheduledExecutor();
|
||||
Duration initialDelay = Duration.between(this.clock.instant(), startTime);
|
||||
Duration delay = Duration.between(this.clock.instant(), startTime);
|
||||
try {
|
||||
return executor.schedule(errorHandlingTask(task, false), initialDelay.toNanos(), TimeUnit.NANOSECONDS);
|
||||
return executor.schedule(errorHandlingTask(task, false), NANO.convert(delay), NANO);
|
||||
}
|
||||
catch (RejectedExecutionException ex) {
|
||||
throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex);
|
||||
@@ -394,7 +397,8 @@ public class ThreadPoolTaskScheduler extends ExecutorConfigurationSupport
|
||||
ScheduledExecutorService executor = getScheduledExecutor();
|
||||
Duration initialDelay = Duration.between(this.clock.instant(), startTime);
|
||||
try {
|
||||
return executor.scheduleAtFixedRate(errorHandlingTask(task, true), initialDelay.toNanos(), period.toNanos(), TimeUnit.NANOSECONDS);
|
||||
return executor.scheduleAtFixedRate(errorHandlingTask(task, true),
|
||||
NANO.convert(initialDelay), NANO.convert(period), NANO);
|
||||
}
|
||||
catch (RejectedExecutionException ex) {
|
||||
throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex);
|
||||
@@ -405,7 +409,8 @@ public class ThreadPoolTaskScheduler extends ExecutorConfigurationSupport
|
||||
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Duration period) {
|
||||
ScheduledExecutorService executor = getScheduledExecutor();
|
||||
try {
|
||||
return executor.scheduleAtFixedRate(errorHandlingTask(task, true), 0, period.toNanos(), TimeUnit.NANOSECONDS);
|
||||
return executor.scheduleAtFixedRate(errorHandlingTask(task, true),
|
||||
0, NANO.convert(period), NANO);
|
||||
}
|
||||
catch (RejectedExecutionException ex) {
|
||||
throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex);
|
||||
@@ -417,7 +422,8 @@ public class ThreadPoolTaskScheduler extends ExecutorConfigurationSupport
|
||||
ScheduledExecutorService executor = getScheduledExecutor();
|
||||
Duration initialDelay = Duration.between(this.clock.instant(), startTime);
|
||||
try {
|
||||
return executor.scheduleWithFixedDelay(errorHandlingTask(task, true), initialDelay.toNanos(), delay.toNanos(), TimeUnit.NANOSECONDS);
|
||||
return executor.scheduleWithFixedDelay(errorHandlingTask(task, true),
|
||||
NANO.convert(initialDelay), NANO.convert(delay), NANO);
|
||||
}
|
||||
catch (RejectedExecutionException ex) {
|
||||
throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex);
|
||||
@@ -428,7 +434,8 @@ public class ThreadPoolTaskScheduler extends ExecutorConfigurationSupport
|
||||
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Duration delay) {
|
||||
ScheduledExecutorService executor = getScheduledExecutor();
|
||||
try {
|
||||
return executor.scheduleWithFixedDelay(errorHandlingTask(task, true), 0, delay.toNanos(), TimeUnit.NANOSECONDS);
|
||||
return executor.scheduleWithFixedDelay(errorHandlingTask(task, true),
|
||||
0, NANO.convert(delay), NANO);
|
||||
}
|
||||
catch (RejectedExecutionException ex) {
|
||||
throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex);
|
||||
|
||||
@@ -124,8 +124,10 @@ public class FieldError extends ObjectError {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
// 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 [" + ObjectUtils.nullSafeConciseToString(this.rejectedValue) + "]; " +
|
||||
"': rejected value [" + ObjectUtils.nullSafeToString(this.rejectedValue) + "]; " +
|
||||
resolvableToString();
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -109,7 +109,7 @@ open class BeanDefinitionDsl internal constructor (private val init: BeanDefinit
|
||||
SINGLETON,
|
||||
|
||||
/**
|
||||
* Scope constant for the standard singleton scope
|
||||
* Scope constant for the standard prototype scope
|
||||
* @see org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE
|
||||
*/
|
||||
PROTOTYPE
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -93,7 +93,6 @@ public abstract class FooServiceImpl implements FooService {
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public Future<String> asyncFoo(int id) {
|
||||
System.out.println(Thread.currentThread().getName());
|
||||
Assert.state(ServiceInvocationCounter.getThreadLocalCount() != null, "Thread-local counter not exposed");
|
||||
return new org.springframework.scheduling.annotation.AsyncResult<>(fooDao().findFoo(id));
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user