diff --git a/framework-docs/modules/ROOT/pages/core/aop/ataspectj/advice.adoc b/framework-docs/modules/ROOT/pages/core/aop/ataspectj/advice.adoc index e5ea40f51fef..998a69b0edac 100644 --- a/framework-docs/modules/ROOT/pages/core/aop/ataspectj/advice.adoc +++ b/framework-docs/modules/ROOT/pages/core/aop/ataspectj/advice.adoc @@ -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 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). diff --git a/framework-docs/modules/ROOT/pages/core/aop/ataspectj/example.adoc b/framework-docs/modules/ROOT/pages/core/aop/ataspectj/example.adoc index 082de8ba3fc5..03cb9d102e9f 100644 --- a/framework-docs/modules/ROOT/pages/core/aop/ataspectj/example.adoc +++ b/framework-docs/modules/ROOT/pages/core/aop/ataspectj/example.adoc @@ -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 diff --git a/framework-docs/modules/ROOT/pages/core/aop/ataspectj/pointcuts.adoc b/framework-docs/modules/ROOT/pages/core/aop/ataspectj/pointcuts.adoc index b809e1f779f7..068bfeaf926f 100644 --- a/framework-docs/modules/ROOT/pages/core/aop/ataspectj/pointcuts.adoc +++ b/framework-docs/modules/ROOT/pages/core/aop/ataspectj/pointcuts.adoc @@ -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 diff --git a/framework-docs/modules/ROOT/pages/core/appendix/xml-custom.adoc b/framework-docs/modules/ROOT/pages/core/appendix/xml-custom.adoc index 116fad995204..a0ccd3cb33b4 100644 --- a/framework-docs/modules/ROOT/pages/core/appendix/xml-custom.adoc +++ b/framework-docs/modules/ROOT/pages/core/appendix/xml-custom.adoc @@ -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) { this.children = children } - + override fun getObject(): Component? { if (this.children != null && this.children!!.isNotEmpty()) { for (child in children!!) { diff --git a/framework-docs/modules/ROOT/pages/core/beans/annotation-config/autowired-qualifiers.adoc b/framework-docs/modules/ROOT/pages/core/beans/annotation-config/autowired-qualifiers.adoc index 9069fdad9da6..946fb6a317b2 100644 --- a/framework-docs/modules/ROOT/pages/core/beans/annotation-config/autowired-qualifiers.adoc +++ b/framework-docs/modules/ROOT/pages/core/beans/annotation-config/autowired-qualifiers.adoc @@ -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: diff --git a/framework-docs/modules/ROOT/pages/core/beans/annotation-config/resource.adoc b/framework-docs/modules/ROOT/pages/core/beans/annotation-config/resource.adoc index a9622299440d..b3457b78833b 100644 --- a/framework-docs/modules/ROOT/pages/core/beans/annotation-config/resource.adoc +++ b/framework-docs/modules/ROOT/pages/core/beans/annotation-config/resource.adoc @@ -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`. +====== -- diff --git a/framework-docs/modules/ROOT/pages/core/beans/classpath-scanning.adoc b/framework-docs/modules/ROOT/pages/core/beans/classpath-scanning.adoc index e3f792392237..6c7f43351bd2 100644 --- a/framework-docs/modules/ROOT/pages/core/beans/classpath-scanning.adoc +++ b/framework-docs/modules/ROOT/pages/core/beans/classpath-scanning.adoc @@ -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 diff --git a/framework-docs/modules/ROOT/pages/core/beans/environment.adoc b/framework-docs/modules/ROOT/pages/core/beans/environment.adoc index 441fdb49b56b..8dcc44c9cc59 100644 --- a/framework-docs/modules/ROOT/pages/core/beans/environment.adoc +++ b/framework-docs/modules/ROOT/pages/core/beans/environment.adoc @@ -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] diff --git a/framework-docs/modules/ROOT/pages/core/beans/java/instantiating-container.adoc b/framework-docs/modules/ROOT/pages/core/beans/java/instantiating-container.adoc index e9c98ea126a9..3a1d9b9176cc 100644 --- a/framework-docs/modules/ROOT/pages/core/beans/java/instantiating-container.adoc +++ b/framework-docs/modules/ROOT/pages/core/beans/java/instantiating-container.adoc @@ -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] diff --git a/framework-docs/modules/ROOT/pages/core/expressions/evaluation.adoc b/framework-docs/modules/ROOT/pages/core/expressions/evaluation.adoc index 79b3f260b915..2ed0079d67be 100644 --- a/framework-docs/modules/ROOT/pages/core/expressions/evaluation.adoc +++ b/framework-docs/modules/ROOT/pages/core/expressions/evaluation.adoc @@ -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 getValue(Class desiredResultType)`. diff --git a/framework-docs/modules/ROOT/pages/data-access/dao.adoc b/framework-docs/modules/ROOT/pages/data-access/dao.adoc index 2af1663add74..4c2844e95490 100644 --- a/framework-docs/modules/ROOT/pages/data-access/dao.adoc +++ b/framework-docs/modules/ROOT/pages/data-access/dao.adoc @@ -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, diff --git a/framework-docs/modules/ROOT/pages/data-access/jdbc/core.adoc b/framework-docs/modules/ROOT/pages/data-access/jdbc/core.adoc index 54a1032c22a8..31bbecce2f29 100644 --- a/framework-docs/modules/ROOT/pages/data-access/jdbc/core.adoc +++ b/framework-docs/modules/ROOT/pages/data-access/jdbc/core.adoc @@ -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`. +====== -- diff --git a/framework-docs/modules/ROOT/pages/data-access/jdbc/parameter-handling.adoc b/framework-docs/modules/ROOT/pages/data-access/jdbc/parameter-handling.adoc index 7d7101d26e77..6ee5fcca9314 100644 --- a/framework-docs/modules/ROOT/pages/data-access/jdbc/parameter-handling.adoc +++ b/framework-docs/modules/ROOT/pages/data-access/jdbc/parameter-handling.adoc @@ -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]] diff --git a/framework-docs/modules/ROOT/pages/data-access/r2dbc.adoc b/framework-docs/modules/ROOT/pages/data-access/r2dbc.adoc index 57d5c9643249..0db9cd2a2a5e 100644 --- a/framework-docs/modules/ROOT/pages/data-access/r2dbc.adoc +++ b/framework-docs/modules/ROOT/pages/data-access/r2dbc.adoc @@ -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 diff --git a/framework-docs/modules/ROOT/pages/rsocket.adoc b/framework-docs/modules/ROOT/pages/rsocket.adoc index 13777f8f9f25..8df3d9d8ccb5 100644 --- a/framework-docs/modules/ROOT/pages/rsocket.adoc +++ b/framework-docs/modules/ROOT/pages/rsocket.adoc @@ -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`. +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 diff --git a/framework-docs/modules/ROOT/pages/testing/annotations/integration-junit-jupiter.adoc b/framework-docs/modules/ROOT/pages/testing/annotations/integration-junit-jupiter.adoc index 98f394130e51..2876f194df48 100644 --- a/framework-docs/modules/ROOT/pages/testing/annotations/integration-junit-jupiter.adoc +++ b/framework-docs/modules/ROOT/pages/testing/annotations/integration-junit-jupiter.adoc @@ -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 diff --git a/framework-docs/modules/ROOT/pages/testing/annotations/integration-junit4.adoc b/framework-docs/modules/ROOT/pages/testing/annotations/integration-junit4.adoc index d228a83c1857..8c4c645d95af 100644 --- a/framework-docs/modules/ROOT/pages/testing/annotations/integration-junit4.adoc +++ b/framework-docs/modules/ROOT/pages/testing/annotations/integration-junit4.adoc @@ -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. +====== diff --git a/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-activeprofiles.adoc b/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-activeprofiles.adoc index 71217d5b79ce..6b3d521b492b 100644 --- a/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-activeprofiles.adoc +++ b/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-activeprofiles.adoc @@ -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 diff --git a/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-aftertransaction.adoc b/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-aftertransaction.adoc index 680c554da08a..3a2e3e73eaad 100644 --- a/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-aftertransaction.adoc +++ b/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-aftertransaction.adoc @@ -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. +====== diff --git a/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-beforetransaction.adoc b/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-beforetransaction.adoc index 3723945221e8..6bf76783406c 100644 --- a/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-beforetransaction.adoc +++ b/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-beforetransaction.adoc @@ -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. +====== diff --git a/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-commit.adoc b/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-commit.adoc index 86278154703b..46440b35ced0 100644 --- a/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-commit.adoc +++ b/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-commit.adoc @@ -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. +====== diff --git a/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-contextconfiguration.adoc b/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-contextconfiguration.adoc index e5e8d853e994..fe883635958b 100644 --- a/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-contextconfiguration.adoc +++ b/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-contextconfiguration.adoc @@ -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 diff --git a/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-dirtiescontext.adoc b/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-dirtiescontext.adoc index 2ccca2f0b08b..dde345578835 100644 --- a/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-dirtiescontext.adoc +++ b/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-dirtiescontext.adoc @@ -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 diff --git a/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-dynamicpropertysource.adoc b/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-dynamicpropertysource.adoc index 94edb68c3674..003175517ccf 100644 --- a/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-dynamicpropertysource.adoc +++ b/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-dynamicpropertysource.adoc @@ -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. diff --git a/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-rollback.adoc b/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-rollback.adoc index 1f2d84cf93eb..93f2e32c6b67 100644 --- a/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-rollback.adoc +++ b/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-rollback.adoc @@ -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. +====== diff --git a/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-sql.adoc b/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-sql.adoc index 982838d8b887..f84aa6b95017 100644 --- a/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-sql.adoc +++ b/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-sql.adoc @@ -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. diff --git a/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-sqlconfig.adoc b/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-sqlconfig.adoc index 128d6794f971..9910dd7b4555 100644 --- a/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-sqlconfig.adoc +++ b/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-sqlconfig.adoc @@ -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. +====== diff --git a/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-sqlgroup.adoc b/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-sqlgroup.adoc index 564645d30b3a..104e03a1a3e2 100644 --- a/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-sqlgroup.adoc +++ b/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-sqlgroup.adoc @@ -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. +====== diff --git a/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-sqlmergemode.adoc b/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-sqlmergemode.adoc index 8e101fcc0837..afb3b91dc220 100644 --- a/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-sqlmergemode.adoc +++ b/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-sqlmergemode.adoc @@ -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. +====== diff --git a/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-testexecutionlisteners.adoc b/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-testexecutionlisteners.adoc index 998cb90a2fa1..3e9505d15235 100644 --- a/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-testexecutionlisteners.adoc +++ b/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-testexecutionlisteners.adoc @@ -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 diff --git a/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-testpropertysource.adoc b/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-testpropertysource.adoc index 97905b1dd4bd..157fc1289024 100644 --- a/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-testpropertysource.adoc +++ b/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-testpropertysource.adoc @@ -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. diff --git a/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-webappconfiguration.adoc b/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-webappconfiguration.adoc index 568a722269db..a367a0ee9451 100644 --- a/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-webappconfiguration.adoc +++ b/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-webappconfiguration.adoc @@ -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. +====== -- diff --git a/framework-docs/modules/ROOT/pages/testing/spring-mvc-test-framework/async-requests.adoc b/framework-docs/modules/ROOT/pages/testing/spring-mvc-test-framework/async-requests.adoc index d8c17db3e94c..9dacf436fd5f 100644 --- a/framework-docs/modules/ROOT/pages/testing/spring-mvc-test-framework/async-requests.adoc +++ b/framework-docs/modules/ROOT/pages/testing/spring-mvc-test-framework/async-requests.adoc @@ -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 +====== diff --git a/framework-docs/modules/ROOT/pages/testing/spring-mvc-test-framework/server-htmlunit/webdriver.adoc b/framework-docs/modules/ROOT/pages/testing/spring-mvc-test-framework/server-htmlunit/webdriver.adoc index 79bb9e9fb823..392ae27a8cbd 100644 --- a/framework-docs/modules/ROOT/pages/testing/spring-mvc-test-framework/server-htmlunit/webdriver.adoc +++ b/framework-docs/modules/ROOT/pages/testing/spring-mvc-test-framework/server-htmlunit/webdriver.adoc @@ -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 diff --git a/framework-docs/modules/ROOT/pages/testing/testcontext-framework/application-events.adoc b/framework-docs/modules/ROOT/pages/testing/testcontext-framework/application-events.adoc index eba93a4326ab..14cf022a8891 100644 --- a/framework-docs/modules/ROOT/pages/testing/testcontext-framework/application-events.adoc +++ b/framework-docs/modules/ROOT/pages/testing/testcontext-framework/application-events.adoc @@ -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` diff --git a/framework-docs/modules/ROOT/pages/testing/testcontext-framework/ctx-management.adoc b/framework-docs/modules/ROOT/pages/testing/testcontext-framework/ctx-management.adoc index c922c4271814..30ce7bd59df4 100644 --- a/framework-docs/modules/ROOT/pages/testing/testcontext-framework/ctx-management.adoc +++ b/framework-docs/modules/ROOT/pages/testing/testcontext-framework/ctx-management.adoc @@ -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 diff --git a/framework-docs/modules/ROOT/pages/testing/testcontext-framework/ctx-management/groovy.adoc b/framework-docs/modules/ROOT/pages/testing/testcontext-framework/ctx-management/groovy.adoc index f44fdfd39652..9049bfc9ee22 100644 --- a/framework-docs/modules/ROOT/pages/testing/testcontext-framework/ctx-management/groovy.adoc +++ b/framework-docs/modules/ROOT/pages/testing/testcontext-framework/ctx-management/groovy.adoc @@ -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 diff --git a/framework-docs/modules/ROOT/pages/testing/testcontext-framework/ctx-management/inheritance.adoc b/framework-docs/modules/ROOT/pages/testing/testcontext-framework/ctx-management/inheritance.adoc index b4b8fed16fb4..0ca98c0965a7 100644 --- a/framework-docs/modules/ROOT/pages/testing/testcontext-framework/ctx-management/inheritance.adoc +++ b/framework-docs/modules/ROOT/pages/testing/testcontext-framework/ctx-management/inheritance.adoc @@ -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. +====== diff --git a/framework-docs/modules/ROOT/pages/testing/testcontext-framework/ctx-management/initializers.adoc b/framework-docs/modules/ROOT/pages/testing/testcontext-framework/ctx-management/initializers.adoc index caba982d2882..ce05e1a382e6 100644 --- a/framework-docs/modules/ROOT/pages/testing/testcontext-framework/ctx-management/initializers.adoc +++ b/framework-docs/modules/ROOT/pages/testing/testcontext-framework/ctx-management/initializers.adoc @@ -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. +====== diff --git a/framework-docs/modules/ROOT/pages/testing/testcontext-framework/ctx-management/javaconfig.adoc b/framework-docs/modules/ROOT/pages/testing/testcontext-framework/ctx-management/javaconfig.adoc index 97cb4df38015..cfd0cedfe235 100644 --- a/framework-docs/modules/ROOT/pages/testing/testcontext-framework/ctx-management/javaconfig.adoc +++ b/framework-docs/modules/ROOT/pages/testing/testcontext-framework/ctx-management/javaconfig.adoc @@ -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. +====== diff --git a/framework-docs/modules/ROOT/pages/testing/testcontext-framework/ctx-management/property-sources.adoc b/framework-docs/modules/ROOT/pages/testing/testcontext-framework/ctx-management/property-sources.adoc index 4d8812325423..06c7f9625b0e 100644 --- a/framework-docs/modules/ROOT/pages/testing/testcontext-framework/ctx-management/property-sources.adoc +++ b/framework-docs/modules/ROOT/pages/testing/testcontext-framework/ctx-management/property-sources.adoc @@ -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] ==== diff --git a/framework-docs/modules/ROOT/pages/testing/testcontext-framework/ctx-management/xml.adoc b/framework-docs/modules/ROOT/pages/testing/testcontext-framework/ctx-management/xml.adoc index 2ee0dc5bddf5..78e998e43ca9 100644 --- a/framework-docs/modules/ROOT/pages/testing/testcontext-framework/ctx-management/xml.adoc +++ b/framework-docs/modules/ROOT/pages/testing/testcontext-framework/ctx-management/xml.adoc @@ -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. +====== diff --git a/framework-docs/modules/ROOT/pages/testing/webtestclient.adoc b/framework-docs/modules/ROOT/pages/testing/webtestclient.adoc index 900596e07ea7..16a44856931c 100644 --- a/framework-docs/modules/ROOT/pages/testing/webtestclient.adoc +++ b/framework-docs/modules/ROOT/pages/testing/webtestclient.adoc @@ -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` +====== diff --git a/framework-docs/modules/ROOT/pages/web/webflux-cors.adoc b/framework-docs/modules/ROOT/pages/web/webflux-cors.adoc index 9c63eed3a298..8aefc7d58e8a 100644 --- a/framework-docs/modules/ROOT/pages/web/webflux-cors.adoc +++ b/framework-docs/modules/ROOT/pages/web/webflux-cors.adoc @@ -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. +====== -- diff --git a/framework-docs/modules/ROOT/pages/web/webflux-functional.adoc b/framework-docs/modules/ROOT/pages/web/webflux-functional.adoc index d0ed6a69d07d..3e3cb23c9f86 100644 --- a/framework-docs/modules/ROOT/pages/web/webflux-functional.adoc +++ b/framework-docs/modules/ROOT/pages/web/webflux-functional.adoc @@ -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)` 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 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 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 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 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 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 diff --git a/framework-docs/modules/ROOT/pages/web/webflux-webclient/client-builder.adoc b/framework-docs/modules/ROOT/pages/web/webflux-webclient/client-builder.adoc index 08d788f0e04d..3d326fadfd32 100644 --- a/framework-docs/modules/ROOT/pages/web/webflux-webclient/client-builder.adoc +++ b/framework-docs/modules/ROOT/pages/web/webflux-webclient/client-builder.adoc @@ -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`. +====== -- diff --git a/framework-docs/modules/ROOT/pages/web/webflux-websocket.adoc b/framework-docs/modules/ROOT/pages/web/webflux-websocket.adoc index a2f3060cd1e7..204c5f771fe8 100644 --- a/framework-docs/modules/ROOT/pages/web/webflux-websocket.adoc +++ b/framework-docs/modules/ROOT/pages/web/webflux-websocket.adoc @@ -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` 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` 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` 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` 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` 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` that completes when either stream ends. +====== diff --git a/framework-docs/modules/ROOT/pages/web/webflux/caching.adoc b/framework-docs/modules/ROOT/pages/web/webflux/caching.adoc index 8ebcf5cf59d8..5da8d201aedf 100644 --- a/framework-docs/modules/ROOT/pages/web/webflux/caching.adoc +++ b/framework-docs/modules/ROOT/pages/web/webflux/caching.adoc @@ -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` diff --git a/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-exceptions.adoc b/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-exceptions.adoc index 078942e9c9ca..c62341d365a7 100644 --- a/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-exceptions.adoc +++ b/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-exceptions.adoc @@ -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 diff --git a/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-initbinder.adoc b/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-initbinder.adoc index c0a369778adc..320d56b3caa1 100644 --- a/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-initbinder.adoc +++ b/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-initbinder.adoc @@ -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). +====== -- diff --git a/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/cookievalue.adoc b/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/cookievalue.adoc index 639273e645ab..79b62352e7b7 100644 --- a/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/cookievalue.adoc +++ b/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/cookievalue.adoc @@ -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 diff --git a/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/modelattrib-method-args.adoc b/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/modelattrib-method-args.adoc index cf92220ce282..347a025545a9 100644 --- a/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/modelattrib-method-args.adoc +++ b/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/modelattrib-method-args.adoc @@ -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` or `io.reactivex.Single`. You can declare a `@ModelAttribute` argument diff --git a/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/multipart-forms.adoc b/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/multipart-forms.adoc index 18eb97cf45d8..3ded73de6d63 100644 --- a/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/multipart-forms.adoc +++ b/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/multipart-forms.adoc @@ -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 { // <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) = { // <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]. diff --git a/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/requestattrib.adoc b/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/requestattrib.adoc index 7eec57f6970b..d5359a575b3b 100644 --- a/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/requestattrib.adoc +++ b/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/requestattrib.adoc @@ -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`. +====== diff --git a/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/requestheader.adoc b/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/requestheader.adoc index e625a46c0f7c..e186391ef862 100644 --- a/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/requestheader.adoc +++ b/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/requestheader.adoc @@ -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]. diff --git a/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/requestparam.adoc b/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/requestparam.adoc index b1da2d825ed3..2752035758fd 100644 --- a/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/requestparam.adoc +++ b/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/requestparam.adoc @@ -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 diff --git a/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/sessionattribute.adoc b/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/sessionattribute.adoc index f34751f91b4d..46ebcba9b49b 100644 --- a/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/sessionattribute.adoc +++ b/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/sessionattribute.adoc @@ -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. diff --git a/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/sessionattributes.adoc b/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/sessionattributes.adoc index 05426026206e..d71db97849a7 100644 --- a/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/sessionattributes.adoc +++ b/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/sessionattributes.adoc @@ -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. +====== diff --git a/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-requestmapping.adoc b/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-requestmapping.adoc index 80db7ea50479..a2b5f261bf75 100644 --- a/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-requestmapping.adoc +++ b/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-requestmapping.adoc @@ -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. +====== diff --git a/framework-docs/modules/ROOT/pages/web/webflux/controller/ann.adoc b/framework-docs/modules/ROOT/pages/web/webflux/controller/ann.adoc index 8ace15c65775..93cc097b1577 100644 --- a/framework-docs/modules/ROOT/pages/web/webflux/controller/ann.adoc +++ b/framework-docs/modules/ROOT/pages/web/webflux/controller/ann.adoc @@ -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 diff --git a/framework-docs/modules/ROOT/pages/web/webmvc-functional.adoc b/framework-docs/modules/ROOT/pages/web/webmvc-functional.adoc index 6f93344081f1..8d146310be73 100644 --- a/framework-docs/modules/ROOT/pages/web/webmvc-functional.adoc +++ b/framework-docs/modules/ROOT/pages/web/webmvc-functional.adoc @@ -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 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 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 diff --git a/framework-docs/modules/ROOT/pages/web/webmvc/mvc-caching.adoc b/framework-docs/modules/ROOT/pages/web/webmvc/mvc-caching.adoc index 758f82cfd761..f011bad15660 100644 --- a/framework-docs/modules/ROOT/pages/web/webmvc/mvc-caching.adoc +++ b/framework-docs/modules/ROOT/pages/web/webmvc/mvc-caching.adoc @@ -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. +====== -- diff --git a/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-initbinder.adoc b/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-initbinder.adoc index d4149a76a912..7507980ae1de 100644 --- a/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-initbinder.adoc +++ b/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-initbinder.adoc @@ -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 diff --git a/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/cookievalue.adoc b/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/cookievalue.adoc index 0d680ce43e87..d61859b5a15b 100644 --- a/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/cookievalue.adoc +++ b/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/cookievalue.adoc @@ -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]. diff --git a/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/modelattrib-method-args.adoc b/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/modelattrib-method-args.adoc index 06a8de2dfd4d..1136ab949588 100644 --- a/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/modelattrib-method-args.adoc +++ b/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/modelattrib-method-args.adoc @@ -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 diff --git a/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/requestattrib.adoc b/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/requestattrib.adoc index 40986e90c979..110b415b4c08 100644 --- a/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/requestattrib.adoc +++ b/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/requestattrib.adoc @@ -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. +====== diff --git a/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/requestheader.adoc b/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/requestheader.adoc index 0e2f952569c7..a63151aa66cf 100644 --- a/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/requestheader.adoc +++ b/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/requestheader.adoc @@ -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]. diff --git a/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/requestparam.adoc b/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/requestparam.adoc index 9017bd2284be..ae5e3a0ed4c7 100644 --- a/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/requestparam.adoc +++ b/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/requestparam.adoc @@ -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 diff --git a/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/sessionattribute.adoc b/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/sessionattribute.adoc index 2ecb0bb8deb3..726952dc5643 100644 --- a/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/sessionattribute.adoc +++ b/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/sessionattribute.adoc @@ -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 diff --git a/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/sessionattributes.adoc b/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/sessionattributes.adoc index 30a606d4ccac..b2ea7ce9e33d 100644 --- a/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/sessionattributes.adoc +++ b/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/sessionattributes.adoc @@ -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. +====== diff --git a/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-requestmapping.adoc b/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-requestmapping.adoc index afa7de56fb0d..661981354c55 100644 --- a/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-requestmapping.adoc +++ b/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-requestmapping.adoc @@ -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. +====== diff --git a/framework-docs/modules/ROOT/partials/web/web-uris.adoc b/framework-docs/modules/ROOT/partials/web/web-uris.adoc index a0e8218b4a46..d6e94cec95dc 100644 --- a/framework-docs/modules/ROOT/partials/web/web-uris.adoc +++ b/framework-docs/modules/ROOT/partials/web/web-uris.adoc @@ -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: