Merge branch '6.0.x'

Closes gh-30435
This commit is contained in:
rstoyanchev 2023-05-09 12:16:13 +01:00
commit bc7ba8cf2b
73 changed files with 458 additions and 310 deletions

View File

@ -605,11 +605,11 @@ Java::
// ...
}
----
======
<1> References the `publicMethod` named pointcut defined in xref:core/aop/ataspectj/pointcuts.adoc#aop-pointcuts-combining[Combining Pointcut Expressions].
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
@Before("com.xyz.Pointcuts.publicMethod() && @annotation(auditable)") // <1>
fun audit(auditable: Auditable) {
@ -618,6 +618,7 @@ Java::
}
----
<1> References the `publicMethod` named pointcut defined in xref:core/aop/ataspectj/pointcuts.adoc#aop-pointcuts-combining[Combining Pointcut Expressions].
======
[[aop-ataspectj-advice-params-generics]]
=== Advice Parameters and Generics
@ -770,12 +771,12 @@ Java::
// ... use code and bean
}
----
======
<1> References the `publicMethod` named pointcut defined in xref:core/aop/ataspectj/pointcuts.adoc#aop-pointcuts-combining[Combining Pointcut Expressions].
<2> Declares `bean` and `auditable` as the argument names.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
@Before(
value = "com.xyz.Pointcuts.publicMethod() && target(bean) && @annotation(auditable)", // <1>
@ -787,6 +788,7 @@ Java::
----
<1> References the `publicMethod` named pointcut defined in xref:core/aop/ataspectj/pointcuts.adoc#aop-pointcuts-combining[Combining Pointcut Expressions].
<2> Declares `bean` and `auditable` as the argument names.
======
If the first parameter is of type `JoinPoint`, `ProceedingJoinPoint`, or
`JoinPoint.StaticPart`, you can omit the name of the parameter from the value of the
@ -807,12 +809,12 @@ Java::
// ... use code, bean, and jp
}
----
======
<1> References the `publicMethod` named pointcut defined in xref:core/aop/ataspectj/pointcuts.adoc#aop-pointcuts-combining[Combining Pointcut Expressions].
<2> Declares `bean` and `auditable` as the argument names.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
@Before(
value = "com.xyz.Pointcuts.publicMethod() && target(bean) && @annotation(auditable)", // <1>
@ -824,6 +826,7 @@ Java::
----
<1> References the `publicMethod` named pointcut defined in xref:core/aop/ataspectj/pointcuts.adoc#aop-pointcuts-combining[Combining Pointcut Expressions].
<2> Declares `bean` and `auditable` as the argument names.
======
The special treatment given to the first parameter of type `JoinPoint`,
`ProceedingJoinPoint`, or `JoinPoint.StaticPart` is particularly convenient for advice
@ -842,11 +845,11 @@ Java::
// ... use jp
}
----
======
<1> References the `publicMethod` named pointcut defined in xref:core/aop/ataspectj/pointcuts.adoc#aop-pointcuts-combining[Combining Pointcut Expressions].
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
@Before("com.xyz.Pointcuts.publicMethod()") // <1>
fun audit(jp: JoinPoint) {
@ -854,6 +857,7 @@ Java::
}
----
<1> References the `publicMethod` named pointcut defined in xref:core/aop/ataspectj/pointcuts.adoc#aop-pointcuts-combining[Combining Pointcut Expressions].
======
[[aop-ataspectj-advice-proceeding-with-the-call]]
@ -879,11 +883,11 @@ Java::
return pjp.proceed(new Object[] {newPattern});
}
----
======
<1> References the `inDataAccessLayer` named pointcut defined in xref:core/aop/ataspectj/pointcuts.adoc#aop-common-pointcuts[Sharing Named Pointcut Definitions].
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
@Around("execution(List<Account> find*(..)) && " +
"com.xyz.CommonPointcuts.inDataAccessLayer() && " +
@ -895,6 +899,7 @@ Java::
}
----
<1> References the `inDataAccessLayer` named pointcut defined in xref:core/aop/ataspectj/pointcuts.adoc#aop-common-pointcuts[Sharing Named Pointcut Definitions].
======
In many cases, you do this binding anyway (as in the preceding example).

View File

@ -59,11 +59,11 @@ Java::
}
}
----
======
<1> References the `businessService` named pointcut defined in xref:core/aop/ataspectj/pointcuts.adoc#aop-common-pointcuts[Sharing Named Pointcut Definitions].
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
@Aspect
class ConcurrentOperationExecutor : Ordered {
@ -102,6 +102,7 @@ Java::
}
----
<1> References the `businessService` named pointcut defined in xref:core/aop/ataspectj/pointcuts.adoc#aop-common-pointcuts[Sharing Named Pointcut Definitions].
======
Note that the aspect implements the `Ordered` interface so that we can set the precedence of
the aspect higher than the transaction advice (we want a fresh transaction each time we

View File

@ -167,15 +167,15 @@ Java::
public void tradingOperation() {} // <3>
}
----
======
<1> `publicMethod` matches if a method execution join point represents the execution
of any public method.
<2> `inTrading` matches if a method execution is in the trading module.
<3> `tradingOperation` matches if a method execution represents any public method in the
trading module.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages"]
.Kotlin
----
package com.xyz
@ -197,6 +197,7 @@ of any public method.
<2> `inTrading` matches if a method execution is in the trading module.
<3> `tradingOperation` matches if a method execution represents any public method in the
trading module.
======
It is a best practice to build more complex pointcut expressions out of smaller _named
pointcuts_, as shown above. When referring to pointcuts by name, normal Java visibility

View File

@ -233,14 +233,14 @@ Java::
}
----
======
<1> We use the Spring-provided `AbstractSingleBeanDefinitionParser` to handle a lot of
the basic grunt work of creating a single `BeanDefinition`.
<2> We supply the `AbstractSingleBeanDefinitionParser` superclass with the type that our
single `BeanDefinition` represents.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
----
package org.springframework.samples.xml
@ -274,6 +274,7 @@ single `BeanDefinition` represents.
the basic grunt work of creating a single `BeanDefinition`.
<2> We supply the `AbstractSingleBeanDefinitionParser` superclass with the type that our
single `BeanDefinition` represents.
======
In this simple case, this is all that we need to do. The creation of our single
@ -540,7 +541,7 @@ Kotlin::
fun setChildren(children: List<Component>) {
this.children = children
}
override fun getObject(): Component? {
if (this.children != null && this.children!!.isNotEmpty()) {
for (child in children!!) {

View File

@ -374,11 +374,11 @@ Java::
// ...
}
----
======
<1> This line adds the `@Offline` annotation.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class MovieRecommender {
@ -390,6 +390,7 @@ class MovieRecommender {
}
----
<1> This line adds the `@Offline` annotation.
======
--
Now the bean definition only needs a qualifier `type`, as shown in the following example:

View File

@ -27,11 +27,11 @@ Java::
}
}
----
======
<1> This line injects a `@Resource`.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class SimpleMovieLister {
@ -40,6 +40,7 @@ class SimpleMovieLister {
}
----
<1> This line injects a `@Resource`.
======
--
@ -118,12 +119,12 @@ Java::
// ...
}
----
======
<1> The `context` field is injected based on the known resolvable dependency type:
`ApplicationContext`.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class MovieRecommender {
@ -139,5 +140,6 @@ Java::
----
<1> The `context` field is injected based on the known resolvable dependency type:
`ApplicationContext`.
======
--

View File

@ -70,11 +70,11 @@ Java::
// ...
}
----
======
<1> The `@Component` causes `@Service` to be treated in the same way as `@Component`.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Target(AnnotationTarget.TYPE)
@Retention(AnnotationRetention.RUNTIME)
@ -86,6 +86,7 @@ Java::
}
----
<1> The `@Component` causes `@Service` to be treated in the same way as `@Component`.
======
You can also combine meta-annotations to create "`composed annotations`". For example,
the `@RestController` annotation from Spring MVC is composed of `@Controller` and

View File

@ -184,11 +184,11 @@ Java::
}
}
----
======
<1> `@Bean(destroyMethod = "")` disables default destroy method inference.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@Profile("production")
@ -202,6 +202,7 @@ Java::
}
----
<1> `@Bean(destroyMethod = "")` disables default destroy method inference.
======
--
NOTE: As mentioned earlier, with `@Bean` methods, you typically choose to use programmatic
@ -294,12 +295,12 @@ Java::
}
}
----
======
<1> The `standaloneDataSource` method is available only in the `development` profile.
<2> The `jndiDataSource` method is available only in the `production` profile.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
class AppConfig {
@ -322,6 +323,7 @@ Java::
----
<1> The `standaloneDataSource` method is available only in the `development` profile.
<2> The `jndiDataSource` method is available only in the `production` profile.
======
--
[NOTE]

View File

@ -144,11 +144,11 @@ Java::
// ...
}
----
======
<1> This annotation enables component scanning.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@ComponentScan(basePackages = ["com.acme"]) // <1>
@ -157,6 +157,7 @@ Java::
}
----
<1> This annotation enables component scanning.
======
[TIP]

View File

@ -18,17 +18,18 @@ Java::
Expression exp = parser.parseExpression("'Hello World'"); // <1>
String message = (String) exp.getValue();
----
======
<1> The value of the message variable is `'Hello World'`.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val parser = SpelExpressionParser()
val exp = parser.parseExpression("'Hello World'") // <1>
val message = exp.value as String
----
<1> The value of the message variable is `'Hello World'`.
======
The SpEL classes and interfaces you are most likely to use are located in the
@ -56,17 +57,18 @@ Java::
Expression exp = parser.parseExpression("'Hello World'.concat('!')"); // <1>
String message = (String) exp.getValue();
----
======
<1> The value of `message` is now 'Hello World!'.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val parser = SpelExpressionParser()
val exp = parser.parseExpression("'Hello World'.concat('!')") // <1>
val message = exp.value as String
----
<1> The value of `message` is now 'Hello World!'.
======
The following example of calling a JavaBean property calls the `String` property `Bytes`:
@ -82,11 +84,11 @@ Java::
Expression exp = parser.parseExpression("'Hello World'.bytes"); // <1>
byte[] bytes = (byte[]) exp.getValue();
----
======
<1> This line converts the literal to a byte array.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val parser = SpelExpressionParser()
@ -95,6 +97,7 @@ Java::
val bytes = exp.value as ByteArray
----
<1> This line converts the literal to a byte array.
======
SpEL also supports nested properties by using the standard dot notation (such as
`prop1.prop2.prop3`) and also the corresponding setting of property values.
@ -114,11 +117,11 @@ Java::
Expression exp = parser.parseExpression("'Hello World'.bytes.length"); // <1>
int length = (Integer) exp.getValue();
----
======
<1> `'Hello World'.bytes.length` gives the length of the literal.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val parser = SpelExpressionParser()
@ -127,6 +130,7 @@ Java::
val length = exp.value as Int
----
<1> `'Hello World'.bytes.length` gives the length of the literal.
======
The String's constructor can be called instead of using a string literal, as the following
example shows:
@ -141,17 +145,18 @@ Java::
Expression exp = parser.parseExpression("new String('hello world').toUpperCase()"); // <1>
String message = exp.getValue(String.class);
----
======
<1> Construct a new `String` from the literal and make it be upper case.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val parser = SpelExpressionParser()
val exp = parser.parseExpression("new String('hello world').toUpperCase()") // <1>
val message = exp.getValue(String::class.java)
----
<1> Construct a new `String` from the literal and make it be upper case.
======
Note the use of the generic method: `public <T> T getValue(Class<T> desiredResultType)`.

View File

@ -63,11 +63,11 @@ Java::
// ...
}
----
======
<1> The `@Repository` annotation.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Repository // <1>
class SomeMovieFinder : MovieFinder {
@ -75,6 +75,7 @@ Java::
}
----
<1> The `@Repository` annotation.
======
Any DAO or repository implementation needs access to a persistence resource,

View File

@ -441,13 +441,13 @@ Java::
// JDBC-backed implementations of the methods on the CorporateEventDao follow...
}
----
======
<1> Annotate the class with `@Repository`.
<2> Annotate the `DataSource` setter method with `@Autowired`.
<3> Create a new `JdbcTemplate` with the `DataSource`.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Repository // <1>
class JdbcCorporateEventDao(dataSource: DataSource) : CorporateEventDao { // <2>
@ -460,6 +460,7 @@ Java::
<1> Annotate the class with `@Repository`.
<2> Constructor injection of the `DataSource`.
<3> Create a new `JdbcTemplate` with the `DataSource`.
======
--

View File

@ -89,13 +89,13 @@ Java::
blobIs.close();
clobReader.close();
----
======
<1> Pass in the `lobHandler` that (in this example) is a plain `DefaultLobHandler`.
<2> Using the method `setClobAsCharacterStream` to pass in the contents of the CLOB.
<3> Using the method `setBlobAsBinaryStream` to pass in the contents of the BLOB.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val blobIn = File("spring2004.jpg")
val blobIs = FileInputStream(blobIn)
@ -119,6 +119,7 @@ Java::
<1> Pass in the `lobHandler` that (in this example) is a plain `DefaultLobHandler`.
<2> Using the method `setClobAsCharacterStream` to pass in the contents of the CLOB.
<3> Using the method `setBlobAsBinaryStream` to pass in the contents of the BLOB.
======
[NOTE]
@ -156,12 +157,12 @@ Java::
}
});
----
======
<1> Using the method `getClobAsString` to retrieve the contents of the CLOB.
<2> Using the method `getBlobAsBytes` to retrieve the contents of the BLOB.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val l = jdbcTemplate.query("select id, a_clob, a_blob from lob_table") { rs, _ ->
val clobText = lobHandler.getClobAsString(rs, "a_clob") // <1>
@ -171,6 +172,7 @@ Java::
----
<1> Using the method `getClobAsString` to retrieve the contents of the CLOB.
<2> Using the method `getBlobAsBytes` to retrieve the contents of the BLOB.
======
[[jdbc-in-clause]]

View File

@ -547,13 +547,13 @@ Java::
// R2DBC-backed implementations of the methods on the CorporateEventDao follow...
}
----
======
<1> Annotate the class with `@Component`.
<2> Annotate the `ConnectionFactory` setter method with `@Autowired`.
<3> Create a new `DatabaseClient` with the `ConnectionFactory`.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Component // <1>
class R2dbcCorporateEventDao(connectionFactory: ConnectionFactory) : CorporateEventDao { // <2>
@ -566,6 +566,7 @@ Java::
<1> Annotate the class with `@Component`.
<2> Constructor injection of the `ConnectionFactory`.
<3> Create a new `DatabaseClient` with the `ConnectionFactory`.
======
--
Regardless of which of the above template initialization styles you choose to use (or

View File

@ -299,14 +299,14 @@ Java::
.rsocketConnector(connector -> connector.acceptor(responder)) // <3>
.tcp("localhost", 7000);
----
======
<1> Use `PathPatternRouteMatcher`, if `spring-web` is present, for efficient
route matching.
<2> Create a responder from a class with `@MessageMapping` and/or `@ConnectMapping` methods.
<3> Register the responder.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val strategies = RSocketStrategies.builder()
.routeMatcher(PathPatternRouteMatcher()) // <1>
@ -323,6 +323,7 @@ Java::
route matching.
<2> Create a responder from a class with `@MessageMapping` and/or `@ConnectMapping` methods.
<3> Register the responder.
======
Note the above is only a shortcut designed for programmatic registration of client
responders. For alternative scenarios, where client responders are in Spring configuration,
@ -428,12 +429,12 @@ Java::
return ... // <2>
}
----
======
<1> Start the request asynchronously, independent from handling.
<2> Perform handling and return completion `Mono<Void>`.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@ConnectMapping
suspend fun handle(requester: RSocketRequester) {
@ -447,6 +448,7 @@ Java::
----
<1> Start the request asynchronously, independent from handling.
<2> Perform handling in the suspending function.
======
@ -469,13 +471,13 @@ Java::
.retrieveFlux(AirportLocation.class); // <3>
----
======
<1> Specify a route to include in the metadata of the request message.
<2> Provide data for the request message.
<3> Declare the expected response.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val viewBox: ViewBox = ...
@ -486,6 +488,7 @@ Java::
<1> Specify a route to include in the metadata of the request message.
<2> Provide data for the request message.
<3> Declare the expected response.
======
The interaction type is determined implicitly from the cardinality of the input and
output. The above example is a `Request-Stream` because one value is sent and a stream

View File

@ -36,11 +36,11 @@ Java::
// class body...
}
----
======
<1> Specify the configuration class.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@SpringJUnitConfig(TestConfig::class) // <1>
class ConfigurationClassJUnitJupiterSpringTests {
@ -48,6 +48,7 @@ Java::
}
----
<1> Specify the configuration class.
======
The following example shows how to use the `@SpringJUnitConfig` annotation to specify the
@ -64,11 +65,11 @@ Java::
// class body...
}
----
======
<1> Specify the location of a configuration file.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@SpringJUnitConfig(locations = ["/test-config.xml"]) // <1>
class XmlJUnitJupiterSpringTests {
@ -76,6 +77,7 @@ Java::
}
----
<1> Specify the location of a configuration file.
======
See xref:testing/testcontext-framework/ctx-management.adoc[Context Management] as well as the javadoc for
@ -109,11 +111,11 @@ Java::
// class body...
}
----
======
<1> Specify the configuration class.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@SpringJUnitWebConfig(TestConfig::class) // <1>
class ConfigurationClassJUnitJupiterSpringWebTests {
@ -121,6 +123,7 @@ Java::
}
----
<1> Specify the configuration class.
======
The following example shows how to use the `@SpringJUnitWebConfig` annotation to specify the
@ -137,11 +140,11 @@ Java::
// class body...
}
----
======
<1> Specify the location of a configuration file.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@SpringJUnitWebConfig(locations = ["/test-config.xml"]) // <1>
class XmlJUnitJupiterSpringWebTests {
@ -149,6 +152,7 @@ Java::
}
----
<1> Specify the location of a configuration file.
======
See xref:testing/testcontext-framework/ctx-management.adoc[Context Management] as well as the javadoc for

View File

@ -39,11 +39,11 @@ Java::
// some logic that should run only on Java VMs from Oracle Corporation
}
----
======
<1> Run this test only when the Java vendor is "Oracle Corporation".
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@IfProfileValue(name="java.vendor", value="Oracle Corporation") // <1>
@Test
@ -52,6 +52,7 @@ Java::
}
----
<1> Run this test only when the Java vendor is "Oracle Corporation".
======
Alternatively, you can configure `@IfProfileValue` with a list of `values` (with `OR`
@ -70,11 +71,11 @@ Java::
// some logic that should run only for unit and integration test groups
}
----
======
<1> Run this test for unit tests and integration tests.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@IfProfileValue(name="test-groups", values=["unit-tests", "integration-tests"]) // <1>
@Test
@ -83,6 +84,7 @@ Java::
}
----
<1> Run this test for unit tests and integration tests.
======
[[integration-testing-annotations-junit4-profilevaluesourceconfiguration]]
@ -105,11 +107,11 @@ Java::
// class body...
}
----
======
<1> Use a custom profile value source.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@ProfileValueSourceConfiguration(CustomProfileValueSource::class) // <1>
class CustomProfileValueSourceTests {
@ -117,6 +119,7 @@ Java::
}
----
<1> Use a custom profile value source.
======
[[integration-testing-annotations-junit4-timed]]
@ -141,11 +144,11 @@ Java::
// some logic that should not take longer than 1 second to run
}
----
======
<1> Set the time period for the test to one second.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Timed(millis = 1000) // <1>
fun testProcessWithOneSecondTimeout() {
@ -153,6 +156,7 @@ Java::
}
----
<1> Set the time period for the test to one second.
======
Spring's `@Timed` annotation has different semantics than JUnit 4's `@Test(timeout=...)`
@ -186,11 +190,11 @@ Java::
// ...
}
----
======
<1> Repeat this test ten times.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Repeat(10) // <1>
@Test
@ -199,6 +203,7 @@ Java::
}
----
<1> Repeat this test ten times.
======

View File

@ -19,11 +19,11 @@ Java::
// class body...
}
----
======
<1> Indicate that the `dev` profile should be active.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@ContextConfiguration
@ActiveProfiles("dev") // <1>
@ -32,6 +32,7 @@ Java::
}
----
<1> Indicate that the `dev` profile should be active.
======
The following example indicates that both the `dev` and the `integration` profiles should
@ -49,11 +50,11 @@ Java::
// class body...
}
----
======
<1> Indicate that the `dev` and `integration` profiles should be active.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@ContextConfiguration
@ActiveProfiles(["dev", "integration"]) // <1>
@ -62,6 +63,7 @@ Java::
}
----
<1> Indicate that the `dev` and `integration` profiles should be active.
======
NOTE: `@ActiveProfiles` provides support for inheriting active bean definition profiles

View File

@ -18,11 +18,11 @@ Java::
// logic to be run after a transaction has ended
}
----
======
<1> Run this method after a transaction.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@AfterTransaction // <1>
fun afterTransaction() {
@ -30,5 +30,6 @@ Java::
}
----
<1> Run this method after a transaction.
======

View File

@ -20,11 +20,11 @@ Java::
// logic to be run before a transaction is started
}
----
======
<1> Run this method before a transaction.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@BeforeTransaction // <1>
fun beforeTransaction() {
@ -32,5 +32,6 @@ Java::
}
----
<1> Run this method before a transaction.
======

View File

@ -21,11 +21,11 @@ Java::
// ...
}
----
======
<1> Commit the result of the test to the database.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Commit // <1>
@Test
@ -34,5 +34,6 @@ Java::
}
----
<1> Commit the result of the test to the database.
======

View File

@ -26,11 +26,11 @@ Java::
// class body...
}
----
======
<1> Referring to an XML file.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@ContextConfiguration("/test-config.xml") // <1>
class XmlApplicationContextTests {
@ -38,6 +38,7 @@ Java::
}
----
<1> Referring to an XML file.
======
The following example shows a `@ContextConfiguration` annotation that refers to a class:
@ -53,11 +54,11 @@ Java::
// class body...
}
----
======
<1> Referring to a class.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@ContextConfiguration(classes = [TestConfig::class]) // <1>
class ConfigClassApplicationContextTests {
@ -65,6 +66,7 @@ Java::
}
----
<1> Referring to a class.
======
As an alternative or in addition to declaring resource locations or component classes,
@ -82,11 +84,11 @@ Java::
// class body...
}
----
======
<1> Declaring an initializer class.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@ContextConfiguration(initializers = [CustomContextInitializer::class]) // <1>
class ContextInitializerTests {
@ -94,6 +96,7 @@ Java::
}
----
<1> Declaring an initializer class.
======
You can optionally use `@ContextConfiguration` to declare the `ContextLoader` strategy as
@ -114,11 +117,11 @@ Java::
// class body...
}
----
======
<1> Configuring both a location and a custom loader.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@ContextConfiguration("/test-context.xml", loader = CustomContextLoader::class) // <1>
class CustomLoaderXmlApplicationContextTests {
@ -126,6 +129,7 @@ Java::
}
----
<1> Configuring both a location and a custom loader.
======
NOTE: `@ContextConfiguration` provides support for inheriting resource locations or

View File

@ -31,11 +31,11 @@ Java::
// some tests that require a new Spring container
}
----
======
<1> Dirty the context before the current test class.
+
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@DirtiesContext(classMode = BEFORE_CLASS) // <1>
class FreshContextTests {
@ -43,6 +43,7 @@ Java::
}
----
<1> Dirty the context before the current test class.
======
* After the current test class, when declared on a class with class mode set to
`AFTER_CLASS` (i.e., the default class mode).
@ -58,11 +59,11 @@ Java::
// some tests that result in the Spring container being dirtied
}
----
======
<1> Dirty the context after the current test class.
+
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@DirtiesContext // <1>
class ContextDirtyingTests {
@ -70,6 +71,7 @@ Java::
}
----
<1> Dirty the context after the current test class.
======
* Before each test method in the current test class, when declared on a class with class
@ -86,11 +88,11 @@ Java::
// some tests that require a new Spring container
}
----
======
<1> Dirty the context before each test method.
+
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@DirtiesContext(classMode = BEFORE_EACH_TEST_METHOD) // <1>
class FreshContextTests {
@ -98,6 +100,7 @@ Java::
}
----
<1> Dirty the context before each test method.
======
* After each test method in the current test class, when declared on a class with class
@ -114,11 +117,11 @@ Java::
// some tests that result in the Spring container being dirtied
}
----
======
<1> Dirty the context after each test method.
+
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@DirtiesContext(classMode = AFTER_EACH_TEST_METHOD) // <1>
class ContextDirtyingTests {
@ -126,6 +129,7 @@ Java::
}
----
<1> Dirty the context after each test method.
======
* Before the current test, when declared on a method with the method mode set to
@ -143,11 +147,11 @@ Java::
// some logic that requires a new Spring container
}
----
======
<1> Dirty the context before the current test method.
+
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@DirtiesContext(methodMode = BEFORE_METHOD) // <1>
@Test
@ -156,6 +160,7 @@ Java::
}
----
<1> Dirty the context before the current test method.
======
* After the current test, when declared on a method with the method mode set to
`AFTER_METHOD` (i.e., the default method mode).
@ -172,11 +177,11 @@ Java::
// some logic that results in the Spring container being dirtied
}
----
======
<1> Dirty the context after the current test method.
+
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@DirtiesContext // <1>
@Test
@ -185,6 +190,7 @@ Java::
}
----
<1> Dirty the context after the current test method.
======
If you use `@DirtiesContext` in a test whose context is configured as part of a context
@ -220,11 +226,11 @@ Java::
}
}
----
======
<1> Use the current-level algorithm.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@ContextHierarchy(
ContextConfiguration("/parent-config.xml"),
@ -243,6 +249,7 @@ Java::
}
----
<1> Use the current-level algorithm.
======
For further details regarding the `EXHAUSTIVE` and `CURRENT_LEVEL` algorithms, see the

View File

@ -29,13 +29,13 @@ Java::
// tests ...
}
----
======
<1> Annotate a `static` method with `@DynamicPropertySource`.
<2> Accept a `DynamicPropertyRegistry` as an argument.
<3> Register a dynamic `server.port` property to be retrieved lazily from the server.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@ContextConfiguration
class MyIntegrationTests {
@ -58,6 +58,7 @@ Java::
<1> Annotate a `static` method with `@DynamicPropertySource`.
<2> Accept a `DynamicPropertyRegistry` as an argument.
<3> Register a dynamic `server.port` property to be retrieved lazily from the server.
======
See xref:testing/testcontext-framework/ctx-management/dynamic-property-sources.adoc[Context Configuration with Dynamic Property Sources] for further details.

View File

@ -27,11 +27,11 @@ Java::
// ...
}
----
======
<1> Do not roll back the result.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Rollback(false) // <1>
@Test
@ -40,5 +40,6 @@ Java::
}
----
<1> Do not roll back the result.
======

View File

@ -17,11 +17,11 @@ Java::
// run code that relies on the test schema and test data
}
----
======
<1> Run two scripts for this test.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Test
@Sql("/test-schema.sql", "/test-user-data.sql") // <1>
@ -30,6 +30,7 @@ Java::
}
----
<1> Run two scripts for this test.
======
See xref:testing/testcontext-framework/executing-sql.adoc#testcontext-executing-sql-declaratively[Executing SQL scripts declaratively with @Sql] for further details.

View File

@ -19,11 +19,11 @@ Java::
// run code that relies on the test data
}
----
======
<1> Set the comment prefix and the separator in SQL scripts.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Test
@Sql("/test-user-data.sql", config = SqlConfig(commentPrefix = "`", separator = "@@")) // <1>
@ -32,4 +32,5 @@ Java::
}
----
<1> Set the comment prefix and the separator in SQL scripts.
======

View File

@ -22,11 +22,11 @@ Java::
// run code that uses the test schema and test data
}
----
======
<1> Declare a group of SQL scripts.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Test
@SqlGroup( // <1>
@ -37,6 +37,7 @@ Java::
}
----
<1> Declare a group of SQL scripts.
======

View File

@ -29,11 +29,11 @@ Java::
}
}
----
======
<1> Set the `@Sql` merge mode to `MERGE` for all test methods in the class.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@SpringJUnitConfig(TestConfig::class)
@Sql("/test-schema.sql")
@ -48,6 +48,7 @@ Java::
}
----
<1> Set the `@Sql` merge mode to `MERGE` for all test methods in the class.
======
The following example shows how to use `@SqlMergeMode` at the method level.
@ -69,11 +70,11 @@ Java::
}
}
----
======
<1> Set the `@Sql` merge mode to `MERGE` for a specific test method.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@SpringJUnitConfig(TestConfig::class)
@Sql("/test-schema.sql")
@ -88,5 +89,6 @@ Java::
}
----
<1> Set the `@Sql` merge mode to `MERGE` for a specific test method.
======

View File

@ -20,11 +20,11 @@ Java::
// class body...
}
----
======
<1> Register two `TestExecutionListener` implementations.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@ContextConfiguration
@TestExecutionListeners(CustomTestExecutionListener::class, AnotherTestExecutionListener::class) // <1>
@ -33,11 +33,12 @@ Java::
}
----
<1> Register two `TestExecutionListener` implementations.
======
By default, `@TestExecutionListeners` provides support for inheriting listeners from
superclasses or enclosing classes. See
xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit-jupiter-nested-test-configuration[`@Nested` test class configuration] and the
xref:testing/testcontext-framework/support-classes.adoc#testcontext-junit-jupiter-nested-test-configuration[`@Nested` test class configuration] and the
{api-spring-framework}/test/context/TestExecutionListeners.html[`@TestExecutionListeners`
javadoc] for an example and further details. If you discover that you need to switch
back to using the default `TestExecutionListener` implementations, see the note

View File

@ -20,11 +20,11 @@ Java::
// class body...
}
----
======
<1> Get properties from `test.properties` in the root of the classpath.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@ContextConfiguration
@TestPropertySource("/test.properties") // <1>
@ -33,6 +33,7 @@ Java::
}
----
<1> Get properties from `test.properties` in the root of the classpath.
======
The following example demonstrates how to declare inlined properties:
@ -49,11 +50,11 @@ Java::
// class body...
}
----
======
<1> Declare `timezone` and `port` properties.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@ContextConfiguration
@TestPropertySource(properties = ["timezone = GMT", "port: 4242"]) // <1>
@ -62,6 +63,7 @@ Java::
}
----
<1> Declare `timezone` and `port` properties.
======
See xref:testing/testcontext-framework/ctx-management/property-sources.adoc[Context Configuration with Test Property Sources] for examples and further details.

View File

@ -25,11 +25,11 @@ Java::
// class body...
}
----
======
<1> The `@WebAppConfiguration` annotation.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@ContextConfiguration
@WebAppConfiguration // <1>
@ -38,6 +38,7 @@ Java::
}
----
<1> The `@WebAppConfiguration` annotation.
======
--
@ -59,11 +60,11 @@ Java::
// class body...
}
----
======
<1> Specifying a classpath resource.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@ContextConfiguration
@WebAppConfiguration("classpath:test-web-resources") // <1>
@ -72,6 +73,7 @@ Java::
}
----
<1> Specifying a classpath resource.
======
--

View File

@ -37,15 +37,15 @@ Java::
.andExpect(content().string("body"));
}
----
======
<1> Check response status is still unchanged
<2> Async processing must have started
<3> Wait and assert the async result
<4> Manually perform an ASYNC dispatch (as there is no running container)
<5> Verify the final response
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Test
fun test() {
@ -69,5 +69,6 @@ Java::
<3> Wait and assert the async result
<4> Manually perform an ASYNC dispatch (as there is no running container)
<5> Verify the final response
======

View File

@ -298,7 +298,6 @@ Java::
}
}
----
======
<1> `CreateMessagePage` extends the `AbstractPage`. We do not go over the details of
`AbstractPage`, but, in summary, it contains common functionality for all of our pages.
For example, if our application has a navigational bar, global error messages, and other
@ -316,8 +315,9 @@ https://github.com/SeleniumHQ/selenium/wiki/PageFactory#making-the-example-work-
to override the default lookup behavior. Our example shows how to use the `@FindBy`
annotation to look up our submit button with a `css` selector (`input[type=submit]`).
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class CreateMessagePage(private val driver: WebDriver) : AbstractPage(driver) { // <1>
@ -358,6 +358,7 @@ by the `id` or `name` of the element within the HTML page.
https://github.com/SeleniumHQ/selenium/wiki/PageFactory#making-the-example-work-using-annotations[`@FindBy` annotation]
to override the default lookup behavior. Our example shows how to use the `@FindBy`
annotation to look up our submit button with a `css` selector (*input[type=submit]*).
======
--
Finally, we can verify that a new message was created successfully. The following

View File

@ -54,14 +54,13 @@ Java::
}
}
----
======
<1> Annotate the test class with `@RecordApplicationEvents`.
<2> Inject the `ApplicationEvents` instance for the current test.
<3> Use the `ApplicationEvents` API to count how many `OrderSubmitted` events were published.
// Don't use "quotes" in the "subs" section because of the asterisks in /* ... */
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
@SpringJUnitConfig(/* ... */)
@RecordApplicationEvents // <1>
@ -86,6 +85,7 @@ Java::
<1> Annotate the test class with `@RecordApplicationEvents`.
<2> Inject the `ApplicationEvents` instance for the current test.
<3> Use the `ApplicationEvents` API to count how many `OrderSubmitted` events were published.
======
See the
{api-spring-framework}/test/context/event/ApplicationEvents.html[`ApplicationEvents`

View File

@ -31,11 +31,11 @@ Java::
// class body...
}
----
======
<1> Injecting the `ApplicationContext`.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@SpringJUnitConfig
class MyTest {
@ -47,6 +47,7 @@ Java::
}
----
<1> Injecting the `ApplicationContext`.
======
Similarly, if your test is configured to load a `WebApplicationContext`, you can inject
@ -67,12 +68,12 @@ Java::
// class body...
}
----
======
<1> Configuring the `WebApplicationContext`.
<2> Injecting the `WebApplicationContext`.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@SpringJUnitWebConfig // <1>
class MyWebAppTest {
@ -84,6 +85,7 @@ Java::
----
<1> Configuring the `WebApplicationContext`.
<2> Injecting the `WebApplicationContext`.
======
Dependency injection by using `@Autowired` is provided by the

View File

@ -28,11 +28,11 @@ Java::
// class body...
}
----
======
<1> Specifying the location of Groovy configuration files.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@ExtendWith(SpringExtension::class)
// ApplicationContext will be loaded from "/AppConfig.groovy" and
@ -43,6 +43,7 @@ Java::
}
----
<1> Specifying the location of Groovy configuration files.
======
If you omit both the `locations` and `value` attributes from the `@ContextConfiguration`
@ -67,11 +68,11 @@ Java::
// class body...
}
----
======
<1> Loading configuration from the default location.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@ExtendWith(SpringExtension::class)
// ApplicationContext will be loaded from
@ -82,6 +83,7 @@ Java::
}
----
<1> Loading configuration from the default location.
======
.Declaring XML configuration and Groovy scripts simultaneously

View File

@ -47,12 +47,12 @@ Java::
// class body...
}
----
======
<1> Configuration file defined in the superclass.
<2> Configuration file defined in the subclass.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@ExtendWith(SpringExtension::class)
// ApplicationContext will be loaded from "/base-config.xml"
@ -71,6 +71,7 @@ Java::
----
<1> Configuration file defined in the superclass.
<2> Configuration file defined in the subclass.
======
Similarly, in the next example, which uses component classes, the `ApplicationContext`
@ -97,12 +98,12 @@ Java::
// class body...
}
----
======
<1> Configuration class defined in the superclass.
<2> Configuration class defined in the subclass.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// ApplicationContext will be loaded from BaseConfig
@SpringJUnitConfig(BaseConfig::class) // <1>
@ -118,6 +119,7 @@ Java::
----
<1> Configuration class defined in the superclass.
<2> Configuration class defined in the subclass.
======
In the next example, which uses context initializers, the `ApplicationContext` for
@ -146,12 +148,12 @@ Java::
// class body...
}
----
======
<1> Initializer defined in the superclass.
<2> Initializer defined in the subclass.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// ApplicationContext will be initialized by BaseInitializer
@SpringJUnitConfig(initializers = [BaseInitializer::class]) // <1>
@ -168,5 +170,6 @@ Java::
----
<1> Initializer defined in the superclass.
<2> Initializer defined in the subclass.
======

View File

@ -29,11 +29,11 @@ Java::
// class body...
}
----
======
<1> Specifying configuration by using a configuration class and an initializer.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@ExtendWith(SpringExtension::class)
// ApplicationContext will be loaded from TestConfig
@ -46,6 +46,7 @@ Java::
}
----
<1> Specifying configuration by using a configuration class and an initializer.
======
You can also omit the declaration of XML configuration files, Groovy scripts, or
@ -68,11 +69,11 @@ Java::
// class body...
}
----
======
<1> Specifying configuration by using only an initializer.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@ExtendWith(SpringExtension::class)
// ApplicationContext will be initialized by EntireAppInitializer
@ -83,5 +84,6 @@ Java::
}
----
<1> Specifying configuration by using only an initializer.
======

View File

@ -19,11 +19,11 @@ Java::
// class body...
}
----
======
<1> Specifying component classes.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@ExtendWith(SpringExtension::class)
// ApplicationContext will be loaded from AppConfig and TestConfig
@ -33,6 +33,7 @@ Java::
}
----
<1> Specifying component classes.
======
[[testcontext-ctx-management-javaconfig-component-classes]]
@ -100,11 +101,11 @@ Java::
}
----
======
<1> Loading configuration information from the nested `Config` class.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@SpringJUnitConfig <1>
// ApplicationContext will be loaded from the nested Config class
@ -131,5 +132,6 @@ Java::
}
----
<1> Loading configuration information from the nested `Config` class.
======

View File

@ -52,11 +52,11 @@ Java::
// class body...
}
----
======
<1> Specifying a properties file with an absolute path.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@ContextConfiguration
@TestPropertySource("/test.properties") // <1>
@ -65,6 +65,7 @@ Java::
}
----
<1> Specifying a properties file with an absolute path.
======
You can configure inlined properties in the form of key-value pairs by using the
@ -93,11 +94,11 @@ Java::
// class body...
}
----
======
<1> Setting two properties by using two variations of the key-value syntax.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@ContextConfiguration
@TestPropertySource(properties = ["timezone = GMT", "port: 4242"]) // <1>
@ -106,6 +107,7 @@ Java::
}
----
<1> Setting two properties by using two variations of the key-value syntax.
======
[NOTE]
====

View File

@ -24,11 +24,11 @@ Java::
// class body...
}
----
======
<1> Setting the locations attribute to a list of XML files.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@ExtendWith(SpringExtension::class)
// ApplicationContext will be loaded from "/app-config.xml" and
@ -39,6 +39,7 @@ Java::
}
----
<1> Setting the locations attribute to a list of XML files.
======
`@ContextConfiguration` supports an alias for the `locations` attribute through the
@ -59,11 +60,11 @@ Java::
// class body...
}
----
======
<1> Specifying XML files without using the `locations` attribute.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@ExtendWith(SpringExtension::class)
@ContextConfiguration("/app-config.xml", "/test-config.xml") // <1>
@ -72,6 +73,7 @@ Java::
}
----
<1> Specifying XML files without using the `locations` attribute.
======
If you omit both the `locations` and the `value` attributes from the
@ -96,11 +98,11 @@ Java::
// class body...
}
----
======
<1> Loading configuration from the default location.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@ExtendWith(SpringExtension::class)
// ApplicationContext will be loaded from
@ -111,5 +113,6 @@ Java::
}
----
<1> Loading configuration from the default location.
======

View File

@ -105,13 +105,13 @@ Java::
}
}
----
======
<1> Specify the configuration to load
<2> Inject the configuration
<3> Create the `WebTestClient`
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@SpringJUnitConfig(WebConfig::class) // <1>
class MyTests {
@ -127,6 +127,7 @@ Java::
<1> Specify the configuration to load
<2> Inject the configuration
<3> Create the `WebTestClient`
======
For Spring MVC, use the following where the Spring `ApplicationContext` is passed to
{api-spring-framework}/test/web/servlet/setup/MockMvcBuilders.html#webAppContextSetup-org.springframework.web.context.WebApplicationContext-[MockMvcBuilders.webAppContextSetup]
@ -158,13 +159,13 @@ Java::
}
}
----
======
<1> Specify the configuration to load
<2> Inject the configuration
<3> Create the `WebTestClient`
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@ExtendWith(SpringExtension.class)
@WebAppConfiguration("classpath:META-INF/web-resources") // <1>
@ -188,6 +189,7 @@ Java::
<1> Specify the configuration to load
<2> Inject the configuration
<3> Create the `WebTestClient`
======

View File

@ -221,12 +221,12 @@ Java::
}
}
----
======
<1> Using `@CrossOrigin` at the class level.
<2> Using `@CrossOrigin` at the method level.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@CrossOrigin(maxAge = 3600) // <1>
@RestController
@ -247,6 +247,7 @@ Java::
----
<1> Using `@CrossOrigin` at the class level.
<2> Using `@CrossOrigin` at the method level.
======
--

View File

@ -67,11 +67,11 @@ Java::
}
}
----
======
<1> Create router using `route()`.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val repository: PersonRepository = ...
val handler = PersonHandler(repository)
@ -103,6 +103,7 @@ Java::
}
----
<1> Create router using Coroutines router DSL; a Reactive alternative is also available via `router { }`.
======
One way to run a `RouterFunction` is to turn it into an `HttpHandler` and install it
through one of the built-in xref:web/webflux/reactive-spring.adoc#webflux-httphandler[server adapters]:
@ -436,7 +437,6 @@ public class PersonHandler {
}
}
----
======
<1> `listPeople` is a handler function that returns all `Person` objects found in the repository as
JSON.
<2> `createPerson` is a handler function that stores a new `Person` contained in the request body.
@ -448,8 +448,9 @@ when the `Person` has been saved).
variable. We retrieve that `Person` from the repository and create a JSON response, if it is
found. If it is not found, we use `switchIfEmpty(Mono<T>)` to return a 404 Not Found response.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class PersonHandler(private val repository: PersonRepository) {
@ -479,6 +480,7 @@ Note that `PersonRepository.savePerson(Person)` is a suspending function with no
<3> `getPerson` is a handler function that returns a single person, identified by the `id` path
variable. We retrieve that `Person` from the repository and create a JSON response, if it is
found. If it is not found, we return a 404 Not Found response.
======
--
@ -515,13 +517,13 @@ Java::
}
}
----
======
<1> Create `Validator` instance.
<2> Apply validation.
<3> Raise exception for a 400 response.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class PersonHandler(private val repository: PersonRepository) {
@ -548,6 +550,7 @@ Java::
<1> Create `Validator` instance.
<2> Apply validation.
<3> Raise exception for a 400 response.
======
Handlers can also use the standard bean validation API (JSR-303) by creating and injecting
a global `Validator` instance based on `LocalValidatorFactoryBean`.
@ -666,7 +669,6 @@ RouterFunction<ServerResponse> route = route()
.add(otherRoute) // <4>
.build();
----
======
<1> pass:q[`GET /person/{id}`] with an `Accept` header that matches JSON is routed to
`PersonHandler.getPerson`
<2> `GET /person` with an `Accept` header that matches JSON is routed to
@ -675,8 +677,9 @@ RouterFunction<ServerResponse> route = route()
`PersonHandler.createPerson`, and
<4> `otherRoute` is a router function that is created elsewhere, and added to the route built.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import org.springframework.http.MediaType.APPLICATION_JSON
@ -698,6 +701,7 @@ RouterFunction<ServerResponse> route = route()
<3> `POST /person` with no additional predicates is mapped to
`PersonHandler.createPerson`, and
<4> `otherRoute` is a router function that is created elsewhere, and added to the route built.
======
[[nested-routes]]
@ -724,11 +728,11 @@ RouterFunction<ServerResponse> route = route()
.POST(handler::createPerson))
.build();
----
======
<1> Note that second parameter of `path` is a consumer that takes the router builder.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val route = coRouter { // <1>
"/person".nest {
@ -739,6 +743,7 @@ RouterFunction<ServerResponse> route = route()
}
----
<1> Create router using Coroutines router DSL; a Reactive alternative is also available via `router { }`.
======
Though path-based nesting is the most common, you can nest on any kind of predicate by using
the `nest` method on the builder.
@ -918,12 +923,12 @@ Java::
.after((request, response) -> logResponse(response)) // <2>
.build();
----
======
<1> The `before` filter that adds a custom request header is only applied to the two GET routes.
<2> The `after` filter that logs the response is applied to all routes, including the nested ones.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val route = router {
"/person".nest {
@ -942,6 +947,7 @@ Java::
----
<1> The `before` filter that adds a custom request header is only applied to the two GET routes.
<2> The `after` filter that logs the response is applied to all routes, including the nested ones.
======
The `filter` method on the router builder takes a `HandlerFilterFunction`: a

View File

@ -214,13 +214,13 @@ Java::
return WebClient.builder().clientConnector(connector).build(); // <3>
}
----
======
<1> Create resources independent of global ones.
<2> Use the `ReactorClientHttpConnector` constructor with resource factory.
<3> Plug the connector into the `WebClient.Builder`.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Bean
fun resourceFactory() = ReactorResourceFactory().apply {
@ -242,6 +242,7 @@ Java::
<1> Create resources independent of global ones.
<2> Use the `ReactorClientHttpConnector` constructor with resource factory.
<3> Plug the connector into the `WebClient.Builder`.
======
--
@ -483,12 +484,12 @@ Java::
return WebClient.builder().clientConnector(connector).build(); <2>
}
----
======
<1> Use the `JettyClientHttpConnector` constructor with resource factory.
<2> Plug the connector into the `WebClient.Builder`.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Bean
fun resourceFactory() = JettyResourceFactory()
@ -506,6 +507,7 @@ Java::
----
<1> Use the `JettyClientHttpConnector` constructor with resource factory.
<2> Plug the connector into the `WebClient.Builder`.
======
--

View File

@ -194,14 +194,14 @@ Java::
}
}
----
======
<1> Access the stream of inbound messages.
<2> Do something with each message.
<3> Perform nested asynchronous operations that use the message content.
<4> Return a `Mono<Void>` that completes when receiving completes.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class ExampleHandler : WebSocketHandler {
@ -221,6 +221,7 @@ Java::
<2> Do something with each message.
<3> Perform nested asynchronous operations that use the message content.
<4> Return a `Mono<Void>` that completes when receiving completes.
======
TIP: For nested, asynchronous operations, you may need to call `message.retain()` on underlying
@ -254,13 +255,13 @@ Java::
}
}
----
======
<1> Handle the inbound message stream.
<2> Create the outbound message, producing a combined flow.
<3> Return a `Mono<Void>` that does not complete while we continue to receive.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class ExampleHandler : WebSocketHandler {
@ -282,6 +283,7 @@ Java::
<1> Handle the inbound message stream.
<2> Create the outbound message, producing a combined flow.
<3> Return a `Mono<Void>` that does not complete while we continue to receive.
======
Inbound and outbound streams can be independent and be joined only for completion,
@ -314,13 +316,13 @@ Java::
}
}
----
======
<1> Handle inbound message stream.
<2> Send outgoing messages.
<3> Join the streams and return a `Mono<Void>` that completes when either stream ends.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class ExampleHandler : WebSocketHandler {
@ -345,6 +347,7 @@ Java::
<1> Handle inbound message stream.
<2> Send outgoing messages.
<3> Join the streams and return a `Mono<Void>` that completes when either stream ends.
======

View File

@ -145,13 +145,13 @@ Java::
return "myViewName";
}
----
======
<1> Application-specific calculation.
<2> Response has been set to 304 (NOT_MODIFIED). No further processing.
<3> Continue with request processing.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@RequestMapping
fun myHandleMethod(exchange: ServerWebExchange, model: Model): String? {
@ -169,6 +169,7 @@ Java::
<1> Application-specific calculation.
<2> Response has been set to 304 (NOT_MODIFIED). No further processing.
<3> Continue with request processing.
======
--
There are three variants for checking conditional requests against `eTag` values, `lastModified`

View File

@ -24,11 +24,11 @@ Java::
}
}
----
======
<1> Declaring an `@ExceptionHandler`.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Controller
class SimpleController {
@ -42,6 +42,8 @@ Java::
}
----
<1> Declaring an `@ExceptionHandler`.
======
The exception can match against a top-level exception being propagated (that is, a direct

View File

@ -41,11 +41,11 @@ Java::
// ...
}
----
======
<1> Using the `@InitBinder` annotation.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Controller
class FormController {
@ -61,6 +61,7 @@ Java::
}
----
<1> Using the `@InitBinder` annotation.
======
--
Alternatively, when using a `Formatter`-based setup through a shared
@ -85,11 +86,11 @@ Java::
// ...
}
----
======
<1> Adding a custom formatter (a `DateFormatter`, in this case).
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Controller
class FormController {
@ -103,6 +104,7 @@ Java::
}
----
<1> Adding a custom formatter (a `DateFormatter`, in this case).
======
--

View File

@ -26,11 +26,11 @@ Java::
//...
}
----
======
<1> Get the cookie value.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@GetMapping("/demo")
fun handle(@CookieValue("JSESSIONID") cookie: String) { // <1>
@ -38,6 +38,7 @@ Java::
}
----
<1> Get the cookie value.
======
Type conversion is applied automatically if the target method parameter type is not

View File

@ -18,16 +18,17 @@ Java::
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@ModelAttribute Pet pet) { } // <1>
----
======
<1> Bind an instance of `Pet`.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
fun processSubmit(@ModelAttribute pet: Pet): String { } // <1>
----
<1> Bind an instance of `Pet`.
======
The `Pet` instance in the preceding example is resolved as follows:
@ -63,11 +64,11 @@ Java::
// ...
}
----
======
<1> Adding a `BindingResult`.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
fun processSubmit(@ModelAttribute("pet") pet: Pet, result: BindingResult): String { // <1>
@ -78,6 +79,7 @@ Java::
}
----
<1> Adding a `BindingResult`.
======
You can automatically apply validation after data binding by adding the
`jakarta.validation.Valid` annotation or Spring's `@Validated` annotation (see also
@ -98,11 +100,11 @@ Java::
// ...
}
----
======
<1> Using `@Valid` on a model attribute argument.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
fun processSubmit(@Valid @ModelAttribute("pet") pet: Pet, result: BindingResult): String { // <1>
@ -113,6 +115,7 @@ Java::
}
----
<1> Using `@Valid` on a model attribute argument.
======
Spring WebFlux, unlike Spring MVC, supports reactive types in the model -- for example,
`Mono<Account>` or `io.reactivex.Single<Account>`. You can declare a `@ModelAttribute` argument

View File

@ -95,12 +95,12 @@ Java::
// ...
}
----
======
<1> Using `@RequestPart` to get the metadata.
<2> Using `@RequestPart` to get the file.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@PostMapping("/")
fun handle(@RequestPart("meta-data") Part metadata, // <1>
@ -110,6 +110,7 @@ Java::
----
<1> Using `@RequestPart` to get the metadata.
<2> Using `@RequestPart` to get the file.
======
--
@ -128,11 +129,11 @@ Java::
// ...
}
----
======
<1> Using `@RequestPart` to get the metadata.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@PostMapping("/")
fun handle(@RequestPart("meta-data") metadata: MetaData): String { // <1>
@ -140,6 +141,7 @@ Java::
}
----
<1> Using `@RequestPart` to get the metadata.
======
--
You can use `@RequestPart` in combination with `jakarta.validation.Valid` or Spring's
@ -189,11 +191,11 @@ Java::
// ...
}
----
======
<1> Using `@RequestBody`.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@PostMapping("/")
fun handle(@RequestBody parts: MultiValueMap<String, Part>): String { // <1>
@ -201,6 +203,7 @@ Java::
}
----
<1> Using `@RequestBody`.
======
--
[[partevent]]
@ -250,7 +253,6 @@ Java::
}));
}
----
======
<1> Using `@RequestBody`.
<2> The final `PartEvent` for a particular part will have `isLast()` set to `true`, and can be
followed by additional events belonging to subsequent parts.
@ -262,8 +264,9 @@ file upload.
<5> Handling the file upload.
<6> The body contents must be completely consumed, relayed, or released to avoid memory leaks.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@PostMapping("/")
fun handle(@RequestBody allPartsEvents: Flux<PartEvent>) = { // <1>
@ -299,6 +302,7 @@ file upload.
<4> Handling the form field.
<5> Handling the file upload.
<6> The body contents must be completely consumed, relayed, or released to avoid memory leaks.
======
Received part events can also be relayed to another service by using the `WebClient`.
See xref:web/webflux-webclient/client-body.adoc#webflux-client-body-multipart[Multipart Data].

View File

@ -18,11 +18,11 @@ Java::
// ...
}
----
======
<1> Using `@RequestAttribute`.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@GetMapping("/")
fun handle(@RequestAttribute client: Client): String { // <1>
@ -30,5 +30,6 @@ Java::
}
----
<1> Using `@RequestAttribute`.
======

View File

@ -34,12 +34,12 @@ Java::
//...
}
----
======
<1> Get the value of the `Accept-Encoding` header.
<2> Get the value of the `Keep-Alive` header.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@GetMapping("/demo")
fun handle(
@ -50,6 +50,7 @@ Java::
----
<1> Get the value of the `Accept-Encoding` header.
<2> Get the value of the `Keep-Alive` header.
======
Type conversion is applied automatically if the target method parameter type is not
`String`. See xref:web/webflux/controller/ann-methods/typeconversion.adoc[Type Conversion].

View File

@ -28,11 +28,11 @@ Java::
// ...
}
----
======
<1> Using `@RequestParam`.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import org.springframework.ui.set
@ -53,6 +53,7 @@ Java::
}
----
<1> Using `@RequestParam`.
======
TIP: The Servlet API "`request parameter`" concept conflates query parameters, form
data, and multiparts into one. However, in WebFlux, each is accessed individually through

View File

@ -18,11 +18,11 @@ Java::
// ...
}
----
======
<1> Using `@SessionAttribute`.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@GetMapping("/")
fun handle(@SessionAttribute user: User): String { // <1>
@ -30,6 +30,7 @@ Java::
}
----
<1> Using `@SessionAttribute`.
======
For use cases that require adding or removing session attributes, consider injecting
`WebSession` into the controller method.

View File

@ -23,11 +23,11 @@ Java::
// ...
}
----
======
<1> Using the `@SessionAttributes` annotation.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Controller
@SessionAttributes("pet") // <1>
@ -36,6 +36,7 @@ Java::
}
----
<1> Using the `@SessionAttributes` annotation.
======
On the first request, when a model attribute with the name, `pet`, is added to the model,
it is automatically promoted to and saved in the `WebSession`. It remains there until
@ -65,12 +66,12 @@ Java::
}
}
----
======
<1> Using the `@SessionAttributes` annotation.
<2> Using a `SessionStatus` variable.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Controller
@SessionAttributes("pet") // <1>
@ -90,5 +91,6 @@ Java::
----
<1> Using the `@SessionAttributes` annotation.
<2> Using a `SessionStatus` variable.
======

View File

@ -155,12 +155,12 @@ Java::
}
}
----
======
<1> Class-level URI mapping.
<2> Method-level URI mapping.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Controller
@RequestMapping("/owners/{ownerId}") // <1>
@ -174,6 +174,7 @@ Java::
----
<1> Class-level URI mapping.
<2> Method-level URI mapping.
======
--
@ -352,11 +353,11 @@ Java::
// ...
}
----
======
<1> Check that `myParam` equals `myValue`.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@GetMapping("/pets/{petId}", params = ["myParam=myValue"]) // <1>
fun findPet(@PathVariable petId: String) {
@ -364,6 +365,7 @@ Java::
}
----
<1> Check that `myParam` equals `myValue`.
======
You can also use the same with request header conditions, as the following example shows:
@ -378,11 +380,11 @@ Java::
// ...
}
----
======
<1> Check that `myHeader` equals `myValue`.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@GetMapping("/pets/{petId}", headers = ["myHeader=myValue"]) // <1>
fun findPet(@PathVariable petId: String) {
@ -390,6 +392,7 @@ Java::
}
----
<1> Check that `myHeader` equals `myValue`.
======
@ -466,14 +469,14 @@ Java::
}
----
======
<1> Inject target handlers and the handler mapping for controllers.
<2> Prepare the request mapping metadata.
<3> Get the handler method.
<4> Add the registration.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
class MyConfig {
@ -493,6 +496,7 @@ Java::
<2> Prepare the request mapping metadata.
<3> Get the handler method.
<4> Add the registration.
======

View File

@ -25,11 +25,11 @@ Java::
// ...
}
----
======
<1> Scan the `org.example.web` package.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@ComponentScan("org.example.web") // <1>
@ -39,6 +39,7 @@ Java::
}
----
<1> Scan the `org.example.web` package.
======
`@RestController` is a xref:core/beans/classpath-scanning.adoc#beans-meta-annotations[composed annotation] that is
itself meta-annotated with `@Controller` and `@ResponseBody`, indicating a controller whose

View File

@ -67,11 +67,11 @@ Java::
}
}
----
======
<1> Create router using `route()`.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import org.springframework.web.servlet.function.router
@ -105,6 +105,7 @@ Java::
}
----
<1> Create router using the router DSL.
======
If you register the `RouterFunction` as a bean, for instance by exposing it in a
@ -409,7 +410,6 @@ public class PersonHandler {
}
----
======
<1> `listPeople` is a handler function that returns all `Person` objects found in the repository as
JSON.
<2> `createPerson` is a handler function that stores a new `Person` contained in the request body.
@ -417,8 +417,9 @@ JSON.
variable. We retrieve that `Person` from the repository and create a JSON response, if it is
found. If it is not found, we return a 404 Not Found response.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class PersonHandler(private val repository: PersonRepository) {
@ -447,6 +448,7 @@ JSON.
<3> `getPerson` is a handler function that returns a single person, identified by the `id` path
variable. We retrieve that `Person` from the repository and create a JSON response, if it is
found. If it is not found, we return a 404 Not Found response.
======
--
@ -485,13 +487,13 @@ Java::
}
}
----
======
<1> Create `Validator` instance.
<2> Apply validation.
<3> Raise exception for a 400 response.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class PersonHandler(private val repository: PersonRepository) {
@ -518,6 +520,7 @@ Java::
<1> Create `Validator` instance.
<2> Apply validation.
<3> Raise exception for a 400 response.
======
Handlers can also use the standard bean validation API (JSR-303) by creating and injecting
a global `Validator` instance based on `LocalValidatorFactoryBean`.
@ -638,7 +641,6 @@ Java::
.add(otherRoute) // <4>
.build();
----
======
<1> pass:q[`GET /person/{id}`] with an `Accept` header that matches JSON is routed to
`PersonHandler.getPerson`
<2> `GET /person` with an `Accept` header that matches JSON is routed to
@ -647,8 +649,9 @@ Java::
`PersonHandler.createPerson`, and
<4> `otherRoute` is a router function that is created elsewhere, and added to the route built.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import org.springframework.http.MediaType.APPLICATION_JSON
import org.springframework.web.servlet.function.router
@ -671,6 +674,7 @@ Java::
<3> `POST /person` with no additional predicates is mapped to
`PersonHandler.createPerson`, and
<4> `otherRoute` is a router function that is created elsewhere, and added to the route built.
======
[[nested-routes]]
@ -698,11 +702,11 @@ RouterFunction<ServerResponse> route = route()
.POST(handler::createPerson))
.build();
----
======
<1> Note that second parameter of `path` is a consumer that takes the router builder.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import org.springframework.web.servlet.function.router
@ -715,6 +719,7 @@ RouterFunction<ServerResponse> route = route()
}
----
<1> Using `nest` DSL.
======
Though path-based nesting is the most common, you can nest on any kind of predicate by using
the `nest` method on the builder.
@ -883,12 +888,12 @@ Java::
.after((request, response) -> logResponse(response)) // <2>
.build();
----
======
<1> The `before` filter that adds a custom request header is only applied to the two GET routes.
<2> The `after` filter that logs the response is applied to all routes, including the nested ones.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import org.springframework.web.servlet.function.router
@ -909,6 +914,7 @@ Java::
----
<1> The `before` filter that adds a custom request header is only applied to the two GET routes.
<2> The `after` filter that logs the response is applied to all routes, including the nested ones.
======
The `filter` method on the router builder takes a `HandlerFilterFunction`: a

View File

@ -154,13 +154,13 @@ Java::
return "myViewName";
}
----
======
<1> Application-specific calculation.
<2> The response has been set to 304 (NOT_MODIFIED) -- no further processing.
<3> Continue with the request processing.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@RequestMapping
fun myHandleMethod(request: WebRequest, model: Model): String? {
@ -178,6 +178,7 @@ Java::
<1> Application-specific calculation.
<2> The response has been set to 304 (NOT_MODIFIED) -- no further processing.
<3> Continue with the request processing.
======
--

View File

@ -40,11 +40,11 @@ Java::
// ...
}
----
======
<1> Defining an `@InitBinder` method.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Controller
class FormController {
@ -60,6 +60,7 @@ Java::
}
----
<1> Defining an `@InitBinder` method.
======
Alternatively, when you use a `Formatter`-based setup through a shared
`FormattingConversionService`, you can re-use the same approach and register
@ -82,11 +83,11 @@ Java::
// ...
}
----
======
<1> Defining an `@InitBinder` method on a custom formatter.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Controller
class FormController {
@ -100,6 +101,7 @@ Java::
}
----
<1> Defining an `@InitBinder` method on a custom formatter.
======
[[mvc-ann-initbinder-model-design]]
== Model Design

View File

@ -26,11 +26,11 @@ Java::
//...
}
----
======
<1> Get the value of the `JSESSIONID` cookie.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@GetMapping("/demo")
fun handle(@CookieValue("JSESSIONID") cookie: String) { // <1>
@ -38,6 +38,7 @@ Java::
}
----
<1> Get the value of the `JSESSIONID` cookie.
======
If the target method parameter type is not `String`, type conversion is applied automatically.
See xref:web/webmvc/mvc-controller/ann-methods/typeconversion.adoc[Type Conversion].

View File

@ -20,11 +20,11 @@ Java::
// method logic...
}
----
======
<1> Bind an instance of `Pet`.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
fun processSubmit(@ModelAttribute pet: Pet): String { // <1>
@ -32,6 +32,7 @@ fun processSubmit(@ModelAttribute pet: Pet): String { // <1>
}
----
<1> Bind an instance of `Pet`.
======
The `Pet` instance above is sourced in one of the following ways:
@ -66,11 +67,11 @@ Java::
// ...
}
----
======
<1> Bind an instance of `Account` using an explicit attribute name.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@PutMapping("/accounts/{account}")
fun save(@ModelAttribute("account") account: Account): String { // <1>
@ -78,6 +79,7 @@ Java::
}
----
<1> Bind an instance of `Account` using an explicit attribute name.
======
After the model attribute instance is obtained, data binding is applied. The
`WebDataBinder` class matches Servlet request parameter names (query parameters and form
@ -104,11 +106,11 @@ Java::
// ...
}
----
======
<1> Adding a `BindingResult` next to the `@ModelAttribute`.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
fun processSubmit(@ModelAttribute("pet") pet: Pet, result: BindingResult): String { // <1>
@ -119,6 +121,7 @@ Java::
}
----
<1> Adding a `BindingResult` next to the `@ModelAttribute`.
======
In some cases, you may want access to a model attribute without data binding. For such
cases, you can inject the `Model` into the controller and access it directly or,
@ -146,11 +149,11 @@ Java::
// ...
}
----
======
<1> Setting `@ModelAttribute(binding=false)`.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@ModelAttribute
fun setUpForm(): AccountForm {
@ -169,6 +172,7 @@ Java::
}
----
<1> Setting `@ModelAttribute(binding=false)`.
======
You can automatically apply validation after data binding by adding the
`jakarta.validation.Valid` annotation or Spring's `@Validated` annotation
@ -189,11 +193,11 @@ Java::
// ...
}
----
======
<1> Validate the `Pet` instance.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
fun processSubmit(@Valid @ModelAttribute("pet") pet: Pet, result: BindingResult): String { // <1>
@ -204,6 +208,7 @@ Java::
}
----
<1> Validate the `Pet` instance.
======
Note that using `@ModelAttribute` is optional (for example, to set its attributes).
By default, any argument that is not a simple value type (as determined by

View File

@ -18,11 +18,11 @@ Java::
// ...
}
----
======
<1> Using the `@RequestAttribute` annotation.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@GetMapping("/")
fun handle(@RequestAttribute client: Client): String { // <1>
@ -30,5 +30,6 @@ Java::
}
----
<1> Using the `@RequestAttribute` annotation.
======

View File

@ -34,12 +34,12 @@ Java::
//...
}
----
======
<1> Get the value of the `Accept-Encoding` header.
<2> Get the value of the `Keep-Alive` header.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@GetMapping("/demo")
fun handle(
@ -50,6 +50,7 @@ Java::
----
<1> Get the value of the `Accept-Encoding` header.
<2> Get the value of the `Keep-Alive` header.
======
If the target method parameter type is not
`String`, type conversion is automatically applied. See xref:web/webmvc/mvc-controller/ann-methods/typeconversion.adoc[Type Conversion].

View File

@ -31,11 +31,11 @@ Java::
}
----
======
<1> Using `@RequestParam` to bind `petId`.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import org.springframework.ui.set
@ -57,6 +57,7 @@ Java::
}
----
<1> Using `@RequestParam` to bind `petId`.
======
By default, method parameters that use this annotation are required, but you can specify that
a method parameter is optional by setting the `@RequestParam` annotation's `required` flag to

View File

@ -19,18 +19,19 @@ Java::
// ...
}
----
======
<1> Using a `@SessionAttribute` annotation.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@RequestMapping("/")
fun handle(@SessionAttribute user: User): String { // <1>
// ...
}
----
<1> Using a `@SessionAttribute` annotation.
<1> Using a `@SessionAttribute` annotation.======
======
For use cases that require adding or removing session attributes, consider injecting
`org.springframework.web.context.request.WebRequest` or

View File

@ -23,11 +23,11 @@ Java::
// ...
}
----
======
<1> Using the `@SessionAttributes` annotation.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Controller
@SessionAttributes("pet") // <1>
@ -36,6 +36,7 @@ Java::
}
----
<1> Using the `@SessionAttributes` annotation.
======
On the first request, when a model attribute with the name, `pet`, is added to the model,
it is automatically promoted to and saved in the HTTP Servlet session. It remains there
@ -64,12 +65,12 @@ Java::
}
}
----
======
<1> Storing the `Pet` value in the Servlet session.
<2> Clearing the `Pet` value from the Servlet session.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Controller
@SessionAttributes("pet") // <1>
@ -89,5 +90,6 @@ class EditPetForm {
----
<1> Storing the `Pet` value in the Servlet session.
<2> Clearing the `Pet` value from the Servlet session.
======

View File

@ -309,11 +309,11 @@ Java::
// ...
}
----
======
<1> Using a `consumes` attribute to narrow the mapping by the content type.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@PostMapping("/pets", consumes = ["application/json"]) // <1>
fun addPet(@RequestBody pet: Pet) {
@ -321,6 +321,7 @@ Java::
}
----
<1> Using a `consumes` attribute to narrow the mapping by the content type.
======
The `consumes` attribute also supports negation expressions -- for example, `!text/plain` means any
content type other than `text/plain`.
@ -352,11 +353,11 @@ Java::
// ...
}
----
======
<1> Using a `produces` attribute to narrow the mapping by the content type.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@GetMapping("/pets/{petId}", produces = ["application/json"]) // <1>
@ResponseBody
@ -365,6 +366,7 @@ Java::
}
----
<1> Using a `produces` attribute to narrow the mapping by the content type.
======
The media type can specify a character set. Negated expressions are supported -- for example,
`!text/plain` means any content type other than "text/plain".
@ -396,11 +398,11 @@ Java::
// ...
}
----
======
<1> Testing whether `myParam` equals `myValue`.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@GetMapping("/pets/{petId}", params = ["myParam=myValue"]) // <1>
fun findPet(@PathVariable petId: String) {
@ -408,6 +410,7 @@ Java::
}
----
<1> Testing whether `myParam` equals `myValue`.
======
You can also use the same with request header conditions, as the following example shows:
@ -422,11 +425,11 @@ Java::
// ...
}
----
======
<1> Testing whether `myHeader` equals `myValue`.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@GetMapping("/pets/{petId}", headers = ["myHeader=myValue"]) // <1>
fun findPet(@PathVariable petId: String) {
@ -434,6 +437,7 @@ Java::
}
----
<1> Testing whether `myHeader` equals `myValue`.
======
TIP: You can match `Content-Type` and `Accept` with the headers condition, but it is better to use
xref:web/webmvc/mvc-controller/ann-requestmapping.adoc#mvc-ann-requestmapping-consumes[consumes] and xref:web/webmvc/mvc-controller/ann-requestmapping.adoc#mvc-ann-requestmapping-produces[produces]
@ -517,14 +521,14 @@ Java::
}
}
----
======
<1> Inject the target handler and the handler mapping for controllers.
<2> Prepare the request mapping meta data.
<3> Get the handler method.
<4> Add the registration.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
class MyConfig {
@ -541,6 +545,7 @@ Java::
<2> Prepare the request mapping meta data.
<3> Get the handler method.
<4> Add the registration.
======

View File

@ -18,15 +18,15 @@ Java::
URI uri = uriComponents.expand("Westin", "123").toUri(); // <5>
----
======
<1> Static factory method with a URI template.
<2> Add or replace URI components.
<3> Request to have the URI template and URI variables encoded.
<4> Build a `UriComponents`.
<5> Expand variables and obtain the `URI`.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val uriComponents = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}") // <1>
@ -41,6 +41,7 @@ Java::
<3> Request to have the URI template and URI variables encoded.
<4> Build a `UriComponents`.
<5> Expand variables and obtain the `URI`.
======
The preceding example can be consolidated into one chain and shortened with `buildAndExpand`,
as the following example shows:

View File

@ -8,10 +8,10 @@ javaPlatform {
dependencies {
api(platform("com.fasterxml.jackson:jackson-bom:2.14.3"))
api(platform("io.micrometer:micrometer-bom:1.10.6"))
api(platform("io.micrometer:micrometer-bom:1.10.7"))
api(platform("io.netty:netty-bom:4.1.92.Final"))
api(platform("io.netty:netty5-bom:5.0.0.Alpha5"))
api(platform("io.projectreactor:reactor-bom:2022.0.6"))
api(platform("io.projectreactor:reactor-bom:2022.0.7"))
api(platform("io.rsocket:rsocket-bom:1.1.3"))
api(platform("org.apache.groovy:groovy-bom:4.0.11"))
api(platform("org.apache.logging.log4j:log4j-bom:2.20.0"))
@ -23,13 +23,13 @@ dependencies {
constraints {
api("com.fasterxml:aalto-xml:1.3.2")
api("com.fasterxml.woodstox:woodstox-core:6.5.0")
api("com.github.ben-manes.caffeine:caffeine:3.1.5")
api("com.fasterxml.woodstox:woodstox-core:6.5.1")
api("com.github.ben-manes.caffeine:caffeine:3.1.6")
api("com.github.librepdf:openpdf:1.3.30")
api("com.google.code.findbugs:findbugs:3.0.1")
api("com.google.code.findbugs:jsr305:3.0.2")
api("com.google.code.gson:gson:2.10.1")
api("com.google.protobuf:protobuf-java-util:3.21.12")
api("com.google.protobuf:protobuf-java-util:3.23.0")
api("com.googlecode.protobuf-java-format:protobuf-java-format:1.4")
api("com.h2database:h2:2.1.214")
api("com.jayway.jsonpath:json-path:2.8.0")
@ -45,11 +45,11 @@ dependencies {
api("com.thoughtworks.xstream:xstream:1.4.20")
api("commons-io:commons-io:2.11.0")
api("de.bechte.junit:junit-hierarchicalcontextrunner:4.12.2")
api("info.picocli:picocli:4.7.1")
api("info.picocli:picocli:4.7.3")
api("io.micrometer:context-propagation:1.0.0")
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.7.RELEASE")
api("io.projectreactor.tools:blockhound:1.0.8.RELEASE")
api("io.r2dbc:r2dbc-h2:1.0.0.RELEASE")
api("io.r2dbc:r2dbc-spi-test:1.0.0.RELEASE")
api("io.r2dbc:r2dbc-spi:1.0.0.RELEASE")
@ -89,9 +89,9 @@ dependencies {
api("net.sf.jopt-simple:jopt-simple:5.0.4")
api("net.sourceforge.htmlunit:htmlunit:2.70.0")
api("org.apache-extras.beanshell:bsh:2.0b6")
api("org.apache.activemq:activemq-broker:5.17.2")
api("org.apache.activemq:activemq-kahadb-store:5.17.2")
api("org.apache.activemq:activemq-stomp:5.17.2")
api("org.apache.activemq:activemq-broker:5.17.4")
api("org.apache.activemq:activemq-kahadb-store:5.17.4")
api("org.apache.activemq:activemq-stomp:5.17.4")
api("org.apache.commons:commons-pool2:2.9.0")
api("org.apache.derby:derby:10.16.1.1")
api("org.apache.derby:derbyclient:10.16.1.1")