Migrate to Asciidoctor Tabs

This commit is contained in:
Rob Winch 2023-04-20 16:21:36 -05:00 committed by rstoyanchev
parent 71154fd16b
commit 39146f9066
243 changed files with 7124 additions and 1779 deletions

View File

@ -4,8 +4,6 @@
antora: antora:
extensions: extensions:
- '@antora/collector-extension' - '@antora/collector-extension'
- require: '@springio/antora-extensions/tabs-migration-extension'
unwrap_example_block: always
site: site:
title: Spring Framework Reference title: Spring Framework Reference
url: https://https://rwinch.github.io/spring-framework/ url: https://https://rwinch.github.io/spring-framework/

View File

@ -54,8 +54,11 @@ point.
The following example shows a simple `MethodInterceptor` implementation: The following example shows a simple `MethodInterceptor` implementation:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class DebugInterceptor implements MethodInterceptor { public class DebugInterceptor implements MethodInterceptor {
@ -67,8 +70,10 @@ The following example shows a simple `MethodInterceptor` implementation:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class DebugInterceptor : MethodInterceptor { class DebugInterceptor : MethodInterceptor {
@ -80,6 +85,7 @@ The following example shows a simple `MethodInterceptor` implementation:
} }
} }
---- ----
======
Note the call to the `proceed()` method of `MethodInvocation`. This proceeds down the Note the call to the `proceed()` method of `MethodInvocation`. This proceeds down the
interceptor chain towards the join point. Most interceptors invoke this method and interceptor chain towards the join point. Most interceptors invoke this method and
@ -129,8 +135,11 @@ wrapped in an unchecked exception by the AOP proxy.
The following example shows a before advice in Spring, which counts all method invocations: The following example shows a before advice in Spring, which counts all method invocations:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class CountingBeforeAdvice implements MethodBeforeAdvice { public class CountingBeforeAdvice implements MethodBeforeAdvice {
@ -145,8 +154,10 @@ The following example shows a before advice in Spring, which counts all method i
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class CountingBeforeAdvice : MethodBeforeAdvice { class CountingBeforeAdvice : MethodBeforeAdvice {
@ -157,6 +168,7 @@ The following example shows a before advice in Spring, which counts all method i
} }
} }
---- ----
======
TIP: Before advice can be used with any pointcut. TIP: Before advice can be used with any pointcut.
@ -181,8 +193,11 @@ arguments. The next two listing show classes that are examples of throws advice.
The following advice is invoked if a `RemoteException` is thrown (including from subclasses): The following advice is invoked if a `RemoteException` is thrown (including from subclasses):
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class RemoteThrowsAdvice implements ThrowsAdvice { public class RemoteThrowsAdvice implements ThrowsAdvice {
@ -191,8 +206,10 @@ The following advice is invoked if a `RemoteException` is thrown (including from
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class RemoteThrowsAdvice : ThrowsAdvice { class RemoteThrowsAdvice : ThrowsAdvice {
@ -201,13 +218,17 @@ The following advice is invoked if a `RemoteException` is thrown (including from
} }
} }
---- ----
======
Unlike the preceding Unlike the preceding
advice, the next example declares four arguments, so that it has access to the invoked method, method advice, the next example declares four arguments, so that it has access to the invoked method, method
arguments, and target object. The following advice is invoked if a `ServletException` is thrown: arguments, and target object. The following advice is invoked if a `ServletException` is thrown:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class ServletThrowsAdviceWithArguments implements ThrowsAdvice { public class ServletThrowsAdviceWithArguments implements ThrowsAdvice {
@ -216,8 +237,10 @@ arguments, and target object. The following advice is invoked if a `ServletExcep
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class ServletThrowsAdviceWithArguments : ThrowsAdvice { class ServletThrowsAdviceWithArguments : ThrowsAdvice {
@ -226,13 +249,17 @@ arguments, and target object. The following advice is invoked if a `ServletExcep
} }
} }
---- ----
======
The final example illustrates how these two methods could be used in a single class The final example illustrates how these two methods could be used in a single class
that handles both `RemoteException` and `ServletException`. Any number of throws advice that handles both `RemoteException` and `ServletException`. Any number of throws advice
methods can be combined in a single class. The following listing shows the final example: methods can be combined in a single class. The following listing shows the final example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public static class CombinedThrowsAdvice implements ThrowsAdvice { public static class CombinedThrowsAdvice implements ThrowsAdvice {
@ -245,8 +272,10 @@ methods can be combined in a single class. The following listing shows the final
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class CombinedThrowsAdvice : ThrowsAdvice { class CombinedThrowsAdvice : ThrowsAdvice {
@ -259,6 +288,7 @@ methods can be combined in a single class. The following listing shows the final
} }
} }
---- ----
======
NOTE: If a throws-advice method throws an exception itself, it overrides the NOTE: If a throws-advice method throws an exception itself, it overrides the
original exception (that is, it changes the exception thrown to the user). The overriding original exception (that is, it changes the exception thrown to the user). The overriding
@ -292,8 +322,11 @@ the invoked method, the method's arguments, and the target.
The following after returning advice counts all successful method invocations that have The following after returning advice counts all successful method invocations that have
not thrown exceptions: not thrown exceptions:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class CountingAfterReturningAdvice implements AfterReturningAdvice { public class CountingAfterReturningAdvice implements AfterReturningAdvice {
@ -309,8 +342,10 @@ not thrown exceptions:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class CountingAfterReturningAdvice : AfterReturningAdvice { class CountingAfterReturningAdvice : AfterReturningAdvice {
@ -322,6 +357,7 @@ not thrown exceptions:
} }
} }
---- ----
======
This advice does not change the execution path. If it throws an exception, it is This advice does not change the execution path. If it throws an exception, it is
thrown up the interceptor chain instead of the return value. thrown up the interceptor chain instead of the return value.
@ -380,8 +416,11 @@ introduced interfaces can be implemented by the configured `IntroductionIntercep
Consider an example from the Spring test suite and suppose we want to Consider an example from the Spring test suite and suppose we want to
introduce the following interface to one or more objects: introduce the following interface to one or more objects:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public interface Lockable { public interface Lockable {
void lock(); void lock();
@ -389,8 +428,10 @@ introduce the following interface to one or more objects:
boolean locked(); boolean locked();
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
interface Lockable { interface Lockable {
fun lock() fun lock()
@ -398,6 +439,7 @@ introduce the following interface to one or more objects:
fun locked(): Boolean fun locked(): Boolean
} }
---- ----
======
This illustrates a mixin. We want to be able to cast advised objects to `Lockable`, This illustrates a mixin. We want to be able to cast advised objects to `Lockable`,
whatever their type and call lock and unlock methods. If we call the `lock()` method, we whatever their type and call lock and unlock methods. If we call the `lock()` method, we
@ -434,8 +476,11 @@ to that held in the target object.
The following example shows the example `LockMixin` class: The following example shows the example `LockMixin` class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class LockMixin extends DelegatingIntroductionInterceptor implements Lockable { public class LockMixin extends DelegatingIntroductionInterceptor implements Lockable {
@ -462,8 +507,10 @@ The following example shows the example `LockMixin` class:
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class LockMixin : DelegatingIntroductionInterceptor(), Lockable { class LockMixin : DelegatingIntroductionInterceptor(), Lockable {
@ -490,6 +537,7 @@ The following example shows the example `LockMixin` class:
} }
---- ----
======
Often, you need not override the `invoke()` method. The Often, you need not override the `invoke()` method. The
`DelegatingIntroductionInterceptor` implementation (which calls the `delegate` method if `DelegatingIntroductionInterceptor` implementation (which calls the `delegate` method if
@ -504,8 +552,11 @@ interceptor (which would be defined as a prototype). In this case, there is no
configuration relevant for a `LockMixin`, so we create it by using `new`. configuration relevant for a `LockMixin`, so we create it by using `new`.
The following example shows our `LockMixinAdvisor` class: The following example shows our `LockMixinAdvisor` class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class LockMixinAdvisor extends DefaultIntroductionAdvisor { public class LockMixinAdvisor extends DefaultIntroductionAdvisor {
@ -514,11 +565,14 @@ The following example shows our `LockMixinAdvisor` class:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class LockMixinAdvisor : DefaultIntroductionAdvisor(LockMixin(), Lockable::class.java) class LockMixinAdvisor : DefaultIntroductionAdvisor(LockMixin(), Lockable::class.java)
---- ----
======
We can apply this advisor very simply, because it requires no configuration. (However, it We can apply this advisor very simply, because it requires no configuration. (However, it
is impossible to use an `IntroductionInterceptor` without an is impossible to use an `IntroductionInterceptor` without an

View File

@ -6,8 +6,11 @@ However you create AOP proxies, you can manipulate them BY using the
interface, no matter which other interfaces it implements. This interface includes the interface, no matter which other interfaces it implements. This interface includes the
following methods: following methods:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
Advisor[] getAdvisors(); Advisor[] getAdvisors();
@ -29,8 +32,10 @@ following methods:
boolean isFrozen(); boolean isFrozen();
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
fun getAdvisors(): Array<Advisor> fun getAdvisors(): Array<Advisor>
@ -59,6 +64,7 @@ following methods:
fun isFrozen(): Boolean fun isFrozen(): Boolean
---- ----
======
The `getAdvisors()` method returns an `Advisor` for every advisor, interceptor, or The `getAdvisors()` method returns an `Advisor` for every advisor, interceptor, or
other advice type that has been added to the factory. If you added an `Advisor`, the other advice type that has been added to the factory. If you added an `Advisor`, the
@ -80,8 +86,11 @@ change. (You can obtain a new proxy from the factory to avoid this problem.)
The following example shows casting an AOP proxy to the `Advised` interface and examining and The following example shows casting an AOP proxy to the `Advised` interface and examining and
manipulating its advice: manipulating its advice:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
Advised advised = (Advised) myObject; Advised advised = (Advised) myObject;
Advisor[] advisors = advised.getAdvisors(); Advisor[] advisors = advised.getAdvisors();
@ -98,8 +107,10 @@ manipulating its advice:
assertEquals("Added two advisors", oldAdvisorCount + 2, advised.getAdvisors().length); assertEquals("Added two advisors", oldAdvisorCount + 2, advised.getAdvisors().length);
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val advised = myObject as Advised val advised = myObject as Advised
val advisors = advised.advisors val advisors = advised.advisors
@ -116,6 +127,7 @@ manipulating its advice:
assertEquals("Added two advisors", oldAdvisorCount + 2, advised.advisors.size) assertEquals("Added two advisors", oldAdvisorCount + 2, advised.advisors.size)
---- ----
======
NOTE: It is questionable whether it is advisable (no pun intended) to modify advice on a NOTE: It is questionable whether it is advisable (no pun intended) to modify advice on a
business object in production, although there are, no doubt, legitimate usage cases. business object in production, although there are, no doubt, legitimate usage cases.

View File

@ -192,16 +192,22 @@ an instance of the prototype from the factory. Holding a reference is not suffic
The `person` bean definition shown earlier can be used in place of a `Person` implementation, as The `person` bean definition shown earlier can be used in place of a `Person` implementation, as
follows: follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
Person person = (Person) factory.getBean("person"); Person person = (Person) factory.getBean("person");
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val person = factory.getBean("person") as Person; val person = factory.getBean("person") as Person;
---- ----
======
Other beans in the same IoC context can express a strongly typed dependency on it, as Other beans in the same IoC context can express a strongly typed dependency on it, as
with an ordinary Java object. The following example shows how to do so: with an ordinary Java object. The following example shows how to do so:

View File

@ -208,8 +208,11 @@ Because static pointcuts are most useful, you should probably subclass
abstract method (although you can override other methods to customize behavior). The abstract method (although you can override other methods to customize behavior). The
following example shows how to subclass `StaticMethodMatcherPointcut`: following example shows how to subclass `StaticMethodMatcherPointcut`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
class TestStaticPointcut extends StaticMethodMatcherPointcut { class TestStaticPointcut extends StaticMethodMatcherPointcut {
@ -218,8 +221,10 @@ following example shows how to subclass `StaticMethodMatcherPointcut`:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class TestStaticPointcut : StaticMethodMatcherPointcut() { class TestStaticPointcut : StaticMethodMatcherPointcut() {
@ -228,6 +233,7 @@ following example shows how to subclass `StaticMethodMatcherPointcut`:
} }
} }
---- ----
======
There are also superclasses for dynamic pointcuts. There are also superclasses for dynamic pointcuts.
You can use custom pointcuts with any advice type. You can use custom pointcuts with any advice type.

View File

@ -8,22 +8,28 @@ The interfaces implemented by the target object are
automatically proxied. The following listing shows creation of a proxy for a target object, with one automatically proxied. The following listing shows creation of a proxy for a target object, with one
interceptor and one advisor: interceptor and one advisor:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
ProxyFactory factory = new ProxyFactory(myBusinessInterfaceImpl); ProxyFactory factory = new ProxyFactory(myBusinessInterfaceImpl);
factory.addAdvice(myMethodInterceptor); factory.addAdvice(myMethodInterceptor);
factory.addAdvisor(myAdvisor); factory.addAdvisor(myAdvisor);
MyBusinessInterface tb = (MyBusinessInterface) factory.getProxy(); MyBusinessInterface tb = (MyBusinessInterface) factory.getProxy();
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val factory = ProxyFactory(myBusinessInterfaceImpl) val factory = ProxyFactory(myBusinessInterfaceImpl)
factory.addAdvice(myMethodInterceptor) factory.addAdvice(myMethodInterceptor)
factory.addAdvisor(myAdvisor) factory.addAdvisor(myAdvisor)
val tb = factory.proxy as MyBusinessInterface val tb = factory.proxy as MyBusinessInterface
---- ----
======
The first step is to construct an object of type The first step is to construct an object of type
`org.springframework.aop.framework.ProxyFactory`. You can create this with a target `org.springframework.aop.framework.ProxyFactory`. You can create this with a target

View File

@ -34,18 +34,24 @@ Changing the target source's target takes effect immediately. The
You can change the target by using the `swap()` method on HotSwappableTargetSource, as the follow example shows: You can change the target by using the `swap()` method on HotSwappableTargetSource, as the follow example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
HotSwappableTargetSource swapper = (HotSwappableTargetSource) beanFactory.getBean("swapper"); HotSwappableTargetSource swapper = (HotSwappableTargetSource) beanFactory.getBean("swapper");
Object oldTarget = swapper.swap(newTarget); Object oldTarget = swapper.swap(newTarget);
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val swapper = beanFactory.getBean("swapper") as HotSwappableTargetSource val swapper = beanFactory.getBean("swapper") as HotSwappableTargetSource
val oldTarget = swapper.swap(newTarget) val oldTarget = swapper.swap(newTarget)
---- ----
======
The following example shows the required XML definitions: The following example shows the required XML definitions:
@ -142,18 +148,24 @@ the `ProxyFactoryBean` that exposes the pooled object.
The cast is defined as follows: The cast is defined as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
PoolingConfig conf = (PoolingConfig) beanFactory.getBean("businessObject"); PoolingConfig conf = (PoolingConfig) beanFactory.getBean("businessObject");
System.out.println("Max pool size is " + conf.getMaxSize()); System.out.println("Max pool size is " + conf.getMaxSize());
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val conf = beanFactory.getBean("businessObject") as PoolingConfig val conf = beanFactory.getBean("businessObject") as PoolingConfig
println("Max pool size is " + conf.maxSize) println("Max pool size is " + conf.maxSize)
---- ----
======
NOTE: Pooling stateless service objects is not usually necessary. We do not believe it should NOTE: Pooling stateless service objects is not usually necessary. We do not believe it should
be the default choice, as most stateless objects are naturally thread safe, and instance be the default choice, as most stateless objects are naturally thread safe, and instance

View File

@ -11,8 +11,11 @@ You can use the `org.springframework.aop.aspectj.annotation.AspectJProxyFactory`
to create a proxy for a target object that is advised by one or more @AspectJ aspects. to create a proxy for a target object that is advised by one or more @AspectJ aspects.
The basic usage for this class is very simple, as the following example shows: The basic usage for this class is very simple, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
// create a factory that can generate a proxy for the given target object // create a factory that can generate a proxy for the given target object
AspectJProxyFactory factory = new AspectJProxyFactory(targetObject); AspectJProxyFactory factory = new AspectJProxyFactory(targetObject);
@ -28,8 +31,10 @@ The basic usage for this class is very simple, as the following example shows:
// now get the proxy object... // now get the proxy object...
MyInterfaceType proxy = factory.getProxy(); MyInterfaceType proxy = factory.getProxy();
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
// create a factory that can generate a proxy for the given target object // create a factory that can generate a proxy for the given target object
val factory = AspectJProxyFactory(targetObject) val factory = AspectJProxyFactory(targetObject)
@ -45,6 +50,7 @@ The basic usage for this class is very simple, as the following example shows:
// now get the proxy object... // now get the proxy object...
val proxy = factory.getProxy<Any>() val proxy = factory.getProxy<Any>()
---- ----
======
See the {api-spring-framework}/aop/aspectj/annotation/AspectJProxyFactory.html[javadoc] for more information. See the {api-spring-framework}/aop/aspectj/annotation/AspectJProxyFactory.html[javadoc] for more information.

View File

@ -13,8 +13,11 @@ You can declare before advice in an aspect by using the `@Before` annotation.
The following example uses an inline pointcut expression. The following example uses an inline pointcut expression.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Before;
@ -28,8 +31,10 @@ The following example uses an inline pointcut expression.
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
import org.aspectj.lang.annotation.Aspect import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Before import org.aspectj.lang.annotation.Before
@ -43,12 +48,16 @@ The following example uses an inline pointcut expression.
} }
} }
---- ----
======
If we use a xref:core/aop/ataspectj/pointcuts.adoc#aop-common-pointcuts[named pointcut], we can rewrite the preceding example If we use a xref:core/aop/ataspectj/pointcuts.adoc#aop-common-pointcuts[named pointcut], we can rewrite the preceding example
as follows: as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Before;
@ -62,8 +71,10 @@ as follows:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
import org.aspectj.lang.annotation.Aspect import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Before import org.aspectj.lang.annotation.Before
@ -77,6 +88,7 @@ as follows:
} }
} }
---- ----
======
[[aop-advice-after-returning]] [[aop-advice-after-returning]]
@ -85,8 +97,11 @@ as follows:
After returning advice runs when a matched method execution returns normally. After returning advice runs when a matched method execution returns normally.
You can declare it by using the `@AfterReturning` annotation. You can declare it by using the `@AfterReturning` annotation.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterReturning;
@ -100,8 +115,10 @@ You can declare it by using the `@AfterReturning` annotation.
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
import org.aspectj.lang.annotation.Aspect import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.AfterReturning import org.aspectj.lang.annotation.AfterReturning
@ -115,6 +132,7 @@ You can declare it by using the `@AfterReturning` annotation.
} }
} }
---- ----
======
NOTE: You can have multiple advice declarations (and other members as well), NOTE: You can have multiple advice declarations (and other members as well),
all inside the same aspect. We show only a single advice declaration in these all inside the same aspect. We show only a single advice declaration in these
@ -124,8 +142,11 @@ Sometimes, you need access in the advice body to the actual value that was retur
You can use the form of `@AfterReturning` that binds the return value to get that You can use the form of `@AfterReturning` that binds the return value to get that
access, as the following example shows: access, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterReturning;
@ -141,8 +162,10 @@ access, as the following example shows:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
import org.aspectj.lang.annotation.Aspect import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.AfterReturning import org.aspectj.lang.annotation.AfterReturning
@ -158,6 +181,7 @@ access, as the following example shows:
} }
} }
---- ----
======
The name used in the `returning` attribute must correspond to the name of a parameter The name used in the `returning` attribute must correspond to the name of a parameter
in the advice method. When a method execution returns, the return value is passed to in the advice method. When a method execution returns, the return value is passed to
@ -176,8 +200,11 @@ After throwing advice runs when a matched method execution exits by throwing an
exception. You can declare it by using the `@AfterThrowing` annotation, as the exception. You can declare it by using the `@AfterThrowing` annotation, as the
following example shows: following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.AfterThrowing;
@ -191,8 +218,10 @@ following example shows:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
import org.aspectj.lang.annotation.Aspect import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.AfterThrowing import org.aspectj.lang.annotation.AfterThrowing
@ -206,6 +235,7 @@ following example shows:
} }
} }
---- ----
======
Often, you want the advice to run only when exceptions of a given type are thrown, Often, you want the advice to run only when exceptions of a given type are thrown,
and you also often need access to the thrown exception in the advice body. You can and you also often need access to the thrown exception in the advice body. You can
@ -213,8 +243,11 @@ use the `throwing` attribute to both restrict matching (if desired -- use `Throw
as the exception type otherwise) and bind the thrown exception to an advice parameter. as the exception type otherwise) and bind the thrown exception to an advice parameter.
The following example shows how to do so: The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.AfterThrowing;
@ -230,8 +263,10 @@ The following example shows how to do so:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
import org.aspectj.lang.annotation.Aspect import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.AfterThrowing import org.aspectj.lang.annotation.AfterThrowing
@ -247,6 +282,7 @@ The following example shows how to do so:
} }
} }
---- ----
======
The name used in the `throwing` attribute must correspond to the name of a parameter in The name used in the `throwing` attribute must correspond to the name of a parameter in
the advice method. When a method execution exits by throwing an exception, the exception the advice method. When a method execution exits by throwing an exception, the exception
@ -271,8 +307,11 @@ using the `@After` annotation. After advice must be prepared to handle both norm
exception return conditions. It is typically used for releasing resources and similar exception return conditions. It is typically used for releasing resources and similar
purposes. The following example shows how to use after finally advice: purposes. The following example shows how to use after finally advice:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.After;
@ -286,8 +325,10 @@ purposes. The following example shows how to use after finally advice:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
import org.aspectj.lang.annotation.Aspect import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.After import org.aspectj.lang.annotation.After
@ -301,6 +342,7 @@ purposes. The following example shows how to use after finally advice:
} }
} }
---- ----
======
[NOTE] [NOTE]
==== ====
@ -371,8 +413,11 @@ value depending on the use case.
The following example shows how to use around advice: The following example shows how to use around advice:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Around;
@ -390,8 +435,10 @@ The following example shows how to use around advice:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
import org.aspectj.lang.annotation.Aspect import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Around import org.aspectj.lang.annotation.Around
@ -409,6 +456,7 @@ The following example shows how to use around advice:
} }
} }
---- ----
======
[[aop-ataspectj-advice-params]] [[aop-ataspectj-advice-params]]
== Advice Parameters == Advice Parameters
@ -448,22 +496,28 @@ Suppose you want to advise the execution of DAO operations that take an `Account
object as the first parameter, and you need access to the account in the advice body. object as the first parameter, and you need access to the account in the advice body.
You could write the following: You could write the following:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
@Before("execution(* com.xyz.dao.*.*(..)) && args(account,..)") @Before("execution(* com.xyz.dao.*.*(..)) && args(account,..)")
public void validateAccount(Account account) { public void validateAccount(Account account) {
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
@Before("execution(* com.xyz.dao.*.*(..)) && args(account,..)") @Before("execution(* com.xyz.dao.*.*(..)) && args(account,..)")
fun validateAccount(account: Account) { fun validateAccount(account: Account) {
// ... // ...
} }
---- ----
======
The `args(account,..)` part of the pointcut expression serves two purposes. First, it The `args(account,..)` part of the pointcut expression serves two purposes. First, it
restricts matching to only those method executions where the method takes at least one restricts matching to only those method executions where the method takes at least one
@ -475,8 +529,11 @@ Another way of writing this is to declare a pointcut that "provides" the `Accoun
object value when it matches a join point, and then refer to the named pointcut object value when it matches a join point, and then refer to the named pointcut
from the advice. This would look as follows: from the advice. This would look as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
@Pointcut("execution(* com.xyz.dao.*.*(..)) && args(account,..)") @Pointcut("execution(* com.xyz.dao.*.*(..)) && args(account,..)")
private void accountDataAccessOperation(Account account) {} private void accountDataAccessOperation(Account account) {}
@ -486,8 +543,10 @@ from the advice. This would look as follows:
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
@Pointcut("execution(* com.xyz.dao.*.*(..)) && args(account,..)") @Pointcut("execution(* com.xyz.dao.*.*(..)) && args(account,..)")
private fun accountDataAccessOperation(account: Account) { private fun accountDataAccessOperation(account: Account) {
@ -498,6 +557,7 @@ from the advice. This would look as follows:
// ... // ...
} }
---- ----
======
See the AspectJ programming guide for more details. See the AspectJ programming guide for more details.
@ -508,8 +568,11 @@ set of examples shows how to match the execution of methods annotated with an
The following shows the definition of the `@Auditable` annotation: The following shows the definition of the `@Auditable` annotation:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD) @Target(ElementType.METHOD)
@ -517,18 +580,24 @@ The following shows the definition of the `@Auditable` annotation:
AuditCode value(); AuditCode value();
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
@Retention(AnnotationRetention.RUNTIME) @Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION) @Target(AnnotationTarget.FUNCTION)
annotation class Auditable(val value: AuditCode) annotation class Auditable(val value: AuditCode)
---- ----
======
The following shows the advice that matches the execution of `@Auditable` methods: The following shows the advice that matches the execution of `@Auditable` methods:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
@Before("com.xyz.Pointcuts.publicMethod() && @annotation(auditable)") // <1> @Before("com.xyz.Pointcuts.publicMethod() && @annotation(auditable)") // <1>
public void audit(Auditable auditable) { public void audit(Auditable auditable) {
@ -536,6 +605,7 @@ The following shows the advice that matches the execution of `@Auditable` method
// ... // ...
} }
---- ----
======
<1> References the `publicMethod` named pointcut defined in xref:core/aop/ataspectj/pointcuts.adoc#aop-pointcuts-combining[Combining Pointcut Expressions]. <1> References the `publicMethod` named pointcut defined in xref:core/aop/ataspectj/pointcuts.adoc#aop-pointcuts-combining[Combining Pointcut Expressions].
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
@ -555,62 +625,80 @@ The following shows the advice that matches the execution of `@Auditable` method
Spring AOP can handle generics used in class declarations and method parameters. Suppose Spring AOP can handle generics used in class declarations and method parameters. Suppose
you have a generic type like the following: you have a generic type like the following:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
public interface Sample<T> { public interface Sample<T> {
void sampleGenericMethod(T param); void sampleGenericMethod(T param);
void sampleGenericCollectionMethod(Collection<T> param); void sampleGenericCollectionMethod(Collection<T> param);
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
interface Sample<T> { interface Sample<T> {
fun sampleGenericMethod(param: T) fun sampleGenericMethod(param: T)
fun sampleGenericCollectionMethod(param: Collection<T>) fun sampleGenericCollectionMethod(param: Collection<T>)
} }
---- ----
======
You can restrict interception of method types to certain parameter types by You can restrict interception of method types to certain parameter types by
tying the advice parameter to the parameter type for which you want to intercept the method: tying the advice parameter to the parameter type for which you want to intercept the method:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
@Before("execution(* ..Sample+.sampleGenericMethod(*)) && args(param)") @Before("execution(* ..Sample+.sampleGenericMethod(*)) && args(param)")
public void beforeSampleMethod(MyType param) { public void beforeSampleMethod(MyType param) {
// Advice implementation // Advice implementation
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
@Before("execution(* ..Sample+.sampleGenericMethod(*)) && args(param)") @Before("execution(* ..Sample+.sampleGenericMethod(*)) && args(param)")
fun beforeSampleMethod(param: MyType) { fun beforeSampleMethod(param: MyType) {
// Advice implementation // Advice implementation
} }
---- ----
======
This approach does not work for generic collections. So you cannot define a This approach does not work for generic collections. So you cannot define a
pointcut as follows: pointcut as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
@Before("execution(* ..Sample+.sampleGenericCollectionMethod(*)) && args(param)") @Before("execution(* ..Sample+.sampleGenericCollectionMethod(*)) && args(param)")
public void beforeSampleMethod(Collection<MyType> param) { public void beforeSampleMethod(Collection<MyType> param) {
// Advice implementation // Advice implementation
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
@Before("execution(* ..Sample+.sampleGenericCollectionMethod(*)) && args(param)") @Before("execution(* ..Sample+.sampleGenericCollectionMethod(*)) && args(param)")
fun beforeSampleMethod(param: Collection<MyType>) { fun beforeSampleMethod(param: Collection<MyType>) {
// Advice implementation // Advice implementation
} }
---- ----
======
To make this work, we would have to inspect every element of the collection, which is not To make this work, we would have to inspect every element of the collection, which is not
reasonable, as we also cannot decide how to treat `null` values in general. To achieve reasonable, as we also cannot decide how to treat `null` values in general. To achieve
@ -668,8 +756,11 @@ needed information.
The following example shows how to use the `argNames` attribute: The following example shows how to use the `argNames` attribute:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
@Before( @Before(
value = "com.xyz.Pointcuts.publicMethod() && target(bean) && @annotation(auditable)", // <1> value = "com.xyz.Pointcuts.publicMethod() && target(bean) && @annotation(auditable)", // <1>
@ -679,6 +770,7 @@ The following example shows how to use the `argNames` attribute:
// ... use code and bean // ... use code and bean
} }
---- ----
======
<1> References the `publicMethod` named pointcut defined in xref:core/aop/ataspectj/pointcuts.adoc#aop-pointcuts-combining[Combining Pointcut Expressions]. <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. <2> Declares `bean` and `auditable` as the argument names.
@ -701,8 +793,11 @@ If the first parameter is of type `JoinPoint`, `ProceedingJoinPoint`, or
`argNames` attribute. For example, if you modify the preceding advice to receive the join `argNames` attribute. For example, if you modify the preceding advice to receive the join
point object, the `argNames` attribute does not need to include it: point object, the `argNames` attribute does not need to include it:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
@Before( @Before(
value = "com.xyz.Pointcuts.publicMethod() && target(bean) && @annotation(auditable)", // <1> value = "com.xyz.Pointcuts.publicMethod() && target(bean) && @annotation(auditable)", // <1>
@ -712,6 +807,7 @@ point object, the `argNames` attribute does not need to include it:
// ... use code, bean, and jp // ... 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]. <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. <2> Declares `bean` and `auditable` as the argument names.
@ -735,14 +831,18 @@ methods that do not collect any other join point context. In such situations, yo
omit the `argNames` attribute. For example, the following advice does not need to declare omit the `argNames` attribute. For example, the following advice does not need to declare
the `argNames` attribute: the `argNames` attribute:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
@Before("com.xyz.Pointcuts.publicMethod()") // <1> @Before("com.xyz.Pointcuts.publicMethod()") // <1>
public void audit(JoinPoint jp) { public void audit(JoinPoint jp) {
// ... use jp // ... use jp
} }
---- ----
======
<1> References the `publicMethod` named pointcut defined in xref:core/aop/ataspectj/pointcuts.adoc#aop-pointcuts-combining[Combining Pointcut Expressions]. <1> References the `publicMethod` named pointcut defined in xref:core/aop/ataspectj/pointcuts.adoc#aop-pointcuts-combining[Combining Pointcut Expressions].
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
@ -764,8 +864,11 @@ arguments that works consistently across Spring AOP and AspectJ. The solution is
to ensure that the advice signature binds each of the method parameters in order. to ensure that the advice signature binds each of the method parameters in order.
The following example shows how to do so: The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
@Around("execution(List<Account> find*(..)) && " + @Around("execution(List<Account> find*(..)) && " +
"com.xyz.CommonPointcuts.inDataAccessLayer() && " + "com.xyz.CommonPointcuts.inDataAccessLayer() && " +
@ -776,6 +879,7 @@ The following example shows how to do so:
return pjp.proceed(new Object[] {newPattern}); 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]. <1> References the `inDataAccessLayer` named pointcut defined in xref:core/aop/ataspectj/pointcuts.adoc#aop-common-pointcuts[Sharing Named Pointcut Definitions].
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]

View File

@ -19,21 +19,27 @@ classpath of your application (version 1.9 or later). This library is available
To enable @AspectJ support with Java `@Configuration`, add the `@EnableAspectJAutoProxy` To enable @AspectJ support with Java `@Configuration`, add the `@EnableAspectJAutoProxy`
annotation, as the following example shows: annotation, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
@Configuration @Configuration
@EnableAspectJAutoProxy @EnableAspectJAutoProxy
public class AppConfig { public class AppConfig {
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
@Configuration @Configuration
@EnableAspectJAutoProxy @EnableAspectJAutoProxy
class AppConfig class AppConfig
---- ----
======
[[aop-enable-aspectj-xml]] [[aop-enable-aspectj-xml]]
== Enabling @AspectJ Support with XML Configuration == Enabling @AspectJ Support with XML Configuration

View File

@ -19,8 +19,11 @@ that points to a bean class that is annotated with `@Aspect`:
The second of the two examples shows the `NotVeryUsefulAspect` class definition, which is The second of the two examples shows the `NotVeryUsefulAspect` class definition, which is
annotated with `@Aspect`: annotated with `@Aspect`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary",chomp="-packages",fold="none"] [source,java,indent=0,subs="verbatim",role="primary",chomp="-packages",fold="none"]
.Java
---- ----
package com.xyz; package com.xyz;
@ -30,8 +33,10 @@ annotated with `@Aspect`:
public class NotVeryUsefulAspect { public class NotVeryUsefulAspect {
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages",fold="none"] [source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages",fold="none"]
.Kotlin
---- ----
package com.xyz package com.xyz
@ -40,6 +45,7 @@ annotated with `@Aspect`:
@Aspect @Aspect
class NotVeryUsefulAspect class NotVeryUsefulAspect
---- ----
======
Aspects (classes annotated with `@Aspect`) can have methods and fields, the same as any Aspects (classes annotated with `@Aspect`) can have methods and fields, the same as any
other class. They can also contain pointcut, advice, and introduction (inter-type) other class. They can also contain pointcut, advice, and introduction (inter-type)

View File

@ -16,8 +16,11 @@ aspect.
Because we want to retry the operation, we need to use around advice so that we can Because we want to retry the operation, we need to use around advice so that we can
call `proceed` multiple times. The following listing shows the basic aspect implementation: call `proceed` multiple times. The following listing shows the basic aspect implementation:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
@Aspect @Aspect
public class ConcurrentOperationExecutor implements Ordered { public class ConcurrentOperationExecutor implements Ordered {
@ -56,6 +59,7 @@ call `proceed` multiple times. The following listing shows the basic aspect impl
} }
} }
---- ----
======
<1> References the `businessService` named pointcut defined in xref:core/aop/ataspectj/pointcuts.adoc#aop-common-pointcuts[Sharing Named Pointcut Definitions]. <1> References the `businessService` named pointcut defined in xref:core/aop/ataspectj/pointcuts.adoc#aop-common-pointcuts[Sharing Named Pointcut Definitions].
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
@ -123,28 +127,37 @@ The corresponding Spring configuration follows:
To refine the aspect so that it retries only idempotent operations, we might define the following To refine the aspect so that it retries only idempotent operations, we might define the following
`Idempotent` annotation: `Idempotent` annotation:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
// marker annotation // marker annotation
public @interface Idempotent { public @interface Idempotent {
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
@Retention(AnnotationRetention.RUNTIME) @Retention(AnnotationRetention.RUNTIME)
// marker annotation // marker annotation
annotation class Idempotent annotation class Idempotent
---- ----
======
We can then use the annotation to annotate the implementation of service operations. The change We can then use the annotation to annotate the implementation of service operations. The change
to the aspect to retry only idempotent operations involves refining the pointcut to the aspect to retry only idempotent operations involves refining the pointcut
expression so that only `@Idempotent` operations match, as follows: expression so that only `@Idempotent` operations match, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
@Around("execution(* com.xyz..service.*.*(..)) && " + @Around("execution(* com.xyz..service.*.*(..)) && " +
"@annotation(com.xyz.service.Idempotent)") "@annotation(com.xyz.service.Idempotent)")
@ -152,8 +165,10 @@ expression so that only `@Idempotent` operations match, as follows:
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
@Around("execution(* com.xyz..service.*.*(..)) && " + @Around("execution(* com.xyz..service.*.*(..)) && " +
"@annotation(com.xyz.service.Idempotent)") "@annotation(com.xyz.service.Idempotent)")
@ -161,6 +176,7 @@ expression so that only `@Idempotent` operations match, as follows:
// ... // ...
} }
---- ----
======

View File

@ -13,8 +13,11 @@ supported.
You can declare a `perthis` aspect by specifying a `perthis` clause in the `@Aspect` You can declare a `perthis` aspect by specifying a `perthis` clause in the `@Aspect`
annotation. Consider the following example: annotation. Consider the following example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
@Aspect("perthis(execution(* com.xyz..service.*.*(..)))") @Aspect("perthis(execution(* com.xyz..service.*.*(..)))")
public class MyAspect { public class MyAspect {
@ -27,8 +30,10 @@ annotation. Consider the following example:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
@Aspect("perthis(execution(* com.xyz..service.*.*(..)))") @Aspect("perthis(execution(* com.xyz..service.*.*(..)))")
class MyAspect { class MyAspect {
@ -41,6 +46,7 @@ annotation. Consider the following example:
} }
} }
---- ----
======
In the preceding example, the effect of the `perthis` clause is that one aspect instance In the preceding example, the effect of the `perthis` clause is that one aspect instance
is created for each unique service object that performs a business service (each unique is created for each unique service object that performs a business service (each unique

View File

@ -11,8 +11,11 @@ given an interface named `UsageTracked` and an implementation of that interface
`DefaultUsageTracked`, the following aspect declares that all implementors of service `DefaultUsageTracked`, the following aspect declares that all implementors of service
interfaces also implement the `UsageTracked` interface (e.g. for statistics via JMX): interfaces also implement the `UsageTracked` interface (e.g. for statistics via JMX):
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
@Aspect @Aspect
public class UsageTracking { public class UsageTracking {
@ -27,8 +30,10 @@ interfaces also implement the `UsageTracked` interface (e.g. for statistics via
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
@Aspect @Aspect
class UsageTracking { class UsageTracking {
@ -45,6 +50,7 @@ interfaces also implement the `UsageTracked` interface (e.g. for statistics via
} }
} }
---- ----
======
The interface to be implemented is determined by the type of the annotated field. The The interface to be implemented is determined by the type of the annotated field. The
`value` attribute of the `@DeclareParents` annotation is an AspectJ type pattern. Any `value` attribute of the `@DeclareParents` annotation is an AspectJ type pattern. Any
@ -53,15 +59,21 @@ before advice of the preceding example, service beans can be directly used as
implementations of the `UsageTracked` interface. If accessing a bean programmatically, implementations of the `UsageTracked` interface. If accessing a bean programmatically,
you would write the following: you would write the following:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
UsageTracked usageTracked = context.getBean("myService", UsageTracked.class); UsageTracked usageTracked = context.getBean("myService", UsageTracked.class);
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
val usageTracked = context.getBean("myService", UsageTracked.class) val usageTracked = context.getBean("myService", UsageTracked.class)
---- ----
======

View File

@ -15,18 +15,24 @@ An example may help make this distinction between a pointcut signature and a poi
expression clear. The following example defines a pointcut named `anyOldTransfer` that expression clear. The following example defines a pointcut named `anyOldTransfer` that
matches the execution of any method named `transfer`: matches the execution of any method named `transfer`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
@Pointcut("execution(* transfer(..))") // the pointcut expression @Pointcut("execution(* transfer(..))") // the pointcut expression
private void anyOldTransfer() {} // the pointcut signature private void anyOldTransfer() {} // the pointcut signature
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
@Pointcut("execution(* transfer(..))") // the pointcut expression @Pointcut("execution(* transfer(..))") // the pointcut expression
private fun anyOldTransfer() {} // the pointcut signature private fun anyOldTransfer() {} // the pointcut signature
---- ----
======
The pointcut expression that forms the value of the `@Pointcut` annotation is a regular The pointcut expression that forms the value of the `@Pointcut` annotation is a regular
AspectJ pointcut expression. For a full discussion of AspectJ's pointcut language, see AspectJ pointcut expression. For a full discussion of AspectJ's pointcut language, see
@ -140,8 +146,11 @@ it is natural and straightforward to identify specific beans by name.
You can combine pointcut expressions by using `&&,` `||` and `!`. You can also refer to You can combine pointcut expressions by using `&&,` `||` and `!`. You can also refer to
pointcut expressions by name. The following example shows three pointcut expressions: pointcut expressions by name. The following example shows three pointcut expressions:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary",chomp="-packages"] [source,java,indent=0,subs="verbatim",role="primary",chomp="-packages"]
.Java
---- ----
package com.xyz; package com.xyz;
@ -158,6 +167,7 @@ pointcut expressions by name. The following example shows three pointcut express
public void tradingOperation() {} // <3> public void tradingOperation() {} // <3>
} }
---- ----
======
<1> `publicMethod` matches if a method execution join point represents the execution <1> `publicMethod` matches if a method execution join point represents the execution
of any public method. of any public method.
<2> `inTrading` matches if a method execution is in the trading module. <2> `inTrading` matches if a method execution is in the trading module.
@ -204,8 +214,11 @@ We recommend defining a dedicated aspect that captures commonly used _named poin
expressions for this purpose. Such an aspect typically resembles the following expressions for this purpose. Such an aspect typically resembles the following
`CommonPointcuts` example (though what you name the aspect is up to you): `CommonPointcuts` example (though what you name the aspect is up to you):
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary",chomp="-packages",fold="none"] [source,java,indent=0,subs="verbatim",role="primary",chomp="-packages",fold="none"]
.Java
---- ----
package com.xyz; package com.xyz;
@ -266,8 +279,10 @@ expressions for this purpose. Such an aspect typically resembles the following
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages",fold="none"] [source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages",fold="none"]
.Kotlin
---- ----
package com.xyz package com.xyz
@ -328,6 +343,7 @@ expressions for this purpose. Such an aspect typically resembles the following
} }
---- ----
======
You can refer to the pointcuts defined in such an aspect anywhere you need a pointcut You can refer to the pointcuts defined in such an aspect anywhere you need a pointcut
expression by referencing the fully-qualified name of the `@Aspect` class combined with expression by referencing the fully-qualified name of the `@Aspect` class combined with

View File

@ -55,8 +55,11 @@ it can express than the @AspectJ style: Only the "singleton" aspect instantiatio
is supported, and it is not possible to combine named pointcuts declared in XML. is supported, and it is not possible to combine named pointcuts declared in XML.
For example, in the @AspectJ style you can write something like the following: For example, in the @AspectJ style you can write something like the following:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
@Pointcut("execution(* get*())") @Pointcut("execution(* get*())")
public void propertyAccess() {} public void propertyAccess() {}
@ -67,8 +70,10 @@ For example, in the @AspectJ style you can write something like the following:
@Pointcut("propertyAccess() && operationReturningAnAccount()") @Pointcut("propertyAccess() && operationReturningAnAccount()")
public void accountPropertyAccess() {} public void accountPropertyAccess() {}
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
@Pointcut("execution(* get*())") @Pointcut("execution(* get*())")
fun propertyAccess() {} fun propertyAccess() {}
@ -79,6 +84,7 @@ For example, in the @AspectJ style you can write something like the following:
@Pointcut("propertyAccess() && operationReturningAnAccount()") @Pointcut("propertyAccess() && operationReturningAnAccount()")
fun accountPropertyAccess() {} fun accountPropertyAccess() {}
---- ----
======
In the XML style you can declare the first two pointcuts: In the XML style you can declare the first two pointcuts:

View File

@ -65,8 +65,11 @@ Consider first the scenario where you have a plain-vanilla, un-proxied,
nothing-special-about-it, straight object reference, as the following nothing-special-about-it, straight object reference, as the following
code snippet shows: code snippet shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
public class SimplePojo implements Pojo { public class SimplePojo implements Pojo {
@ -80,8 +83,10 @@ code snippet shows:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
class SimplePojo : Pojo { class SimplePojo : Pojo {
@ -95,14 +100,18 @@ code snippet shows:
} }
} }
---- ----
======
If you invoke a method on an object reference, the method is invoked directly on If you invoke a method on an object reference, the method is invoked directly on
that object reference, as the following image and listing show: that object reference, as the following image and listing show:
image::aop-proxy-plain-pojo-call.png[] image::aop-proxy-plain-pojo-call.png[]
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
public class Main { public class Main {
@ -113,8 +122,10 @@ image::aop-proxy-plain-pojo-call.png[]
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
fun main() { fun main() {
val pojo = SimplePojo() val pojo = SimplePojo()
@ -122,14 +133,18 @@ image::aop-proxy-plain-pojo-call.png[]
pojo.foo() pojo.foo()
} }
---- ----
======
Things change slightly when the reference that client code has is a proxy. Consider the Things change slightly when the reference that client code has is a proxy. Consider the
following diagram and code snippet: following diagram and code snippet:
image::aop-proxy-call.png[] image::aop-proxy-call.png[]
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
public class Main { public class Main {
@ -144,8 +159,10 @@ image::aop-proxy-call.png[]
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
fun main() { fun main() {
val factory = ProxyFactory(SimplePojo()) val factory = ProxyFactory(SimplePojo())
@ -157,6 +174,7 @@ fun main() {
pojo.foo() pojo.foo()
} }
---- ----
======
The key thing to understand here is that the client code inside the `main(..)` method The key thing to understand here is that the client code inside the `main(..)` method
of the `Main` class has a reference to the proxy. This means that method calls on that of the `Main` class has a reference to the proxy. This means that method calls on that
@ -175,8 +193,11 @@ The next approach is absolutely horrendous, and we hesitate to point it out, pre
because it is so horrendous. You can (painful as it is to us) totally tie the logic because it is so horrendous. You can (painful as it is to us) totally tie the logic
within your class to Spring AOP, as the following example shows: within your class to Spring AOP, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
public class SimplePojo implements Pojo { public class SimplePojo implements Pojo {
@ -190,8 +211,10 @@ within your class to Spring AOP, as the following example shows:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
class SimplePojo : Pojo { class SimplePojo : Pojo {
@ -205,14 +228,18 @@ within your class to Spring AOP, as the following example shows:
} }
} }
---- ----
======
This totally couples your code to Spring AOP, and it makes the class itself aware of This totally couples your code to Spring AOP, and it makes the class itself aware of
the fact that it is being used in an AOP context, which flies in the face of AOP. It the fact that it is being used in an AOP context, which flies in the face of AOP. It
also requires some additional configuration when the proxy is being created, as the also requires some additional configuration when the proxy is being created, as the
following example shows: following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
public class Main { public class Main {
@ -228,8 +255,10 @@ following example shows:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
fun main() { fun main() {
val factory = ProxyFactory(SimplePojo()) val factory = ProxyFactory(SimplePojo())
@ -242,6 +271,7 @@ following example shows:
pojo.foo() pojo.foo()
} }
---- ----
======
Finally, it must be noted that AspectJ does not have this self-invocation issue because Finally, it must be noted that AspectJ does not have this self-invocation issue because
it is not a proxy-based AOP framework. it is not a proxy-based AOP framework.

View File

@ -132,20 +132,26 @@ collects the `this` object as the join point context and passes it to the advice
The advice must be declared to receive the collected join point context by including The advice must be declared to receive the collected join point context by including
parameters of the matching names, as follows: parameters of the matching names, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
public void monitor(Object service) { public void monitor(Object service) {
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
fun monitor(service: Any) { fun monitor(service: Any) {
// ... // ...
} }
---- ----
======
When combining pointcut sub-expressions, `+&amp;&amp;+` is awkward within an XML When combining pointcut sub-expressions, `+&amp;&amp;+` is awkward within an XML
document, so you can use the `and`, `or`, and `not` keywords in place of `+&amp;&amp;+`, document, so you can use the `and`, `or`, and `not` keywords in place of `+&amp;&amp;+`,
@ -272,16 +278,22 @@ The `doAccessCheck` method must declare a parameter named `retVal`. The type of
parameter constrains matching in the same way as described for `@AfterReturning`. For parameter constrains matching in the same way as described for `@AfterReturning`. For
example, you can declare the method signature as follows: example, you can declare the method signature as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
public void doAccessCheck(Object retVal) {... public void doAccessCheck(Object retVal) {...
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
fun doAccessCheck(retVal: Any) {... fun doAccessCheck(retVal: Any) {...
---- ----
======
[[aop-schema-advice-after-throwing]] [[aop-schema-advice-after-throwing]]
@ -324,16 +336,22 @@ The `doRecoveryActions` method must declare a parameter named `dataAccessEx`.
The type of this parameter constrains matching in the same way as described for The type of this parameter constrains matching in the same way as described for
`@AfterThrowing`. For example, the method signature may be declared as follows: `@AfterThrowing`. For example, the method signature may be declared as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
public void doRecoveryActions(DataAccessException dataAccessEx) {... public void doRecoveryActions(DataAccessException dataAccessEx) {...
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
fun doRecoveryActions(dataAccessEx: DataAccessException) {... fun doRecoveryActions(dataAccessEx: DataAccessException) {...
---- ----
======
[[aop-schema-advice-after-finally]] [[aop-schema-advice-after-finally]]
@ -399,8 +417,11 @@ The following example shows how to declare around advice in XML:
The implementation of the `doBasicProfiling` advice can be exactly the same as in the The implementation of the `doBasicProfiling` advice can be exactly the same as in the
@AspectJ example (minus the annotation, of course), as the following example shows: @AspectJ example (minus the annotation, of course), as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable { public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
// start stopwatch // start stopwatch
@ -409,8 +430,10 @@ The implementation of the `doBasicProfiling` advice can be exactly the same as i
return retVal; return retVal;
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
fun doBasicProfiling(pjp: ProceedingJoinPoint): Any { fun doBasicProfiling(pjp: ProceedingJoinPoint): Any {
// start stopwatch // start stopwatch
@ -419,6 +442,7 @@ The implementation of the `doBasicProfiling` advice can be exactly the same as i
return pjp.proceed() return pjp.proceed()
} }
---- ----
======
[[aop-schema-params]] [[aop-schema-params]]
@ -447,8 +471,11 @@ The `arg-names` attribute accepts a comma-delimited list of parameter names.
The following slightly more involved example of the XSD-based approach shows The following slightly more involved example of the XSD-based approach shows
some around advice used in conjunction with a number of strongly typed parameters: some around advice used in conjunction with a number of strongly typed parameters:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary",chomp="-packages"] [source,java,indent=0,subs="verbatim",role="primary",chomp="-packages"]
.Java
---- ----
package com.xyz.service; package com.xyz.service;
@ -464,8 +491,10 @@ some around advice used in conjunction with a number of strongly typed parameter
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages"] [source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages"]
.Kotlin
---- ----
package com.xyz.service package com.xyz.service
@ -481,14 +510,18 @@ some around advice used in conjunction with a number of strongly typed parameter
} }
} }
---- ----
======
Next up is the aspect. Notice the fact that the `profile(..)` method accepts a number of Next up is the aspect. Notice the fact that the `profile(..)` method accepts a number of
strongly-typed parameters, the first of which happens to be the join point used to strongly-typed parameters, the first of which happens to be the join point used to
proceed with the method call. The presence of this parameter is an indication that the proceed with the method call. The presence of this parameter is an indication that the
`profile(..)` is to be used as `around` advice, as the following example shows: `profile(..)` is to be used as `around` advice, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary",chomp="-packages"] [source,java,indent=0,subs="verbatim",role="primary",chomp="-packages"]
.Java
---- ----
package com.xyz; package com.xyz;
@ -509,8 +542,10 @@ proceed with the method call. The presence of this parameter is an indication th
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages"] [source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages"]
.Kotlin
---- ----
package com.xyz package com.xyz
@ -531,6 +566,7 @@ proceed with the method call. The presence of this parameter is an indication th
} }
} }
---- ----
======
Finally, the following example XML configuration effects the execution of the Finally, the following example XML configuration effects the execution of the
preceding advice for a particular join point: preceding advice for a particular join point:
@ -570,8 +606,11 @@ preceding advice for a particular join point:
Consider the following driver script: Consider the following driver script:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
public class Boot { public class Boot {
@ -582,8 +621,10 @@ Consider the following driver script:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
fun main() { fun main() {
val ctx = ClassPathXmlApplicationContext("beans.xml") val ctx = ClassPathXmlApplicationContext("beans.xml")
@ -591,6 +632,7 @@ Consider the following driver script:
person.getPerson("Pengo", 12) person.getPerson("Pengo", 12)
} }
---- ----
======
With such a `Boot` class, we would get output similar to the following on standard output: With such a `Boot` class, we would get output similar to the following on standard output:
@ -668,20 +710,26 @@ through JMX for example.)
The class that backs the `usageTracking` bean would then contain the following method: The class that backs the `usageTracking` bean would then contain the following method:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
public void recordUsage(UsageTracked usageTracked) { public void recordUsage(UsageTracked usageTracked) {
usageTracked.incrementUseCount(); usageTracked.incrementUseCount();
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
fun recordUsage(usageTracked: UsageTracked) { fun recordUsage(usageTracked: UsageTracked) {
usageTracked.incrementUseCount() usageTracked.incrementUseCount()
} }
---- ----
======
The interface to be implemented is determined by the `implement-interface` attribute. The The interface to be implemented is determined by the `implement-interface` attribute. The
value of the `types-matching` attribute is an AspectJ type pattern. Any bean of a value of the `types-matching` attribute is an AspectJ type pattern. Any bean of a
@ -690,16 +738,22 @@ advice of the preceding example, service beans can be directly used as implement
the `UsageTracked` interface. To access a bean programmatically, you could write the the `UsageTracked` interface. To access a bean programmatically, you could write the
following: following:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
UsageTracked usageTracked = context.getBean("myService", UsageTracked.class); UsageTracked usageTracked = context.getBean("myService", UsageTracked.class);
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
val usageTracked = context.getBean("myService", UsageTracked.class) val usageTracked = context.getBean("myService", UsageTracked.class)
---- ----
======
@ -771,8 +825,11 @@ Because we want to retry the operation, we need to use around advice so that we
call `proceed` multiple times. The following listing shows the basic aspect implementation call `proceed` multiple times. The following listing shows the basic aspect implementation
(which is a regular Java class that uses the schema support): (which is a regular Java class that uses the schema support):
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
public class ConcurrentOperationExecutor implements Ordered { public class ConcurrentOperationExecutor implements Ordered {
@ -809,8 +866,10 @@ call `proceed` multiple times. The following listing shows the basic aspect impl
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
class ConcurrentOperationExecutor : Ordered { class ConcurrentOperationExecutor : Ordered {
@ -847,6 +906,7 @@ call `proceed` multiple times. The following listing shows the basic aspect impl
} }
} }
---- ----
======
Note that the aspect implements the `Ordered` interface so that we can set the precedence of 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 the aspect higher than the transaction advice (we want a fresh transaction each time we
@ -889,21 +949,27 @@ this is not the case, we can refine the aspect so that it retries only genuinely
idempotent operations, by introducing an `Idempotent` annotation and using the annotation idempotent operations, by introducing an `Idempotent` annotation and using the annotation
to annotate the implementation of service operations, as the following example shows: to annotate the implementation of service operations, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
// marker annotation // marker annotation
public @interface Idempotent { public @interface Idempotent {
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
@Retention(AnnotationRetention.RUNTIME) @Retention(AnnotationRetention.RUNTIME)
// marker annotation // marker annotation
annotation class Idempotent annotation class Idempotent
---- ----
======
The The
change to the aspect to retry only idempotent operations involves refining the change to the aspect to retry only idempotent operations involves refining the

View File

@ -32,8 +32,11 @@ The `@Configurable` annotation marks a class as being eligible for Spring-driven
configuration. In the simplest case, you can use purely it as a marker annotation, as the configuration. In the simplest case, you can use purely it as a marker annotation, as the
following example shows: following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary",chomp="-packages"] [source,java,indent=0,subs="verbatim",role="primary",chomp="-packages"]
.Java
---- ----
package com.xyz.domain; package com.xyz.domain;
@ -44,8 +47,10 @@ following example shows:
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages"] [source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages"]
.Kotlin
---- ----
package com.xyz.domain package com.xyz.domain
@ -56,6 +61,7 @@ following example shows:
// ... // ...
} }
---- ----
======
When used as a marker interface in this way, Spring configures new instances of the When used as a marker interface in this way, Spring configures new instances of the
annotated type (`Account`, in this case) by using a bean definition (typically annotated type (`Account`, in this case) by using a bean definition (typically
@ -74,8 +80,11 @@ is to omit the `id` attribute, as the following example shows:
If you want to explicitly specify the name of the prototype bean definition to use, you If you want to explicitly specify the name of the prototype bean definition to use, you
can do so directly in the annotation, as the following example shows: can do so directly in the annotation, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary",chomp="-packages"] [source,java,indent=0,subs="verbatim",role="primary",chomp="-packages"]
.Java
---- ----
package com.xyz.domain; package com.xyz.domain;
@ -86,8 +95,10 @@ can do so directly in the annotation, as the following example shows:
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages"] [source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages"]
.Kotlin
---- ----
package com.xyz.domain package com.xyz.domain
@ -98,6 +109,7 @@ can do so directly in the annotation, as the following example shows:
// ... // ...
} }
---- ----
======
Spring now looks for a bean definition named `account` and uses that as the Spring now looks for a bean definition named `account` and uses that as the
definition to configure new `Account` instances. definition to configure new `Account` instances.
@ -137,16 +149,22 @@ dependencies to be injected before the constructor bodies run and thus be
available for use in the body of the constructors, you need to define this on the available for use in the body of the constructors, you need to define this on the
`@Configurable` declaration, as follows: `@Configurable` declaration, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
@Configurable(preConstruction = true) @Configurable(preConstruction = true)
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
@Configurable(preConstruction = true) @Configurable(preConstruction = true)
---- ----
======
You can find more information about the language semantics of the various pointcut You can find more information about the language semantics of the various pointcut
types in AspectJ types in AspectJ
@ -164,22 +182,28 @@ a reference to the bean factory that is to be used to configure new objects). If
use Java-based configuration, you can add `@EnableSpringConfigured` to any use Java-based configuration, you can add `@EnableSpringConfigured` to any
`@Configuration` class, as follows: `@Configuration` class, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
@Configuration @Configuration
@EnableSpringConfigured @EnableSpringConfigured
public class AppConfig { public class AppConfig {
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
@Configuration @Configuration
@EnableSpringConfigured @EnableSpringConfigured
class AppConfig { class AppConfig {
} }
---- ----
======
If you prefer XML based configuration, the Spring If you prefer XML based configuration, the Spring
xref:core/appendix/xsd-schemas.adoc#context[`context` namespace] xref:core/appendix/xsd-schemas.adoc#context[`context` namespace]
@ -417,8 +441,11 @@ use @AspectJ with xref:core/beans/java.adoc[Java configuration]. Specifically, y
The following example shows the profiling aspect, which is not fancy. The following example shows the profiling aspect, which is not fancy.
It is a time-based profiler that uses the @AspectJ-style of aspect declaration: It is a time-based profiler that uses the @AspectJ-style of aspect declaration:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary",chomp="-packages"] [source,java,indent=0,subs="verbatim",role="primary",chomp="-packages"]
.Java
---- ----
package com.xyz; package com.xyz;
@ -448,8 +475,10 @@ It is a time-based profiler that uses the @AspectJ-style of aspect declaration:
public void methodsToBeProfiled(){} public void methodsToBeProfiled(){}
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages"] [source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages"]
.Kotlin
---- ----
package com.xyz package com.xyz
@ -480,6 +509,7 @@ It is a time-based profiler that uses the @AspectJ-style of aspect declaration:
} }
} }
---- ----
======
We also need to create an `META-INF/aop.xml` file, to inform the AspectJ weaver that We also need to create an `META-INF/aop.xml` file, to inform the AspectJ weaver that
we want to weave our `ProfilingAspect` into our classes. This file convention, namely we want to weave our `ProfilingAspect` into our classes. This file convention, namely
@ -537,8 +567,11 @@ Now that all the required artifacts (the aspect, the `META-INF/aop.xml`
file, and the Spring configuration) are in place, we can create the following file, and the Spring configuration) are in place, we can create the following
driver class with a `main(..)` method to demonstrate the LTW in action: driver class with a `main(..)` method to demonstrate the LTW in action:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary",chomp="-packages"] [source,java,indent=0,subs="verbatim",role="primary",chomp="-packages"]
.Java
---- ----
package com.xyz; package com.xyz;
@ -557,8 +590,10 @@ driver class with a `main(..)` method to demonstrate the LTW in action:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages"] [source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages"]
.Kotlin
---- ----
package com.xyz package com.xyz
@ -573,6 +608,7 @@ driver class with a `main(..)` method to demonstrate the LTW in action:
service.calculateEntitlement() service.calculateEntitlement()
} }
---- ----
======
We have one last thing to do. The introduction to this section did say that one could We have one last thing to do. The introduction to this section did say that one could
switch on LTW selectively on a per-`ClassLoader` basis with Spring, and this is true. switch on LTW selectively on a per-`ClassLoader` basis with Spring, and this is true.
@ -612,8 +648,11 @@ Since this LTW is effected by using full-blown AspectJ, we are not limited only
Spring beans. The following slight variation on the `Main` program yields the same Spring beans. The following slight variation on the `Main` program yields the same
result: result:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary",chomp="-packages"] [source,java,indent=0,subs="verbatim",role="primary",chomp="-packages"]
.Java
---- ----
package com.xyz; package com.xyz;
@ -632,8 +671,10 @@ result:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages"] [source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages"]
.Kotlin
---- ----
package com.xyz package com.xyz
@ -648,6 +689,7 @@ result:
service.calculateEntitlement() service.calculateEntitlement()
} }
---- ----
======
Notice how, in the preceding program, we bootstrap the Spring container and Notice how, in the preceding program, we bootstrap the Spring container and
then create a new instance of the `StubEntitlementCalculationService` totally outside then create a new instance of the `StubEntitlementCalculationService` totally outside
@ -721,22 +763,28 @@ enough because the LTW support uses `BeanFactoryPostProcessors`.)
To enable the Spring Framework's LTW support, you need to configure a `LoadTimeWeaver`, To enable the Spring Framework's LTW support, you need to configure a `LoadTimeWeaver`,
which typically is done by using the `@EnableLoadTimeWeaving` annotation, as follows: which typically is done by using the `@EnableLoadTimeWeaving` annotation, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
@Configuration @Configuration
@EnableLoadTimeWeaving @EnableLoadTimeWeaving
public class AppConfig { public class AppConfig {
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
@Configuration @Configuration
@EnableLoadTimeWeaving @EnableLoadTimeWeaving
class AppConfig { class AppConfig {
} }
---- ----
======
Alternatively, if you prefer XML-based configuration, use the Alternatively, if you prefer XML-based configuration, use the
`<context:load-time-weaver/>` element. Note that the element is defined in the `<context:load-time-weaver/>` element. Note that the element is defined in the
@ -804,8 +852,11 @@ To specify a specific `LoadTimeWeaver` with Java configuration, implement the
`LoadTimeWeavingConfigurer` interface and override the `getLoadTimeWeaver()` method. `LoadTimeWeavingConfigurer` interface and override the `getLoadTimeWeaver()` method.
The following example specifies a `ReflectiveLoadTimeWeaver`: The following example specifies a `ReflectiveLoadTimeWeaver`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"] [source,java,indent=0,subs="verbatim",role="primary"]
.Java
---- ----
@Configuration @Configuration
@EnableLoadTimeWeaving @EnableLoadTimeWeaving
@ -817,8 +868,10 @@ The following example specifies a `ReflectiveLoadTimeWeaver`:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
@Configuration @Configuration
@EnableLoadTimeWeaving @EnableLoadTimeWeaving
@ -829,6 +882,7 @@ The following example specifies a `ReflectiveLoadTimeWeaver`:
} }
} }
---- ----
======
If you use XML-based configuration, you can specify the fully qualified class name If you use XML-based configuration, you can specify the fully qualified class name
as the value of the `weaver-class` attribute on the `<context:load-time-weaver/>` as the value of the `weaver-class` attribute on the `<context:load-time-weaver/>`

View File

@ -124,8 +124,11 @@ This is the default behavior, since tuning the generated code for a bean definit
Taking our previous example, let's assume that `DataSourceConfiguration` is as follows: Taking our previous example, let's assume that `DataSourceConfiguration` is as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
public class DataSourceConfiguration { public class DataSourceConfiguration {
@ -137,12 +140,16 @@ Taking our previous example, let's assume that `DataSourceConfiguration` is as f
} }
---- ----
======
Since there isn't any particular condition on this class, `dataSourceConfiguration` and `dataSource` are identified as candidates. Since there isn't any particular condition on this class, `dataSourceConfiguration` and `dataSource` are identified as candidates.
The AOT engine will convert the configuration class above to code similar to the following: The AOT engine will convert the configuration class above to code similar to the following:
[tabs]
======
Java::
+
[source,java,indent=0,role="primary"] [source,java,indent=0,role="primary"]
.Java
---- ----
/** /**
* Bean definitions for {@link DataSourceConfiguration} * Bean definitions for {@link DataSourceConfiguration}
@ -177,6 +184,7 @@ The AOT engine will convert the configuration class above to code similar to the
} }
} }
---- ----
======
NOTE: The exact code generated may differ depending on the exact nature of your bean definitions. NOTE: The exact code generated may differ depending on the exact nature of your bean definitions.
@ -197,11 +205,15 @@ Consequently, if the application needs to load a resource, it must be referenced
The {api-spring-framework}/aot/hint/RuntimeHints.html[`RuntimeHints`] API collects the need for reflection, resource loading, serialization, and JDK proxies at runtime. The {api-spring-framework}/aot/hint/RuntimeHints.html[`RuntimeHints`] API collects the need for reflection, resource loading, serialization, and JDK proxies at runtime.
The following example makes sure that `config/app.properties` can be loaded from the classpath at runtime within a native image: The following example makes sure that `config/app.properties` can be loaded from the classpath at runtime within a native image:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
runtimeHints.resources().registerPattern("config/app.properties"); runtimeHints.resources().registerPattern("config/app.properties");
---- ----
======
A number of contracts are handled automatically during AOT processing. A number of contracts are handled automatically during AOT processing.
For instance, the return type of a `@Controller` method is inspected, and relevant reflection hints are added if Spring detects that the type should be serialized (typically to JSON). For instance, the return type of a `@Controller` method is inspected, and relevant reflection hints are added if Spring detects that the type should be serialized (typically to JSON).
@ -248,8 +260,11 @@ A typical use case is the use of DTOs that the container cannot infer, such as u
`@RegisterReflectionForBinding` can be applied to any Spring bean at the class level, but it can also be applied directly to a method, field, or constructor to better indicate where the hints are actually required. `@RegisterReflectionForBinding` can be applied to any Spring bean at the class level, but it can also be applied directly to a method, field, or constructor to better indicate where the hints are actually required.
The following example registers `Account` for serialization. The following example registers `Account` for serialization.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Component @Component
public class OrderService { public class OrderService {
@ -261,6 +276,7 @@ The following example registers `Account` for serialization.
} }
---- ----
======
[[aot.hints.testing]] [[aot.hints.testing]]
=== Testing Runtime Hints === Testing Runtime Hints

View File

@ -141,8 +141,11 @@ element results in a single `SimpleDateFormat` bean definition). Spring features
number of convenience classes that support this scenario. In the following example, we number of convenience classes that support this scenario. In the following example, we
use the `NamespaceHandlerSupport` class: use the `NamespaceHandlerSupport` class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"] [source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
---- ----
package org.springframework.samples.xml; package org.springframework.samples.xml;
@ -155,8 +158,10 @@ use the `NamespaceHandlerSupport` class:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
---- ----
package org.springframework.samples.xml package org.springframework.samples.xml
@ -169,6 +174,7 @@ use the `NamespaceHandlerSupport` class:
} }
} }
---- ----
======
You may notice that there is not actually a whole lot of parsing logic You may notice that there is not actually a whole lot of parsing logic
in this class. Indeed, the `NamespaceHandlerSupport` class has a built-in notion of in this class. Indeed, the `NamespaceHandlerSupport` class has a built-in notion of
@ -192,8 +198,11 @@ responsible for parsing one distinct top-level XML element defined in the schema
the parser, we' have access to the XML element (and thus to its subelements, too) so that the parser, we' have access to the XML element (and thus to its subelements, too) so that
we can parse our custom XML content, as you can see in the following example: we can parse our custom XML content, as you can see in the following example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"] [source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
---- ----
package org.springframework.samples.xml; package org.springframework.samples.xml;
@ -224,6 +233,7 @@ we can parse our custom XML content, as you can see in the following example:
} }
---- ----
======
<1> We use the Spring-provided `AbstractSingleBeanDefinitionParser` to handle a lot of <1> We use the Spring-provided `AbstractSingleBeanDefinitionParser` to handle a lot of
the basic grunt work of creating a single `BeanDefinition`. the basic grunt work of creating a single `BeanDefinition`.
<2> We supply the `AbstractSingleBeanDefinitionParser` superclass with the type that our <2> We supply the `AbstractSingleBeanDefinitionParser` superclass with the type that our
@ -401,8 +411,11 @@ setter method for the `components` property. This makes it hard (or rather impos
to configure a bean definition for the `Component` class by using setter injection. to configure a bean definition for the `Component` class by using setter injection.
The following listing shows the `Component` class: The following listing shows the `Component` class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"] [source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
---- ----
package com.foo; package com.foo;
@ -432,8 +445,10 @@ The following listing shows the `Component` class:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
---- ----
package com.foo package com.foo
@ -454,13 +469,17 @@ The following listing shows the `Component` class:
} }
} }
---- ----
======
The typical solution to this issue is to create a custom `FactoryBean` that exposes a The typical solution to this issue is to create a custom `FactoryBean` that exposes a
setter property for the `components` property. The following listing shows such a custom setter property for the `components` property. The following listing shows such a custom
`FactoryBean`: `FactoryBean`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"] [source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
---- ----
package com.foo; package com.foo;
@ -499,8 +518,10 @@ setter property for the `components` property. The following listing shows such
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
---- ----
package com.foo package com.foo
@ -538,6 +559,7 @@ setter property for the `components` property. The following listing shows such
} }
} }
---- ----
======
This works nicely, but it exposes a lot of Spring plumbing to the end user. What we are This works nicely, but it exposes a lot of Spring plumbing to the end user. What we are
going to do is write a custom extension that hides away all of this Spring plumbing. going to do is write a custom extension that hides away all of this Spring plumbing.
@ -571,8 +593,11 @@ listing shows:
Again following xref:core/appendix/xml-custom.adoc#core.appendix.xsd-custom-introduction[the process described earlier], Again following xref:core/appendix/xml-custom.adoc#core.appendix.xsd-custom-introduction[the process described earlier],
we then create a custom `NamespaceHandler`: we then create a custom `NamespaceHandler`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"] [source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
---- ----
package com.foo; package com.foo;
@ -585,8 +610,10 @@ we then create a custom `NamespaceHandler`:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
---- ----
package com.foo package com.foo
@ -599,13 +626,17 @@ we then create a custom `NamespaceHandler`:
} }
} }
---- ----
======
Next up is the custom `BeanDefinitionParser`. Remember that we are creating Next up is the custom `BeanDefinitionParser`. Remember that we are creating
a `BeanDefinition` that describes a `ComponentFactoryBean`. The following a `BeanDefinition` that describes a `ComponentFactoryBean`. The following
listing shows our custom `BeanDefinitionParser` implementation: listing shows our custom `BeanDefinitionParser` implementation:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"] [source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
---- ----
package com.foo; package com.foo;
@ -653,8 +684,10 @@ listing shows our custom `BeanDefinitionParser` implementation:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
---- ----
package com.foo package com.foo
@ -702,6 +735,7 @@ listing shows our custom `BeanDefinitionParser` implementation:
} }
} }
---- ----
======
Finally, the various artifacts need to be registered with the Spring XML infrastructure, Finally, the various artifacts need to be registered with the Spring XML infrastructure,
by modifying the `META-INF/spring.handlers` and `META-INF/spring.schemas` files, as follows: by modifying the `META-INF/spring.handlers` and `META-INF/spring.schemas` files, as follows:
@ -748,8 +782,11 @@ the named JCache for us. We can also modify the existing `BeanDefinition` for th
`'checkingAccountService'` so that it has a dependency on this new `'checkingAccountService'` so that it has a dependency on this new
JCache-initializing `BeanDefinition`. The following listing shows our `JCacheInitializer`: JCache-initializing `BeanDefinition`. The following listing shows our `JCacheInitializer`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"] [source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
---- ----
package com.foo; package com.foo;
@ -766,8 +803,10 @@ JCache-initializing `BeanDefinition`. The following listing shows our `JCacheIni
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
---- ----
package com.foo package com.foo
@ -778,6 +817,7 @@ JCache-initializing `BeanDefinition`. The following listing shows our `JCacheIni
} }
} }
---- ----
======
Now we can move onto the custom extension. First, we need to author Now we can move onto the custom extension. First, we need to author
the XSD schema that describes the custom attribute, as follows: the XSD schema that describes the custom attribute, as follows:
@ -798,8 +838,11 @@ the XSD schema that describes the custom attribute, as follows:
Next, we need to create the associated `NamespaceHandler`, as follows: Next, we need to create the associated `NamespaceHandler`, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"] [source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
---- ----
package com.foo; package com.foo;
@ -814,8 +857,10 @@ Next, we need to create the associated `NamespaceHandler`, as follows:
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
---- ----
package com.foo package com.foo
@ -830,13 +875,17 @@ Next, we need to create the associated `NamespaceHandler`, as follows:
} }
---- ----
======
Next, we need to create the parser. Note that, in this case, because we are going to parse Next, we need to create the parser. Note that, in this case, because we are going to parse
an XML attribute, we write a `BeanDefinitionDecorator` rather than a `BeanDefinitionParser`. an XML attribute, we write a `BeanDefinitionDecorator` rather than a `BeanDefinitionParser`.
The following listing shows our `BeanDefinitionDecorator` implementation: The following listing shows our `BeanDefinitionDecorator` implementation:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"] [source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
---- ----
package com.foo; package com.foo;
@ -889,8 +938,10 @@ The following listing shows our `BeanDefinitionDecorator` implementation:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
---- ----
package com.foo package com.foo
@ -939,6 +990,7 @@ The following listing shows our `BeanDefinitionDecorator` implementation:
} }
} }
---- ----
======
Finally, we need to register the various artifacts with the Spring XML infrastructure Finally, we need to register the various artifacts with the Spring XML infrastructure
by modifying the `META-INF/spring.handlers` and `META-INF/spring.schemas` files, as follows: by modifying the `META-INF/spring.handlers` and `META-INF/spring.schemas` files, as follows:

View File

@ -117,8 +117,11 @@ easy to do in Spring. You do not actually have to do anything or know anything a
the Spring internals (or even about classes such as the `FieldRetrievingFactoryBean`). the Spring internals (or even about classes such as the `FieldRetrievingFactoryBean`).
The following example enumeration shows how easy injecting an enum value is: The following example enumeration shows how easy injecting an enum value is:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"] [source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
---- ----
package jakarta.persistence; package jakarta.persistence;
@ -128,8 +131,10 @@ The following example enumeration shows how easy injecting an enum value is:
EXTENDED EXTENDED
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
---- ----
package jakarta.persistence package jakarta.persistence
@ -139,11 +144,15 @@ The following example enumeration shows how easy injecting an enum value is:
EXTENDED EXTENDED
} }
---- ----
======
Now consider the following setter of type `PersistenceContextType` and the corresponding bean definition: Now consider the following setter of type `PersistenceContextType` and the corresponding bean definition:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"] [source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
---- ----
package example; package example;
@ -156,8 +165,10 @@ Now consider the following setter of type `PersistenceContextType` and the corre
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
---- ----
package example package example
@ -166,6 +177,7 @@ Now consider the following setter of type `PersistenceContextType` and the corre
lateinit var persistenceContextType: PersistenceContextType lateinit var persistenceContextType: PersistenceContextType
} }
---- ----
======
[source,xml,indent=0,subs="verbatim,quotes"] [source,xml,indent=0,subs="verbatim,quotes"]
---- ----

View File

@ -11,8 +11,11 @@ autowired value.
Consider the following configuration that defines `firstMovieCatalog` as the Consider the following configuration that defines `firstMovieCatalog` as the
primary `MovieCatalog`: primary `MovieCatalog`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Configuration @Configuration
public class MovieConfiguration { public class MovieConfiguration {
@ -27,8 +30,10 @@ primary `MovieCatalog`:
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Configuration @Configuration
class MovieConfiguration { class MovieConfiguration {
@ -43,12 +48,16 @@ primary `MovieCatalog`:
// ... // ...
} }
---- ----
======
With the preceding configuration, the following `MovieRecommender` is autowired with the With the preceding configuration, the following `MovieRecommender` is autowired with the
`firstMovieCatalog`: `firstMovieCatalog`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class MovieRecommender { public class MovieRecommender {
@ -58,8 +67,10 @@ With the preceding configuration, the following `MovieRecommender` is autowired
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class MovieRecommender { class MovieRecommender {
@ -69,6 +80,7 @@ class MovieRecommender {
// ... // ...
} }
---- ----
======
The corresponding bean definitions follow: The corresponding bean definitions follow:

View File

@ -9,8 +9,11 @@ chosen for each argument. In the simplest case, this can be a plain descriptive
shown in the following example: shown in the following example:
-- --
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class MovieRecommender { public class MovieRecommender {
@ -21,8 +24,10 @@ shown in the following example:
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class MovieRecommender { class MovieRecommender {
@ -33,14 +38,18 @@ shown in the following example:
// ... // ...
} }
---- ----
======
-- --
You can also specify the `@Qualifier` annotation on individual constructor arguments or You can also specify the `@Qualifier` annotation on individual constructor arguments or
method parameters, as shown in the following example: method parameters, as shown in the following example:
-- --
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class MovieRecommender { public class MovieRecommender {
@ -58,8 +67,10 @@ method parameters, as shown in the following example:
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class MovieRecommender { class MovieRecommender {
@ -77,6 +88,7 @@ method parameters, as shown in the following example:
// ... // ...
} }
---- ----
======
-- --
The following example shows corresponding bean definitions. The following example shows corresponding bean definitions.
@ -194,8 +206,11 @@ You can create your own custom qualifier annotations. To do so, define an annota
provide the `@Qualifier` annotation within your definition, as the following example shows: provide the `@Qualifier` annotation within your definition, as the following example shows:
-- --
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Target({ElementType.FIELD, ElementType.PARAMETER}) @Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@ -205,22 +220,28 @@ provide the `@Qualifier` annotation within your definition, as the following exa
String value(); String value();
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER) @Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME) @Retention(AnnotationRetention.RUNTIME)
@Qualifier @Qualifier
annotation class Genre(val value: String) annotation class Genre(val value: String)
---- ----
======
-- --
Then you can provide the custom qualifier on autowired fields and parameters, as the Then you can provide the custom qualifier on autowired fields and parameters, as the
following example shows: following example shows:
-- --
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class MovieRecommender { public class MovieRecommender {
@ -238,8 +259,10 @@ following example shows:
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class MovieRecommender { class MovieRecommender {
@ -257,6 +280,7 @@ following example shows:
// ... // ...
} }
---- ----
======
-- --
Next, you can provide the information for the candidate bean definitions. You can add Next, you can provide the information for the candidate bean definitions. You can add
@ -306,8 +330,11 @@ catalog that can be searched when no Internet connection is available. First, de
the simple annotation, as the following example shows: the simple annotation, as the following example shows:
-- --
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Target({ElementType.FIELD, ElementType.PARAMETER}) @Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@ -315,22 +342,28 @@ the simple annotation, as the following example shows:
public @interface Offline { public @interface Offline {
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER) @Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME) @Retention(AnnotationRetention.RUNTIME)
@Qualifier @Qualifier
annotation class Offline annotation class Offline
---- ----
======
-- --
Then add the annotation to the field or property to be autowired, as shown in the Then add the annotation to the field or property to be autowired, as shown in the
following example: following example:
-- --
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class MovieRecommender { public class MovieRecommender {
@ -341,6 +374,7 @@ following example:
// ... // ...
} }
---- ----
======
<1> This line adds the `@Offline` annotation. <1> This line adds the `@Offline` annotation.
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
@ -379,8 +413,11 @@ all such attribute values to be considered an autowire candidate. As an example,
consider the following annotation definition: consider the following annotation definition:
-- --
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Target({ElementType.FIELD, ElementType.PARAMETER}) @Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@ -392,41 +429,53 @@ consider the following annotation definition:
Format format(); Format format();
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER) @Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME) @Retention(AnnotationRetention.RUNTIME)
@Qualifier @Qualifier
annotation class MovieQualifier(val genre: String, val format: Format) annotation class MovieQualifier(val genre: String, val format: Format)
---- ----
======
-- --
In this case `Format` is an enum, defined as follows: In this case `Format` is an enum, defined as follows:
-- --
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public enum Format { public enum Format {
VHS, DVD, BLURAY VHS, DVD, BLURAY
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
enum class Format { enum class Format {
VHS, DVD, BLURAY VHS, DVD, BLURAY
} }
---- ----
======
-- --
The fields to be autowired are annotated with the custom qualifier and include values The fields to be autowired are annotated with the custom qualifier and include values
for both attributes: `genre` and `format`, as the following example shows: for both attributes: `genre` and `format`, as the following example shows:
-- --
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class MovieRecommender { public class MovieRecommender {
@ -449,8 +498,10 @@ for both attributes: `genre` and `format`, as the following example shows:
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class MovieRecommender { class MovieRecommender {
@ -473,6 +524,7 @@ for both attributes: `genre` and `format`, as the following example shows:
// ... // ...
} }
---- ----
======
-- --
Finally, the bean definitions should contain matching qualifier values. This example Finally, the bean definitions should contain matching qualifier values. This example

View File

@ -9,8 +9,11 @@ examples included in this section. See xref:core/beans/standard-annotations.adoc
You can apply the `@Autowired` annotation to constructors, as the following example shows: You can apply the `@Autowired` annotation to constructors, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class MovieRecommender { public class MovieRecommender {
@ -24,12 +27,15 @@ You can apply the `@Autowired` annotation to constructors, as the following exam
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class MovieRecommender @Autowired constructor( class MovieRecommender @Autowired constructor(
private val customerPreferenceDao: CustomerPreferenceDao) private val customerPreferenceDao: CustomerPreferenceDao)
---- ----
======
[NOTE] [NOTE]
==== ====
@ -44,8 +50,11 @@ xref:core/beans/annotation-config/autowired.adoc#beans-autowired-annotation-cons
You can also apply the `@Autowired` annotation to _traditional_ setter methods, You can also apply the `@Autowired` annotation to _traditional_ setter methods,
as the following example shows: as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class SimpleMovieLister { public class SimpleMovieLister {
@ -59,8 +68,10 @@ as the following example shows:
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class SimpleMovieLister { class SimpleMovieLister {
@ -71,12 +82,16 @@ as the following example shows:
} }
---- ----
======
You can also apply the annotation to methods with arbitrary names and multiple You can also apply the annotation to methods with arbitrary names and multiple
arguments, as the following example shows: arguments, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class MovieRecommender { public class MovieRecommender {
@ -94,8 +109,10 @@ arguments, as the following example shows:
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class MovieRecommender { class MovieRecommender {
@ -113,12 +130,16 @@ arguments, as the following example shows:
// ... // ...
} }
---- ----
======
You can apply `@Autowired` to fields as well and even mix it with constructors, as the You can apply `@Autowired` to fields as well and even mix it with constructors, as the
following example shows: following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class MovieRecommender { public class MovieRecommender {
@ -135,8 +156,10 @@ following example shows:
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class MovieRecommender @Autowired constructor( class MovieRecommender @Autowired constructor(
private val customerPreferenceDao: CustomerPreferenceDao) { private val customerPreferenceDao: CustomerPreferenceDao) {
@ -147,6 +170,7 @@ following example shows:
// ... // ...
} }
---- ----
======
[TIP] [TIP]
==== ====
@ -166,8 +190,11 @@ You can also instruct Spring to provide all beans of a particular type from the
`ApplicationContext` by adding the `@Autowired` annotation to a field or method that `ApplicationContext` by adding the `@Autowired` annotation to a field or method that
expects an array of that type, as the following example shows: expects an array of that type, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class MovieRecommender { public class MovieRecommender {
@ -177,8 +204,10 @@ expects an array of that type, as the following example shows:
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class MovieRecommender { class MovieRecommender {
@ -188,11 +217,15 @@ expects an array of that type, as the following example shows:
// ... // ...
} }
---- ----
======
The same applies for typed collections, as the following example shows: The same applies for typed collections, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class MovieRecommender { public class MovieRecommender {
@ -206,8 +239,10 @@ The same applies for typed collections, as the following example shows:
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class MovieRecommender { class MovieRecommender {
@ -217,6 +252,7 @@ The same applies for typed collections, as the following example shows:
// ... // ...
} }
---- ----
======
[[beans-factory-ordered]] [[beans-factory-ordered]]
[TIP] [TIP]
@ -241,8 +277,11 @@ Even typed `Map` instances can be autowired as long as the expected key type is
The map values contain all beans of the expected type, and the keys contain the The map values contain all beans of the expected type, and the keys contain the
corresponding bean names, as the following example shows: corresponding bean names, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class MovieRecommender { public class MovieRecommender {
@ -256,8 +295,10 @@ corresponding bean names, as the following example shows:
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class MovieRecommender { class MovieRecommender {
@ -267,6 +308,7 @@ corresponding bean names, as the following example shows:
// ... // ...
} }
---- ----
======
By default, autowiring fails when no matching candidate beans are available for a given By default, autowiring fails when no matching candidate beans are available for a given
injection point. In the case of a declared array, collection, or map, at least one injection point. In the case of a declared array, collection, or map, at least one
@ -277,8 +319,11 @@ dependencies. You can change this behavior as demonstrated in the following exam
enabling the framework to skip a non-satisfiable injection point through marking it as enabling the framework to skip a non-satisfiable injection point through marking it as
non-required (i.e., by setting the `required` attribute in `@Autowired` to `false`): non-required (i.e., by setting the `required` attribute in `@Autowired` to `false`):
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class SimpleMovieLister { public class SimpleMovieLister {
@ -292,8 +337,10 @@ non-required (i.e., by setting the `required` attribute in `@Autowired` to `fals
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class SimpleMovieLister { class SimpleMovieLister {
@ -303,6 +350,7 @@ non-required (i.e., by setting the `required` attribute in `@Autowired` to `fals
// ... // ...
} }
---- ----
======
[NOTE] [NOTE]
==== ====
@ -317,8 +365,8 @@ that can be optionally overridden via dependency injection.
==== ====
[[beans-autowired-annotation-constructor-resolution]]
[[beans-autowired-annotation-constructor-resolution]]
Injected constructor and factory method arguments are a special case since the `required` Injected constructor and factory method arguments are a special case since the `required`
attribute in `@Autowired` has a somewhat different meaning due to Spring's constructor attribute in `@Autowired` has a somewhat different meaning due to Spring's constructor
resolution algorithm that may potentially deal with multiple constructors. Constructor resolution algorithm that may potentially deal with multiple constructors. Constructor
@ -364,8 +412,11 @@ As of Spring Framework 5.0, you can also use a `@Nullable` annotation (of any ki
in any package -- for example, `javax.annotation.Nullable` from JSR-305) or just leverage in any package -- for example, `javax.annotation.Nullable` from JSR-305) or just leverage
Kotlin built-in null-safety support: Kotlin built-in null-safety support:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class SimpleMovieLister { public class SimpleMovieLister {
@ -375,8 +426,10 @@ Kotlin built-in null-safety support:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class SimpleMovieLister { class SimpleMovieLister {
@ -386,6 +439,7 @@ Kotlin built-in null-safety support:
// ... // ...
} }
---- ----
======
You can also use `@Autowired` for interfaces that are well-known resolvable You can also use `@Autowired` for interfaces that are well-known resolvable
dependencies: `BeanFactory`, `ApplicationContext`, `Environment`, `ResourceLoader`, dependencies: `BeanFactory`, `ApplicationContext`, `Environment`, `ResourceLoader`,
@ -394,8 +448,11 @@ interfaces, such as `ConfigurableApplicationContext` or `ResourcePatternResolver
automatically resolved, with no special setup necessary. The following example autowires automatically resolved, with no special setup necessary. The following example autowires
an `ApplicationContext` object: an `ApplicationContext` object:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class MovieRecommender { public class MovieRecommender {
@ -408,8 +465,10 @@ an `ApplicationContext` object:
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class MovieRecommender { class MovieRecommender {
@ -419,6 +478,7 @@ class MovieRecommender {
// ... // ...
} }
---- ----
======
[NOTE] [NOTE]
==== ====

View File

@ -5,8 +5,11 @@ In addition to the `@Qualifier` annotation, you can use Java generic types
as an implicit form of qualification. For example, suppose you have the following as an implicit form of qualification. For example, suppose you have the following
configuration: configuration:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Configuration @Configuration
public class MyConfiguration { public class MyConfiguration {
@ -22,8 +25,10 @@ configuration:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Configuration @Configuration
class MyConfiguration { class MyConfiguration {
@ -35,13 +40,17 @@ configuration:
fun integerStore() = IntegerStore() fun integerStore() = IntegerStore()
} }
---- ----
======
Assuming that the preceding beans implement a generic interface, (that is, `Store<String>` and Assuming that the preceding beans implement a generic interface, (that is, `Store<String>` and
`Store<Integer>`), you can `@Autowire` the `Store` interface and the generic is `Store<Integer>`), you can `@Autowire` the `Store` interface and the generic is
used as a qualifier, as the following example shows: used as a qualifier, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Autowired @Autowired
private Store<String> s1; // <String> qualifier, injects the stringStore bean private Store<String> s1; // <String> qualifier, injects the stringStore bean
@ -49,8 +58,10 @@ used as a qualifier, as the following example shows:
@Autowired @Autowired
private Store<Integer> s2; // <Integer> qualifier, injects the integerStore bean private Store<Integer> s2; // <Integer> qualifier, injects the integerStore bean
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Autowired @Autowired
private lateinit var s1: Store<String> // <String> qualifier, injects the stringStore bean private lateinit var s1: Store<String> // <String> qualifier, injects the stringStore bean
@ -58,26 +69,33 @@ used as a qualifier, as the following example shows:
@Autowired @Autowired
private lateinit var s2: Store<Integer> // <Integer> qualifier, injects the integerStore bean private lateinit var s2: Store<Integer> // <Integer> qualifier, injects the integerStore bean
---- ----
======
Generic qualifiers also apply when autowiring lists, `Map` instances and arrays. The Generic qualifiers also apply when autowiring lists, `Map` instances and arrays. The
following example autowires a generic `List`: following example autowires a generic `List`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
// Inject all Store beans as long as they have an <Integer> generic // Inject all Store beans as long as they have an <Integer> generic
// Store<String> beans will not appear in this list // Store<String> beans will not appear in this list
@Autowired @Autowired
private List<Store<Integer>> s; private List<Store<Integer>> s;
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
// Inject all Store beans as long as they have an <Integer> generic // Inject all Store beans as long as they have an <Integer> generic
// Store<String> beans will not appear in this list // Store<String> beans will not appear in this list
@Autowired @Autowired
private lateinit var s: List<Store<Integer>> private lateinit var s: List<Store<Integer>>
---- ----
======

View File

@ -13,8 +13,11 @@ as the corresponding Spring lifecycle interface method or explicitly declared ca
method. In the following example, the cache is pre-populated upon initialization and method. In the following example, the cache is pre-populated upon initialization and
cleared upon destruction: cleared upon destruction:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class CachingMovieLister { public class CachingMovieLister {
@ -29,8 +32,10 @@ cleared upon destruction:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class CachingMovieLister { class CachingMovieLister {
@ -45,6 +50,7 @@ cleared upon destruction:
} }
} }
---- ----
======
For details about the effects of combining various lifecycle mechanisms, see For details about the effects of combining various lifecycle mechanisms, see
xref:core/beans/factory-nature.adoc#beans-factory-lifecycle-combined-effects[Combining Lifecycle Mechanisms]. xref:core/beans/factory-nature.adoc#beans-factory-lifecycle-combined-effects[Combining Lifecycle Mechanisms].

View File

@ -11,8 +11,11 @@ the bean name to be injected. In other words, it follows by-name semantics,
as demonstrated in the following example: as demonstrated in the following example:
-- --
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class SimpleMovieLister { public class SimpleMovieLister {
@ -24,6 +27,7 @@ as demonstrated in the following example:
} }
} }
---- ----
======
<1> This line injects a `@Resource`. <1> This line injects a `@Resource`.
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
@ -45,8 +49,11 @@ it takes the bean property name. The following example is going to have the bean
named `movieFinder` injected into its setter method: named `movieFinder` injected into its setter method:
-- --
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class SimpleMovieLister { public class SimpleMovieLister {
@ -58,8 +65,10 @@ named `movieFinder` injected into its setter method:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class SimpleMovieLister { class SimpleMovieLister {
@ -68,6 +77,7 @@ named `movieFinder` injected into its setter method:
} }
---- ----
======
-- --
NOTE: The name provided with the annotation is resolved as a bean name by the NOTE: The name provided with the annotation is resolved as a bean name by the
@ -88,8 +98,11 @@ named "customerPreferenceDao" and then falls back to a primary type match for th
`CustomerPreferenceDao`: `CustomerPreferenceDao`:
-- --
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class MovieRecommender { public class MovieRecommender {
@ -105,6 +118,7 @@ named "customerPreferenceDao" and then falls back to a primary type match for th
// ... // ...
} }
---- ----
======
<1> The `context` field is injected based on the known resolvable dependency type: <1> The `context` field is injected based on the known resolvable dependency type:
`ApplicationContext`. `ApplicationContext`.

View File

@ -3,8 +3,11 @@
`@Value` is typically used to inject externalized properties: `@Value` is typically used to inject externalized properties:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Component @Component
public class MovieRecommender { public class MovieRecommender {
@ -16,29 +19,38 @@
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Component @Component
class MovieRecommender(@Value("\${catalog.name}") private val catalog: String) class MovieRecommender(@Value("\${catalog.name}") private val catalog: String)
---- ----
======
With the following configuration: With the following configuration:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Configuration @Configuration
@PropertySource("classpath:application.properties") @PropertySource("classpath:application.properties")
public class AppConfig { } public class AppConfig { }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Configuration @Configuration
@PropertySource("classpath:application.properties") @PropertySource("classpath:application.properties")
class AppConfig class AppConfig
---- ----
======
And the following `application.properties` file: And the following `application.properties` file:
@ -55,8 +67,11 @@ will be injected as the value. If you want to maintain strict control over nonex
values, you should declare a `PropertySourcesPlaceholderConfigurer` bean, as the following values, you should declare a `PropertySourcesPlaceholderConfigurer` bean, as the following
example shows: example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Configuration @Configuration
public class AppConfig { public class AppConfig {
@ -67,8 +82,10 @@ example shows:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Configuration @Configuration
class AppConfig { class AppConfig {
@ -77,6 +94,7 @@ example shows:
fun propertyPlaceholderConfigurer() = PropertySourcesPlaceholderConfigurer() fun propertyPlaceholderConfigurer() = PropertySourcesPlaceholderConfigurer()
} }
---- ----
======
NOTE: When configuring a `PropertySourcesPlaceholderConfigurer` using JavaConfig, the NOTE: When configuring a `PropertySourcesPlaceholderConfigurer` using JavaConfig, the
`@Bean` method must be `static`. `@Bean` method must be `static`.
@ -95,8 +113,11 @@ automatically converted to `String` array without extra effort.
It is possible to provide a default value as following: It is possible to provide a default value as following:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Component @Component
public class MovieRecommender { public class MovieRecommender {
@ -108,20 +129,26 @@ It is possible to provide a default value as following:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Component @Component
class MovieRecommender(@Value("\${catalog.name:defaultCatalog}") private val catalog: String) class MovieRecommender(@Value("\${catalog.name:defaultCatalog}") private val catalog: String)
---- ----
======
A Spring `BeanPostProcessor` uses a `ConversionService` behind the scenes to handle the A Spring `BeanPostProcessor` uses a `ConversionService` behind the scenes to handle the
process for converting the `String` value in `@Value` to the target type. If you want to process for converting the `String` value in `@Value` to the target type. If you want to
provide conversion support for your own custom type, you can provide your own provide conversion support for your own custom type, you can provide your own
`ConversionService` bean instance as the following example shows: `ConversionService` bean instance as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Configuration @Configuration
public class AppConfig { public class AppConfig {
@ -134,8 +161,10 @@ provide conversion support for your own custom type, you can provide your own
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Configuration @Configuration
class AppConfig { class AppConfig {
@ -148,12 +177,16 @@ provide conversion support for your own custom type, you can provide your own
} }
} }
---- ----
======
When `@Value` contains a xref:core/expressions.adoc[`SpEL` expression] the value will be dynamically When `@Value` contains a xref:core/expressions.adoc[`SpEL` expression] the value will be dynamically
computed at runtime as the following example shows: computed at runtime as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Component @Component
public class MovieRecommender { public class MovieRecommender {
@ -165,18 +198,24 @@ computed at runtime as the following example shows:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Component @Component
class MovieRecommender( class MovieRecommender(
@Value("#{systemProperties['user.catalog'] + 'Catalog' }") private val catalog: String) @Value("#{systemProperties['user.catalog'] + 'Catalog' }") private val catalog: String)
---- ----
======
SpEL also enables the use of more complex data structures: SpEL also enables the use of more complex data structures:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Component @Component
public class MovieRecommender { public class MovieRecommender {
@ -189,12 +228,15 @@ SpEL also enables the use of more complex data structures:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Component @Component
class MovieRecommender( class MovieRecommender(
@Value("#{{'Thriller': 100, 'Comedy': 300}}") private val countOfMoviesPerCatalog: Map<String, Int>) @Value("#{{'Thriller': 100, 'Comedy': 300}}") private val countOfMoviesPerCatalog: Map<String, Int>)
---- ----
======

View File

@ -119,16 +119,22 @@ supplied to an `ApplicationContext` constructor are resource strings that let
the container load configuration metadata from a variety of external resources, such the container load configuration metadata from a variety of external resources, such
as the local file system, the Java `CLASSPATH`, and so on. as the local file system, the Java `CLASSPATH`, and so on.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml"); ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
---- ----
.Kotlin
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
---- ----
val context = ClassPathXmlApplicationContext("services.xml", "daos.xml") val context = ClassPathXmlApplicationContext("services.xml", "daos.xml")
---- ----
======
[NOTE] [NOTE]
==== ====
@ -295,8 +301,11 @@ a registry of different beans and their dependencies. By using the method
The `ApplicationContext` lets you read bean definitions and access them, as the following The `ApplicationContext` lets you read bean definitions and access them, as the following
example shows: example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
// create and configure beans // create and configure beans
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml"); ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
@ -307,7 +316,9 @@ example shows:
// use configured instance // use configured instance
List<String> userList = service.getUsernameList(); List<String> userList = service.getUsernameList();
---- ----
.Kotlin
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
---- ----
import org.springframework.beans.factory.getBean import org.springframework.beans.factory.getBean
@ -321,58 +332,77 @@ example shows:
// use configured instance // use configured instance
var userList = service.getUsernameList() var userList = service.getUsernameList()
---- ----
======
With Groovy configuration, bootstrapping looks very similar. It has a different context With Groovy configuration, bootstrapping looks very similar. It has a different context
implementation class which is Groovy-aware (but also understands XML bean definitions). implementation class which is Groovy-aware (but also understands XML bean definitions).
The following example shows Groovy configuration: The following example shows Groovy configuration:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
ApplicationContext context = new GenericGroovyApplicationContext("services.groovy", "daos.groovy"); ApplicationContext context = new GenericGroovyApplicationContext("services.groovy", "daos.groovy");
---- ----
.Kotlin
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
---- ----
val context = GenericGroovyApplicationContext("services.groovy", "daos.groovy") val context = GenericGroovyApplicationContext("services.groovy", "daos.groovy")
---- ----
======
The most flexible variant is `GenericApplicationContext` in combination with reader The most flexible variant is `GenericApplicationContext` in combination with reader
delegates -- for example, with `XmlBeanDefinitionReader` for XML files, as the following delegates -- for example, with `XmlBeanDefinitionReader` for XML files, as the following
example shows: example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
GenericApplicationContext context = new GenericApplicationContext(); GenericApplicationContext context = new GenericApplicationContext();
new XmlBeanDefinitionReader(context).loadBeanDefinitions("services.xml", "daos.xml"); new XmlBeanDefinitionReader(context).loadBeanDefinitions("services.xml", "daos.xml");
context.refresh(); context.refresh();
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val context = GenericApplicationContext() val context = GenericApplicationContext()
XmlBeanDefinitionReader(context).loadBeanDefinitions("services.xml", "daos.xml") XmlBeanDefinitionReader(context).loadBeanDefinitions("services.xml", "daos.xml")
context.refresh() context.refresh()
---- ----
======
You can also use the `GroovyBeanDefinitionReader` for Groovy files, as the following You can also use the `GroovyBeanDefinitionReader` for Groovy files, as the following
example shows: example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
GenericApplicationContext context = new GenericApplicationContext(); GenericApplicationContext context = new GenericApplicationContext();
new GroovyBeanDefinitionReader(context).loadBeanDefinitions("services.groovy", "daos.groovy"); new GroovyBeanDefinitionReader(context).loadBeanDefinitions("services.groovy", "daos.groovy");
context.refresh(); context.refresh();
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val context = GenericApplicationContext() val context = GenericApplicationContext()
GroovyBeanDefinitionReader(context).loadBeanDefinitions("services.groovy", "daos.groovy") GroovyBeanDefinitionReader(context).loadBeanDefinitions("services.groovy", "daos.groovy")
context.refresh() context.refresh()
---- ----
======
You can mix and match such reader delegates on the same `ApplicationContext`, You can mix and match such reader delegates on the same `ApplicationContext`,
reading bean definitions from diverse configuration sources. reading bean definitions from diverse configuration sources.

View File

@ -86,8 +86,11 @@ The following table lists features provided by the `BeanFactory` and
To explicitly register a bean post-processor with a `DefaultListableBeanFactory`, To explicitly register a bean post-processor with a `DefaultListableBeanFactory`,
you need to programmatically call `addBeanPostProcessor`, as the following example shows: you need to programmatically call `addBeanPostProcessor`, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
// populate the factory with bean definitions // populate the factory with bean definitions
@ -98,8 +101,10 @@ you need to programmatically call `addBeanPostProcessor`, as the following examp
// now start using the factory // now start using the factory
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val factory = DefaultListableBeanFactory() val factory = DefaultListableBeanFactory()
// populate the factory with bean definitions // populate the factory with bean definitions
@ -110,12 +115,16 @@ you need to programmatically call `addBeanPostProcessor`, as the following examp
// now start using the factory // now start using the factory
---- ----
======
To apply a `BeanFactoryPostProcessor` to a plain `DefaultListableBeanFactory`, To apply a `BeanFactoryPostProcessor` to a plain `DefaultListableBeanFactory`,
you need to call its `postProcessBeanFactory` method, as the following example shows: you need to call its `postProcessBeanFactory` method, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
@ -128,8 +137,10 @@ you need to call its `postProcessBeanFactory` method, as the following example s
// now actually do the replacement // now actually do the replacement
cfg.postProcessBeanFactory(factory); cfg.postProcessBeanFactory(factory);
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val factory = DefaultListableBeanFactory() val factory = DefaultListableBeanFactory()
val reader = XmlBeanDefinitionReader(factory) val reader = XmlBeanDefinitionReader(factory)
@ -142,6 +153,7 @@ you need to call its `postProcessBeanFactory` method, as the following example s
// now actually do the replacement // now actually do the replacement
cfg.postProcessBeanFactory(factory) cfg.postProcessBeanFactory(factory)
---- ----
======
In both cases, the explicit registration steps are inconvenient, which is In both cases, the explicit registration steps are inconvenient, which is
why the various `ApplicationContext` variants are preferred over a plain why the various `ApplicationContext` variants are preferred over a plain

View File

@ -55,8 +55,11 @@ own code. A meta-annotation is an annotation that can be applied to another anno
For example, the `@Service` annotation mentioned xref:core/beans/classpath-scanning.adoc#beans-stereotype-annotations[earlier] For example, the `@Service` annotation mentioned xref:core/beans/classpath-scanning.adoc#beans-stereotype-annotations[earlier]
is meta-annotated with `@Component`, as the following example shows: is meta-annotated with `@Component`, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Target(ElementType.TYPE) @Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@ -67,6 +70,7 @@ is meta-annotated with `@Component`, as the following example shows:
// ... // ...
} }
---- ----
======
<1> The `@Component` causes `@Service` to be treated in the same way as `@Component`. <1> The `@Component` causes `@Service` to be treated in the same way as `@Component`.
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
@ -94,8 +98,11 @@ want to only expose a subset of the meta-annotation's attributes. For example, S
customization of the `proxyMode`. The following listing shows the definition of the customization of the `proxyMode`. The following listing shows the definition of the
`SessionScope` annotation: `SessionScope` annotation:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Target({ElementType.TYPE, ElementType.METHOD}) @Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@ -112,8 +119,10 @@ customization of the `proxyMode`. The following listing shows the definition of
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Target(AnnotationTarget.TYPE, AnnotationTarget.FUNCTION) @Target(AnnotationTarget.TYPE, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME) @Retention(AnnotationRetention.RUNTIME)
@ -124,11 +133,15 @@ customization of the `proxyMode`. The following listing shows the definition of
val proxyMode: ScopedProxyMode = ScopedProxyMode.TARGET_CLASS val proxyMode: ScopedProxyMode = ScopedProxyMode.TARGET_CLASS
) )
---- ----
======
You can then use `@SessionScope` without declaring the `proxyMode` as follows: You can then use `@SessionScope` without declaring the `proxyMode` as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Service @Service
@SessionScope @SessionScope
@ -136,8 +149,10 @@ You can then use `@SessionScope` without declaring the `proxyMode` as follows:
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Service @Service
@SessionScope @SessionScope
@ -145,11 +160,15 @@ You can then use `@SessionScope` without declaring the `proxyMode` as follows:
// ... // ...
} }
---- ----
======
You can also override the value for the `proxyMode`, as the following example shows: You can also override the value for the `proxyMode`, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Service @Service
@SessionScope(proxyMode = ScopedProxyMode.INTERFACES) @SessionScope(proxyMode = ScopedProxyMode.INTERFACES)
@ -157,8 +176,10 @@ You can also override the value for the `proxyMode`, as the following example sh
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Service @Service
@SessionScope(proxyMode = ScopedProxyMode.INTERFACES) @SessionScope(proxyMode = ScopedProxyMode.INTERFACES)
@ -166,6 +187,7 @@ You can also override the value for the `proxyMode`, as the following example sh
// ... // ...
} }
---- ----
======
For further details, see the For further details, see the
https://github.com/spring-projects/spring-framework/wiki/Spring-Annotation-Programming-Model[Spring Annotation Programming Model] https://github.com/spring-projects/spring-framework/wiki/Spring-Annotation-Programming-Model[Spring Annotation Programming Model]
@ -180,8 +202,11 @@ Spring can automatically detect stereotyped classes and register corresponding
`BeanDefinition` instances with the `ApplicationContext`. For example, the following two classes `BeanDefinition` instances with the `ApplicationContext`. For example, the following two classes
are eligible for such autodetection: are eligible for such autodetection:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Service @Service
public class SimpleMovieLister { public class SimpleMovieLister {
@ -193,29 +218,38 @@ are eligible for such autodetection:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Service @Service
class SimpleMovieLister(private val movieFinder: MovieFinder) class SimpleMovieLister(private val movieFinder: MovieFinder)
---- ----
======
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Repository @Repository
public class JpaMovieFinder implements MovieFinder { public class JpaMovieFinder implements MovieFinder {
// implementation elided for clarity // implementation elided for clarity
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Repository @Repository
class JpaMovieFinder : MovieFinder { class JpaMovieFinder : MovieFinder {
// implementation elided for clarity // implementation elided for clarity
} }
---- ----
======
To autodetect these classes and register the corresponding beans, you need to add To autodetect these classes and register the corresponding beans, you need to add
@ -223,8 +257,11 @@ To autodetect these classes and register the corresponding beans, you need to ad
is a common parent package for the two classes. (Alternatively, you can specify a is a common parent package for the two classes. (Alternatively, you can specify a
comma- or semicolon- or space-separated list that includes the parent package of each class.) comma- or semicolon- or space-separated list that includes the parent package of each class.)
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Configuration @Configuration
@ComponentScan(basePackages = "org.example") @ComponentScan(basePackages = "org.example")
@ -232,8 +269,10 @@ comma- or semicolon- or space-separated list that includes the parent package of
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Configuration @Configuration
@ComponentScan(basePackages = ["org.example"]) @ComponentScan(basePackages = ["org.example"])
@ -241,6 +280,7 @@ comma- or semicolon- or space-separated list that includes the parent package of
// ... // ...
} }
---- ----
======
NOTE: For brevity, the preceding example could have used the `value` attribute of the NOTE: For brevity, the preceding example could have used the `value` attribute of the
annotation (that is, `@ComponentScan("org.example")`). annotation (that is, `@ComponentScan("org.example")`).
@ -335,8 +375,11 @@ The following table describes the filtering options:
The following example shows the configuration ignoring all `@Repository` annotations The following example shows the configuration ignoring all `@Repository` annotations
and using "`stub`" repositories instead: and using "`stub`" repositories instead:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Configuration @Configuration
@ComponentScan(basePackages = "org.example", @ComponentScan(basePackages = "org.example",
@ -346,8 +389,10 @@ and using "`stub`" repositories instead:
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Configuration @Configuration
@ComponentScan(basePackages = ["org.example"], @ComponentScan(basePackages = ["org.example"],
@ -357,6 +402,7 @@ and using "`stub`" repositories instead:
// ... // ...
} }
---- ----
======
The following listing shows the equivalent XML: The following listing shows the equivalent XML:
@ -387,8 +433,11 @@ Spring components can also contribute bean definition metadata to the container.
this with the same `@Bean` annotation used to define bean metadata within `@Configuration` this with the same `@Bean` annotation used to define bean metadata within `@Configuration`
annotated classes. The following example shows how to do so: annotated classes. The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Component @Component
public class FactoryMethodComponent { public class FactoryMethodComponent {
@ -404,8 +453,10 @@ annotated classes. The following example shows how to do so:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Component @Component
class FactoryMethodComponent { class FactoryMethodComponent {
@ -419,6 +470,7 @@ annotated classes. The following example shows how to do so:
} }
} }
---- ----
======
The preceding class is a Spring component that has application-specific code in its The preceding class is a Spring component that has application-specific code in its
`doWork()` method. However, it also contributes a bean definition that has a factory `doWork()` method. However, it also contributes a bean definition that has a factory
@ -436,8 +488,11 @@ with optional dependencies, we recommend `ObjectProvider<MyTargetBean>` instead.
Autowired fields and methods are supported, as previously discussed, with additional Autowired fields and methods are supported, as previously discussed, with additional
support for autowiring of `@Bean` methods. The following example shows how to do so: support for autowiring of `@Bean` methods. The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Component @Component
public class FactoryMethodComponent { public class FactoryMethodComponent {
@ -473,8 +528,10 @@ support for autowiring of `@Bean` methods. The following example shows how to do
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Component @Component
class FactoryMethodComponent { class FactoryMethodComponent {
@ -504,6 +561,7 @@ support for autowiring of `@Bean` methods. The following example shows how to do
fun requestScopedInstance() = TestBean("requestScopedInstance", 3) fun requestScopedInstance() = TestBean("requestScopedInstance", 3)
} }
---- ----
======
The example autowires the `String` method parameter `country` to the value of the `age` The example autowires the `String` method parameter `country` to the value of the `age`
property on another bean named `privateInstance`. A Spring Expression Language element property on another bean named `privateInstance`. A Spring Expression Language element
@ -522,8 +580,11 @@ injection point that triggered the creation of a new bean instance in the given
You can use the provided injection point metadata with semantic care in such scenarios. You can use the provided injection point metadata with semantic care in such scenarios.
The following example shows how to use `InjectionPoint`: The following example shows how to use `InjectionPoint`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Component @Component
public class FactoryMethodComponent { public class FactoryMethodComponent {
@ -534,8 +595,10 @@ The following example shows how to use `InjectionPoint`:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Component @Component
class FactoryMethodComponent { class FactoryMethodComponent {
@ -546,6 +609,7 @@ The following example shows how to use `InjectionPoint`:
TestBean("prototypeInstance for ${injectionPoint.member}") TestBean("prototypeInstance for ${injectionPoint.member}")
} }
---- ----
======
The `@Bean` methods in a regular Spring component are processed differently than their The `@Bean` methods in a regular Spring component are processed differently than their
counterparts inside a Spring `@Configuration` class. The difference is that `@Component` counterparts inside a Spring `@Configuration` class. The difference is that `@Component`
@ -609,39 +673,51 @@ If such an annotation contains no name `value` or for any other detected compone
the uncapitalized non-qualified class name. For example, if the following component the uncapitalized non-qualified class name. For example, if the following component
classes were detected, the names would be `myMovieLister` and `movieFinderImpl`: classes were detected, the names would be `myMovieLister` and `movieFinderImpl`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Service("myMovieLister") @Service("myMovieLister")
public class SimpleMovieLister { public class SimpleMovieLister {
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Service("myMovieLister") @Service("myMovieLister")
class SimpleMovieLister { class SimpleMovieLister {
// ... // ...
} }
---- ----
======
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Repository @Repository
public class MovieFinderImpl implements MovieFinder { public class MovieFinderImpl implements MovieFinder {
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Repository @Repository
class MovieFinderImpl : MovieFinder { class MovieFinderImpl : MovieFinder {
// ... // ...
} }
---- ----
======
If you do not want to rely on the default bean-naming strategy, you can provide a custom If you do not want to rely on the default bean-naming strategy, you can provide a custom
bean-naming strategy. First, implement the bean-naming strategy. First, implement the
@ -657,8 +733,11 @@ fully qualified class name for the generated bean name. As of Spring Framework 5
`FullyQualifiedAnnotationBeanNameGenerator` located in package `FullyQualifiedAnnotationBeanNameGenerator` located in package
`org.springframework.context.annotation` can be used for such purposes. `org.springframework.context.annotation` can be used for such purposes.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Configuration @Configuration
@ComponentScan(basePackages = "org.example", nameGenerator = MyNameGenerator.class) @ComponentScan(basePackages = "org.example", nameGenerator = MyNameGenerator.class)
@ -666,8 +745,10 @@ fully qualified class name for the generated bean name. As of Spring Framework 5
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Configuration @Configuration
@ComponentScan(basePackages = ["org.example"], nameGenerator = MyNameGenerator::class) @ComponentScan(basePackages = ["org.example"], nameGenerator = MyNameGenerator::class)
@ -675,6 +756,7 @@ fully qualified class name for the generated bean name. As of Spring Framework 5
// ... // ...
} }
---- ----
======
[source,xml,indent=0,subs="verbatim,quotes"] [source,xml,indent=0,subs="verbatim,quotes"]
---- ----
@ -698,8 +780,11 @@ autodetected components is `singleton`. However, sometimes you need a different
that can be specified by the `@Scope` annotation. You can provide the name of the that can be specified by the `@Scope` annotation. You can provide the name of the
scope within the annotation, as the following example shows: scope within the annotation, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Scope("prototype") @Scope("prototype")
@Repository @Repository
@ -707,8 +792,10 @@ scope within the annotation, as the following example shows:
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Scope("prototype") @Scope("prototype")
@Repository @Repository
@ -716,6 +803,7 @@ scope within the annotation, as the following example shows:
// ... // ...
} }
---- ----
======
NOTE: `@Scope` annotations are only introspected on the concrete bean class (for annotated NOTE: `@Scope` annotations are only introspected on the concrete bean class (for annotated
components) or the factory method (for `@Bean` methods). In contrast to XML bean components) or the factory method (for `@Bean` methods). In contrast to XML bean
@ -735,8 +823,11 @@ interface. Be sure to include a default no-arg constructor. Then you can provide
fully qualified class name when configuring the scanner, as the following example of both fully qualified class name when configuring the scanner, as the following example of both
an annotation and a bean definition shows: an annotation and a bean definition shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Configuration @Configuration
@ComponentScan(basePackages = "org.example", scopeResolver = MyScopeResolver.class) @ComponentScan(basePackages = "org.example", scopeResolver = MyScopeResolver.class)
@ -744,8 +835,10 @@ an annotation and a bean definition shows:
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Configuration @Configuration
@ComponentScan(basePackages = ["org.example"], scopeResolver = MyScopeResolver::class) @ComponentScan(basePackages = ["org.example"], scopeResolver = MyScopeResolver::class)
@ -753,6 +846,7 @@ an annotation and a bean definition shows:
// ... // ...
} }
---- ----
======
[source,xml,indent=0,subs="verbatim,quotes"] [source,xml,indent=0,subs="verbatim,quotes"]
---- ----
@ -767,8 +861,11 @@ For this purpose, a scoped-proxy attribute is available on the component-scan
element. The three possible values are: `no`, `interfaces`, and `targetClass`. For example, element. The three possible values are: `no`, `interfaces`, and `targetClass`. For example,
the following configuration results in standard JDK dynamic proxies: the following configuration results in standard JDK dynamic proxies:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Configuration @Configuration
@ComponentScan(basePackages = "org.example", scopedProxy = ScopedProxyMode.INTERFACES) @ComponentScan(basePackages = "org.example", scopedProxy = ScopedProxyMode.INTERFACES)
@ -776,8 +873,10 @@ the following configuration results in standard JDK dynamic proxies:
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Configuration @Configuration
@ComponentScan(basePackages = ["org.example"], scopedProxy = ScopedProxyMode.INTERFACES) @ComponentScan(basePackages = ["org.example"], scopedProxy = ScopedProxyMode.INTERFACES)
@ -785,6 +884,7 @@ the following configuration results in standard JDK dynamic proxies:
// ... // ...
} }
---- ----
======
[source,xml,indent=0,subs="verbatim,quotes"] [source,xml,indent=0,subs="verbatim,quotes"]
---- ----
@ -808,8 +908,11 @@ auto-detection of components, you can provide the qualifier metadata with type-l
annotations on the candidate class. The following three examples demonstrate this annotations on the candidate class. The following three examples demonstrate this
technique: technique:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Component @Component
@Qualifier("Action") @Qualifier("Action")
@ -817,16 +920,22 @@ technique:
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Component @Component
@Qualifier("Action") @Qualifier("Action")
class ActionMovieCatalog : MovieCatalog class ActionMovieCatalog : MovieCatalog
---- ----
======
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Component @Component
@Genre("Action") @Genre("Action")
@ -834,8 +943,10 @@ technique:
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Component @Component
@Genre("Action") @Genre("Action")
@ -843,9 +954,13 @@ technique:
// ... // ...
} }
---- ----
======
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Component @Component
@Offline @Offline
@ -853,8 +968,10 @@ technique:
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Component @Component
@Offline @Offline
@ -862,6 +979,7 @@ class CachingMovieCatalog : MovieCatalog {
// ... // ...
} }
---- ----
======
NOTE: As with most annotation-based alternatives, keep in mind that the annotation metadata is NOTE: As with most annotation-based alternatives, keep in mind that the annotation metadata is
bound to the class definition itself, while the use of XML allows for multiple beans bound to the class definition itself, while the use of XML allows for multiple beans

View File

@ -98,8 +98,11 @@ The next example shows a program to run the `MessageSource` functionality.
Remember that all `ApplicationContext` implementations are also `MessageSource` Remember that all `ApplicationContext` implementations are also `MessageSource`
implementations and so can be cast to the `MessageSource` interface. implementations and so can be cast to the `MessageSource` interface.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public static void main(String[] args) { public static void main(String[] args) {
MessageSource resources = new ClassPathXmlApplicationContext("beans.xml"); MessageSource resources = new ClassPathXmlApplicationContext("beans.xml");
@ -107,8 +110,10 @@ implementations and so can be cast to the `MessageSource` interface.
System.out.println(message); System.out.println(message);
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
fun main() { fun main() {
val resources = ClassPathXmlApplicationContext("beans.xml") val resources = ClassPathXmlApplicationContext("beans.xml")
@ -116,6 +121,7 @@ implementations and so can be cast to the `MessageSource` interface.
println(message) println(message)
} }
---- ----
======
The resulting output from the above program is as follows: The resulting output from the above program is as follows:
@ -151,8 +157,11 @@ converted into `String` objects and inserted into placeholders in the lookup mes
</beans> </beans>
---- ----
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class Example { public class Example {
@ -169,8 +178,10 @@ converted into `String` objects and inserted into placeholders in the lookup mes
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class Example { class Example {
@ -183,6 +194,7 @@ converted into `String` objects and inserted into placeholders in the lookup mes
} }
} }
---- ----
======
The resulting output from the invocation of the `execute()` method is as follows: The resulting output from the invocation of the `execute()` method is as follows:
@ -208,8 +220,11 @@ resolved is specified manually:
argument.required=Ebagum lad, the ''{0}'' argument is required, I say, required. argument.required=Ebagum lad, the ''{0}'' argument is required, I say, required.
---- ----
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public static void main(final String[] args) { public static void main(final String[] args) {
MessageSource resources = new ClassPathXmlApplicationContext("beans.xml"); MessageSource resources = new ClassPathXmlApplicationContext("beans.xml");
@ -218,8 +233,10 @@ argument.required=Ebagum lad, the ''{0}'' argument is required, I say, required.
System.out.println(message); System.out.println(message);
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
fun main() { fun main() {
val resources = ClassPathXmlApplicationContext("beans.xml") val resources = ClassPathXmlApplicationContext("beans.xml")
@ -228,6 +245,7 @@ argument.required=Ebagum lad, the ''{0}'' argument is required, I say, required.
println(message) println(message)
} }
---- ----
======
The resulting output from the running of the above program is as follows: The resulting output from the running of the above program is as follows:
@ -322,8 +340,11 @@ The following table describes the standard events that Spring provides:
You can also create and publish your own custom events. The following example shows a You can also create and publish your own custom events. The following example shows a
simple class that extends Spring's `ApplicationEvent` base class: simple class that extends Spring's `ApplicationEvent` base class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class BlockedListEvent extends ApplicationEvent { public class BlockedListEvent extends ApplicationEvent {
@ -339,21 +360,27 @@ simple class that extends Spring's `ApplicationEvent` base class:
// accessor and other methods... // accessor and other methods...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class BlockedListEvent(source: Any, class BlockedListEvent(source: Any,
val address: String, val address: String,
val content: String) : ApplicationEvent(source) val content: String) : ApplicationEvent(source)
---- ----
======
To publish a custom `ApplicationEvent`, call the `publishEvent()` method on an To publish a custom `ApplicationEvent`, call the `publishEvent()` method on an
`ApplicationEventPublisher`. Typically, this is done by creating a class that implements `ApplicationEventPublisher`. Typically, this is done by creating a class that implements
`ApplicationEventPublisherAware` and registering it as a Spring bean. The following `ApplicationEventPublisherAware` and registering it as a Spring bean. The following
example shows such a class: example shows such a class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class EmailService implements ApplicationEventPublisherAware { public class EmailService implements ApplicationEventPublisherAware {
@ -377,8 +404,10 @@ example shows such a class:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class EmailService : ApplicationEventPublisherAware { class EmailService : ApplicationEventPublisherAware {
@ -402,6 +431,7 @@ example shows such a class:
} }
} }
---- ----
======
At configuration time, the Spring container detects that `EmailService` implements At configuration time, the Spring container detects that `EmailService` implements
`ApplicationEventPublisherAware` and automatically calls `ApplicationEventPublisherAware` and automatically calls
@ -413,8 +443,11 @@ To receive the custom `ApplicationEvent`, you can create a class that implements
`ApplicationListener` and register it as a Spring bean. The following example `ApplicationListener` and register it as a Spring bean. The following example
shows such a class: shows such a class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class BlockedListNotifier implements ApplicationListener<BlockedListEvent> { public class BlockedListNotifier implements ApplicationListener<BlockedListEvent> {
@ -429,8 +462,10 @@ shows such a class:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class BlockedListNotifier : ApplicationListener<BlockedListEvent> { class BlockedListNotifier : ApplicationListener<BlockedListEvent> {
@ -441,6 +476,7 @@ shows such a class:
} }
} }
---- ----
======
Notice that `ApplicationListener` is generically parameterized with the type of your Notice that `ApplicationListener` is generically parameterized with the type of your
custom event (`BlockedListEvent` in the preceding example). This means that the custom event (`BlockedListEvent` in the preceding example). This means that the
@ -497,8 +533,11 @@ architectures that build upon the well-known Spring programming model.
You can register an event listener on any method of a managed bean by using the You can register an event listener on any method of a managed bean by using the
`@EventListener` annotation. The `BlockedListNotifier` can be rewritten as follows: `@EventListener` annotation. The `BlockedListNotifier` can be rewritten as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class BlockedListNotifier { public class BlockedListNotifier {
@ -514,8 +553,10 @@ You can register an event listener on any method of a managed bean by using the
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class BlockedListNotifier { class BlockedListNotifier {
@ -527,6 +568,7 @@ You can register an event listener on any method of a managed bean by using the
} }
} }
---- ----
======
The method signature once again declares the event type to which it listens, The method signature once again declares the event type to which it listens,
but, this time, with a flexible name and without implementing a specific listener interface. but, this time, with a flexible name and without implementing a specific listener interface.
@ -537,22 +579,28 @@ If your method should listen to several events or if you want to define it with
parameter at all, the event types can also be specified on the annotation itself. The parameter at all, the event types can also be specified on the annotation itself. The
following example shows how to do so: following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class}) @EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class})
public void handleContextStart() { public void handleContextStart() {
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@EventListener(ContextStartedEvent::class, ContextRefreshedEvent::class) @EventListener(ContextStartedEvent::class, ContextRefreshedEvent::class)
fun handleContextStart() { fun handleContextStart() {
// ... // ...
} }
---- ----
======
It is also possible to add additional runtime filtering by using the `condition` attribute It is also possible to add additional runtime filtering by using the `condition` attribute
of the annotation that defines a xref:core/expressions.adoc[`SpEL` expression], which should match of the annotation that defines a xref:core/expressions.adoc[`SpEL` expression], which should match
@ -561,22 +609,28 @@ to actually invoke the method for a particular event.
The following example shows how our notifier can be rewritten to be invoked only if the The following example shows how our notifier can be rewritten to be invoked only if the
`content` attribute of the event is equal to `my-event`: `content` attribute of the event is equal to `my-event`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@EventListener(condition = "#blEvent.content == 'my-event'") @EventListener(condition = "#blEvent.content == 'my-event'")
public void processBlockedListEvent(BlockedListEvent blEvent) { public void processBlockedListEvent(BlockedListEvent blEvent) {
// notify appropriate parties via notificationAddress... // notify appropriate parties via notificationAddress...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@EventListener(condition = "#blEvent.content == 'my-event'") @EventListener(condition = "#blEvent.content == 'my-event'")
fun processBlockedListEvent(blEvent: BlockedListEvent) { fun processBlockedListEvent(blEvent: BlockedListEvent) {
// notify appropriate parties via notificationAddress... // notify appropriate parties via notificationAddress...
} }
---- ----
======
Each `SpEL` expression evaluates against a dedicated context. The following table lists the Each `SpEL` expression evaluates against a dedicated context. The following table lists the
items made available to the context so that you can use them for conditional event processing: items made available to the context so that you can use them for conditional event processing:
@ -611,8 +665,11 @@ signature actually refers to an arbitrary object that was published.
If you need to publish an event as the result of processing another event, you can change the If you need to publish an event as the result of processing another event, you can change the
method signature to return the event that should be published, as the following example shows: method signature to return the event that should be published, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@EventListener @EventListener
public ListUpdateEvent handleBlockedListEvent(BlockedListEvent event) { public ListUpdateEvent handleBlockedListEvent(BlockedListEvent event) {
@ -620,8 +677,10 @@ method signature to return the event that should be published, as the following
// then publish a ListUpdateEvent... // then publish a ListUpdateEvent...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@EventListener @EventListener
fun handleBlockedListEvent(event: BlockedListEvent): ListUpdateEvent { fun handleBlockedListEvent(event: BlockedListEvent): ListUpdateEvent {
@ -629,6 +688,7 @@ method signature to return the event that should be published, as the following
// then publish a ListUpdateEvent... // then publish a ListUpdateEvent...
} }
---- ----
======
NOTE: This feature is not supported for NOTE: This feature is not supported for
xref:core/beans/context-introduction.adoc#context-functionality-events-async[asynchronous listeners]. xref:core/beans/context-introduction.adoc#context-functionality-events-async[asynchronous listeners].
@ -645,8 +705,11 @@ If you want a particular listener to process events asynchronously, you can reus
xref:integration/scheduling.adoc#scheduling-annotation-support-async[regular `@Async` support]. xref:integration/scheduling.adoc#scheduling-annotation-support-async[regular `@Async` support].
The following example shows how to do so: The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@EventListener @EventListener
@Async @Async
@ -654,8 +717,10 @@ The following example shows how to do so:
// BlockedListEvent is processed in a separate thread // BlockedListEvent is processed in a separate thread
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@EventListener @EventListener
@Async @Async
@ -663,6 +728,7 @@ The following example shows how to do so:
// BlockedListEvent is processed in a separate thread // BlockedListEvent is processed in a separate thread
} }
---- ----
======
Be aware of the following limitations when using asynchronous events: Be aware of the following limitations when using asynchronous events:
@ -682,8 +748,11 @@ Be aware of the following limitations when using asynchronous events:
If you need one listener to be invoked before another one, you can add the `@Order` If you need one listener to be invoked before another one, you can add the `@Order`
annotation to the method declaration, as the following example shows: annotation to the method declaration, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@EventListener @EventListener
@Order(42) @Order(42)
@ -691,8 +760,10 @@ annotation to the method declaration, as the following example shows:
// notify appropriate parties via notificationAddress... // notify appropriate parties via notificationAddress...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@EventListener @EventListener
@Order(42) @Order(42)
@ -700,6 +771,7 @@ annotation to the method declaration, as the following example shows:
// notify appropriate parties via notificationAddress... // notify appropriate parties via notificationAddress...
} }
---- ----
======
[[context-functionality-events-generics]] [[context-functionality-events-generics]]
@ -710,22 +782,28 @@ You can also use generics to further define the structure of your event. Conside
can create the following listener definition to receive only `EntityCreatedEvent` for a can create the following listener definition to receive only `EntityCreatedEvent` for a
`Person`: `Person`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@EventListener @EventListener
public void onPersonCreated(EntityCreatedEvent<Person> event) { public void onPersonCreated(EntityCreatedEvent<Person> event) {
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@EventListener @EventListener
fun onPersonCreated(event: EntityCreatedEvent<Person>) { fun onPersonCreated(event: EntityCreatedEvent<Person>) {
// ... // ...
} }
---- ----
======
Due to type erasure, this works only if the event that is fired resolves the generic Due to type erasure, this works only if the event that is fired resolves the generic
parameters on which the event listener filters (that is, something like parameters on which the event listener filters (that is, something like
@ -736,8 +814,11 @@ structure (as should be the case for the event in the preceding example). In suc
you can implement `ResolvableTypeProvider` to guide the framework beyond what the runtime you can implement `ResolvableTypeProvider` to guide the framework beyond what the runtime
environment provides. The following event shows how to do so: environment provides. The following event shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class EntityCreatedEvent<T> extends ApplicationEvent implements ResolvableTypeProvider { public class EntityCreatedEvent<T> extends ApplicationEvent implements ResolvableTypeProvider {
@ -751,8 +832,10 @@ environment provides. The following event shows how to do so:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class EntityCreatedEvent<T>(entity: T) : ApplicationEvent(entity), ResolvableTypeProvider { class EntityCreatedEvent<T>(entity: T) : ApplicationEvent(entity), ResolvableTypeProvider {
@ -761,6 +844,7 @@ environment provides. The following event shows how to do so:
} }
} }
---- ----
======
TIP: This works not only for `ApplicationEvent` but any arbitrary object that you send as TIP: This works not only for `ApplicationEvent` but any arbitrary object that you send as
an event. an event.
@ -819,8 +903,11 @@ The `AbstractApplicationContext` (and its subclasses) is instrumented with an
Here is an example of instrumentation in the `AnnotationConfigApplicationContext`: Here is an example of instrumentation in the `AnnotationConfigApplicationContext`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
// create a startup step and start recording // create a startup step and start recording
StartupStep scanPackages = this.getApplicationStartup().start("spring.context.base-packages.scan"); StartupStep scanPackages = this.getApplicationStartup().start("spring.context.base-packages.scan");
@ -831,8 +918,10 @@ Here is an example of instrumentation in the `AnnotationConfigApplicationContext
// end the current step // end the current step
scanPackages.end(); scanPackages.end();
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
// create a startup step and start recording // create a startup step and start recording
val scanPackages = this.getApplicationStartup().start("spring.context.base-packages.scan") val scanPackages = this.getApplicationStartup().start("spring.context.base-packages.scan")
@ -843,6 +932,7 @@ Here is an example of instrumentation in the `AnnotationConfigApplicationContext
// end the current step // end the current step
scanPackages.end() scanPackages.end()
---- ----
======
The application context is already instrumented with multiple steps. The application context is already instrumented with multiple steps.
Once recorded, these startup steps can be collected, displayed and analyzed with specific tools. Once recorded, these startup steps can be collected, displayed and analyzed with specific tools.

View File

@ -7,21 +7,27 @@ loaded into the Java virtual machine (JVM).
To enable load-time weaving, you can add the `@EnableLoadTimeWeaving` to one of your To enable load-time weaving, you can add the `@EnableLoadTimeWeaving` to one of your
`@Configuration` classes, as the following example shows: `@Configuration` classes, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Configuration @Configuration
@EnableLoadTimeWeaving @EnableLoadTimeWeaving
public class AppConfig { public class AppConfig {
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Configuration @Configuration
@EnableLoadTimeWeaving @EnableLoadTimeWeaving
class AppConfig class AppConfig
---- ----
======
Alternatively, for XML configuration, you can use the `context:load-time-weaver` element: Alternatively, for XML configuration, you can use the `context:load-time-weaver` element:

View File

@ -260,8 +260,11 @@ specify a factory method:
The following example shows a class that would work with the preceding bean definition: The following example shows a class that would work with the preceding bean definition:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class ClientService { public class ClientService {
private static ClientService clientService = new ClientService(); private static ClientService clientService = new ClientService();
@ -272,8 +275,10 @@ The following example shows a class that would work with the preceding bean defi
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class ClientService private constructor() { class ClientService private constructor() {
companion object { companion object {
@ -283,6 +288,7 @@ The following example shows a class that would work with the preceding bean defi
} }
} }
---- ----
======
For details about the mechanism for supplying (optional) arguments to the factory method For details about the mechanism for supplying (optional) arguments to the factory method
and setting object instance properties after the object is returned from the factory, and setting object instance properties after the object is returned from the factory,
@ -316,8 +322,11 @@ how to configure such a bean:
The following example shows the corresponding class: The following example shows the corresponding class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class DefaultServiceLocator { public class DefaultServiceLocator {
@ -328,8 +337,10 @@ The following example shows the corresponding class:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class DefaultServiceLocator { class DefaultServiceLocator {
companion object { companion object {
@ -340,6 +351,7 @@ The following example shows the corresponding class:
} }
} }
---- ----
======
One factory class can also hold more than one factory method, as the following example shows: One factory class can also hold more than one factory method, as the following example shows:
@ -360,8 +372,11 @@ One factory class can also hold more than one factory method, as the following e
The following example shows the corresponding class: The following example shows the corresponding class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class DefaultServiceLocator { public class DefaultServiceLocator {
@ -378,8 +393,10 @@ The following example shows the corresponding class:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class DefaultServiceLocator { class DefaultServiceLocator {
companion object { companion object {
@ -396,6 +413,7 @@ The following example shows the corresponding class:
} }
} }
---- ----
======
This approach shows that the factory bean itself can be managed and configured through This approach shows that the factory bean itself can be managed and configured through
dependency injection (DI). See xref:core/beans/dependencies/factory-properties-detailed.adoc[Dependencies and Configuration in Detail] dependency injection (DI). See xref:core/beans/dependencies/factory-properties-detailed.adoc[Dependencies and Configuration in Detail]

View File

@ -30,8 +30,11 @@ treats arguments to a constructor and to a `static` factory method similarly. Th
following example shows a class that can only be dependency-injected with constructor following example shows a class that can only be dependency-injected with constructor
injection: injection:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class SimpleMovieLister { public class SimpleMovieLister {
@ -46,14 +49,17 @@ injection:
// business logic that actually uses the injected MovieFinder is omitted... // business logic that actually uses the injected MovieFinder is omitted...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
// a constructor so that the Spring container can inject a MovieFinder // a constructor so that the Spring container can inject a MovieFinder
class SimpleMovieLister(private val movieFinder: MovieFinder) { class SimpleMovieLister(private val movieFinder: MovieFinder) {
// business logic that actually uses the injected MovieFinder is omitted... // business logic that actually uses the injected MovieFinder is omitted...
} }
---- ----
======
Notice that there is nothing special about this class. It is a POJO that Notice that there is nothing special about this class. It is a POJO that
has no dependencies on container specific interfaces, base classes, or annotations. has no dependencies on container specific interfaces, base classes, or annotations.
@ -67,8 +73,11 @@ order in which the constructor arguments are defined in a bean definition is the
in which those arguments are supplied to the appropriate constructor when the bean is in which those arguments are supplied to the appropriate constructor when the bean is
being instantiated. Consider the following class: being instantiated. Consider the following class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"] [source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
---- ----
package x.y; package x.y;
@ -79,13 +88,16 @@ being instantiated. Consider the following class:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
---- ----
package x.y package x.y
class ThingOne(thingTwo: ThingTwo, thingThree: ThingThree) class ThingOne(thingTwo: ThingTwo, thingThree: ThingThree)
---- ----
======
Assuming that the `ThingTwo` and `ThingThree` classes are not related by inheritance, no Assuming that the `ThingTwo` and `ThingThree` classes are not related by inheritance, no
potential ambiguity exists. Thus, the following configuration works fine, and you do not potential ambiguity exists. Thus, the following configuration works fine, and you do not
@ -111,8 +123,11 @@ case with the preceding example). When a simple type is used, such as
`<value>true</value>`, Spring cannot determine the type of the value, and so cannot match `<value>true</value>`, Spring cannot determine the type of the value, and so cannot match
by type without help. Consider the following class: by type without help. Consider the following class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"] [source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
---- ----
package examples; package examples;
@ -130,8 +145,10 @@ by type without help. Consider the following class:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
---- ----
package examples package examples
@ -140,6 +157,7 @@ by type without help. Consider the following class:
private val ultimateAnswer: String // The Answer to Life, the Universe, and Everything private val ultimateAnswer: String // The Answer to Life, the Universe, and Everything
) )
---- ----
======
.[[beans-factory-ctor-arguments-type]]Constructor argument type matching .[[beans-factory-ctor-arguments-type]]Constructor argument type matching
-- --
@ -195,8 +213,11 @@ https://download.oracle.com/javase/8/docs/api/java/beans/ConstructorProperties.h
JDK annotation to explicitly name your constructor arguments. The sample class would JDK annotation to explicitly name your constructor arguments. The sample class would
then have to look as follows: then have to look as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"] [source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
---- ----
package examples; package examples;
@ -211,8 +232,10 @@ then have to look as follows:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
---- ----
package examples package examples
@ -220,6 +243,7 @@ then have to look as follows:
@ConstructorProperties("years", "ultimateAnswer") @ConstructorProperties("years", "ultimateAnswer")
constructor(val years: Int, val ultimateAnswer: String) constructor(val years: Int, val ultimateAnswer: String)
---- ----
======
-- --
@ -234,8 +258,11 @@ The following example shows a class that can only be dependency-injected by usin
setter injection. This class is conventional Java. It is a POJO that has no dependencies setter injection. This class is conventional Java. It is a POJO that has no dependencies
on container specific interfaces, base classes, or annotations. on container specific interfaces, base classes, or annotations.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class SimpleMovieLister { public class SimpleMovieLister {
@ -250,8 +277,10 @@ on container specific interfaces, base classes, or annotations.
// business logic that actually uses the injected MovieFinder is omitted... // business logic that actually uses the injected MovieFinder is omitted...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class SimpleMovieLister { class SimpleMovieLister {
@ -261,6 +290,7 @@ class SimpleMovieLister {
// business logic that actually uses the injected MovieFinder is omitted... // business logic that actually uses the injected MovieFinder is omitted...
} }
---- ----
======
The `ApplicationContext` supports constructor-based and setter-based DI for the beans it The `ApplicationContext` supports constructor-based and setter-based DI for the beans it
@ -403,8 +433,11 @@ part of a Spring XML configuration file specifies some bean definitions as follo
The following example shows the corresponding `ExampleBean` class: The following example shows the corresponding `ExampleBean` class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class ExampleBean { public class ExampleBean {
@ -427,8 +460,10 @@ The following example shows the corresponding `ExampleBean` class:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class ExampleBean { class ExampleBean {
lateinit var beanOne: AnotherBean lateinit var beanOne: AnotherBean
@ -436,6 +471,7 @@ class ExampleBean {
var i: Int = 0 var i: Int = 0
} }
---- ----
======
In the preceding example, setters are declared to match against the properties specified In the preceding example, setters are declared to match against the properties specified
in the XML file. The following example uses constructor-based DI: in the XML file. The following example uses constructor-based DI:
@ -460,8 +496,11 @@ in the XML file. The following example uses constructor-based DI:
The following example shows the corresponding `ExampleBean` class: The following example shows the corresponding `ExampleBean` class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class ExampleBean { public class ExampleBean {
@ -479,14 +518,17 @@ The following example shows the corresponding `ExampleBean` class:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class ExampleBean( class ExampleBean(
private val beanOne: AnotherBean, private val beanOne: AnotherBean,
private val beanTwo: YetAnotherBean, private val beanTwo: YetAnotherBean,
private val i: Int) private val i: Int)
---- ----
======
The constructor arguments specified in the bean definition are used as arguments to The constructor arguments specified in the bean definition are used as arguments to
the constructor of the `ExampleBean`. the constructor of the `ExampleBean`.
@ -508,8 +550,11 @@ told to call a `static` factory method to return an instance of the object:
The following example shows the corresponding `ExampleBean` class: The following example shows the corresponding `ExampleBean` class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class ExampleBean { public class ExampleBean {
@ -530,8 +575,10 @@ The following example shows the corresponding `ExampleBean` class:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class ExampleBean private constructor() { class ExampleBean private constructor() {
companion object { companion object {
@ -547,6 +594,7 @@ The following example shows the corresponding `ExampleBean` class:
} }
} }
---- ----
======
Arguments to the `static` factory method are supplied by `<constructor-arg/>` elements, Arguments to the `static` factory method are supplied by `<constructor-arg/>` elements,
exactly the same as if a constructor had actually been used. The type of the class being exactly the same as if a constructor had actually been used. The type of the class being

View File

@ -17,8 +17,11 @@ and by xref:core/beans/basics.adoc#beans-factory-client[making a `getBean("B")`
typically new) bean B instance every time bean A needs it. The following example typically new) bean B instance every time bean A needs it. The following example
shows this approach: shows this approach:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages",fold="none"] [source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages",fold="none"]
.Java
---- ----
package fiona.apple; package fiona.apple;
@ -54,8 +57,10 @@ shows this approach:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages",fold="none"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages",fold="none"]
.Kotlin
---- ----
package fiona.apple package fiona.apple
@ -86,6 +91,7 @@ shows this approach:
} }
} }
---- ----
======
The preceding is not desirable, because the business code is aware of and coupled to the The preceding is not desirable, because the business code is aware of and coupled to the
Spring Framework. Method Injection, a somewhat advanced feature of the Spring IoC Spring Framework. Method Injection, a somewhat advanced feature of the Spring IoC
@ -127,8 +133,11 @@ Spring container dynamically overrides the implementation of the `createCommand(
method. The `CommandManager` class does not have any Spring dependencies, as method. The `CommandManager` class does not have any Spring dependencies, as
the reworked example shows: the reworked example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages",fold="none"] [source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages",fold="none"]
.Java
---- ----
package fiona.apple; package fiona.apple;
@ -148,8 +157,10 @@ the reworked example shows:
protected abstract Command createCommand(); protected abstract Command createCommand();
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages",fold="none"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages",fold="none"]
.Kotlin
---- ----
package fiona.apple package fiona.apple
@ -169,6 +180,7 @@ the reworked example shows:
protected abstract fun createCommand(): Command protected abstract fun createCommand(): Command
} }
---- ----
======
In the client class that contains the method to be injected (the `CommandManager` in this In the client class that contains the method to be injected (the `CommandManager` in this
case), the method to be injected requires a signature of the following form: case), the method to be injected requires a signature of the following form:
@ -204,8 +216,11 @@ bean is returned each time.
Alternatively, within the annotation-based component model, you can declare a lookup Alternatively, within the annotation-based component model, you can declare a lookup
method through the `@Lookup` annotation, as the following example shows: method through the `@Lookup` annotation, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public abstract class CommandManager { public abstract class CommandManager {
@ -219,8 +234,10 @@ method through the `@Lookup` annotation, as the following example shows:
protected abstract Command createCommand(); protected abstract Command createCommand();
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
abstract class CommandManager { abstract class CommandManager {
@ -234,12 +251,16 @@ method through the `@Lookup` annotation, as the following example shows:
protected abstract fun createCommand(): Command protected abstract fun createCommand(): Command
} }
---- ----
======
Or, more idiomatically, you can rely on the target bean getting resolved against the Or, more idiomatically, you can rely on the target bean getting resolved against the
declared return type of the lookup method: declared return type of the lookup method:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public abstract class CommandManager { public abstract class CommandManager {
@ -253,8 +274,10 @@ declared return type of the lookup method:
protected abstract Command createCommand(); protected abstract Command createCommand();
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
abstract class CommandManager { abstract class CommandManager {
@ -268,6 +291,7 @@ declared return type of the lookup method:
protected abstract fun createCommand(): Command protected abstract fun createCommand(): Command
} }
---- ----
======
Note that you should typically declare such annotated lookup methods with a concrete Note that you should typically declare such annotated lookup methods with a concrete
stub implementation, in order for them to be compatible with Spring's component stub implementation, in order for them to be compatible with Spring's component
@ -296,8 +320,11 @@ With XML-based configuration metadata, you can use the `replaced-method` element
replace an existing method implementation with another, for a deployed bean. Consider replace an existing method implementation with another, for a deployed bean. Consider
the following class, which has a method called `computeValue` that we want to override: the following class, which has a method called `computeValue` that we want to override:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class MyValueCalculator { public class MyValueCalculator {
@ -308,8 +335,10 @@ the following class, which has a method called `computeValue` that we want to ov
// some other methods... // some other methods...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class MyValueCalculator { class MyValueCalculator {
@ -320,12 +349,16 @@ the following class, which has a method called `computeValue` that we want to ov
// some other methods... // some other methods...
} }
---- ----
======
A class that implements the `org.springframework.beans.factory.support.MethodReplacer` A class that implements the `org.springframework.beans.factory.support.MethodReplacer`
interface provides the new method definition, as the following example shows: interface provides the new method definition, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
/** /**
* meant to be used to override the existing computeValue(String) * meant to be used to override the existing computeValue(String)
@ -341,8 +374,10 @@ interface provides the new method definition, as the following example shows:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
/** /**
* meant to be used to override the existing computeValue(String) * meant to be used to override the existing computeValue(String)
@ -358,6 +393,7 @@ interface provides the new method definition, as the following example shows:
} }
} }
---- ----
======

View File

@ -350,8 +350,11 @@ type-conversion support such that the elements of your strongly-typed `Collectio
instances are converted to the appropriate type prior to being added to the `Collection`. instances are converted to the appropriate type prior to being added to the `Collection`.
The following Java class and bean definition show how to do so: The following Java class and bean definition show how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class SomeClass { public class SomeClass {
@ -362,13 +365,16 @@ The following Java class and bean definition show how to do so:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class SomeClass { class SomeClass {
lateinit var accounts: Map<String, Float> lateinit var accounts: Map<String, Float>
} }
---- ----
======
[source,xml,indent=0,subs="verbatim,quotes"] [source,xml,indent=0,subs="verbatim,quotes"]
---- ----
@ -408,16 +414,22 @@ following XML-based configuration metadata snippet sets the `email` property to
The preceding example is equivalent to the following Java code: The preceding example is equivalent to the following Java code:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
exampleBean.setEmail(""); exampleBean.setEmail("");
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
exampleBean.email = "" exampleBean.email = ""
---- ----
======
The `<null/>` element handles `null` values. The following listing shows an example: The `<null/>` element handles `null` values. The following listing shows an example:
@ -433,16 +445,22 @@ The `<null/>` element handles `null` values. The following listing shows an exam
The preceding configuration is equivalent to the following Java code: The preceding configuration is equivalent to the following Java code:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
exampleBean.setEmail(null); exampleBean.setEmail(null);
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
exampleBean.email = null exampleBean.email = null
---- ----
======
[[beans-p-namespace]] [[beans-p-namespace]]

View File

@ -39,8 +39,11 @@ B deployments.
Consider the first use case in a practical application that requires a Consider the first use case in a practical application that requires a
`DataSource`. In a test environment, the configuration might resemble the following: `DataSource`. In a test environment, the configuration might resemble the following:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Bean @Bean
public DataSource dataSource() { public DataSource dataSource() {
@ -51,8 +54,10 @@ Consider the first use case in a practical application that requires a
.build(); .build();
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Bean @Bean
fun dataSource(): DataSource { fun dataSource(): DataSource {
@ -63,14 +68,18 @@ Consider the first use case in a practical application that requires a
.build() .build()
} }
---- ----
======
Now consider how this application can be deployed into a QA or production Now consider how this application can be deployed into a QA or production
environment, assuming that the datasource for the application is registered environment, assuming that the datasource for the application is registered
with the production application server's JNDI directory. Our `dataSource` bean with the production application server's JNDI directory. Our `dataSource` bean
now looks like the following listing: now looks like the following listing:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Bean(destroyMethod = "") @Bean(destroyMethod = "")
public DataSource dataSource() throws Exception { public DataSource dataSource() throws Exception {
@ -78,8 +87,10 @@ now looks like the following listing:
return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource"); return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Bean(destroyMethod = "") @Bean(destroyMethod = "")
fun dataSource(): DataSource { fun dataSource(): DataSource {
@ -87,6 +98,7 @@ now looks like the following listing:
return ctx.lookup("java:comp/env/jdbc/datasource") as DataSource return ctx.lookup("java:comp/env/jdbc/datasource") as DataSource
} }
---- ----
======
The problem is how to switch between using these two variations based on the The problem is how to switch between using these two variations based on the
current environment. Over time, Spring users have devised a number of ways to current environment. Over time, Spring users have devised a number of ways to
@ -112,8 +124,11 @@ when one or more specified profiles are active. Using our preceding example, we
can rewrite the `dataSource` configuration as follows: can rewrite the `dataSource` configuration as follows:
-- --
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Configuration @Configuration
@Profile("development") @Profile("development")
@ -129,8 +144,10 @@ can rewrite the `dataSource` configuration as follows:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Configuration @Configuration
@Profile("development") @Profile("development")
@ -146,11 +163,15 @@ can rewrite the `dataSource` configuration as follows:
} }
} }
---- ----
======
-- --
-- --
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Configuration @Configuration
@Profile("production") @Profile("production")
@ -163,6 +184,7 @@ can rewrite the `dataSource` configuration as follows:
} }
} }
---- ----
======
<1> `@Bean(destroyMethod = "")` disables default destroy method inference. <1> `@Bean(destroyMethod = "")` disables default destroy method inference.
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
@ -206,8 +228,11 @@ of creating a custom composed annotation. The following example defines a custom
`@Profile("production")`: `@Profile("production")`:
-- --
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Target(ElementType.TYPE) @Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@ -215,14 +240,17 @@ of creating a custom composed annotation. The following example defines a custom
public @interface Production { public @interface Production {
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Target(AnnotationTarget.CLASS) @Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME) @Retention(AnnotationRetention.RUNTIME)
@Profile("production") @Profile("production")
annotation class Production annotation class Production
---- ----
======
-- --
TIP: If a `@Configuration` class is marked with `@Profile`, all of the `@Bean` methods and TIP: If a `@Configuration` class is marked with `@Profile`, all of the `@Bean` methods and
@ -239,8 +267,11 @@ of a configuration class (for example, for alternative variants of a particular
the following example shows: the following example shows:
-- --
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Configuration @Configuration
public class AppConfig { public class AppConfig {
@ -263,6 +294,7 @@ the following example shows:
} }
} }
---- ----
======
<1> The `standaloneDataSource` method is available only in the `development` profile. <1> The `standaloneDataSource` method is available only in the `development` profile.
<2> The `jndiDataSource` method is available only in the `production` profile. <2> The `jndiDataSource` method is available only in the `production` profile.
@ -416,16 +448,21 @@ Activating a profile can be done in several ways, but the most straightforward i
it programmatically against the `Environment` API which is available through an it programmatically against the `Environment` API which is available through an
`ApplicationContext`. The following example shows how to do so: `ApplicationContext`. The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("development"); ctx.getEnvironment().setActiveProfiles("development");
ctx.register(SomeConfig.class, StandaloneDataConfig.class, JndiDataConfig.class); ctx.register(SomeConfig.class, StandaloneDataConfig.class, JndiDataConfig.class);
ctx.refresh(); ctx.refresh();
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val ctx = AnnotationConfigApplicationContext().apply { val ctx = AnnotationConfigApplicationContext().apply {
environment.setActiveProfiles("development") environment.setActiveProfiles("development")
@ -433,6 +470,7 @@ it programmatically against the `Environment` API which is available through an
refresh() refresh()
} }
---- ----
======
In addition, you can also declaratively activate profiles through the In addition, you can also declaratively activate profiles through the
`spring.profiles.active` property, which may be specified through system environment `spring.profiles.active` property, which may be specified through system environment
@ -447,16 +485,22 @@ profiles at once. Programmatically, you can provide multiple profile names to th
`setActiveProfiles()` method, which accepts `String...` varargs. The following example `setActiveProfiles()` method, which accepts `String...` varargs. The following example
activates multiple profiles: activates multiple profiles:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
ctx.getEnvironment().setActiveProfiles("profile1", "profile2"); ctx.getEnvironment().setActiveProfiles("profile1", "profile2");
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
ctx.getEnvironment().setActiveProfiles("profile1", "profile2") ctx.getEnvironment().setActiveProfiles("profile1", "profile2")
---- ----
======
Declaratively, `spring.profiles.active` may accept a comma-separated list of profile names, Declaratively, `spring.profiles.active` may accept a comma-separated list of profile names,
as the following example shows: as the following example shows:
@ -473,8 +517,11 @@ as the following example shows:
The default profile represents the profile that is enabled by default. Consider the The default profile represents the profile that is enabled by default. Consider the
following example: following example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Configuration @Configuration
@Profile("default") @Profile("default")
@ -489,8 +536,10 @@ following example:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Configuration @Configuration
@Profile("default") @Profile("default")
@ -505,6 +554,7 @@ following example:
} }
} }
---- ----
======
If no profile is active, the `dataSource` is created. You can see this If no profile is active, the `dataSource` is created. You can see this
as a way to provide a default definition for one or more beans. If any as a way to provide a default definition for one or more beans. If any
@ -521,22 +571,28 @@ the `Environment` or, declaratively, by using the `spring.profiles.default` prop
Spring's `Environment` abstraction provides search operations over a configurable Spring's `Environment` abstraction provides search operations over a configurable
hierarchy of property sources. Consider the following listing: hierarchy of property sources. Consider the following listing:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
ApplicationContext ctx = new GenericApplicationContext(); ApplicationContext ctx = new GenericApplicationContext();
Environment env = ctx.getEnvironment(); Environment env = ctx.getEnvironment();
boolean containsMyProperty = env.containsProperty("my-property"); boolean containsMyProperty = env.containsProperty("my-property");
System.out.println("Does my environment contain the 'my-property' property? " + containsMyProperty); System.out.println("Does my environment contain the 'my-property' property? " + containsMyProperty);
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val ctx = GenericApplicationContext() val ctx = GenericApplicationContext()
val env = ctx.environment val env = ctx.environment
val containsMyProperty = env.containsProperty("my-property") val containsMyProperty = env.containsProperty("my-property")
println("Does my environment contain the 'my-property' property? $containsMyProperty") println("Does my environment contain the 'my-property' property? $containsMyProperty")
---- ----
======
In the preceding snippet, we see a high-level way of asking Spring whether the `my-property` property is In the preceding snippet, we see a high-level way of asking Spring whether the `my-property` property is
defined for the current environment. To answer this question, the `Environment` object performs defined for the current environment. To answer this question, the `Environment` object performs
@ -580,20 +636,26 @@ of properties that you want to integrate into this search. To do so, implement
and instantiate your own `PropertySource` and add it to the set of `PropertySources` for the and instantiate your own `PropertySource` and add it to the set of `PropertySources` for the
current `Environment`. The following example shows how to do so: current `Environment`. The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
ConfigurableApplicationContext ctx = new GenericApplicationContext(); ConfigurableApplicationContext ctx = new GenericApplicationContext();
MutablePropertySources sources = ctx.getEnvironment().getPropertySources(); MutablePropertySources sources = ctx.getEnvironment().getPropertySources();
sources.addFirst(new MyPropertySource()); sources.addFirst(new MyPropertySource());
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val ctx = GenericApplicationContext() val ctx = GenericApplicationContext()
val sources = ctx.environment.propertySources val sources = ctx.environment.propertySources
sources.addFirst(MyPropertySource()) sources.addFirst(MyPropertySource())
---- ----
======
In the preceding code, `MyPropertySource` has been added with highest precedence in the In the preceding code, `MyPropertySource` has been added with highest precedence in the
search. If it contains a `my-property` property, the property is detected and returned, in favor of search. If it contains a `my-property` property, the property is detected and returned, in favor of
@ -615,8 +677,11 @@ Given a file called `app.properties` that contains the key-value pair `testbean.
the following `@Configuration` class uses `@PropertySource` in such a way that the following `@Configuration` class uses `@PropertySource` in such a way that
a call to `testBean.getName()` returns `myTestBean`: a call to `testBean.getName()` returns `myTestBean`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Configuration @Configuration
@PropertySource("classpath:/com/myco/app.properties") @PropertySource("classpath:/com/myco/app.properties")
@ -633,8 +698,10 @@ a call to `testBean.getName()` returns `myTestBean`:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Configuration @Configuration
@PropertySource("classpath:/com/myco/app.properties") @PropertySource("classpath:/com/myco/app.properties")
@ -649,13 +716,17 @@ a call to `testBean.getName()` returns `myTestBean`:
} }
} }
---- ----
======
Any `${...}` placeholders present in a `@PropertySource` resource location are Any `${...}` placeholders present in a `@PropertySource` resource location are
resolved against the set of property sources already registered against the resolved against the set of property sources already registered against the
environment, as the following example shows: environment, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Configuration @Configuration
@PropertySource("classpath:/com/${my.placeholder:default/path}/app.properties") @PropertySource("classpath:/com/${my.placeholder:default/path}/app.properties")
@ -672,8 +743,10 @@ environment, as the following example shows:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Configuration @Configuration
@PropertySource("classpath:/com/\${my.placeholder:default/path}/app.properties") @PropertySource("classpath:/com/\${my.placeholder:default/path}/app.properties")
@ -688,6 +761,7 @@ environment, as the following example shows:
} }
} }
---- ----
======
Assuming that `my.placeholder` is present in one of the property sources already Assuming that `my.placeholder` is present in one of the property sources already
registered (for example, system properties or environment variables), the placeholder is registered (for example, system properties or environment variables), the placeholder is

View File

@ -120,8 +120,11 @@ it is created by the container and prints the resulting string to the system con
The following listing shows the custom `BeanPostProcessor` implementation class definition: The following listing shows the custom `BeanPostProcessor` implementation class definition:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"] [source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
---- ----
package scripting; package scripting;
@ -140,8 +143,10 @@ The following listing shows the custom `BeanPostProcessor` implementation class
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
---- ----
package scripting package scripting
@ -160,6 +165,7 @@ The following listing shows the custom `BeanPostProcessor` implementation class
} }
} }
---- ----
======
The following `beans` element uses the `InstantiationTracingBeanPostProcessor`: The following `beans` element uses the `InstantiationTracingBeanPostProcessor`:
@ -196,8 +202,11 @@ xref:languages/dynamic.adoc[Dynamic Language Support].)
The following Java application runs the preceding code and configuration: The following Java application runs the preceding code and configuration:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;
@ -213,8 +222,10 @@ The following Java application runs the preceding code and configuration:
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
import org.springframework.beans.factory.getBean import org.springframework.beans.factory.getBean
@ -224,6 +235,7 @@ The following Java application runs the preceding code and configuration:
println(messenger) println(messenger)
} }
---- ----
======
The output of the preceding application resembles the following: The output of the preceding application resembles the following:

View File

@ -67,8 +67,11 @@ no-argument signature. With Java configuration, you can use the `initMethod` att
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/> <bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
---- ----
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class ExampleBean { public class ExampleBean {
@ -77,8 +80,10 @@ no-argument signature. With Java configuration, you can use the `initMethod` att
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class ExampleBean { class ExampleBean {
@ -87,6 +92,7 @@ no-argument signature. With Java configuration, you can use the `initMethod` att
} }
} }
---- ----
======
The preceding example has almost exactly the same effect as the following example The preceding example has almost exactly the same effect as the following example
(which consists of two listings): (which consists of two listings):
@ -96,8 +102,11 @@ The preceding example has almost exactly the same effect as the following exampl
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/> <bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
---- ----
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class AnotherExampleBean implements InitializingBean { public class AnotherExampleBean implements InitializingBean {
@ -107,8 +116,10 @@ The preceding example has almost exactly the same effect as the following exampl
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class AnotherExampleBean : InitializingBean { class AnotherExampleBean : InitializingBean {
@ -117,6 +128,7 @@ The preceding example has almost exactly the same effect as the following exampl
} }
} }
---- ----
======
However, the first of the two preceding examples does not couple the code to Spring. However, the first of the two preceding examples does not couple the code to Spring.
@ -146,8 +158,11 @@ xref:core/beans/java/bean-annotation.adoc#beans-java-lifecycle-callbacks[Receivi
<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/> <bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>
---- ----
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class ExampleBean { public class ExampleBean {
@ -156,8 +171,10 @@ xref:core/beans/java/bean-annotation.adoc#beans-java-lifecycle-callbacks[Receivi
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class ExampleBean { class ExampleBean {
@ -166,6 +183,7 @@ xref:core/beans/java/bean-annotation.adoc#beans-java-lifecycle-callbacks[Receivi
} }
} }
---- ----
======
The preceding definition has almost exactly the same effect as the following definition: The preceding definition has almost exactly the same effect as the following definition:
@ -174,8 +192,11 @@ The preceding definition has almost exactly the same effect as the following def
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/> <bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
---- ----
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class AnotherExampleBean implements DisposableBean { public class AnotherExampleBean implements DisposableBean {
@ -185,8 +206,10 @@ The preceding definition has almost exactly the same effect as the following def
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class AnotherExampleBean : DisposableBean { class AnotherExampleBean : DisposableBean {
@ -195,6 +218,7 @@ The preceding definition has almost exactly the same effect as the following def
} }
} }
---- ----
======
However, the first of the two preceding definitions does not couple the code to Spring. However, the first of the two preceding definitions does not couple the code to Spring.
@ -229,8 +253,11 @@ Suppose that your initialization callback methods are named `init()` and your de
callback methods are named `destroy()`. Your class then resembles the class in the callback methods are named `destroy()`. Your class then resembles the class in the
following example: following example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class DefaultBlogService implements BlogService { public class DefaultBlogService implements BlogService {
@ -248,8 +275,10 @@ following example:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class DefaultBlogService : BlogService { class DefaultBlogService : BlogService {
@ -263,6 +292,7 @@ following example:
} }
} }
---- ----
======
You could then use that class in a bean resembling the following: You could then use that class in a bean resembling the following:
@ -478,8 +508,11 @@ and implement these destroy callbacks correctly.
To register a shutdown hook, call the `registerShutdownHook()` method that is To register a shutdown hook, call the `registerShutdownHook()` method that is
declared on the `ConfigurableApplicationContext` interface, as the following example shows: declared on the `ConfigurableApplicationContext` interface, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;
@ -498,8 +531,10 @@ declared on the `ConfigurableApplicationContext` interface, as the following exa
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
import org.springframework.context.support.ClassPathXmlApplicationContext import org.springframework.context.support.ClassPathXmlApplicationContext
@ -514,6 +549,7 @@ declared on the `ConfigurableApplicationContext` interface, as the following exa
// main method exits, hook is called prior to the app shutting down... // main method exits, hook is called prior to the app shutting down...
} }
---- ----
======

View File

@ -247,8 +247,11 @@ When using annotation-driven components or Java configuration, the `@RequestScop
can be used to assign a component to the `request` scope. The following example shows how can be used to assign a component to the `request` scope. The following example shows how
to do so: to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@RequestScope @RequestScope
@Component @Component
@ -256,8 +259,10 @@ to do so:
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@RequestScope @RequestScope
@Component @Component
@ -265,6 +270,7 @@ to do so:
// ... // ...
} }
---- ----
======
@ -291,8 +297,11 @@ HTTP `Session` is eventually discarded, the bean that is scoped to that particul
When using annotation-driven components or Java configuration, you can use the When using annotation-driven components or Java configuration, you can use the
`@SessionScope` annotation to assign a component to the `session` scope. `@SessionScope` annotation to assign a component to the `session` scope.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@SessionScope @SessionScope
@Component @Component
@ -300,8 +309,10 @@ When using annotation-driven components or Java configuration, you can use the
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@SessionScope @SessionScope
@Component @Component
@ -309,6 +320,7 @@ When using annotation-driven components or Java configuration, you can use the
// ... // ...
} }
---- ----
======
@ -335,8 +347,11 @@ When using annotation-driven components or Java configuration, you can use the
`@ApplicationScope` annotation to assign a component to the `application` scope. The `@ApplicationScope` annotation to assign a component to the `application` scope. The
following example shows how to do so: following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@ApplicationScope @ApplicationScope
@Component @Component
@ -344,8 +359,10 @@ following example shows how to do so:
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@ApplicationScope @ApplicationScope
@Component @Component
@ -353,6 +370,7 @@ following example shows how to do so:
// ... // ...
} }
---- ----
======
@ -551,62 +569,86 @@ does not exist, the method returns a new instance of the bean, after having boun
the session for future reference). The following method returns the object from the the session for future reference). The following method returns the object from the
underlying scope: underlying scope:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
Object get(String name, ObjectFactory<?> objectFactory) Object get(String name, ObjectFactory<?> objectFactory)
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
fun get(name: String, objectFactory: ObjectFactory<*>): Any fun get(name: String, objectFactory: ObjectFactory<*>): Any
---- ----
======
The session scope implementation, for example, removes the session-scoped bean from the The session scope implementation, for example, removes the session-scoped bean from the
underlying session. The object should be returned, but you can return `null` if the underlying session. The object should be returned, but you can return `null` if the
object with the specified name is not found. The following method removes the object from object with the specified name is not found. The following method removes the object from
the underlying scope: the underlying scope:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
Object remove(String name) Object remove(String name)
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
fun remove(name: String): Any fun remove(name: String): Any
---- ----
======
The following method registers a callback that the scope should invoke when it is The following method registers a callback that the scope should invoke when it is
destroyed or when the specified object in the scope is destroyed: destroyed or when the specified object in the scope is destroyed:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
void registerDestructionCallback(String name, Runnable destructionCallback) void registerDestructionCallback(String name, Runnable destructionCallback)
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
fun registerDestructionCallback(name: String, destructionCallback: Runnable) fun registerDestructionCallback(name: String, destructionCallback: Runnable)
---- ----
======
See the {api-spring-framework}/beans/factory/config/Scope.html#registerDestructionCallback[javadoc] See the {api-spring-framework}/beans/factory/config/Scope.html#registerDestructionCallback[javadoc]
or a Spring scope implementation for more information on destruction callbacks. or a Spring scope implementation for more information on destruction callbacks.
The following method obtains the conversation identifier for the underlying scope: The following method obtains the conversation identifier for the underlying scope:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
String getConversationId() String getConversationId()
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
fun getConversationId(): String fun getConversationId(): String
---- ----
======
This identifier is different for each scope. For a session scoped implementation, this This identifier is different for each scope. For a session scoped implementation, this
identifier can be the session identifier. identifier can be the session identifier.
@ -620,16 +662,22 @@ After you write and test one or more custom `Scope` implementations, you need to
the Spring container aware of your new scopes. The following method is the central the Spring container aware of your new scopes. The following method is the central
method to register a new `Scope` with the Spring container: method to register a new `Scope` with the Spring container:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
void registerScope(String scopeName, Scope scope); void registerScope(String scopeName, Scope scope);
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
fun registerScope(scopeName: String, scope: Scope) fun registerScope(scopeName: String, scope: Scope)
---- ----
======
This method is declared on the `ConfigurableBeanFactory` interface, which is available This method is declared on the `ConfigurableBeanFactory` interface, which is available
through the `BeanFactory` property on most of the concrete `ApplicationContext` through the `BeanFactory` property on most of the concrete `ApplicationContext`
@ -647,18 +695,24 @@ NOTE: The next example uses `SimpleThreadScope`, which is included with Spring b
registered by default. The instructions would be the same for your own custom `Scope` registered by default. The instructions would be the same for your own custom `Scope`
implementations. implementations.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
Scope threadScope = new SimpleThreadScope(); Scope threadScope = new SimpleThreadScope();
beanFactory.registerScope("thread", threadScope); beanFactory.registerScope("thread", threadScope);
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val threadScope = SimpleThreadScope() val threadScope = SimpleThreadScope()
beanFactory.registerScope("thread", threadScope) beanFactory.registerScope("thread", threadScope)
---- ----
======
You can then create bean definitions that adhere to the scoping rules of your custom You can then create bean definitions that adhere to the scoping rules of your custom
`Scope`, as follows: `Scope`, as follows:

View File

@ -15,8 +15,11 @@ source of bean definitions. Furthermore, `@Configuration` classes let inter-bean
dependencies be defined by calling other `@Bean` methods in the same class. dependencies be defined by calling other `@Bean` methods in the same class.
The simplest possible `@Configuration` class reads as follows: The simplest possible `@Configuration` class reads as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Configuration @Configuration
public class AppConfig { public class AppConfig {
@ -27,8 +30,10 @@ The simplest possible `@Configuration` class reads as follows:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Configuration @Configuration
class AppConfig { class AppConfig {
@ -39,6 +44,7 @@ The simplest possible `@Configuration` class reads as follows:
} }
} }
---- ----
======
The preceding `AppConfig` class is equivalent to the following Spring `<beans/>` XML: The preceding `AppConfig` class is equivalent to the following Spring `<beans/>` XML:

View File

@ -21,8 +21,11 @@ method to register a bean definition within an `ApplicationContext` of the type
specified as the method's return value. By default, the bean name is the same as specified as the method's return value. By default, the bean name is the same as
the method name. The following example shows a `@Bean` method declaration: the method name. The following example shows a `@Bean` method declaration:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Configuration @Configuration
public class AppConfig { public class AppConfig {
@ -33,8 +36,10 @@ the method name. The following example shows a `@Bean` method declaration:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Configuration @Configuration
class AppConfig { class AppConfig {
@ -43,6 +48,7 @@ the method name. The following example shows a `@Bean` method declaration:
fun transferService() = TransferServiceImpl() fun transferService() = TransferServiceImpl()
} }
---- ----
======
The preceding configuration is exactly equivalent to the following Spring XML: The preceding configuration is exactly equivalent to the following Spring XML:
@ -65,8 +71,11 @@ transferService -> com.acme.TransferServiceImpl
You can also use default methods to define beans. This allows composition of bean You can also use default methods to define beans. This allows composition of bean
configurations by implementing interfaces with bean definitions on default methods. configurations by implementing interfaces with bean definitions on default methods.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public interface BaseConfig { public interface BaseConfig {
@ -81,12 +90,16 @@ configurations by implementing interfaces with bean definitions on default metho
} }
---- ----
======
You can also declare your `@Bean` method with an interface (or base class) You can also declare your `@Bean` method with an interface (or base class)
return type, as the following example shows: return type, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Configuration @Configuration
public class AppConfig { public class AppConfig {
@ -97,8 +110,10 @@ return type, as the following example shows:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Configuration @Configuration
class AppConfig { class AppConfig {
@ -109,6 +124,7 @@ return type, as the following example shows:
} }
} }
---- ----
======
However, this limits the visibility for advance type prediction to the specified However, this limits the visibility for advance type prediction to the specified
interface type (`TransferService`). Then, with the full type (`TransferServiceImpl`) interface type (`TransferService`). Then, with the full type (`TransferServiceImpl`)
@ -133,8 +149,11 @@ dependencies required to build that bean. For instance, if our `TransferService`
requires an `AccountRepository`, we can materialize that dependency with a method requires an `AccountRepository`, we can materialize that dependency with a method
parameter, as the following example shows: parameter, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Configuration @Configuration
public class AppConfig { public class AppConfig {
@ -145,8 +164,10 @@ parameter, as the following example shows:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Configuration @Configuration
class AppConfig { class AppConfig {
@ -157,6 +178,7 @@ parameter, as the following example shows:
} }
} }
---- ----
======
The resolution mechanism is pretty much identical to constructor-based dependency The resolution mechanism is pretty much identical to constructor-based dependency
@ -184,8 +206,11 @@ The `@Bean` annotation supports specifying arbitrary initialization and destruct
callback methods, much like Spring XML's `init-method` and `destroy-method` attributes callback methods, much like Spring XML's `init-method` and `destroy-method` attributes
on the `bean` element, as the following example shows: on the `bean` element, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class BeanOne { public class BeanOne {
@ -215,8 +240,10 @@ on the `bean` element, as the following example shows:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class BeanOne { class BeanOne {
@ -242,6 +269,7 @@ class AppConfig {
fun beanTwo() = BeanTwo() fun beanTwo() = BeanTwo()
} }
---- ----
======
[NOTE] [NOTE]
===== =====
@ -258,22 +286,28 @@ for a `DataSource`, as it is known to be problematic on Jakarta EE application s
The following example shows how to prevent an automatic destruction callback for a The following example shows how to prevent an automatic destruction callback for a
`DataSource`: `DataSource`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Bean(destroyMethod = "") @Bean(destroyMethod = "")
public DataSource dataSource() throws NamingException { public DataSource dataSource() throws NamingException {
return (DataSource) jndiTemplate.lookup("MyDS"); return (DataSource) jndiTemplate.lookup("MyDS");
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Bean(destroyMethod = "") @Bean(destroyMethod = "")
fun dataSource(): DataSource { fun dataSource(): DataSource {
return jndiTemplate.lookup("MyDS") as DataSource return jndiTemplate.lookup("MyDS") as DataSource
} }
---- ----
======
Also, with `@Bean` methods, you typically use programmatic JNDI lookups, either by Also, with `@Bean` methods, you typically use programmatic JNDI lookups, either by
using Spring's `JndiTemplate` or `JndiLocatorDelegate` helpers or straight JNDI using Spring's `JndiTemplate` or `JndiLocatorDelegate` helpers or straight JNDI
@ -286,8 +320,11 @@ intend to refer to the provided resource here).
In the case of `BeanOne` from the example above the preceding note, it would be equally valid to call the `init()` In the case of `BeanOne` from the example above the preceding note, it would be equally valid to call the `init()`
method directly during construction, as the following example shows: method directly during construction, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Configuration @Configuration
public class AppConfig { public class AppConfig {
@ -302,8 +339,10 @@ method directly during construction, as the following example shows:
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Configuration @Configuration
class AppConfig { class AppConfig {
@ -316,6 +355,7 @@ method directly during construction, as the following example shows:
// ... // ...
} }
---- ----
======
TIP: When you work directly in Java, you can do anything you like with your objects and do TIP: When you work directly in Java, you can do anything you like with your objects and do
not always need to rely on the container lifecycle. not always need to rely on the container lifecycle.
@ -336,8 +376,11 @@ xref:core/beans/factory-scopes.adoc[Bean Scopes] section.
The default scope is `singleton`, but you can override this with the `@Scope` annotation, The default scope is `singleton`, but you can override this with the `@Scope` annotation,
as the following example shows: as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Configuration @Configuration
public class MyConfiguration { public class MyConfiguration {
@ -349,8 +392,10 @@ as the following example shows:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Configuration @Configuration
class MyConfiguration { class MyConfiguration {
@ -362,6 +407,7 @@ as the following example shows:
} }
} }
---- ----
======
[[beans-java-scoped-proxy]] [[beans-java-scoped-proxy]]
=== `@Scope` and `scoped-proxy` === `@Scope` and `scoped-proxy`
@ -379,8 +425,11 @@ If you port the scoped proxy example from the XML reference documentation (see
xref:core/beans/factory-scopes.adoc#beans-factory-scopes-other-injection[scoped proxies]) to our `@Bean` using Java, xref:core/beans/factory-scopes.adoc#beans-factory-scopes-other-injection[scoped proxies]) to our `@Bean` using Java,
it resembles the following: it resembles the following:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
// an HTTP Session-scoped bean exposed as a proxy // an HTTP Session-scoped bean exposed as a proxy
@Bean @Bean
@ -397,8 +446,10 @@ it resembles the following:
return service; return service;
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
// an HTTP Session-scoped bean exposed as a proxy // an HTTP Session-scoped bean exposed as a proxy
@Bean @Bean
@ -413,6 +464,7 @@ it resembles the following:
} }
} }
---- ----
======
[[beans-java-customizing-bean-naming]] [[beans-java-customizing-bean-naming]]
== Customizing Bean Naming == Customizing Bean Naming
@ -421,8 +473,11 @@ By default, configuration classes use a `@Bean` method's name as the name of the
resulting bean. This functionality can be overridden, however, with the `name` attribute, resulting bean. This functionality can be overridden, however, with the `name` attribute,
as the following example shows: as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Configuration @Configuration
public class AppConfig { public class AppConfig {
@ -433,8 +488,10 @@ as the following example shows:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Configuration @Configuration
class AppConfig { class AppConfig {
@ -443,6 +500,7 @@ as the following example shows:
fun thing() = Thing() fun thing() = Thing()
} }
---- ----
======
[[beans-java-bean-aliasing]] [[beans-java-bean-aliasing]]
@ -453,8 +511,11 @@ multiple names, otherwise known as bean aliasing. The `name` attribute of the `@
annotation accepts a String array for this purpose. The following example shows how to set annotation accepts a String array for this purpose. The following example shows how to set
a number of aliases for a bean: a number of aliases for a bean:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Configuration @Configuration
public class AppConfig { public class AppConfig {
@ -465,8 +526,10 @@ a number of aliases for a bean:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Configuration @Configuration
class AppConfig { class AppConfig {
@ -477,6 +540,7 @@ a number of aliases for a bean:
} }
} }
---- ----
======
[[beans-java-bean-description]] [[beans-java-bean-description]]
@ -489,8 +553,11 @@ To add a description to a `@Bean`, you can use the
{api-spring-framework}/context/annotation/Description.html[`@Description`] {api-spring-framework}/context/annotation/Description.html[`@Description`]
annotation, as the following example shows: annotation, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Configuration @Configuration
public class AppConfig { public class AppConfig {
@ -502,8 +569,10 @@ annotation, as the following example shows:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Configuration @Configuration
class AppConfig { class AppConfig {
@ -513,6 +582,7 @@ annotation, as the following example shows:
fun thing() = Thing() fun thing() = Thing()
} }
---- ----
======

View File

@ -12,8 +12,11 @@ Much as the `<import/>` element is used within Spring XML files to aid in modula
configurations, the `@Import` annotation allows for loading `@Bean` definitions from configurations, the `@Import` annotation allows for loading `@Bean` definitions from
another configuration class, as the following example shows: another configuration class, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Configuration @Configuration
public class ConfigA { public class ConfigA {
@ -34,8 +37,10 @@ another configuration class, as the following example shows:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Configuration @Configuration
class ConfigA { class ConfigA {
@ -52,13 +57,17 @@ another configuration class, as the following example shows:
fun b() = B() fun b() = B()
} }
---- ----
======
Now, rather than needing to specify both `ConfigA.class` and `ConfigB.class` when Now, rather than needing to specify both `ConfigA.class` and `ConfigB.class` when
instantiating the context, only `ConfigB` needs to be supplied explicitly, as the instantiating the context, only `ConfigB` needs to be supplied explicitly, as the
following example shows: following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public static void main(String[] args) { public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class); ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class);
@ -68,8 +77,10 @@ following example shows:
B b = ctx.getBean(B.class); B b = ctx.getBean(B.class);
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
import org.springframework.beans.factory.getBean import org.springframework.beans.factory.getBean
@ -81,6 +92,7 @@ following example shows:
val b = ctx.getBean<B>() val b = ctx.getBean<B>()
} }
---- ----
======
This approach simplifies container instantiation, as only one class needs to be dealt This approach simplifies container instantiation, as only one class needs to be dealt
with, rather than requiring you to remember a potentially large number of with, rather than requiring you to remember a potentially large number of
@ -106,8 +118,11 @@ a `@Bean` method can have an arbitrary number of parameters that describe the be
dependencies. Consider the following more real-world scenario with several `@Configuration` dependencies. Consider the following more real-world scenario with several `@Configuration`
classes, each depending on beans declared in the others: classes, each depending on beans declared in the others:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Configuration @Configuration
public class ServiceConfig { public class ServiceConfig {
@ -144,8 +159,10 @@ classes, each depending on beans declared in the others:
transferService.transfer(100.00, "A123", "C456"); transferService.transfer(100.00, "A123", "C456");
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
import org.springframework.beans.factory.getBean import org.springframework.beans.factory.getBean
@ -185,6 +202,7 @@ classes, each depending on beans declared in the others:
transferService.transfer(100.00, "A123", "C456") transferService.transfer(100.00, "A123", "C456")
} }
---- ----
======
There is another way to achieve the same result. Remember that `@Configuration` classes are There is another way to achieve the same result. Remember that `@Configuration` classes are
@ -207,8 +225,11 @@ work on the configuration class itself, since it is possible to create it as a b
The following example shows how one bean can be autowired to another bean: The following example shows how one bean can be autowired to another bean:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Configuration @Configuration
public class ServiceConfig { public class ServiceConfig {
@ -254,8 +275,10 @@ The following example shows how one bean can be autowired to another bean:
transferService.transfer(100.00, "A123", "C456"); transferService.transfer(100.00, "A123", "C456");
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
import org.springframework.beans.factory.getBean import org.springframework.beans.factory.getBean
@ -297,6 +320,7 @@ The following example shows how one bean can be autowired to another bean:
transferService.transfer(100.00, "A123", "C456") transferService.transfer(100.00, "A123", "C456")
} }
---- ----
======
TIP: Constructor injection in `@Configuration` classes is only supported as of Spring TIP: Constructor injection in `@Configuration` classes is only supported as of Spring
Framework 4.3. Note also that there is no need to specify `@Autowired` if the target Framework 4.3. Note also that there is no need to specify `@Autowired` if the target
@ -318,8 +342,11 @@ In cases where this ambiguity is not acceptable and you wish to have direct navi
from within your IDE from one `@Configuration` class to another, consider autowiring the from within your IDE from one `@Configuration` class to another, consider autowiring the
configuration classes themselves. The following example shows how to do so: configuration classes themselves. The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Configuration @Configuration
public class ServiceConfig { public class ServiceConfig {
@ -334,8 +361,10 @@ configuration classes themselves. The following example shows how to do so:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Configuration @Configuration
class ServiceConfig { class ServiceConfig {
@ -350,14 +379,18 @@ class ServiceConfig {
} }
} }
---- ----
======
In the preceding situation, where `AccountRepository` is defined is completely explicit. In the preceding situation, where `AccountRepository` is defined is completely explicit.
However, `ServiceConfig` is now tightly coupled to `RepositoryConfig`. That is the However, `ServiceConfig` is now tightly coupled to `RepositoryConfig`. That is the
tradeoff. This tight coupling can be somewhat mitigated by using interface-based or tradeoff. This tight coupling can be somewhat mitigated by using interface-based or
abstract class-based `@Configuration` classes. Consider the following example: abstract class-based `@Configuration` classes. Consider the following example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Configuration @Configuration
public class ServiceConfig { public class ServiceConfig {
@ -404,8 +437,10 @@ abstract class-based `@Configuration` classes. Consider the following example:
transferService.transfer(100.00, "A123", "C456"); transferService.transfer(100.00, "A123", "C456");
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
import org.springframework.beans.factory.getBean import org.springframework.beans.factory.getBean
@ -454,6 +489,7 @@ abstract class-based `@Configuration` classes. Consider the following example:
transferService.transfer(100.00, "A123", "C456") transferService.transfer(100.00, "A123", "C456")
} }
---- ----
======
Now `ServiceConfig` is loosely coupled with respect to the concrete Now `ServiceConfig` is loosely coupled with respect to the concrete
`DefaultRepositoryConfig`, and built-in IDE tooling is still useful: You can easily `DefaultRepositoryConfig`, and built-in IDE tooling is still useful: You can easily
@ -487,8 +523,11 @@ Implementations of the `Condition` interface provide a `matches(...)`
method that returns `true` or `false`. For example, the following listing shows the actual method that returns `true` or `false`. For example, the following listing shows the actual
`Condition` implementation used for `@Profile`: `Condition` implementation used for `@Profile`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Override @Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
@ -505,8 +544,10 @@ method that returns `true` or `false`. For example, the following listing shows
return true; return true;
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
override fun matches(context: ConditionContext, metadata: AnnotatedTypeMetadata): Boolean { override fun matches(context: ConditionContext, metadata: AnnotatedTypeMetadata): Boolean {
// Read the @Profile annotation attributes // Read the @Profile annotation attributes
@ -522,6 +563,7 @@ method that returns `true` or `false`. For example, the following listing shows
return true return true
} }
---- ----
======
See the {api-spring-framework}/context/annotation/Conditional.html[`@Conditional`] See the {api-spring-framework}/context/annotation/Conditional.html[`@Conditional`]
javadoc for more detail. javadoc for more detail.
@ -558,8 +600,11 @@ properly.
The following example shows an ordinary configuration class in Java: The following example shows an ordinary configuration class in Java:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Configuration @Configuration
public class AppConfig { public class AppConfig {
@ -578,8 +623,10 @@ The following example shows an ordinary configuration class in Java:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Configuration @Configuration
class AppConfig { class AppConfig {
@ -596,6 +643,7 @@ The following example shows an ordinary configuration class in Java:
fun transferService() = TransferService(accountRepository()) fun transferService() = TransferService(accountRepository())
} }
---- ----
======
The following example shows part of a sample `system-test-config.xml` file: The following example shows part of a sample `system-test-config.xml` file:
@ -625,8 +673,11 @@ jdbc.username=sa
jdbc.password= jdbc.password=
---- ----
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public static void main(String[] args) { public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/com/acme/system-test-config.xml"); ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/com/acme/system-test-config.xml");
@ -634,8 +685,10 @@ jdbc.password=
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
fun main() { fun main() {
val ctx = ClassPathXmlApplicationContext("classpath:/com/acme/system-test-config.xml") val ctx = ClassPathXmlApplicationContext("classpath:/com/acme/system-test-config.xml")
@ -643,6 +696,7 @@ jdbc.password=
// ... // ...
} }
---- ----
======
NOTE: In `system-test-config.xml` file, the `AppConfig` `<bean/>` does not declare an `id` NOTE: In `system-test-config.xml` file, the `AppConfig` `<bean/>` does not declare an `id`
@ -691,8 +745,11 @@ that defines a bean, a properties file, and the `main` class) shows how to use
the `@ImportResource` annotation to achieve "`Java-centric`" configuration that uses XML the `@ImportResource` annotation to achieve "`Java-centric`" configuration that uses XML
as needed: as needed:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Configuration @Configuration
@ImportResource("classpath:/com/acme/properties-config.xml") @ImportResource("classpath:/com/acme/properties-config.xml")
@ -713,8 +770,10 @@ as needed:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Configuration @Configuration
@ImportResource("classpath:/com/acme/properties-config.xml") @ImportResource("classpath:/com/acme/properties-config.xml")
@ -735,6 +794,7 @@ as needed:
} }
} }
---- ----
======
[source,xml,indent=0,subs="verbatim,quotes"] [source,xml,indent=0,subs="verbatim,quotes"]
---- ----
@ -752,8 +812,11 @@ jdbc.username=sa
jdbc.password= jdbc.password=
---- ----
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public static void main(String[] args) { public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class); ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
@ -761,8 +824,10 @@ jdbc.password=
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
import org.springframework.beans.factory.getBean import org.springframework.beans.factory.getBean
@ -772,6 +837,7 @@ jdbc.password=
// ... // ...
} }
---- ----
======

View File

@ -13,8 +13,11 @@ inter-bean dependencies. See xref:core/beans/java/basic-concepts.adoc[Basic Conc
When beans have dependencies on one another, expressing that dependency is as simple When beans have dependencies on one another, expressing that dependency is as simple
as having one bean method call another, as the following example shows: as having one bean method call another, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Configuration @Configuration
public class AppConfig { public class AppConfig {
@ -30,8 +33,10 @@ as having one bean method call another, as the following example shows:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Configuration @Configuration
class AppConfig { class AppConfig {
@ -43,6 +48,7 @@ as having one bean method call another, as the following example shows:
fun beanTwo() = BeanTwo() fun beanTwo() = BeanTwo()
} }
---- ----
======
In the preceding example, `beanOne` receives a reference to `beanTwo` through constructor In the preceding example, `beanOne` receives a reference to `beanTwo` through constructor
injection. injection.
@ -62,8 +68,11 @@ singleton-scoped bean has a dependency on a prototype-scoped bean. Using Java fo
type of configuration provides a natural means for implementing this pattern. The type of configuration provides a natural means for implementing this pattern. The
following example shows how to use lookup method injection: following example shows how to use lookup method injection:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public abstract class CommandManager { public abstract class CommandManager {
public Object process(Object commandState) { public Object process(Object commandState) {
@ -78,8 +87,10 @@ following example shows how to use lookup method injection:
protected abstract Command createCommand(); protected abstract Command createCommand();
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
abstract class CommandManager { abstract class CommandManager {
fun process(commandState: Any): Any { fun process(commandState: Any): Any {
@ -94,13 +105,17 @@ following example shows how to use lookup method injection:
protected abstract fun createCommand(): Command protected abstract fun createCommand(): Command
} }
---- ----
======
By using Java configuration, you can create a subclass of `CommandManager` where By using Java configuration, you can create a subclass of `CommandManager` where
the abstract `createCommand()` method is overridden in such a way that it looks up a new the abstract `createCommand()` method is overridden in such a way that it looks up a new
(prototype) command object. The following example shows how to do so: (prototype) command object. The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Bean @Bean
@Scope("prototype") @Scope("prototype")
@ -121,8 +136,10 @@ the abstract `createCommand()` method is overridden in such a way that it looks
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Bean @Bean
@Scope("prototype") @Scope("prototype")
@ -143,6 +160,7 @@ the abstract `createCommand()` method is overridden in such a way that it looks
} }
} }
---- ----
======
[[beans-java-further-information-java-config]] [[beans-java-further-information-java-config]]
@ -150,8 +168,11 @@ the abstract `createCommand()` method is overridden in such a way that it looks
Consider the following example, which shows a `@Bean` annotated method being called twice: Consider the following example, which shows a `@Bean` annotated method being called twice:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Configuration @Configuration
public class AppConfig { public class AppConfig {
@ -176,8 +197,10 @@ Consider the following example, which shows a `@Bean` annotated method being cal
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Configuration @Configuration
class AppConfig { class AppConfig {
@ -202,6 +225,7 @@ Consider the following example, which shows a `@Bean` annotated method being cal
} }
} }
---- ----
======
`clientDao()` has been called once in `clientService1()` and once in `clientService2()`. `clientDao()` has been called once in `clientService1()` and once in `clientService2()`.
Since this method creates a new instance of `ClientDaoImpl` and returns it, you would Since this method creates a new instance of `ClientDaoImpl` and returns it, you would

View File

@ -23,8 +23,11 @@ In much the same way that Spring XML files are used as input when instantiating
instantiating an `AnnotationConfigApplicationContext`. This allows for completely instantiating an `AnnotationConfigApplicationContext`. This allows for completely
XML-free usage of the Spring container, as the following example shows: XML-free usage of the Spring container, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public static void main(String[] args) { public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class); ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
@ -32,8 +35,10 @@ XML-free usage of the Spring container, as the following example shows:
myService.doStuff(); myService.doStuff();
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
import org.springframework.beans.factory.getBean import org.springframework.beans.factory.getBean
@ -43,13 +48,17 @@ XML-free usage of the Spring container, as the following example shows:
myService.doStuff() myService.doStuff()
} }
---- ----
======
As mentioned earlier, `AnnotationConfigApplicationContext` is not limited to working only As mentioned earlier, `AnnotationConfigApplicationContext` is not limited to working only
with `@Configuration` classes. Any `@Component` or JSR-330 annotated class may be supplied with `@Configuration` classes. Any `@Component` or JSR-330 annotated class may be supplied
as input to the constructor, as the following example shows: as input to the constructor, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public static void main(String[] args) { public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(MyServiceImpl.class, Dependency1.class, Dependency2.class); ApplicationContext ctx = new AnnotationConfigApplicationContext(MyServiceImpl.class, Dependency1.class, Dependency2.class);
@ -57,8 +66,10 @@ as input to the constructor, as the following example shows:
myService.doStuff(); myService.doStuff();
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
import org.springframework.beans.factory.getBean import org.springframework.beans.factory.getBean
@ -68,6 +79,7 @@ as input to the constructor, as the following example shows:
myService.doStuff() myService.doStuff()
} }
---- ----
======
The preceding example assumes that `MyServiceImpl`, `Dependency1`, and `Dependency2` use Spring The preceding example assumes that `MyServiceImpl`, `Dependency1`, and `Dependency2` use Spring
dependency injection annotations such as `@Autowired`. dependency injection annotations such as `@Autowired`.
@ -81,8 +93,11 @@ and then configure it by using the `register()` method. This approach is particu
when programmatically building an `AnnotationConfigApplicationContext`. The following when programmatically building an `AnnotationConfigApplicationContext`. The following
example shows how to do so: example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public static void main(String[] args) { public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
@ -93,8 +108,10 @@ example shows how to do so:
myService.doStuff(); myService.doStuff();
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
import org.springframework.beans.factory.getBean import org.springframework.beans.factory.getBean
@ -107,6 +124,7 @@ example shows how to do so:
myService.doStuff() myService.doStuff()
} }
---- ----
======
[[beans-java-instantiating-container-scan]] [[beans-java-instantiating-container-scan]]
@ -114,8 +132,11 @@ example shows how to do so:
To enable component scanning, you can annotate your `@Configuration` class as follows: To enable component scanning, you can annotate your `@Configuration` class as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Configuration @Configuration
@ComponentScan(basePackages = "com.acme") // <1> @ComponentScan(basePackages = "com.acme") // <1>
@ -123,6 +144,7 @@ To enable component scanning, you can annotate your `@Configuration` class as fo
// ... // ...
} }
---- ----
======
<1> This annotation enables component scanning. <1> This annotation enables component scanning.
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
@ -156,8 +178,11 @@ definitions within the container. `AnnotationConfigApplicationContext` exposes t
`scan(String...)` method to allow for the same component-scanning functionality, as the `scan(String...)` method to allow for the same component-scanning functionality, as the
following example shows: following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public static void main(String[] args) { public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
@ -166,8 +191,10 @@ following example shows:
MyService myService = ctx.getBean(MyService.class); MyService myService = ctx.getBean(MyService.class);
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
fun main() { fun main() {
val ctx = AnnotationConfigApplicationContext() val ctx = AnnotationConfigApplicationContext()
@ -176,6 +203,7 @@ following example shows:
val myService = ctx.getBean<MyService>() val myService = ctx.getBean<MyService>()
} }
---- ----
======
NOTE: Remember that `@Configuration` classes are xref:core/beans/classpath-scanning.adoc#beans-meta-annotations[meta-annotated] NOTE: Remember that `@Configuration` classes are xref:core/beans/classpath-scanning.adoc#beans-meta-annotations[meta-annotated]
with `@Component`, so they are candidates for component-scanning. In the preceding example, with `@Component`, so they are candidates for component-scanning. In the preceding example,

View File

@ -29,8 +29,11 @@ You can add the following dependency to your file pom.xml:
Instead of `@Autowired`, you can use `@jakarta.inject.Inject` as follows: Instead of `@Autowired`, you can use `@jakarta.inject.Inject` as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
import jakarta.inject.Inject; import jakarta.inject.Inject;
@ -49,8 +52,10 @@ Instead of `@Autowired`, you can use `@jakarta.inject.Inject` as follows:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
import jakarta.inject.Inject import jakarta.inject.Inject
@ -66,6 +71,7 @@ Instead of `@Autowired`, you can use `@jakarta.inject.Inject` as follows:
} }
} }
---- ----
======
As with `@Autowired`, you can use `@Inject` at the field level, method level As with `@Autowired`, you can use `@Inject` at the field level, method level
and constructor-argument level. Furthermore, you may declare your injection point as a and constructor-argument level. Furthermore, you may declare your injection point as a
@ -73,8 +79,11 @@ and constructor-argument level. Furthermore, you may declare your injection poin
other beans through a `Provider.get()` call. The following example offers a variant of the other beans through a `Provider.get()` call. The following example offers a variant of the
preceding example: preceding example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
import jakarta.inject.Inject; import jakarta.inject.Inject;
import jakarta.inject.Provider; import jakarta.inject.Provider;
@ -94,8 +103,10 @@ preceding example:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
import jakarta.inject.Inject import jakarta.inject.Inject
@ -111,12 +122,16 @@ preceding example:
} }
} }
---- ----
======
If you would like to use a qualified name for the dependency that should be injected, If you would like to use a qualified name for the dependency that should be injected,
you should use the `@Named` annotation, as the following example shows: you should use the `@Named` annotation, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
import jakarta.inject.Inject; import jakarta.inject.Inject;
import jakarta.inject.Named; import jakarta.inject.Named;
@ -133,8 +148,10 @@ you should use the `@Named` annotation, as the following example shows:
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
import jakarta.inject.Inject import jakarta.inject.Inject
import jakarta.inject.Named import jakarta.inject.Named
@ -151,6 +168,7 @@ you should use the `@Named` annotation, as the following example shows:
// ... // ...
} }
---- ----
======
As with `@Autowired`, `@Inject` can also be used with `java.util.Optional` or As with `@Autowired`, `@Inject` can also be used with `java.util.Optional` or
`@Nullable`. This is even more applicable here, since `@Inject` does not have `@Nullable`. This is even more applicable here, since `@Inject` does not have
@ -168,8 +186,11 @@ a `required` attribute. The following pair of examples show how to use `@Inject`
} }
---- ----
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class SimpleMovieLister { public class SimpleMovieLister {
@ -179,8 +200,10 @@ a `required` attribute. The following pair of examples show how to use `@Inject`
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class SimpleMovieLister { class SimpleMovieLister {
@ -188,6 +211,7 @@ a `required` attribute. The following pair of examples show how to use `@Inject`
var movieFinder: MovieFinder? = null var movieFinder: MovieFinder? = null
} }
---- ----
======
@ -197,8 +221,11 @@ a `required` attribute. The following pair of examples show how to use `@Inject`
Instead of `@Component`, you can use `@jakarta.inject.Named` or `jakarta.annotation.ManagedBean`, Instead of `@Component`, you can use `@jakarta.inject.Named` or `jakarta.annotation.ManagedBean`,
as the following example shows: as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
import jakarta.inject.Inject; import jakarta.inject.Inject;
import jakarta.inject.Named; import jakarta.inject.Named;
@ -216,8 +243,10 @@ as the following example shows:
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
import jakarta.inject.Inject import jakarta.inject.Inject
import jakarta.inject.Named import jakarta.inject.Named
@ -231,12 +260,16 @@ as the following example shows:
// ... // ...
} }
---- ----
======
It is very common to use `@Component` without specifying a name for the component. It is very common to use `@Component` without specifying a name for the component.
`@Named` can be used in a similar fashion, as the following example shows: `@Named` can be used in a similar fashion, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
import jakarta.inject.Inject; import jakarta.inject.Inject;
import jakarta.inject.Named; import jakarta.inject.Named;
@ -254,8 +287,10 @@ It is very common to use `@Component` without specifying a name for the componen
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
import jakarta.inject.Inject import jakarta.inject.Inject
import jakarta.inject.Named import jakarta.inject.Named
@ -269,12 +304,16 @@ It is very common to use `@Component` without specifying a name for the componen
// ... // ...
} }
---- ----
======
When you use `@Named` or `@ManagedBean`, you can use component scanning in the When you use `@Named` or `@ManagedBean`, you can use component scanning in the
exact same way as when you use Spring annotations, as the following example shows: exact same way as when you use Spring annotations, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Configuration @Configuration
@ComponentScan(basePackages = "org.example") @ComponentScan(basePackages = "org.example")
@ -282,8 +321,10 @@ exact same way as when you use Spring annotations, as the following example show
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Configuration @Configuration
@ComponentScan(basePackages = ["org.example"]) @ComponentScan(basePackages = ["org.example"])
@ -291,6 +332,7 @@ exact same way as when you use Spring annotations, as the following example show
// ... // ...
} }
---- ----
======
NOTE: In contrast to `@Component`, the JSR-330 `@Named` and the JSR-250 `@ManagedBean` NOTE: In contrast to `@Component`, the JSR-330 `@Named` and the JSR-250 `@ManagedBean`
annotations are not composable. You should use Spring's stereotype model for building annotations are not composable. You should use Spring's stereotype model for building

View File

@ -140,8 +140,11 @@ An `Encoder` allocates data buffers that others must read (and release). So an `
doesn't have much to do. However an `Encoder` must take care to release a data buffer if doesn't have much to do. However an `Encoder` must take care to release a data buffer if
a serialization error occurs while populating the buffer with data. For example: a serialization error occurs while populating the buffer with data. For example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
DataBuffer buffer = factory.allocateBuffer(); DataBuffer buffer = factory.allocateBuffer();
boolean release = true; boolean release = true;
@ -156,8 +159,10 @@ a serialization error occurs while populating the buffer with data. For example:
} }
return buffer; return buffer;
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val buffer = factory.allocateBuffer() val buffer = factory.allocateBuffer()
var release = true var release = true
@ -171,6 +176,7 @@ a serialization error occurs while populating the buffer with data. For example:
} }
return buffer return buffer
---- ----
======
The consumer of an `Encoder` is responsible for releasing the data buffers it receives. The consumer of an `Encoder` is responsible for releasing the data buffers it receives.
In a WebFlux application, the output of the `Encoder` is used to write to the HTTP server In a WebFlux application, the output of the `Encoder` is used to write to the HTTP server

View File

@ -67,8 +67,11 @@ and method or constructor parameters.
The following example sets the default value of a field: The following example sets the default value of a field:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class FieldValueTestBean { public class FieldValueTestBean {
@ -84,8 +87,10 @@ The following example sets the default value of a field:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class FieldValueTestBean { class FieldValueTestBean {
@ -93,11 +98,15 @@ The following example sets the default value of a field:
var defaultLocale: String? = null var defaultLocale: String? = null
} }
---- ----
======
The following example shows the equivalent but on a property setter method: The following example shows the equivalent but on a property setter method:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class PropertyValueTestBean { public class PropertyValueTestBean {
@ -113,8 +122,10 @@ The following example shows the equivalent but on a property setter method:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class PropertyValueTestBean { class PropertyValueTestBean {
@ -122,12 +133,16 @@ The following example shows the equivalent but on a property setter method:
var defaultLocale: String? = null var defaultLocale: String? = null
} }
---- ----
======
Autowired methods and constructors can also use the `@Value` annotation, as the following Autowired methods and constructors can also use the `@Value` annotation, as the following
examples show: examples show:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class SimpleMovieLister { public class SimpleMovieLister {
@ -144,8 +159,10 @@ examples show:
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class SimpleMovieLister { class SimpleMovieLister {
@ -162,9 +179,13 @@ examples show:
// ... // ...
} }
---- ----
======
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class MovieRecommender { public class MovieRecommender {
@ -181,14 +202,17 @@ examples show:
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class MovieRecommender(private val customerPreferenceDao: CustomerPreferenceDao, class MovieRecommender(private val customerPreferenceDao: CustomerPreferenceDao,
@Value("#{systemProperties['user.country']}") private val defaultLocale: String) { @Value("#{systemProperties['user.country']}") private val defaultLocale: String) {
// ... // ...
} }
---- ----
======

View File

@ -8,13 +8,17 @@ xref:core/expressions/language-ref.adoc[Language Reference].
The following code introduces the SpEL API to evaluate the literal string expression, The following code introduces the SpEL API to evaluate the literal string expression,
`Hello World`. `Hello World`.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
ExpressionParser parser = new SpelExpressionParser(); ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("'Hello World'"); // <1> Expression exp = parser.parseExpression("'Hello World'"); // <1>
String message = (String) exp.getValue(); String message = (String) exp.getValue();
---- ----
======
<1> The value of the message variable is `'Hello World'`. <1> The value of the message variable is `'Hello World'`.
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
@ -42,13 +46,17 @@ and calling constructors.
In the following example of method invocation, we call the `concat` method on the string literal: In the following example of method invocation, we call the `concat` method on the string literal:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
ExpressionParser parser = new SpelExpressionParser(); ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("'Hello World'.concat('!')"); // <1> Expression exp = parser.parseExpression("'Hello World'.concat('!')"); // <1>
String message = (String) exp.getValue(); String message = (String) exp.getValue();
---- ----
======
<1> The value of `message` is now 'Hello World!'. <1> The value of `message` is now 'Hello World!'.
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
@ -62,8 +70,11 @@ In the following example of method invocation, we call the `concat` method on th
The following example of calling a JavaBean property calls the `String` property `Bytes`: The following example of calling a JavaBean property calls the `String` property `Bytes`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
ExpressionParser parser = new SpelExpressionParser(); ExpressionParser parser = new SpelExpressionParser();
@ -71,6 +82,7 @@ The following example of calling a JavaBean property calls the `String` property
Expression exp = parser.parseExpression("'Hello World'.bytes"); // <1> Expression exp = parser.parseExpression("'Hello World'.bytes"); // <1>
byte[] bytes = (byte[]) exp.getValue(); byte[] bytes = (byte[]) exp.getValue();
---- ----
======
<1> This line converts the literal to a byte array. <1> This line converts the literal to a byte array.
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
@ -90,8 +102,11 @@ Public fields may also be accessed.
The following example shows how to use dot notation to get the length of a literal: The following example shows how to use dot notation to get the length of a literal:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
ExpressionParser parser = new SpelExpressionParser(); ExpressionParser parser = new SpelExpressionParser();
@ -99,6 +114,7 @@ The following example shows how to use dot notation to get the length of a liter
Expression exp = parser.parseExpression("'Hello World'.bytes.length"); // <1> Expression exp = parser.parseExpression("'Hello World'.bytes.length"); // <1>
int length = (Integer) exp.getValue(); int length = (Integer) exp.getValue();
---- ----
======
<1> `'Hello World'.bytes.length` gives the length of the literal. <1> `'Hello World'.bytes.length` gives the length of the literal.
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
@ -115,13 +131,17 @@ The following example shows how to use dot notation to get the length of a liter
The String's constructor can be called instead of using a string literal, as the following The String's constructor can be called instead of using a string literal, as the following
example shows: example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
ExpressionParser parser = new SpelExpressionParser(); ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("new String('hello world').toUpperCase()"); // <1> Expression exp = parser.parseExpression("new String('hello world').toUpperCase()"); // <1>
String message = exp.getValue(String.class); String message = exp.getValue(String.class);
---- ----
======
<1> Construct a new `String` from the literal and make it be upper case. <1> Construct a new `String` from the literal and make it be upper case.
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
@ -144,8 +164,11 @@ against a specific object instance (called the root object). The following examp
how to retrieve the `name` property from an instance of the `Inventor` class or how to retrieve the `name` property from an instance of the `Inventor` class or
create a boolean condition: create a boolean condition:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
// Create and set a calendar // Create and set a calendar
GregorianCalendar c = new GregorianCalendar(); GregorianCalendar c = new GregorianCalendar();
@ -164,8 +187,10 @@ create a boolean condition:
boolean result = exp.getValue(tesla, Boolean.class); boolean result = exp.getValue(tesla, Boolean.class);
// result == true // result == true
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
// Create and set a calendar // Create and set a calendar
val c = GregorianCalendar() val c = GregorianCalendar()
@ -184,6 +209,7 @@ create a boolean condition:
val result = exp.getValue(tesla, Boolean::class.java) val result = exp.getValue(tesla, Boolean::class.java)
// result == true // result == true
---- ----
======
@ -232,8 +258,11 @@ to set a `List` property. The type of the property is actually `List<Boolean>`.
recognizes that the elements of the list need to be converted to `Boolean` before recognizes that the elements of the list need to be converted to `Boolean` before
being placed in it. The following example shows how to do so: being placed in it. The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
class Simple { class Simple {
public List<Boolean> booleanList = new ArrayList<>(); public List<Boolean> booleanList = new ArrayList<>();
@ -251,8 +280,10 @@ being placed in it. The following example shows how to do so:
// b is false // b is false
Boolean b = simple.booleanList.get(0); Boolean b = simple.booleanList.get(0);
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class Simple { class Simple {
var booleanList: MutableList<Boolean> = ArrayList() var booleanList: MutableList<Boolean> = ArrayList()
@ -270,6 +301,7 @@ being placed in it. The following example shows how to do so:
// b is false // b is false
val b = simple.booleanList[0] val b = simple.booleanList[0]
---- ----
======
[[expressions-parser-configuration]] [[expressions-parser-configuration]]
@ -290,8 +322,11 @@ or custom converter that knows how to set the value, `null` will remain in the a
list at the specified index. The following example demonstrates how to automatically grow list at the specified index. The following example demonstrates how to automatically grow
the list: the list:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
class Demo { class Demo {
public List<String> list; public List<String> list;
@ -313,8 +348,10 @@ the list:
// demo.list will now be a real collection of 4 entries // demo.list will now be a real collection of 4 entries
// Each entry is a new empty String // Each entry is a new empty String
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class Demo { class Demo {
var list: List<String>? = null var list: List<String>? = null
@ -336,6 +373,7 @@ the list:
// demo.list will now be a real collection of 4 entries // demo.list will now be a real collection of 4 entries
// Each entry is a new empty String // Each entry is a new empty String
---- ----
======
@ -404,8 +442,11 @@ since part of the expression may be running twice.
After selecting a mode, use the `SpelParserConfiguration` to configure the parser. The After selecting a mode, use the `SpelParserConfiguration` to configure the parser. The
following example shows how to do so: following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
SpelParserConfiguration config = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE, SpelParserConfiguration config = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE,
this.getClass().getClassLoader()); this.getClass().getClassLoader());
@ -418,8 +459,10 @@ following example shows how to do so:
Object payload = expr.getValue(message); Object payload = expr.getValue(message);
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val config = SpelParserConfiguration(SpelCompilerMode.IMMEDIATE, val config = SpelParserConfiguration(SpelCompilerMode.IMMEDIATE,
this.javaClass.classLoader) this.javaClass.classLoader)
@ -432,6 +475,7 @@ following example shows how to do so:
val payload = expr.getValue(message) val payload = expr.getValue(message)
---- ----
======
When you specify the compiler mode, you can also specify a classloader (passing null is allowed). When you specify the compiler mode, you can also specify a classloader (passing null is allowed).
Compiled expressions are defined in a child classloader created under any that is supplied. Compiled expressions are defined in a child classloader created under any that is supplied.

View File

@ -3,8 +3,11 @@
This section lists the classes used in the examples throughout this chapter. This section lists the classes used in the examples throughout this chapter.
[tabs]
======
Inventor.Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"] [source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Inventor.Java
---- ----
package org.spring.samples.spel.inventor; package org.spring.samples.spel.inventor;
@ -76,8 +79,10 @@ This section lists the classes used in the examples throughout this chapter.
} }
} }
---- ----
Inventor.kt::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Inventor.kt
---- ----
package org.spring.samples.spel.inventor package org.spring.samples.spel.inventor
@ -88,9 +93,13 @@ This section lists the classes used in the examples throughout this chapter.
var birthdate: Date = GregorianCalendar().time, var birthdate: Date = GregorianCalendar().time,
var placeOfBirth: PlaceOfBirth? = null) var placeOfBirth: PlaceOfBirth? = null)
---- ----
======
[tabs]
======
PlaceOfBirth.java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"] [source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.PlaceOfBirth.java
---- ----
package org.spring.samples.spel.inventor; package org.spring.samples.spel.inventor;
@ -125,16 +134,22 @@ This section lists the classes used in the examples throughout this chapter.
} }
} }
---- ----
PlaceOfBirth.kt::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.PlaceOfBirth.kt
---- ----
package org.spring.samples.spel.inventor package org.spring.samples.spel.inventor
class PlaceOfBirth(var city: String, var country: String? = null) { class PlaceOfBirth(var city: String, var country: String? = null) {
---- ----
======
[tabs]
======
Society.java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"] [source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Society.java
---- ----
package org.spring.samples.spel.inventor; package org.spring.samples.spel.inventor;
@ -176,8 +191,10 @@ This section lists the classes used in the examples throughout this chapter.
} }
} }
---- ----
Society.kt::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Society.kt
---- ----
package org.spring.samples.spel.inventor package org.spring.samples.spel.inventor
@ -203,3 +220,4 @@ This section lists the classes used in the examples throughout this chapter.
} }
} }
---- ----
======

View File

@ -4,8 +4,11 @@
You can build arrays by using the familiar Java syntax, optionally supplying an initializer You can build arrays by using the familiar Java syntax, optionally supplying an initializer
to have the array populated at construction time. The following example shows how to do so: to have the array populated at construction time. The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
int[] numbers1 = (int[]) parser.parseExpression("new int[4]").getValue(context); int[] numbers1 = (int[]) parser.parseExpression("new int[4]").getValue(context);
@ -15,8 +18,10 @@ to have the array populated at construction time. The following example shows ho
// Multi dimensional array // Multi dimensional array
int[][] numbers3 = (int[][]) parser.parseExpression("new int[4][5]").getValue(context); int[][] numbers3 = (int[][]) parser.parseExpression("new int[4][5]").getValue(context);
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val numbers1 = parser.parseExpression("new int[4]").getValue(context) as IntArray val numbers1 = parser.parseExpression("new int[4]").getValue(context) as IntArray
@ -26,6 +31,7 @@ to have the array populated at construction time. The following example shows ho
// Multi dimensional array // Multi dimensional array
val numbers3 = parser.parseExpression("new int[4][5]").getValue(context) as Array<IntArray> val numbers3 = parser.parseExpression("new int[4][5]").getValue(context) as Array<IntArray>
---- ----
======
You cannot currently supply an initializer when you construct a multi-dimensional array. You cannot currently supply an initializer when you construct a multi-dimensional array.

View File

@ -5,8 +5,11 @@ If the evaluation context has been configured with a bean resolver, you can
look up beans from an expression by using the `@` symbol. The following example shows how look up beans from an expression by using the `@` symbol. The following example shows how
to do so: to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
ExpressionParser parser = new SpelExpressionParser(); ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext(); StandardEvaluationContext context = new StandardEvaluationContext();
@ -15,8 +18,10 @@ to do so:
// This will end up calling resolve(context,"something") on MyBeanResolver during evaluation // This will end up calling resolve(context,"something") on MyBeanResolver during evaluation
Object bean = parser.parseExpression("@something").getValue(context); Object bean = parser.parseExpression("@something").getValue(context);
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val parser = SpelExpressionParser() val parser = SpelExpressionParser()
val context = StandardEvaluationContext() val context = StandardEvaluationContext()
@ -25,12 +30,16 @@ to do so:
// This will end up calling resolve(context,"something") on MyBeanResolver during evaluation // This will end up calling resolve(context,"something") on MyBeanResolver during evaluation
val bean = parser.parseExpression("@something").getValue(context) val bean = parser.parseExpression("@something").getValue(context)
---- ----
======
To access a factory bean itself, you should instead prefix the bean name with an `&` symbol. To access a factory bean itself, you should instead prefix the bean name with an `&` symbol.
The following example shows how to do so: The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
ExpressionParser parser = new SpelExpressionParser(); ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext(); StandardEvaluationContext context = new StandardEvaluationContext();
@ -39,8 +48,10 @@ The following example shows how to do so:
// This will end up calling resolve(context,"&foo") on MyBeanResolver during evaluation // This will end up calling resolve(context,"&foo") on MyBeanResolver during evaluation
Object bean = parser.parseExpression("&foo").getValue(context); Object bean = parser.parseExpression("&foo").getValue(context);
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val parser = SpelExpressionParser() val parser = SpelExpressionParser()
val context = StandardEvaluationContext() val context = StandardEvaluationContext()
@ -49,5 +60,6 @@ The following example shows how to do so:
// This will end up calling resolve(context,"&foo") on MyBeanResolver during evaluation // This will end up calling resolve(context,"&foo") on MyBeanResolver during evaluation
val bean = parser.parseExpression("&foo").getValue(context) val bean = parser.parseExpression("&foo").getValue(context)
---- ----
======

View File

@ -7,18 +7,24 @@ suppose we have a list of inventors but want the list of cities where they were
Effectively, we want to evaluate 'placeOfBirth.city' for every entry in the inventor Effectively, we want to evaluate 'placeOfBirth.city' for every entry in the inventor
list. The following example uses projection to do so: list. The following example uses projection to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
// returns ['Smiljan', 'Idvor' ] // returns ['Smiljan', 'Idvor' ]
List placesOfBirth = (List)parser.parseExpression("members.![placeOfBirth.city]"); List placesOfBirth = (List)parser.parseExpression("members.![placeOfBirth.city]");
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
// returns ['Smiljan', 'Idvor' ] // returns ['Smiljan', 'Idvor' ]
val placesOfBirth = parser.parseExpression("members.![placeOfBirth.city]") as List<*> val placesOfBirth = parser.parseExpression("members.![placeOfBirth.city]") as List<*>
---- ----
======
Projection is supported for arrays and anything that implements `java.lang.Iterable` or Projection is supported for arrays and anything that implements `java.lang.Iterable` or
`java.util.Map`. When using a map to drive projection, the projection expression is `java.util.Map`. When using a map to drive projection, the projection expression is

View File

@ -8,18 +8,24 @@ Selection uses a syntax of `.?[selectionExpression]`. It filters the collection
returns a new collection that contains a subset of the original elements. For example, returns a new collection that contains a subset of the original elements. For example,
selection lets us easily get a list of Serbian inventors, as the following example shows: selection lets us easily get a list of Serbian inventors, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
List<Inventor> list = (List<Inventor>) parser.parseExpression( List<Inventor> list = (List<Inventor>) parser.parseExpression(
"members.?[nationality == 'Serbian']").getValue(societyContext); "members.?[nationality == 'Serbian']").getValue(societyContext);
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val list = parser.parseExpression( val list = parser.parseExpression(
"members.?[nationality == 'Serbian']").getValue(societyContext) as List<Inventor> "members.?[nationality == 'Serbian']").getValue(societyContext) as List<Inventor>
---- ----
======
Selection is supported for arrays and anything that implements `java.lang.Iterable` or Selection is supported for arrays and anything that implements `java.lang.Iterable` or
`java.util.Map`. For a list or array, the selection criteria is evaluated against each `java.util.Map`. For a list or array, the selection criteria is evaluated against each
@ -30,16 +36,22 @@ accessible as properties for use in the selection.
The following expression returns a new map that consists of those elements of the The following expression returns a new map that consists of those elements of the
original map where the entry's value is less than 27: original map where the entry's value is less than 27:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
Map newMap = parser.parseExpression("map.?[value<27]").getValue(); Map newMap = parser.parseExpression("map.?[value<27]").getValue();
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val newMap = parser.parseExpression("map.?[value<27]").getValue() val newMap = parser.parseExpression("map.?[value<27]").getValue()
---- ----
======
In addition to returning all the selected elements, you can retrieve only the first or In addition to returning all the selected elements, you can retrieve only the first or
the last element. To obtain the first element matching the selection, the syntax is the last element. To obtain the first element matching the selection, the syntax is

View File

@ -6,8 +6,11 @@ qualified class name for all types except those located in the `java.lang` packa
(`Integer`, `Float`, `String`, and so on). The following example shows how to use the (`Integer`, `Float`, `String`, and so on). The following example shows how to use the
`new` operator to invoke constructors: `new` operator to invoke constructors:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
Inventor einstein = p.parseExpression( Inventor einstein = p.parseExpression(
"new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German')") "new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German')")
@ -18,8 +21,10 @@ qualified class name for all types except those located in the `java.lang` packa
"Members.add(new org.spring.samples.spel.inventor.Inventor( "Members.add(new org.spring.samples.spel.inventor.Inventor(
'Albert Einstein', 'German'))").getValue(societyContext); 'Albert Einstein', 'German'))").getValue(societyContext);
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val einstein = p.parseExpression( val einstein = p.parseExpression(
"new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German')") "new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German')")
@ -30,6 +35,7 @@ qualified class name for all types except those located in the `java.lang` packa
"Members.add(new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German'))") "Members.add(new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German'))")
.getValue(societyContext) .getValue(societyContext)
---- ----
======

View File

@ -5,27 +5,36 @@ You can extend SpEL by registering user-defined functions that can be called wit
expression string. The function is registered through the `EvaluationContext`. The expression string. The function is registered through the `EvaluationContext`. The
following example shows how to register a user-defined function: following example shows how to register a user-defined function:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
Method method = ...; Method method = ...;
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build(); EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
context.setVariable("myFunction", method); context.setVariable("myFunction", method);
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val method: Method = ... val method: Method = ...
val context = SimpleEvaluationContext.forReadOnlyDataBinding().build() val context = SimpleEvaluationContext.forReadOnlyDataBinding().build()
context.setVariable("myFunction", method) context.setVariable("myFunction", method)
---- ----
======
For example, consider the following utility method that reverses a string: For example, consider the following utility method that reverses a string:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public abstract class StringUtils { public abstract class StringUtils {
@ -38,8 +47,10 @@ For example, consider the following utility method that reverses a string:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
fun reverseString(input: String): String { fun reverseString(input: String): String {
val backwards = StringBuilder(input.length) val backwards = StringBuilder(input.length)
@ -49,11 +60,15 @@ For example, consider the following utility method that reverses a string:
return backwards.toString() return backwards.toString()
} }
---- ----
======
You can then register and use the preceding method, as the following example shows: You can then register and use the preceding method, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
ExpressionParser parser = new SpelExpressionParser(); ExpressionParser parser = new SpelExpressionParser();
@ -64,8 +79,10 @@ You can then register and use the preceding method, as the following example sho
String helloWorldReversed = parser.parseExpression( String helloWorldReversed = parser.parseExpression(
"#reverseString('hello')").getValue(context, String.class); "#reverseString('hello')").getValue(context, String.class);
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val parser = SpelExpressionParser() val parser = SpelExpressionParser()
@ -75,6 +92,7 @@ You can then register and use the preceding method, as the following example sho
val helloWorldReversed = parser.parseExpression( val helloWorldReversed = parser.parseExpression(
"#reverseString('hello')").getValue(context, String::class.java) "#reverseString('hello')").getValue(context, String::class.java)
---- ----
======

View File

@ -3,22 +3,28 @@
You can directly express lists in an expression by using `{}` notation. You can directly express lists in an expression by using `{}` notation.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
// evaluates to a Java list containing the four numbers // evaluates to a Java list containing the four numbers
List numbers = (List) parser.parseExpression("{1,2,3,4}").getValue(context); List numbers = (List) parser.parseExpression("{1,2,3,4}").getValue(context);
List listOfLists = (List) parser.parseExpression("{{'a','b'},{'x','y'}}").getValue(context); List listOfLists = (List) parser.parseExpression("{{'a','b'},{'x','y'}}").getValue(context);
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
// evaluates to a Java list containing the four numbers // evaluates to a Java list containing the four numbers
val numbers = parser.parseExpression("{1,2,3,4}").getValue(context) as List<*> val numbers = parser.parseExpression("{1,2,3,4}").getValue(context) as List<*>
val listOfLists = parser.parseExpression("{{'a','b'},{'x','y'}}").getValue(context) as List<*> val listOfLists = parser.parseExpression("{{'a','b'},{'x','y'}}").getValue(context) as List<*>
---- ----
======
`{}` by itself means an empty list. For performance reasons, if the list is itself `{}` by itself means an empty list. For performance reasons, if the list is itself
entirely composed of fixed literals, a constant list is created to represent the entirely composed of fixed literals, a constant list is created to represent the

View File

@ -4,22 +4,28 @@
You can also directly express maps in an expression by using `{key:value}` notation. The You can also directly express maps in an expression by using `{key:value}` notation. The
following example shows how to do so: following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
// evaluates to a Java map containing the two entries // evaluates to a Java map containing the two entries
Map inventorInfo = (Map) parser.parseExpression("{name:'Nikola',dob:'10-July-1856'}").getValue(context); Map inventorInfo = (Map) parser.parseExpression("{name:'Nikola',dob:'10-July-1856'}").getValue(context);
Map mapOfMaps = (Map) parser.parseExpression("{name:{first:'Nikola',last:'Tesla'},dob:{day:10,month:'July',year:1856}}").getValue(context); Map mapOfMaps = (Map) parser.parseExpression("{name:{first:'Nikola',last:'Tesla'},dob:{day:10,month:'July',year:1856}}").getValue(context);
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
// evaluates to a Java map containing the two entries // evaluates to a Java map containing the two entries
val inventorInfo = parser.parseExpression("{name:'Nikola',dob:'10-July-1856'}").getValue(context) as Map<*, *> val inventorInfo = parser.parseExpression("{name:'Nikola',dob:'10-July-1856'}").getValue(context) as Map<*, *>
val mapOfMaps = parser.parseExpression("{name:{first:'Nikola',last:'Tesla'},dob:{day:10,month:'July',year:1856}}").getValue(context) as Map<*, *> val mapOfMaps = parser.parseExpression("{name:{first:'Nikola',last:'Tesla'},dob:{day:10,month:'July',year:1856}}").getValue(context) as Map<*, *>
---- ----
======
`{:}` by itself means an empty map. For performance reasons, if the map is itself `{:}` by itself means an empty map. For performance reasons, if the map is itself
composed of fixed literals or other nested constant structures (lists or maps), a composed of fixed literals or other nested constant structures (lists or maps), a

View File

@ -23,8 +23,11 @@ isolation like this but, rather, as part of a more complex expression -- for exa
using a literal on one side of a logical comparison operator or as an argument to a using a literal on one side of a logical comparison operator or as an argument to a
method. method.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
ExpressionParser parser = new SpelExpressionParser(); ExpressionParser parser = new SpelExpressionParser();
@ -43,8 +46,10 @@ method.
Object nullValue = parser.parseExpression("null").getValue(); Object nullValue = parser.parseExpression("null").getValue();
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val parser = SpelExpressionParser() val parser = SpelExpressionParser()
@ -63,6 +68,7 @@ method.
val nullValue = parser.parseExpression("null").value val nullValue = parser.parseExpression("null").value
---- ----
======

View File

@ -5,8 +5,11 @@ You can invoke methods by using typical Java programming syntax. You can also in
on literals. Variable arguments are also supported. The following examples show how to on literals. Variable arguments are also supported. The following examples show how to
invoke methods: invoke methods:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
// string literal, evaluates to "bc" // string literal, evaluates to "bc"
String bc = parser.parseExpression("'abc'.substring(1, 3)").getValue(String.class); String bc = parser.parseExpression("'abc'.substring(1, 3)").getValue(String.class);
@ -15,8 +18,10 @@ invoke methods:
boolean isMember = parser.parseExpression("isMember('Mihajlo Pupin')").getValue( boolean isMember = parser.parseExpression("isMember('Mihajlo Pupin')").getValue(
societyContext, Boolean.class); societyContext, Boolean.class);
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
// string literal, evaluates to "bc" // string literal, evaluates to "bc"
val bc = parser.parseExpression("'abc'.substring(1, 3)").getValue(String::class.java) val bc = parser.parseExpression("'abc'.substring(1, 3)").getValue(String::class.java)
@ -25,5 +30,6 @@ invoke methods:
val isMember = parser.parseExpression("isMember('Mihajlo Pupin')").getValue( val isMember = parser.parseExpression("isMember('Mihajlo Pupin')").getValue(
societyContext, Boolean::class.java) societyContext, Boolean::class.java)
---- ----
======

View File

@ -15,27 +15,36 @@ following example shows:
Instead, you can use the Elvis operator (named for the resemblance to Elvis' hair style). Instead, you can use the Elvis operator (named for the resemblance to Elvis' hair style).
The following example shows how to use the Elvis operator: The following example shows how to use the Elvis operator:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
ExpressionParser parser = new SpelExpressionParser(); ExpressionParser parser = new SpelExpressionParser();
String name = parser.parseExpression("name?:'Unknown'").getValue(new Inventor(), String.class); String name = parser.parseExpression("name?:'Unknown'").getValue(new Inventor(), String.class);
System.out.println(name); // 'Unknown' System.out.println(name); // 'Unknown'
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val parser = SpelExpressionParser() val parser = SpelExpressionParser()
val name = parser.parseExpression("name?:'Unknown'").getValue(Inventor(), String::class.java) val name = parser.parseExpression("name?:'Unknown'").getValue(Inventor(), String::class.java)
println(name) // 'Unknown' println(name) // 'Unknown'
---- ----
======
The following listing shows a more complex example: The following listing shows a more complex example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
ExpressionParser parser = new SpelExpressionParser(); ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build(); EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
@ -48,8 +57,10 @@ The following listing shows a more complex example:
name = parser.parseExpression("name?:'Elvis Presley'").getValue(context, tesla, String.class); name = parser.parseExpression("name?:'Elvis Presley'").getValue(context, tesla, String.class);
System.out.println(name); // Elvis Presley System.out.println(name); // Elvis Presley
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val parser = SpelExpressionParser() val parser = SpelExpressionParser()
val context = SimpleEvaluationContext.forReadOnlyDataBinding().build() val context = SimpleEvaluationContext.forReadOnlyDataBinding().build()
@ -62,6 +73,7 @@ The following listing shows a more complex example:
name = parser.parseExpression("name?:'Elvis Presley'").getValue(context, tesla, String::class.java) name = parser.parseExpression("name?:'Elvis Presley'").getValue(context, tesla, String::class.java)
println(name) // Elvis Presley println(name) // Elvis Presley
---- ----
======
[NOTE] [NOTE]
===== =====

View File

@ -8,8 +8,11 @@ it is not null before accessing methods or properties of the object. To avoid th
safe navigation operator returns null instead of throwing an exception. The following safe navigation operator returns null instead of throwing an exception. The following
example shows how to use the safe navigation operator: example shows how to use the safe navigation operator:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
ExpressionParser parser = new SpelExpressionParser(); ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build(); EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
@ -24,8 +27,10 @@ example shows how to use the safe navigation operator:
city = parser.parseExpression("placeOfBirth?.city").getValue(context, tesla, String.class); city = parser.parseExpression("placeOfBirth?.city").getValue(context, tesla, String.class);
System.out.println(city); // null - does not throw NullPointerException!!! System.out.println(city); // null - does not throw NullPointerException!!!
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val parser = SpelExpressionParser() val parser = SpelExpressionParser()
val context = SimpleEvaluationContext.forReadOnlyDataBinding().build() val context = SimpleEvaluationContext.forReadOnlyDataBinding().build()
@ -40,6 +45,7 @@ example shows how to use the safe navigation operator:
city = parser.parseExpression("placeOfBirth?.city").getValue(context, tesla, String::class.java) city = parser.parseExpression("placeOfBirth?.city").getValue(context, tesla, String::class.java)
println(city) // null - does not throw NullPointerException!!! println(city) // null - does not throw NullPointerException!!!
---- ----
======

View File

@ -4,24 +4,33 @@
You can use the ternary operator for performing if-then-else conditional logic inside You can use the ternary operator for performing if-then-else conditional logic inside
the expression. The following listing shows a minimal example: the expression. The following listing shows a minimal example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
String falseString = parser.parseExpression( String falseString = parser.parseExpression(
"false ? 'trueExp' : 'falseExp'").getValue(String.class); "false ? 'trueExp' : 'falseExp'").getValue(String.class);
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val falseString = parser.parseExpression( val falseString = parser.parseExpression(
"false ? 'trueExp' : 'falseExp'").getValue(String::class.java) "false ? 'trueExp' : 'falseExp'").getValue(String::class.java)
---- ----
======
In this case, the boolean `false` results in returning the string value `'falseExp'`. A more In this case, the boolean `false` results in returning the string value `'falseExp'`. A more
realistic example follows: realistic example follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
parser.parseExpression("name").setValue(societyContext, "IEEE"); parser.parseExpression("name").setValue(societyContext, "IEEE");
societyContext.setVariable("queryName", "Nikola Tesla"); societyContext.setVariable("queryName", "Nikola Tesla");
@ -33,8 +42,10 @@ realistic example follows:
.getValue(societyContext, String.class); .getValue(societyContext, String.class);
// queryResultString = "Nikola Tesla is a member of the IEEE Society" // queryResultString = "Nikola Tesla is a member of the IEEE Society"
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
parser.parseExpression("name").setValue(societyContext, "IEEE") parser.parseExpression("name").setValue(societyContext, "IEEE")
societyContext.setVariable("queryName", "Nikola Tesla") societyContext.setVariable("queryName", "Nikola Tesla")
@ -45,6 +56,7 @@ realistic example follows:
.getValue(societyContext, String::class.java) .getValue(societyContext, String::class.java)
// queryResultString = "Nikola Tesla is a member of the IEEE Society" // queryResultString = "Nikola Tesla is a member of the IEEE Society"
---- ----
======
See the next section on the Elvis operator for an even shorter syntax for the See the next section on the Elvis operator for an even shorter syntax for the
ternary operator. ternary operator.

View File

@ -17,8 +17,11 @@ and greater than or equal) are supported by using standard operator notation.
These operators work on `Number` types as well as types implementing `Comparable`. These operators work on `Number` types as well as types implementing `Comparable`.
The following listing shows a few examples of operators: The following listing shows a few examples of operators:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
// evaluates to true // evaluates to true
boolean trueValue = parser.parseExpression("2 == 2").getValue(Boolean.class); boolean trueValue = parser.parseExpression("2 == 2").getValue(Boolean.class);
@ -32,8 +35,10 @@ The following listing shows a few examples of operators:
// uses CustomValue:::compareTo // uses CustomValue:::compareTo
boolean trueValue = parser.parseExpression("new CustomValue(1) < new CustomValue(2)").getValue(Boolean.class); boolean trueValue = parser.parseExpression("new CustomValue(1) < new CustomValue(2)").getValue(Boolean.class);
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
// evaluates to true // evaluates to true
val trueValue = parser.parseExpression("2 == 2").getValue(Boolean::class.java) val trueValue = parser.parseExpression("2 == 2").getValue(Boolean::class.java)
@ -47,6 +52,7 @@ The following listing shows a few examples of operators:
// uses CustomValue:::compareTo // uses CustomValue:::compareTo
val trueValue = parser.parseExpression("new CustomValue(1) < new CustomValue(2)").getValue(Boolean::class.java); val trueValue = parser.parseExpression("new CustomValue(1) < new CustomValue(2)").getValue(Boolean::class.java);
---- ----
======
[NOTE] [NOTE]
==== ====
@ -62,8 +68,11 @@ in favor of comparisons against zero (for example, `X > 0` or `X < 0`).
In addition to the standard relational operators, SpEL supports the `instanceof` and regular In addition to the standard relational operators, SpEL supports the `instanceof` and regular
expression-based `matches` operator. The following listing shows examples of both: expression-based `matches` operator. The following listing shows examples of both:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
// evaluates to false // evaluates to false
boolean falseValue = parser.parseExpression( boolean falseValue = parser.parseExpression(
@ -77,8 +86,10 @@ expression-based `matches` operator. The following listing shows examples of bot
boolean falseValue = parser.parseExpression( boolean falseValue = parser.parseExpression(
"'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class); "'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
// evaluates to false // evaluates to false
val falseValue = parser.parseExpression( val falseValue = parser.parseExpression(
@ -92,6 +103,7 @@ expression-based `matches` operator. The following listing shows examples of bot
val falseValue = parser.parseExpression( val falseValue = parser.parseExpression(
"'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean::class.java) "'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean::class.java)
---- ----
======
CAUTION: Be careful with primitive types, as they are immediately boxed up to their CAUTION: Be careful with primitive types, as they are immediately boxed up to their
wrapper types. For example, `1 instanceof T(int)` evaluates to `false`, while wrapper types. For example, `1 instanceof T(int)` evaluates to `false`, while
@ -125,8 +137,11 @@ SpEL supports the following logical operators:
The following example shows how to use the logical operators: The following example shows how to use the logical operators:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
// -- AND -- // -- AND --
@ -155,8 +170,10 @@ The following example shows how to use the logical operators:
String expression = "isMember('Nikola Tesla') and !isMember('Mihajlo Pupin')"; String expression = "isMember('Nikola Tesla') and !isMember('Mihajlo Pupin')";
boolean falseValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class); boolean falseValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
// -- AND -- // -- AND --
@ -185,6 +202,7 @@ The following example shows how to use the logical operators:
val expression = "isMember('Nikola Tesla') and !isMember('Mihajlo Pupin')" val expression = "isMember('Nikola Tesla') and !isMember('Mihajlo Pupin')"
val falseValue = parser.parseExpression(expression).getValue(societyContext, Boolean::class.java) val falseValue = parser.parseExpression(expression).getValue(societyContext, Boolean::class.java)
---- ----
======
[[expressions-operators-mathematical]] [[expressions-operators-mathematical]]
@ -196,8 +214,11 @@ You can also use the modulus (`%`) and exponential power (`^`) operators on numb
Standard operator precedence is enforced. The following example shows the mathematical Standard operator precedence is enforced. The following example shows the mathematical
operators in use: operators in use:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
// Addition // Addition
int two = parser.parseExpression("1 + 1").getValue(Integer.class); // 2 int two = parser.parseExpression("1 + 1").getValue(Integer.class); // 2
@ -228,8 +249,10 @@ operators in use:
// Operator precedence // Operator precedence
int minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Integer.class); // -21 int minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Integer.class); // -21
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
// Addition // Addition
val two = parser.parseExpression("1 + 1").getValue(Int::class.java) // 2 val two = parser.parseExpression("1 + 1").getValue(Int::class.java) // 2
@ -260,6 +283,7 @@ operators in use:
// Operator precedence // Operator precedence
val minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Int::class.java) // -21 val minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Int::class.java) // -21
---- ----
======
[[expressions-assignment]] [[expressions-assignment]]
@ -269,8 +293,11 @@ To set a property, use the assignment operator (`=`). This is typically done wit
call to `setValue` but can also be done inside a call to `getValue`. The following call to `setValue` but can also be done inside a call to `getValue`. The following
listing shows both ways to use the assignment operator: listing shows both ways to use the assignment operator:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
Inventor inventor = new Inventor(); Inventor inventor = new Inventor();
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build(); EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();
@ -281,8 +308,10 @@ listing shows both ways to use the assignment operator:
String aleks = parser.parseExpression( String aleks = parser.parseExpression(
"name = 'Aleksandar Seovic'").getValue(context, inventor, String.class); "name = 'Aleksandar Seovic'").getValue(context, inventor, String.class);
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val inventor = Inventor() val inventor = Inventor()
val context = SimpleEvaluationContext.forReadWriteDataBinding().build() val context = SimpleEvaluationContext.forReadWriteDataBinding().build()
@ -293,5 +322,6 @@ listing shows both ways to use the assignment operator:
val aleks = parser.parseExpression( val aleks = parser.parseExpression(
"name = 'Aleksandar Seovic'").getValue(context, inventor, String::class.java) "name = 'Aleksandar Seovic'").getValue(context, inventor, String::class.java)
---- ----
======

View File

@ -7,22 +7,28 @@ populated with data listed in the xref:core/expressions/example-classes.adoc[Cla
section. To navigate "down" the object graph and get Tesla's year of birth and section. To navigate "down" the object graph and get Tesla's year of birth and
Pupin's city of birth, we use the following expressions: Pupin's city of birth, we use the following expressions:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
// evaluates to 1856 // evaluates to 1856
int year = (Integer) parser.parseExpression("birthdate.year + 1900").getValue(context); int year = (Integer) parser.parseExpression("birthdate.year + 1900").getValue(context);
String city = (String) parser.parseExpression("placeOfBirth.city").getValue(context); String city = (String) parser.parseExpression("placeOfBirth.city").getValue(context);
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
// evaluates to 1856 // evaluates to 1856
val year = parser.parseExpression("birthdate.year + 1900").getValue(context) as Int val year = parser.parseExpression("birthdate.year + 1900").getValue(context) as Int
val city = parser.parseExpression("placeOfBirth.city").getValue(context) as String val city = parser.parseExpression("placeOfBirth.city").getValue(context) as String
---- ----
======
[NOTE] [NOTE]
==== ====
@ -36,8 +42,11 @@ method invocations -- for example, `getPlaceOfBirth().getCity()` instead of
The contents of arrays and lists are obtained by using square bracket notation, as the The contents of arrays and lists are obtained by using square bracket notation, as the
following example shows: following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
ExpressionParser parser = new SpelExpressionParser(); ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build(); EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
@ -59,8 +68,10 @@ following example shows:
String invention = parser.parseExpression("members[0].inventions[6]").getValue( String invention = parser.parseExpression("members[0].inventions[6]").getValue(
context, ieee, String.class); context, ieee, String.class);
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val parser = SpelExpressionParser() val parser = SpelExpressionParser()
val context = SimpleEvaluationContext.forReadOnlyDataBinding().build() val context = SimpleEvaluationContext.forReadOnlyDataBinding().build()
@ -82,13 +93,17 @@ following example shows:
val invention = parser.parseExpression("members[0].inventions[6]").getValue( val invention = parser.parseExpression("members[0].inventions[6]").getValue(
context, ieee, String::class.java) context, ieee, String::class.java)
---- ----
======
The contents of maps are obtained by specifying the literal key value within the The contents of maps are obtained by specifying the literal key value within the
brackets. In the following example, because keys for the `officers` map are strings, we can specify brackets. In the following example, because keys for the `officers` map are strings, we can specify
string literals: string literals:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
// Officer's Dictionary // Officer's Dictionary
@ -103,8 +118,10 @@ string literals:
parser.parseExpression("officers['advisors'][0].placeOfBirth.country").setValue( parser.parseExpression("officers['advisors'][0].placeOfBirth.country").setValue(
societyContext, "Croatia"); societyContext, "Croatia");
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
// Officer's Dictionary // Officer's Dictionary
@ -119,6 +136,7 @@ string literals:
parser.parseExpression("officers['advisors'][0].placeOfBirth.country").setValue( parser.parseExpression("officers['advisors'][0].placeOfBirth.country").setValue(
societyContext, "Croatia") societyContext, "Croatia")
---- ----
======

View File

@ -6,8 +6,11 @@ Each evaluation block is delimited with prefix and suffix characters that you ca
define. A common choice is to use `#{ }` as the delimiters, as the following example define. A common choice is to use `#{ }` as the delimiters, as the following example
shows: shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
String randomPhrase = parser.parseExpression( String randomPhrase = parser.parseExpression(
"random number is #{T(java.lang.Math).random()}", "random number is #{T(java.lang.Math).random()}",
@ -15,8 +18,10 @@ shows:
// evaluates to "random number is 0.7038186818312008" // evaluates to "random number is 0.7038186818312008"
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val randomPhrase = parser.parseExpression( val randomPhrase = parser.parseExpression(
"random number is #{T(java.lang.Math).random()}", "random number is #{T(java.lang.Math).random()}",
@ -24,6 +29,7 @@ shows:
// evaluates to "random number is 0.7038186818312008" // evaluates to "random number is 0.7038186818312008"
---- ----
======
The string is evaluated by concatenating the literal text `'random number is '` with the The string is evaluated by concatenating the literal text `'random number is '` with the
result of evaluating the expression inside the `#{ }` delimiter (in this case, the result result of evaluating the expression inside the `#{ }` delimiter (in this case, the result
@ -32,8 +38,11 @@ is of the type `ParserContext`. The `ParserContext` interface is used to influen
the expression is parsed in order to support the expression templating functionality. the expression is parsed in order to support the expression templating functionality.
The definition of `TemplateParserContext` follows: The definition of `TemplateParserContext` follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class TemplateParserContext implements ParserContext { public class TemplateParserContext implements ParserContext {
@ -50,8 +59,10 @@ The definition of `TemplateParserContext` follows:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class TemplateParserContext : ParserContext { class TemplateParserContext : ParserContext {
@ -68,5 +79,6 @@ The definition of `TemplateParserContext` follows:
} }
} }
---- ----
======

View File

@ -9,8 +9,11 @@ type). Static methods are invoked by using this operator as well. The
package do not need to be fully qualified, but all other type references must be. The package do not need to be fully qualified, but all other type references must be. The
following example shows how to use the `T` operator: following example shows how to use the `T` operator:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
Class dateClass = parser.parseExpression("T(java.util.Date)").getValue(Class.class); Class dateClass = parser.parseExpression("T(java.util.Date)").getValue(Class.class);
@ -20,8 +23,10 @@ following example shows how to use the `T` operator:
"T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR") "T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR")
.getValue(Boolean.class); .getValue(Boolean.class);
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val dateClass = parser.parseExpression("T(java.util.Date)").getValue(Class::class.java) val dateClass = parser.parseExpression("T(java.util.Date)").getValue(Class::class.java)
@ -31,6 +36,7 @@ following example shows how to use the `T` operator:
"T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR") "T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR")
.getValue(Boolean::class.java) .getValue(Boolean::class.java)
---- ----
======

View File

@ -17,8 +17,11 @@ characters.
The following example shows how to use variables. The following example shows how to use variables.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
Inventor tesla = new Inventor("Nikola Tesla", "Serbian"); Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
@ -28,8 +31,10 @@ The following example shows how to use variables.
parser.parseExpression("name = #newName").getValue(context, tesla); parser.parseExpression("name = #newName").getValue(context, tesla);
System.out.println(tesla.getName()) // "Mike Tesla" System.out.println(tesla.getName()) // "Mike Tesla"
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val tesla = Inventor("Nikola Tesla", "Serbian") val tesla = Inventor("Nikola Tesla", "Serbian")
@ -39,6 +44,7 @@ The following example shows how to use variables.
parser.parseExpression("name = #newName").getValue(context, tesla) parser.parseExpression("name = #newName").getValue(context, tesla)
println(tesla.name) // "Mike Tesla" println(tesla.name) // "Mike Tesla"
---- ----
======
[[expressions-this-root]] [[expressions-this-root]]
@ -50,8 +56,11 @@ defined and refers to the root context object. Although `#this` may vary as comp
an expression are evaluated, `#root` always refers to the root. The following examples an expression are evaluated, `#root` always refers to the root. The following examples
show how to use the `#this` and `#root` variables: show how to use the `#this` and `#root` variables:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
// create an array of integers // create an array of integers
List<Integer> primes = new ArrayList<>(); List<Integer> primes = new ArrayList<>();
@ -67,8 +76,10 @@ show how to use the `#this` and `#root` variables:
List<Integer> primesGreaterThanTen = (List<Integer>) parser.parseExpression( List<Integer> primesGreaterThanTen = (List<Integer>) parser.parseExpression(
"#primes.?[#this>10]").getValue(context); "#primes.?[#this>10]").getValue(context);
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
// create an array of integers // create an array of integers
val primes = ArrayList<Int>() val primes = ArrayList<Int>()
@ -84,6 +95,7 @@ show how to use the `#this` and `#root` variables:
val primesGreaterThanTen = parser.parseExpression( val primesGreaterThanTen = parser.parseExpression(
"#primes.?[#this>10]").getValue(context) as List<Int> "#primes.?[#this>10]").getValue(context) as List<Int>
---- ----
======

View File

@ -276,16 +276,22 @@ specified doesn't have a specific prefix, you get back a `Resource` type that is
appropriate to that particular application context. For example, assume the following appropriate to that particular application context. For example, assume the following
snippet of code was run against a `ClassPathXmlApplicationContext` instance: snippet of code was run against a `ClassPathXmlApplicationContext` instance:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
Resource template = ctx.getResource("some/resource/path/myTemplate.txt"); Resource template = ctx.getResource("some/resource/path/myTemplate.txt");
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val template = ctx.getResource("some/resource/path/myTemplate.txt") val template = ctx.getResource("some/resource/path/myTemplate.txt")
---- ----
======
Against a `ClassPathXmlApplicationContext`, that code returns a `ClassPathResource`. If Against a `ClassPathXmlApplicationContext`, that code returns a `ClassPathResource`. If
the same method were run against a `FileSystemXmlApplicationContext` instance, it would the same method were run against a `FileSystemXmlApplicationContext` instance, it would
@ -299,41 +305,59 @@ On the other hand, you may also force `ClassPathResource` to be used, regardless
application context type, by specifying the special `classpath:` prefix, as the following application context type, by specifying the special `classpath:` prefix, as the following
example shows: example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
Resource template = ctx.getResource("classpath:some/resource/path/myTemplate.txt"); Resource template = ctx.getResource("classpath:some/resource/path/myTemplate.txt");
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val template = ctx.getResource("classpath:some/resource/path/myTemplate.txt") val template = ctx.getResource("classpath:some/resource/path/myTemplate.txt")
---- ----
======
Similarly, you can force a `UrlResource` to be used by specifying any of the standard Similarly, you can force a `UrlResource` to be used by specifying any of the standard
`java.net.URL` prefixes. The following examples use the `file` and `https` prefixes: `java.net.URL` prefixes. The following examples use the `file` and `https` prefixes:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
Resource template = ctx.getResource("file:///some/resource/path/myTemplate.txt"); Resource template = ctx.getResource("file:///some/resource/path/myTemplate.txt");
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val template = ctx.getResource("file:///some/resource/path/myTemplate.txt") val template = ctx.getResource("file:///some/resource/path/myTemplate.txt")
---- ----
======
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
Resource template = ctx.getResource("https://myhost.com/resource/path/myTemplate.txt"); Resource template = ctx.getResource("https://myhost.com/resource/path/myTemplate.txt");
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val template = ctx.getResource("https://myhost.com/resource/path/myTemplate.txt") val template = ctx.getResource("https://myhost.com/resource/path/myTemplate.txt")
---- ----
======
The following table summarizes the strategy for converting `String` objects to `Resource` The following table summarizes the strategy for converting `String` objects to `Resource`
objects: objects:
@ -477,8 +501,11 @@ register and use a special JavaBeans `PropertyEditor`, which can convert `String
to `Resource` objects. For example, the following `MyBean` class has a `template` to `Resource` objects. For example, the following `MyBean` class has a `template`
property of type `Resource`. property of type `Resource`.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
package example; package example;
@ -493,11 +520,14 @@ property of type `Resource`.
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class MyBean(var template: Resource) class MyBean(var template: Resource)
---- ----
======
In an XML configuration file, the `template` property can be configured with a simple In an XML configuration file, the `template` property can be configured with a simple
string for that resource, as the following example shows: string for that resource, as the following example shows:
@ -537,8 +567,11 @@ retrieve the value of the template path as a string, and a special `PropertyEdit
convert the string to a `Resource` object to be injected into the `MyBean` constructor. convert the string to a `Resource` object to be injected into the `MyBean` constructor.
The following example demonstrates how to achieve this. The following example demonstrates how to achieve this.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Component @Component
public class MyBean { public class MyBean {
@ -552,12 +585,15 @@ The following example demonstrates how to achieve this.
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Component @Component
class MyBean(@Value("\${template.path}") private val template: Resource) class MyBean(@Value("\${template.path}") private val template: Resource)
---- ----
======
If we want to support multiple templates discovered under the same path in multiple If we want to support multiple templates discovered under the same path in multiple
locations in the classpath -- for example, in multiple jars in the classpath -- we can locations in the classpath -- for example, in multiple jars in the classpath -- we can
@ -566,8 +602,11 @@ use the special `classpath*:` prefix and wildcarding to define a `templates.path
Spring will convert the template path pattern into an array of `Resource` objects that Spring will convert the template path pattern into an array of `Resource` objects that
can be injected into the `MyBean` constructor. can be injected into the `MyBean` constructor.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Component @Component
public class MyBean { public class MyBean {
@ -581,12 +620,15 @@ can be injected into the `MyBean` constructor.
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Component @Component
class MyBean(@Value("\${templates.path}") private val templates: Resource[]) class MyBean(@Value("\${templates.path}") private val templates: Resource[])
---- ----
======
@ -611,31 +653,43 @@ that path and used to load the bean definitions depends on and is appropriate to
specific application context. For example, consider the following example, which creates a specific application context. For example, consider the following example, which creates a
`ClassPathXmlApplicationContext`: `ClassPathXmlApplicationContext`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
ApplicationContext ctx = new ClassPathXmlApplicationContext("conf/appContext.xml"); ApplicationContext ctx = new ClassPathXmlApplicationContext("conf/appContext.xml");
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val ctx = ClassPathXmlApplicationContext("conf/appContext.xml") val ctx = ClassPathXmlApplicationContext("conf/appContext.xml")
---- ----
======
The bean definitions are loaded from the classpath, because a `ClassPathResource` is The bean definitions are loaded from the classpath, because a `ClassPathResource` is
used. However, consider the following example, which creates a `FileSystemXmlApplicationContext`: used. However, consider the following example, which creates a `FileSystemXmlApplicationContext`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
ApplicationContext ctx = ApplicationContext ctx =
new FileSystemXmlApplicationContext("conf/appContext.xml"); new FileSystemXmlApplicationContext("conf/appContext.xml");
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val ctx = FileSystemXmlApplicationContext("conf/appContext.xml") val ctx = FileSystemXmlApplicationContext("conf/appContext.xml")
---- ----
======
Now the bean definitions are loaded from a filesystem location (in this case, relative to Now the bean definitions are loaded from a filesystem location (in this case, relative to
the current working directory). the current working directory).
@ -644,17 +698,23 @@ Note that the use of the special `classpath` prefix or a standard URL prefix on
location path overrides the default type of `Resource` created to load the bean location path overrides the default type of `Resource` created to load the bean
definitions. Consider the following example: definitions. Consider the following example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
ApplicationContext ctx = ApplicationContext ctx =
new FileSystemXmlApplicationContext("classpath:conf/appContext.xml"); new FileSystemXmlApplicationContext("classpath:conf/appContext.xml");
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val ctx = FileSystemXmlApplicationContext("classpath:conf/appContext.xml") val ctx = FileSystemXmlApplicationContext("classpath:conf/appContext.xml")
---- ----
======
Using `FileSystemXmlApplicationContext` loads the bean definitions from the classpath. Using `FileSystemXmlApplicationContext` loads the bean definitions from the classpath.
However, it is still a `FileSystemXmlApplicationContext`. If it is subsequently used as a However, it is still a `FileSystemXmlApplicationContext`. If it is subsequently used as a
@ -685,17 +745,23 @@ The following example shows how a `ClassPathXmlApplicationContext` instance comp
the beans defined in files named `services.xml` and `repositories.xml` (which are on the the beans defined in files named `services.xml` and `repositories.xml` (which are on the
classpath) can be instantiated: classpath) can be instantiated:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
ApplicationContext ctx = new ClassPathXmlApplicationContext( ApplicationContext ctx = new ClassPathXmlApplicationContext(
new String[] {"services.xml", "repositories.xml"}, MessengerService.class); new String[] {"services.xml", "repositories.xml"}, MessengerService.class);
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val ctx = ClassPathXmlApplicationContext(arrayOf("services.xml", "repositories.xml"), MessengerService::class.java) val ctx = ClassPathXmlApplicationContext(arrayOf("services.xml", "repositories.xml"), MessengerService::class.java)
---- ----
======
See the {api-spring-framework}/context/support/ClassPathXmlApplicationContext.html[`ClassPathXmlApplicationContext`] See the {api-spring-framework}/context/support/ClassPathXmlApplicationContext.html[`ClassPathXmlApplicationContext`]
javadoc for details on the various constructors. javadoc for details on the various constructors.
@ -773,17 +839,23 @@ coming from jars be thoroughly tested in your specific environment before you re
When constructing an XML-based application context, a location string may use the When constructing an XML-based application context, a location string may use the
special `classpath*:` prefix, as the following example shows: special `classpath*:` prefix, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
ApplicationContext ctx = ApplicationContext ctx =
new ClassPathXmlApplicationContext("classpath*:conf/appContext.xml"); new ClassPathXmlApplicationContext("classpath*:conf/appContext.xml");
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val ctx = ClassPathXmlApplicationContext("classpath*:conf/appContext.xml") val ctx = ClassPathXmlApplicationContext("classpath*:conf/appContext.xml")
---- ----
======
This special prefix specifies that all classpath resources that match the given name This special prefix specifies that all classpath resources that match the given name
must be obtained (internally, this essentially happens through a call to must be obtained (internally, this essentially happens through a call to
@ -879,87 +951,123 @@ For backwards compatibility (historical) reasons however, this changes when the
to treat all location paths as relative, whether they start with a leading slash or not. to treat all location paths as relative, whether they start with a leading slash or not.
In practice, this means the following examples are equivalent: In practice, this means the following examples are equivalent:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
ApplicationContext ctx = ApplicationContext ctx =
new FileSystemXmlApplicationContext("conf/context.xml"); new FileSystemXmlApplicationContext("conf/context.xml");
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val ctx = FileSystemXmlApplicationContext("conf/context.xml") val ctx = FileSystemXmlApplicationContext("conf/context.xml")
---- ----
======
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
ApplicationContext ctx = ApplicationContext ctx =
new FileSystemXmlApplicationContext("/conf/context.xml"); new FileSystemXmlApplicationContext("/conf/context.xml");
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val ctx = FileSystemXmlApplicationContext("/conf/context.xml") val ctx = FileSystemXmlApplicationContext("/conf/context.xml")
---- ----
======
The following examples are also equivalent (even though it would make sense for them to be different, as one The following examples are also equivalent (even though it would make sense for them to be different, as one
case is relative and the other absolute): case is relative and the other absolute):
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
FileSystemXmlApplicationContext ctx = ...; FileSystemXmlApplicationContext ctx = ...;
ctx.getResource("some/resource/path/myTemplate.txt"); ctx.getResource("some/resource/path/myTemplate.txt");
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val ctx: FileSystemXmlApplicationContext = ... val ctx: FileSystemXmlApplicationContext = ...
ctx.getResource("some/resource/path/myTemplate.txt") ctx.getResource("some/resource/path/myTemplate.txt")
---- ----
======
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
FileSystemXmlApplicationContext ctx = ...; FileSystemXmlApplicationContext ctx = ...;
ctx.getResource("/some/resource/path/myTemplate.txt"); ctx.getResource("/some/resource/path/myTemplate.txt");
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val ctx: FileSystemXmlApplicationContext = ... val ctx: FileSystemXmlApplicationContext = ...
ctx.getResource("/some/resource/path/myTemplate.txt") ctx.getResource("/some/resource/path/myTemplate.txt")
---- ----
======
In practice, if you need true absolute filesystem paths, you should avoid using In practice, if you need true absolute filesystem paths, you should avoid using
absolute paths with `FileSystemResource` or `FileSystemXmlApplicationContext` and absolute paths with `FileSystemResource` or `FileSystemXmlApplicationContext` and
force the use of a `UrlResource` by using the `file:` URL prefix. The following examples force the use of a `UrlResource` by using the `file:` URL prefix. The following examples
show how to do so: show how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
// actual context type doesn't matter, the Resource will always be UrlResource // actual context type doesn't matter, the Resource will always be UrlResource
ctx.getResource("file:///some/resource/path/myTemplate.txt"); ctx.getResource("file:///some/resource/path/myTemplate.txt");
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
// actual context type doesn't matter, the Resource will always be UrlResource // actual context type doesn't matter, the Resource will always be UrlResource
ctx.getResource("file:///some/resource/path/myTemplate.txt") ctx.getResource("file:///some/resource/path/myTemplate.txt")
---- ----
======
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
// force this FileSystemXmlApplicationContext to load its definition via a UrlResource // force this FileSystemXmlApplicationContext to load its definition via a UrlResource
ApplicationContext ctx = ApplicationContext ctx =
new FileSystemXmlApplicationContext("file:///conf/context.xml"); new FileSystemXmlApplicationContext("file:///conf/context.xml");
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
// force this FileSystemXmlApplicationContext to load its definition via a UrlResource // force this FileSystemXmlApplicationContext to load its definition via a UrlResource
val ctx = FileSystemXmlApplicationContext("file:///conf/context.xml") val ctx = FileSystemXmlApplicationContext("file:///conf/context.xml")
---- ----
======

View File

@ -23,19 +23,25 @@ For logging needs within application code, prefer direct use of Log4j 2.x, SLF4J
A `Log` implementation may be retrieved via `org.apache.commons.logging.LogFactory` as in A `Log` implementation may be retrieved via `org.apache.commons.logging.LogFactory` as in
the following example. the following example.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class MyBean { public class MyBean {
private final Log log = LogFactory.getLog(getClass()); private final Log log = LogFactory.getLog(getClass());
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class MyBean { class MyBean {
private val log = LogFactory.getLog(javaClass) private val log = LogFactory.getLog(javaClass)
// ... // ...
} }
---- ----
======

View File

@ -62,8 +62,11 @@ xref:core/validation/beans-beans.adoc#beans-beans-conversion[section on `Propert
The following two example classes use the `BeanWrapper` to get and set The following two example classes use the `BeanWrapper` to get and set
properties: properties:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class Company { public class Company {
@ -87,17 +90,23 @@ properties:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class Company { class Company {
var name: String? = null var name: String? = null
var managingDirector: Employee? = null var managingDirector: Employee? = null
} }
---- ----
======
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class Employee { public class Employee {
@ -122,20 +131,26 @@ properties:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class Employee { class Employee {
var name: String? = null var name: String? = null
var salary: Float? = null var salary: Float? = null
} }
---- ----
======
The following code snippets show some examples of how to retrieve and manipulate some of The following code snippets show some examples of how to retrieve and manipulate some of
the properties of instantiated ``Company``s and ``Employee``s: the properties of instantiated ``Company``s and ``Employee``s:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
BeanWrapper company = new BeanWrapperImpl(new Company()); BeanWrapper company = new BeanWrapperImpl(new Company());
// setting the company name.. // setting the company name..
@ -152,8 +167,10 @@ the properties of instantiated ``Company``s and ``Employee``s:
// retrieving the salary of the managingDirector through the company // retrieving the salary of the managingDirector through the company
Float salary = (Float) company.getPropertyValue("managingDirector.salary"); Float salary = (Float) company.getPropertyValue("managingDirector.salary");
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val company = BeanWrapperImpl(Company()) val company = BeanWrapperImpl(Company())
// setting the company name.. // setting the company name..
@ -170,6 +187,7 @@ the properties of instantiated ``Company``s and ``Employee``s:
// retrieving the salary of the managingDirector through the company // retrieving the salary of the managingDirector through the company
val salary = company.getPropertyValue("managingDirector.salary") as Float? val salary = company.getPropertyValue("managingDirector.salary") as Float?
---- ----
======
@ -308,8 +326,11 @@ com
The following Java source code for the referenced `SomethingBeanInfo` class The following Java source code for the referenced `SomethingBeanInfo` class
associates a `CustomNumberEditor` with the `age` property of the `Something` class: associates a `CustomNumberEditor` with the `age` property of the `Something` class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class SomethingBeanInfo extends SimpleBeanInfo { public class SomethingBeanInfo extends SimpleBeanInfo {
@ -330,8 +351,10 @@ associates a `CustomNumberEditor` with the `age` property of the `Something` cla
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class SomethingBeanInfo : SimpleBeanInfo() { class SomethingBeanInfo : SimpleBeanInfo() {
@ -351,6 +374,7 @@ associates a `CustomNumberEditor` with the `age` property of the `Something` cla
} }
} }
---- ----
======
[[beans-beans-conversion-customeditor-registration]] [[beans-beans-conversion-customeditor-registration]]
@ -390,8 +414,11 @@ support for additional `PropertyEditor` instances to an `ApplicationContext`.
Consider the following example, which defines a user class called `ExoticType` and Consider the following example, which defines a user class called `ExoticType` and
another class called `DependsOnExoticType`, which needs `ExoticType` set as a property: another class called `DependsOnExoticType`, which needs `ExoticType` set as a property:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"] [source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
---- ----
package example; package example;
@ -413,8 +440,10 @@ another class called `DependsOnExoticType`, which needs `ExoticType` set as a pr
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
---- ----
package example package example
@ -425,6 +454,7 @@ another class called `DependsOnExoticType`, which needs `ExoticType` set as a pr
var type: ExoticType? = null var type: ExoticType? = null
} }
---- ----
======
When things are properly set up, we want to be able to assign the type property as a When things are properly set up, we want to be able to assign the type property as a
string, which a `PropertyEditor` converts into an actual string, which a `PropertyEditor` converts into an actual
@ -439,8 +469,11 @@ string, which a `PropertyEditor` converts into an actual
The `PropertyEditor` implementation could look similar to the following: The `PropertyEditor` implementation could look similar to the following:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"] [source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
---- ----
package example; package example;
@ -454,8 +487,10 @@ The `PropertyEditor` implementation could look similar to the following:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
---- ----
package example package example
@ -469,6 +504,7 @@ The `PropertyEditor` implementation could look similar to the following:
} }
} }
---- ----
======
Finally, the following example shows how to use `CustomEditorConfigurer` to register the new `PropertyEditor` with the Finally, the following example shows how to use `CustomEditorConfigurer` to register the new `PropertyEditor` with the
`ApplicationContext`, which will then be able to use it as needed: `ApplicationContext`, which will then be able to use it as needed:
@ -504,8 +540,11 @@ instances for each bean creation attempt.
The following example shows how to create your own `PropertyEditorRegistrar` implementation: The following example shows how to create your own `PropertyEditorRegistrar` implementation:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"] [source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
---- ----
package com.foo.editors.spring; package com.foo.editors.spring;
@ -520,8 +559,10 @@ The following example shows how to create your own `PropertyEditorRegistrar` imp
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
---- ----
package com.foo.editors.spring package com.foo.editors.spring
@ -539,6 +580,7 @@ The following example shows how to create your own `PropertyEditorRegistrar` imp
} }
} }
---- ----
======
See also the `org.springframework.beans.support.ResourceEditorRegistrar` for an example See also the `org.springframework.beans.support.ResourceEditorRegistrar` for an example
`PropertyEditorRegistrar` implementation. Notice how in its implementation of the `PropertyEditorRegistrar` implementation. Notice how in its implementation of the
@ -566,8 +608,11 @@ using xref:web/webmvc.adoc#mvc[Spring's MVC web framework], using a `PropertyEdi
conjunction with data-binding web controllers can be very convenient. The following conjunction with data-binding web controllers can be very convenient. The following
example uses a `PropertyEditorRegistrar` in the implementation of an `@InitBinder` method: example uses a `PropertyEditorRegistrar` in the implementation of an `@InitBinder` method:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Controller @Controller
public class RegisterUserController { public class RegisterUserController {
@ -586,8 +631,10 @@ example uses a `PropertyEditorRegistrar` in the implementation of an `@InitBinde
// other methods related to registering a User // other methods related to registering a User
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Controller @Controller
class RegisterUserController( class RegisterUserController(
@ -601,6 +648,7 @@ example uses a `PropertyEditorRegistrar` in the implementation of an `@InitBinde
// other methods related to registering a User // other methods related to registering a User
} }
---- ----
======
This style of `PropertyEditor` registration can lead to concise code (the implementation This style of `PropertyEditor` registration can lead to concise code (the implementation
of the `@InitBinder` method is only one line long) and lets common `PropertyEditor` of the `@InitBinder` method is only one line long) and lets common `PropertyEditor`

View File

@ -16,27 +16,36 @@ built-in constraints, and you can also define your own custom constraints.
Consider the following example, which shows a simple `PersonForm` model with two properties: Consider the following example, which shows a simple `PersonForm` model with two properties:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class PersonForm { public class PersonForm {
private String name; private String name;
private int age; private int age;
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class PersonForm( class PersonForm(
private val name: String, private val name: String,
private val age: Int private val age: Int
) )
---- ----
======
Bean Validation lets you declare constraints as the following example shows: Bean Validation lets you declare constraints as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class PersonForm { public class PersonForm {
@ -48,8 +57,10 @@ Bean Validation lets you declare constraints as the following example shows:
private int age; private int age;
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class PersonForm( class PersonForm(
@get:NotNull @get:Size(max=64) @get:NotNull @get:Size(max=64)
@ -58,6 +69,7 @@ Bean Validation lets you declare constraints as the following example shows:
private val age: Int private val age: Int
) )
---- ----
======
A Bean Validation validator then validates instances of this class based on the declared A Bean Validation validator then validates instances of this class based on the declared
constraints. See https://beanvalidation.org/[Bean Validation] for general information about constraints. See https://beanvalidation.org/[Bean Validation] for general information about
@ -78,8 +90,11 @@ is needed in your application.
You can use the `LocalValidatorFactoryBean` to configure a default Validator as a Spring You can use the `LocalValidatorFactoryBean` to configure a default Validator as a Spring
bean, as the following example shows: bean, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
@ -92,12 +107,15 @@ bean, as the following example shows:
} }
} }
---- ----
XML::
+
[source,xml,indent=0,subs="verbatim,quotes",role="secondary"] [source,xml,indent=0,subs="verbatim,quotes",role="secondary"]
.XML
---- ----
<bean id="validator" <bean id="validator"
class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/> class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
---- ----
======
The basic configuration in the preceding example triggers bean validation to initialize by The basic configuration in the preceding example triggers bean validation to initialize by
using its default bootstrap mechanism. A Bean Validation provider, such as the Hibernate using its default bootstrap mechanism. A Bean Validation provider, such as the Hibernate
@ -115,8 +133,11 @@ validation logic.
You can inject a reference to `jakarta.validation.Validator` if you prefer to work with the Bean You can inject a reference to `jakarta.validation.Validator` if you prefer to work with the Bean
Validation API directly, as the following example shows: Validation API directly, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
import jakarta.validation.Validator; import jakarta.validation.Validator;
@ -127,20 +148,26 @@ Validation API directly, as the following example shows:
private Validator validator; private Validator validator;
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
import jakarta.validation.Validator; import jakarta.validation.Validator;
@Service @Service
class MyService(@Autowired private val validator: Validator) class MyService(@Autowired private val validator: Validator)
---- ----
======
You can inject a reference to `org.springframework.validation.Validator` if your bean You can inject a reference to `org.springframework.validation.Validator` if your bean
requires the Spring Validation API, as the following example shows: requires the Spring Validation API, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
import org.springframework.validation.Validator; import org.springframework.validation.Validator;
@ -151,14 +178,17 @@ requires the Spring Validation API, as the following example shows:
private Validator validator; private Validator validator;
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
import org.springframework.validation.Validator import org.springframework.validation.Validator
@Service @Service
class MyService(@Autowired private val validator: Validator) class MyService(@Autowired private val validator: Validator)
---- ----
======
[[validation-beanvalidation-spring-constraints]] [[validation-beanvalidation-spring-constraints]]
@ -182,8 +212,11 @@ that uses Spring to create `ConstraintValidator` instances. This lets your custo
The following example shows a custom `@Constraint` declaration followed by an associated The following example shows a custom `@Constraint` declaration followed by an associated
`ConstraintValidator` implementation that uses Spring for dependency injection: `ConstraintValidator` implementation that uses Spring for dependency injection:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Target({ElementType.METHOD, ElementType.FIELD}) @Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@ -191,17 +224,23 @@ The following example shows a custom `@Constraint` declaration followed by an as
public @interface MyConstraint { public @interface MyConstraint {
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.FIELD) @Target(AnnotationTarget.FUNCTION, AnnotationTarget.FIELD)
@Retention(AnnotationRetention.RUNTIME) @Retention(AnnotationRetention.RUNTIME)
@Constraint(validatedBy = MyConstraintValidator::class) @Constraint(validatedBy = MyConstraintValidator::class)
annotation class MyConstraint annotation class MyConstraint
---- ----
======
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidator;
@ -213,8 +252,10 @@ The following example shows a custom `@Constraint` declaration followed by an as
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
import jakarta.validation.ConstraintValidator import jakarta.validation.ConstraintValidator
@ -223,6 +264,7 @@ The following example shows a custom `@Constraint` declaration followed by an as
// ... // ...
} }
---- ----
======
As the preceding example shows, a `ConstraintValidator` implementation can have its dependencies As the preceding example shows, a `ConstraintValidator` implementation can have its dependencies
@ -236,8 +278,11 @@ You can integrate the method validation feature supported by Bean Validation 1.1
a custom extension, also by Hibernate Validator 4.3) into a Spring context through a a custom extension, also by Hibernate Validator 4.3) into a Spring context through a
`MethodValidationPostProcessor` bean definition: `MethodValidationPostProcessor` bean definition:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor; import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
@ -251,11 +296,14 @@ a custom extension, also by Hibernate Validator 4.3) into a Spring context throu
} }
---- ----
XML::
+
[source,xml,indent=0,subs="verbatim,quotes",role="secondary"] [source,xml,indent=0,subs="verbatim,quotes",role="secondary"]
.XML
---- ----
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"/> <bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"/>
---- ----
======
To be eligible for Spring-driven method validation, all target classes need to be annotated To be eligible for Spring-driven method validation, all target classes need to be annotated
with Spring's `@Validated` annotation, which can optionally also declare the validation with Spring's `@Validated` annotation, which can optionally also declare the validation
@ -296,8 +344,11 @@ automatically added to the binder's `BindingResult`.
The following example shows how to use a `DataBinder` programmatically to invoke validation The following example shows how to use a `DataBinder` programmatically to invoke validation
logic after binding to a target object: logic after binding to a target object:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
Foo target = new Foo(); Foo target = new Foo();
DataBinder binder = new DataBinder(target); DataBinder binder = new DataBinder(target);
@ -312,8 +363,10 @@ logic after binding to a target object:
// get BindingResult that includes any validation errors // get BindingResult that includes any validation errors
BindingResult results = binder.getBindingResult(); BindingResult results = binder.getBindingResult();
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val target = Foo() val target = Foo()
val binder = DataBinder(target) val binder = DataBinder(target)
@ -328,6 +381,7 @@ logic after binding to a target object:
// get BindingResult that includes any validation errors // get BindingResult that includes any validation errors
val results = binder.bindingResult val results = binder.bindingResult
---- ----
======
You can also configure a `DataBinder` with multiple `Validator` instances through You can also configure a `DataBinder` with multiple `Validator` instances through
`dataBinder.addValidators` and `dataBinder.replaceValidators`. This is useful when `dataBinder.addValidators` and `dataBinder.replaceValidators`. This is useful when

View File

@ -259,8 +259,11 @@ xref:core/validation/format.adoc#format-FormatterRegistry-SPI[The `FormatterRegi
To work with a `ConversionService` instance programmatically, you can inject a reference to To work with a `ConversionService` instance programmatically, you can inject a reference to
it like you would for any other bean. The following example shows how to do so: it like you would for any other bean. The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Service @Service
public class MyService { public class MyService {
@ -274,8 +277,10 @@ it like you would for any other bean. The following example shows how to do so:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Service @Service
class MyService(private val conversionService: ConversionService) { class MyService(private val conversionService: ConversionService) {
@ -285,6 +290,7 @@ it like you would for any other bean. The following example shows how to do so:
} }
} }
---- ----
======
For most use cases, you can use the `convert` method that specifies the `targetType`, but it For most use cases, you can use the `convert` method that specifies the `targetType`, but it
does not work with more complex types, such as a collection of a parameterized element. does not work with more complex types, such as a collection of a parameterized element.
@ -294,8 +300,11 @@ you need to provide a formal definition of the source and target types.
Fortunately, `TypeDescriptor` provides various options to make doing so straightforward, Fortunately, `TypeDescriptor` provides various options to make doing so straightforward,
as the following example shows: as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
DefaultConversionService cs = new DefaultConversionService(); DefaultConversionService cs = new DefaultConversionService();
@ -304,8 +313,10 @@ as the following example shows:
TypeDescriptor.forObject(input), // List<Integer> type descriptor TypeDescriptor.forObject(input), // List<Integer> type descriptor
TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(String.class))); TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(String.class)));
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val cs = DefaultConversionService() val cs = DefaultConversionService()
@ -314,6 +325,7 @@ as the following example shows:
TypeDescriptor.forObject(input), // List<Integer> type descriptor TypeDescriptor.forObject(input), // List<Integer> type descriptor
TypeDescriptor.collection(List::class.java, TypeDescriptor.valueOf(String::class.java))) TypeDescriptor.collection(List::class.java, TypeDescriptor.valueOf(String::class.java)))
---- ----
======
Note that `DefaultConversionService` automatically registers converters that are Note that `DefaultConversionService` automatically registers converters that are
appropriate for most environments. This includes collection converters, scalar appropriate for most environments. This includes collection converters, scalar

View File

@ -13,8 +13,11 @@ formatters manually with the help of:
For example, the following Java configuration registers a global `yyyyMMdd` format: For example, the following Java configuration registers a global `yyyyMMdd` format:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Configuration @Configuration
public class AppConfig { public class AppConfig {
@ -44,8 +47,10 @@ For example, the following Java configuration registers a global `yyyyMMdd` form
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Configuration @Configuration
class AppConfig { class AppConfig {
@ -71,6 +76,7 @@ For example, the following Java configuration registers a global `yyyyMMdd` form
} }
} }
---- ----
======
If you prefer XML-based configuration, you can use a If you prefer XML-based configuration, you can use a
`FormattingConversionServiceFactoryBean`. The following example shows how to do so: `FormattingConversionServiceFactoryBean`. The following example shows how to do so:

View File

@ -78,8 +78,11 @@ a `java.text.DateFormat`.
The following `DateFormatter` is an example `Formatter` implementation: The following `DateFormatter` is an example `Formatter` implementation:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"] [source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
---- ----
package org.springframework.format.datetime; package org.springframework.format.datetime;
@ -112,8 +115,10 @@ The following `DateFormatter` is an example `Formatter` implementation:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
---- ----
class DateFormatter(private val pattern: String) : Formatter<Date> { class DateFormatter(private val pattern: String) : Formatter<Date> {
@ -131,6 +136,7 @@ The following `DateFormatter` is an example `Formatter` implementation:
} }
} }
---- ----
======
The Spring team welcomes community-driven `Formatter` contributions. See The Spring team welcomes community-driven `Formatter` contributions. See
https://github.com/spring-projects/spring-framework/issues[GitHub Issues] to contribute. https://github.com/spring-projects/spring-framework/issues[GitHub Issues] to contribute.
@ -169,8 +175,11 @@ formatting logic -- for example `org.springframework.format.annotation.DateTime
The following example `AnnotationFormatterFactory` implementation binds the `@NumberFormat` The following example `AnnotationFormatterFactory` implementation binds the `@NumberFormat`
annotation to a formatter to let a number style or pattern be specified: annotation to a formatter to let a number style or pattern be specified:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public final class NumberFormatAnnotationFormatterFactory public final class NumberFormatAnnotationFormatterFactory
implements AnnotationFormatterFactory<NumberFormat> { implements AnnotationFormatterFactory<NumberFormat> {
@ -204,8 +213,10 @@ annotation to a formatter to let a number style or pattern be specified:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class NumberFormatAnnotationFormatterFactory : AnnotationFormatterFactory<NumberFormat> { class NumberFormatAnnotationFormatterFactory : AnnotationFormatterFactory<NumberFormat> {
@ -235,12 +246,16 @@ annotation to a formatter to let a number style or pattern be specified:
} }
} }
---- ----
======
To trigger formatting, you can annotate fields with `@NumberFormat`, as the following To trigger formatting, you can annotate fields with `@NumberFormat`, as the following
example shows: example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class MyModel { public class MyModel {
@ -248,13 +263,16 @@ example shows:
private BigDecimal decimal; private BigDecimal decimal;
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class MyModel( class MyModel(
@field:NumberFormat(style = Style.CURRENCY) private val decimal: BigDecimal @field:NumberFormat(style = Style.CURRENCY) private val decimal: BigDecimal
) )
---- ----
======
[[format-annotations-api]] [[format-annotations-api]]
@ -268,8 +286,11 @@ package. You can use `@NumberFormat` to format `Number` fields such as `Double`
The following example uses `@DateTimeFormat` to format a `java.util.Date` as an ISO Date The following example uses `@DateTimeFormat` to format a `java.util.Date` as an ISO Date
(yyyy-MM-dd): (yyyy-MM-dd):
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class MyModel { public class MyModel {
@ -277,13 +298,16 @@ The following example uses `@DateTimeFormat` to format a `java.util.Date` as an
private Date date; private Date date;
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class MyModel( class MyModel(
@DateTimeFormat(iso=ISO.DATE) private val date: Date @DateTimeFormat(iso=ISO.DATE) private val date: Date
) )
---- ----
======
[[format-FormatterRegistry-SPI]] [[format-FormatterRegistry-SPI]]

View File

@ -7,8 +7,11 @@ validators can report validation failures to the `Errors` object.
Consider the following example of a small data object: Consider the following example of a small data object:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class Person { public class Person {
@ -18,11 +21,14 @@ Consider the following example of a small data object:
// the usual getters and setters... // the usual getters and setters...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class Person(val name: String, val age: Int) class Person(val name: String, val age: Int)
---- ----
======
The next example provides validation behavior for the `Person` class by implementing the The next example provides validation behavior for the `Person` class by implementing the
following two methods of the `org.springframework.validation.Validator` interface: following two methods of the `org.springframework.validation.Validator` interface:
@ -35,8 +41,11 @@ Implementing a `Validator` is fairly straightforward, especially when you know o
`ValidationUtils` helper class that the Spring Framework also provides. The following `ValidationUtils` helper class that the Spring Framework also provides. The following
example implements `Validator` for `Person` instances: example implements `Validator` for `Person` instances:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class PersonValidator implements Validator { public class PersonValidator implements Validator {
@ -58,8 +67,10 @@ example implements `Validator` for `Person` instances:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class PersonValidator : Validator { class PersonValidator : Validator {
@ -81,6 +92,7 @@ example implements `Validator` for `Person` instances:
} }
} }
---- ----
======
The `static` `rejectIfEmpty(..)` method on the `ValidationUtils` class is used to The `static` `rejectIfEmpty(..)` method on the `ValidationUtils` class is used to
reject the `name` property if it is `null` or the empty string. Have a look at the reject the `name` property if it is `null` or the empty string. Have a look at the
@ -98,8 +110,11 @@ within the `AddressValidator` class without resorting to copy-and-paste, you can
dependency-inject or instantiate an `AddressValidator` within your `CustomerValidator`, dependency-inject or instantiate an `AddressValidator` within your `CustomerValidator`,
as the following example shows: as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class CustomerValidator implements Validator { public class CustomerValidator implements Validator {
@ -137,8 +152,10 @@ as the following example shows:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class CustomerValidator(private val addressValidator: Validator) : Validator { class CustomerValidator(private val addressValidator: Validator) : Validator {
@ -171,6 +188,7 @@ as the following example shows:
} }
} }
---- ----
======
Validation errors are reported to the `Errors` object passed to the validator. In the case Validation errors are reported to the `Errors` object passed to the validator. In the case
of Spring Web MVC, you can use the `<spring:bind/>` tag to inspect the error messages, but of Spring Web MVC, you can use the `<spring:bind/>` tag to inspect the error messages, but

View File

@ -52,14 +52,18 @@ lets the component scanning support find and configure your DAOs and repositorie
without having to provide XML configuration entries for them. The following example shows without having to provide XML configuration entries for them. The following example shows
how to use the `@Repository` annotation: how to use the `@Repository` annotation:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Repository // <1> @Repository // <1>
public class SomeMovieFinder implements MovieFinder { public class SomeMovieFinder implements MovieFinder {
// ... // ...
} }
---- ----
======
<1> The `@Repository` annotation. <1> The `@Repository` annotation.
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
@ -80,8 +84,11 @@ needs access to a JDBC `DataSource`, and a JPA-based repository needs access to
injected by using one of the `@Autowired`, `@Inject`, `@Resource` or `@PersistenceContext` injected by using one of the `@Autowired`, `@Inject`, `@Resource` or `@PersistenceContext`
annotations. The following example works for a JPA repository: annotations. The following example works for a JPA repository:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Repository @Repository
public class JpaMovieFinder implements MovieFinder { public class JpaMovieFinder implements MovieFinder {
@ -93,8 +100,9 @@ annotations. The following example works for a JPA repository:
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Repository @Repository
class JpaMovieFinder : MovieFinder { class JpaMovieFinder : MovieFinder {
@ -105,13 +113,17 @@ annotations. The following example works for a JPA repository:
// ... // ...
} }
---- ----
======
If you use the classic Hibernate APIs, you can inject `SessionFactory`, as the following If you use the classic Hibernate APIs, you can inject `SessionFactory`, as the following
example shows: example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Repository @Repository
public class HibernateMovieFinder implements MovieFinder { public class HibernateMovieFinder implements MovieFinder {
@ -126,22 +138,28 @@ example shows:
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Repository @Repository
class HibernateMovieFinder(private val sessionFactory: SessionFactory) : MovieFinder { class HibernateMovieFinder(private val sessionFactory: SessionFactory) : MovieFinder {
// ... // ...
} }
---- ----
======
The last example we show here is for typical JDBC support. You could have the The last example we show here is for typical JDBC support. You could have the
`DataSource` injected into an initialization method or a constructor, where you would create a `DataSource` injected into an initialization method or a constructor, where you would create a
`JdbcTemplate` and other data access support classes (such as `SimpleJdbcCall` and others) by using `JdbcTemplate` and other data access support classes (such as `SimpleJdbcCall` and others) by using
this `DataSource`. The following example autowires a `DataSource`: this `DataSource`. The following example autowires a `DataSource`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Repository @Repository
public class JdbcMovieFinder implements MovieFinder { public class JdbcMovieFinder implements MovieFinder {
@ -156,8 +174,10 @@ this `DataSource`. The following example autowires a `DataSource`:
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Repository @Repository
class JdbcMovieFinder(dataSource: DataSource) : MovieFinder { class JdbcMovieFinder(dataSource: DataSource) : MovieFinder {
@ -167,6 +187,7 @@ this `DataSource`. The following example autowires a `DataSource`:
// ... // ...
} }
---- ----
======
NOTE: See the specific coverage of each persistence technology for details on how to NOTE: See the specific coverage of each persistence technology for details on how to
configure the application context to take advantage of these annotations. configure the application context to take advantage of these annotations.

View File

@ -17,8 +17,11 @@ the prepared statement. This method is called the number of times that you
specified in the `getBatchSize` call. The following example updates the `t_actor` table specified in the `getBatchSize` call. The following example updates the `t_actor` table
based on entries in a list, and the entire list is used as the batch: based on entries in a list, and the entire list is used as the batch:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class JdbcActorDao implements ActorDao { public class JdbcActorDao implements ActorDao {
@ -47,8 +50,10 @@ based on entries in a list, and the entire list is used as the batch:
// ... additional methods // ... additional methods
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class JdbcActorDao(dataSource: DataSource) : ActorDao { class JdbcActorDao(dataSource: DataSource) : ActorDao {
@ -71,6 +76,7 @@ based on entries in a list, and the entire list is used as the batch:
// ... additional methods // ... additional methods
} }
---- ----
======
If you process a stream of updates or reading from a file, you might have a If you process a stream of updates or reading from a file, you might have a
preferred batch size, but the last batch might not have that number of entries. In this preferred batch size, but the last batch might not have that number of entries. In this
@ -94,8 +100,11 @@ in an array of bean-style objects (with getter methods corresponding to paramete
The following example shows a batch update using named parameters: The following example shows a batch update using named parameters:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class JdbcActorDao implements ActorDao { public class JdbcActorDao implements ActorDao {
@ -114,8 +123,10 @@ The following example shows a batch update using named parameters:
// ... additional methods // ... additional methods
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class JdbcActorDao(dataSource: DataSource) : ActorDao { class JdbcActorDao(dataSource: DataSource) : ActorDao {
@ -130,6 +141,7 @@ The following example shows a batch update using named parameters:
// ... additional methods // ... additional methods
} }
---- ----
======
For an SQL statement that uses the classic `?` placeholders, you pass in a list For an SQL statement that uses the classic `?` placeholders, you pass in a list
containing an object array with the update values. This object array must have one entry containing an object array with the update values. This object array must have one entry
@ -139,8 +151,11 @@ defined in the SQL statement.
The following example is the same as the preceding example, except that it uses classic The following example is the same as the preceding example, except that it uses classic
JDBC `?` placeholders: JDBC `?` placeholders:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class JdbcActorDao implements ActorDao { public class JdbcActorDao implements ActorDao {
@ -165,8 +180,10 @@ JDBC `?` placeholders:
// ... additional methods // ... additional methods
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class JdbcActorDao(dataSource: DataSource) : ActorDao { class JdbcActorDao(dataSource: DataSource) : ActorDao {
@ -184,6 +201,7 @@ JDBC `?` placeholders:
// ... additional methods // ... additional methods
} }
---- ----
======
All of the batch update methods that we described earlier return an `int` array All of the batch update methods that we described earlier return an `int` array
containing the number of affected rows for each batch entry. This count is reported by containing the number of affected rows for each batch entry. This count is reported by
@ -223,8 +241,11 @@ update calls into batches of the size specified.
The following example shows a batch update that uses a batch size of 100: The following example shows a batch update that uses a batch size of 100:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class JdbcActorDao implements ActorDao { public class JdbcActorDao implements ActorDao {
@ -250,8 +271,10 @@ The following example shows a batch update that uses a batch size of 100:
// ... additional methods // ... additional methods
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class JdbcActorDao(dataSource: DataSource) : ActorDao { class JdbcActorDao(dataSource: DataSource) : ActorDao {
@ -270,6 +293,7 @@ The following example shows a batch update that uses a batch size of 100:
// ... additional methods // ... additional methods
} }
---- ----
======
The batch update method for this call returns an array of `int` arrays that contains an The batch update method for this call returns an array of `int` arrays that contains an
array entry for each batch with an array of the number of affected rows for each update. array entry for each batch with an array of the number of affected rows for each update.

View File

@ -48,8 +48,11 @@ To configure a `DriverManagerDataSource`:
The following example shows how to configure a `DriverManagerDataSource` in Java: The following example shows how to configure a `DriverManagerDataSource` in Java:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
DriverManagerDataSource dataSource = new DriverManagerDataSource(); DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.hsqldb.jdbcDriver"); dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
@ -57,8 +60,10 @@ The following example shows how to configure a `DriverManagerDataSource` in Java
dataSource.setUsername("sa"); dataSource.setUsername("sa");
dataSource.setPassword(""); dataSource.setPassword("");
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val dataSource = DriverManagerDataSource().apply { val dataSource = DriverManagerDataSource().apply {
setDriverClassName("org.hsqldb.jdbcDriver") setDriverClassName("org.hsqldb.jdbcDriver")
@ -67,6 +72,7 @@ The following example shows how to configure a `DriverManagerDataSource` in Java
password = "" password = ""
} }
---- ----
======
The following example shows the corresponding XML configuration: The following example shows the corresponding XML configuration:

View File

@ -57,54 +57,75 @@ See the attendant {api-spring-framework}/jdbc/core/JdbcTemplate.html[javadoc] fo
The following query gets the number of rows in a relation: The following query gets the number of rows in a relation:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
int rowCount = this.jdbcTemplate.queryForObject("select count(*) from t_actor", Integer.class); int rowCount = this.jdbcTemplate.queryForObject("select count(*) from t_actor", Integer.class);
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val rowCount = jdbcTemplate.queryForObject<Int>("select count(*) from t_actor")!! val rowCount = jdbcTemplate.queryForObject<Int>("select count(*) from t_actor")!!
---- ----
======
The following query uses a bind variable: The following query uses a bind variable:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
int countOfActorsNamedJoe = this.jdbcTemplate.queryForObject( int countOfActorsNamedJoe = this.jdbcTemplate.queryForObject(
"select count(*) from t_actor where first_name = ?", Integer.class, "Joe"); "select count(*) from t_actor where first_name = ?", Integer.class, "Joe");
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val countOfActorsNamedJoe = jdbcTemplate.queryForObject<Int>( val countOfActorsNamedJoe = jdbcTemplate.queryForObject<Int>(
"select count(*) from t_actor where first_name = ?", arrayOf("Joe"))!! "select count(*) from t_actor where first_name = ?", arrayOf("Joe"))!!
---- ----
======
The following query looks for a `String`: The following query looks for a `String`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
String lastName = this.jdbcTemplate.queryForObject( String lastName = this.jdbcTemplate.queryForObject(
"select last_name from t_actor where id = ?", "select last_name from t_actor where id = ?",
String.class, 1212L); String.class, 1212L);
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val lastName = this.jdbcTemplate.queryForObject<String>( val lastName = this.jdbcTemplate.queryForObject<String>(
"select last_name from t_actor where id = ?", "select last_name from t_actor where id = ?",
arrayOf(1212L))!! arrayOf(1212L))!!
---- ----
======
The following query finds and populates a single domain object: The following query finds and populates a single domain object:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
Actor actor = jdbcTemplate.queryForObject( Actor actor = jdbcTemplate.queryForObject(
"select first_name, last_name from t_actor where id = ?", "select first_name, last_name from t_actor where id = ?",
@ -116,8 +137,10 @@ The following query finds and populates a single domain object:
}, },
1212L); 1212L);
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val actor = jdbcTemplate.queryForObject( val actor = jdbcTemplate.queryForObject(
"select first_name, last_name from t_actor where id = ?", "select first_name, last_name from t_actor where id = ?",
@ -125,11 +148,15 @@ The following query finds and populates a single domain object:
Actor(rs.getString("first_name"), rs.getString("last_name")) Actor(rs.getString("first_name"), rs.getString("last_name"))
} }
---- ----
======
The following query finds and populates a list of domain objects: The following query finds and populates a list of domain objects:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
List<Actor> actors = this.jdbcTemplate.query( List<Actor> actors = this.jdbcTemplate.query(
"select first_name, last_name from t_actor", "select first_name, last_name from t_actor",
@ -140,20 +167,26 @@ The following query finds and populates a list of domain objects:
return actor; return actor;
}); });
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val actors = jdbcTemplate.query("select first_name, last_name from t_actor") { rs, _ -> val actors = jdbcTemplate.query("select first_name, last_name from t_actor") { rs, _ ->
Actor(rs.getString("first_name"), rs.getString("last_name")) Actor(rs.getString("first_name"), rs.getString("last_name"))
---- ----
======
If the last two snippets of code actually existed in the same application, it would make If the last two snippets of code actually existed in the same application, it would make
sense to remove the duplication present in the two `RowMapper` lambda expressions and sense to remove the duplication present in the two `RowMapper` lambda expressions and
extract them out into a single field that could then be referenced by DAO methods as needed. extract them out into a single field that could then be referenced by DAO methods as needed.
For example, it may be better to write the preceding code snippet as follows: For example, it may be better to write the preceding code snippet as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
private final RowMapper<Actor> actorRowMapper = (resultSet, rowNum) -> { private final RowMapper<Actor> actorRowMapper = (resultSet, rowNum) -> {
Actor actor = new Actor(); Actor actor = new Actor();
@ -166,8 +199,10 @@ For example, it may be better to write the preceding code snippet as follows:
return this.jdbcTemplate.query("select first_name, last_name from t_actor", actorRowMapper); return this.jdbcTemplate.query("select first_name, last_name from t_actor", actorRowMapper);
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val actorMapper = RowMapper<Actor> { rs: ResultSet, rowNum: Int -> val actorMapper = RowMapper<Actor> { rs: ResultSet, rowNum: Int ->
Actor(rs.getString("first_name"), rs.getString("last_name")) Actor(rs.getString("first_name"), rs.getString("last_name"))
@ -177,6 +212,7 @@ For example, it may be better to write the preceding code snippet as follows:
return jdbcTemplate.query("select first_name, last_name from t_actor", actorMapper) return jdbcTemplate.query("select first_name, last_name from t_actor", actorMapper)
} }
---- ----
======
[[jdbc-JdbcTemplate-examples-update]] [[jdbc-JdbcTemplate-examples-update]]
=== Updating (`INSERT`, `UPDATE`, and `DELETE`) with `JdbcTemplate` === Updating (`INSERT`, `UPDATE`, and `DELETE`) with `JdbcTemplate`
@ -186,52 +222,70 @@ Parameter values are usually provided as variable arguments or, alternatively, a
The following example inserts a new entry: The following example inserts a new entry:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
this.jdbcTemplate.update( this.jdbcTemplate.update(
"insert into t_actor (first_name, last_name) values (?, ?)", "insert into t_actor (first_name, last_name) values (?, ?)",
"Leonor", "Watling"); "Leonor", "Watling");
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
jdbcTemplate.update( jdbcTemplate.update(
"insert into t_actor (first_name, last_name) values (?, ?)", "insert into t_actor (first_name, last_name) values (?, ?)",
"Leonor", "Watling") "Leonor", "Watling")
---- ----
======
The following example updates an existing entry: The following example updates an existing entry:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
this.jdbcTemplate.update( this.jdbcTemplate.update(
"update t_actor set last_name = ? where id = ?", "update t_actor set last_name = ? where id = ?",
"Banjo", 5276L); "Banjo", 5276L);
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
jdbcTemplate.update( jdbcTemplate.update(
"update t_actor set last_name = ? where id = ?", "update t_actor set last_name = ? where id = ?",
"Banjo", 5276L) "Banjo", 5276L)
---- ----
======
The following example deletes an entry: The following example deletes an entry:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
this.jdbcTemplate.update( this.jdbcTemplate.update(
"delete from t_actor where id = ?", "delete from t_actor where id = ?",
Long.valueOf(actorId)); Long.valueOf(actorId));
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
jdbcTemplate.update("delete from t_actor where id = ?", actorId.toLong()) jdbcTemplate.update("delete from t_actor where id = ?", actorId.toLong())
---- ----
======
[[jdbc-JdbcTemplate-examples-other]] [[jdbc-JdbcTemplate-examples-other]]
=== Other `JdbcTemplate` Operations === Other `JdbcTemplate` Operations
@ -241,33 +295,45 @@ method is often used for DDL statements. It is heavily overloaded with variants
callback interfaces, binding variable arrays, and so on. The following example creates a callback interfaces, binding variable arrays, and so on. The following example creates a
table: table:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))"); this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))");
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
jdbcTemplate.execute("create table mytable (id integer, name varchar(100))") jdbcTemplate.execute("create table mytable (id integer, name varchar(100))")
---- ----
======
The following example invokes a stored procedure: The following example invokes a stored procedure:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
this.jdbcTemplate.update( this.jdbcTemplate.update(
"call SUPPORT.REFRESH_ACTORS_SUMMARY(?)", "call SUPPORT.REFRESH_ACTORS_SUMMARY(?)",
Long.valueOf(unionId)); Long.valueOf(unionId));
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
jdbcTemplate.update( jdbcTemplate.update(
"call SUPPORT.REFRESH_ACTORS_SUMMARY(?)", "call SUPPORT.REFRESH_ACTORS_SUMMARY(?)",
unionId.toLong()) unionId.toLong())
---- ----
======
More sophisticated stored procedure support is xref:data-access/jdbc/object.adoc#jdbc-StoredProcedure[covered later]. More sophisticated stored procedure support is xref:data-access/jdbc/object.adoc#jdbc-StoredProcedure[covered later].
@ -288,8 +354,11 @@ that shared `DataSource` bean into your DAO classes. The `JdbcTemplate` is creat
the setter for the `DataSource`. This leads to DAOs that resemble the following: the setter for the `DataSource`. This leads to DAOs that resemble the following:
-- --
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class JdbcCorporateEventDao implements CorporateEventDao { public class JdbcCorporateEventDao implements CorporateEventDao {
@ -302,8 +371,10 @@ the setter for the `DataSource`. This leads to DAOs that resemble the following:
// JDBC-backed implementations of the methods on the CorporateEventDao follow... // JDBC-backed implementations of the methods on the CorporateEventDao follow...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class JdbcCorporateEventDao(dataSource: DataSource) : CorporateEventDao { class JdbcCorporateEventDao(dataSource: DataSource) : CorporateEventDao {
@ -312,6 +383,7 @@ the setter for the `DataSource`. This leads to DAOs that resemble the following:
// JDBC-backed implementations of the methods on the CorporateEventDao follow... // JDBC-backed implementations of the methods on the CorporateEventDao follow...
} }
---- ----
======
-- --
The following example shows the corresponding XML configuration: The following example shows the corresponding XML configuration:
@ -350,8 +422,11 @@ support for dependency injection. In this case, you can annotate the class with
method with `@Autowired`. The following example shows how to do so: method with `@Autowired`. The following example shows how to do so:
-- --
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Repository // <1> @Repository // <1>
public class JdbcCorporateEventDao implements CorporateEventDao { public class JdbcCorporateEventDao implements CorporateEventDao {
@ -366,6 +441,7 @@ method with `@Autowired`. The following example shows how to do so:
// JDBC-backed implementations of the methods on the CorporateEventDao follow... // JDBC-backed implementations of the methods on the CorporateEventDao follow...
} }
---- ----
======
<1> Annotate the class with `@Repository`. <1> Annotate the class with `@Repository`.
<2> Annotate the `DataSource` setter method with `@Autowired`. <2> Annotate the `DataSource` setter method with `@Autowired`.
<3> Create a new `JdbcTemplate` with the `DataSource`. <3> Create a new `JdbcTemplate` with the `DataSource`.
@ -440,8 +516,11 @@ section describes only those areas of the `NamedParameterJdbcTemplate` class tha
from the `JdbcTemplate` itself -- namely, programming JDBC statements by using named from the `JdbcTemplate` itself -- namely, programming JDBC statements by using named
parameters. The following example shows how to use `NamedParameterJdbcTemplate`: parameters. The following example shows how to use `NamedParameterJdbcTemplate`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
// some JDBC-backed DAO class... // some JDBC-backed DAO class...
private NamedParameterJdbcTemplate namedParameterJdbcTemplate; private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
@ -460,8 +539,9 @@ parameters. The following example shows how to use `NamedParameterJdbcTemplate`:
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
private val namedParameterJdbcTemplate = NamedParameterJdbcTemplate(dataSource) private val namedParameterJdbcTemplate = NamedParameterJdbcTemplate(dataSource)
@ -471,6 +551,7 @@ parameters. The following example shows how to use `NamedParameterJdbcTemplate`:
return namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Int::class.java)!! return namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Int::class.java)!!
} }
---- ----
======
Notice the use of the named parameter notation in the value assigned to the `sql` Notice the use of the named parameter notation in the value assigned to the `sql`
variable and the corresponding value that is plugged into the `namedParameters` variable and the corresponding value that is plugged into the `namedParameters`
@ -483,8 +564,11 @@ methods exposed by the `NamedParameterJdbcOperations` and implemented by the
The following example shows the use of the `Map`-based style: The following example shows the use of the `Map`-based style:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
// some JDBC-backed DAO class... // some JDBC-backed DAO class...
private NamedParameterJdbcTemplate namedParameterJdbcTemplate; private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
@ -502,8 +586,10 @@ The following example shows the use of the `Map`-based style:
return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class); return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
// some JDBC-backed DAO class... // some JDBC-backed DAO class...
private val namedParameterJdbcTemplate = NamedParameterJdbcTemplate(dataSource) private val namedParameterJdbcTemplate = NamedParameterJdbcTemplate(dataSource)
@ -514,6 +600,7 @@ The following example shows the use of the `Map`-based style:
return namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Int::class.java)!! return namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Int::class.java)!!
} }
---- ----
======
One nice feature related to the `NamedParameterJdbcTemplate` (and existing in the same One nice feature related to the `NamedParameterJdbcTemplate` (and existing in the same
Java package) is the `SqlParameterSource` interface. You have already seen an example of Java package) is the `SqlParameterSource` interface. You have already seen an example of
@ -531,8 +618,11 @@ of named parameter values.
The following example shows a typical JavaBean: The following example shows a typical JavaBean:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class Actor { public class Actor {
@ -556,17 +646,23 @@ The following example shows a typical JavaBean:
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
data class Actor(val id: Long, val firstName: String, val lastName: String) data class Actor(val id: Long, val firstName: String, val lastName: String)
---- ----
======
The following example uses a `NamedParameterJdbcTemplate` to return the count of the The following example uses a `NamedParameterJdbcTemplate` to return the count of the
members of the class shown in the preceding example: members of the class shown in the preceding example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
// some JDBC-backed DAO class... // some JDBC-backed DAO class...
private NamedParameterJdbcTemplate namedParameterJdbcTemplate; private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
@ -585,8 +681,10 @@ members of the class shown in the preceding example:
return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class); return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
// some JDBC-backed DAO class... // some JDBC-backed DAO class...
private val namedParameterJdbcTemplate = NamedParameterJdbcTemplate(dataSource) private val namedParameterJdbcTemplate = NamedParameterJdbcTemplate(dataSource)
@ -600,6 +698,7 @@ members of the class shown in the preceding example:
return namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Int::class.java)!! return namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Int::class.java)!!
} }
---- ----
======
Remember that the `NamedParameterJdbcTemplate` class wraps a classic `JdbcTemplate` Remember that the `NamedParameterJdbcTemplate` class wraps a classic `JdbcTemplate`
template. If you need access to the wrapped `JdbcTemplate` instance to access template. If you need access to the wrapped `JdbcTemplate` instance to access
@ -651,8 +750,11 @@ name from the database metadata of the database in use.
You can extend `SQLErrorCodeSQLExceptionTranslator`, as the following example shows: You can extend `SQLErrorCodeSQLExceptionTranslator`, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class CustomSQLErrorCodesTranslator extends SQLErrorCodeSQLExceptionTranslator { public class CustomSQLErrorCodesTranslator extends SQLErrorCodeSQLExceptionTranslator {
@ -664,8 +766,10 @@ You can extend `SQLErrorCodeSQLExceptionTranslator`, as the following example sh
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class CustomSQLErrorCodesTranslator : SQLErrorCodeSQLExceptionTranslator() { class CustomSQLErrorCodesTranslator : SQLErrorCodeSQLExceptionTranslator() {
@ -677,6 +781,7 @@ You can extend `SQLErrorCodeSQLExceptionTranslator`, as the following example sh
} }
} }
---- ----
======
In the preceding example, the specific error code (`-12345`) is translated, while other errors are In the preceding example, the specific error code (`-12345`) is translated, while other errors are
left to be translated by the default translator implementation. To use this custom left to be translated by the default translator implementation. To use this custom
@ -685,8 +790,11 @@ translator, you must pass it to the `JdbcTemplate` through the method
processing where this translator is needed. The following example shows how you can use this custom processing where this translator is needed. The following example shows how you can use this custom
translator: translator:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
private JdbcTemplate jdbcTemplate; private JdbcTemplate jdbcTemplate;
@ -710,8 +818,10 @@ translator:
" where id = ?", pct, orderId); " where id = ?", pct, orderId);
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
// create a JdbcTemplate and set data source // create a JdbcTemplate and set data source
private val jdbcTemplate = JdbcTemplate(dataSource).apply { private val jdbcTemplate = JdbcTemplate(dataSource).apply {
@ -728,6 +838,7 @@ translator:
" where id = ?", pct, orderId) " where id = ?", pct, orderId)
} }
---- ----
======
The custom translator is passed a data source in order to look up the error codes in The custom translator is passed a data source in order to look up the error codes in
`sql-error-codes.xml`. `sql-error-codes.xml`.
@ -741,8 +852,11 @@ Running an SQL statement requires very little code. You need a `DataSource` and
`JdbcTemplate`. The following example shows what you need to include for a minimal but `JdbcTemplate`. The following example shows what you need to include for a minimal but
fully functional class that creates a new table: fully functional class that creates a new table:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
import javax.sql.DataSource; import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
@ -760,8 +874,10 @@ fully functional class that creates a new table:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
import javax.sql.DataSource import javax.sql.DataSource
import org.springframework.jdbc.core.JdbcTemplate import org.springframework.jdbc.core.JdbcTemplate
@ -775,6 +891,7 @@ fully functional class that creates a new table:
} }
} }
---- ----
======
[[jdbc-statements-querying]] [[jdbc-statements-querying]]
@ -786,8 +903,11 @@ Java class that is passed in as an argument. If the type conversion is invalid,
`InvalidDataAccessApiUsageException` is thrown. The following example contains two `InvalidDataAccessApiUsageException` is thrown. The following example contains two
query methods, one for an `int` and one that queries for a `String`: query methods, one for an `int` and one that queries for a `String`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
import javax.sql.DataSource; import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
@ -809,8 +929,10 @@ query methods, one for an `int` and one that queries for a `String`:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
import javax.sql.DataSource import javax.sql.DataSource
import org.springframework.jdbc.core.JdbcTemplate import org.springframework.jdbc.core.JdbcTemplate
@ -826,6 +948,7 @@ class RunAQuery(dataSource: DataSource) {
get() = jdbcTemplate.queryForObject("select name from mytable") get() = jdbcTemplate.queryForObject("select name from mytable")
} }
---- ----
======
In addition to the single result query methods, several methods return a list with an In addition to the single result query methods, several methods return a list with an
entry for each row that the query returned. The most generic method is `queryForList(..)`, entry for each row that the query returned. The most generic method is `queryForList(..)`,
@ -833,8 +956,11 @@ which returns a `List` where each element is a `Map` containing one entry for ea
using the column name as the key. If you add a method to the preceding example to retrieve a using the column name as the key. If you add a method to the preceding example to retrieve a
list of all the rows, it might be as follows: list of all the rows, it might be as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
private JdbcTemplate jdbcTemplate; private JdbcTemplate jdbcTemplate;
@ -846,8 +972,10 @@ list of all the rows, it might be as follows:
return this.jdbcTemplate.queryForList("select * from mytable"); return this.jdbcTemplate.queryForList("select * from mytable");
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
private val jdbcTemplate = JdbcTemplate(dataSource) private val jdbcTemplate = JdbcTemplate(dataSource)
@ -855,6 +983,7 @@ list of all the rows, it might be as follows:
return jdbcTemplate.queryForList("select * from mytable") return jdbcTemplate.queryForList("select * from mytable")
} }
---- ----
======
The returned list would resemble the following: The returned list would resemble the following:
@ -869,8 +998,11 @@ The returned list would resemble the following:
The following example updates a column for a certain primary key: The following example updates a column for a certain primary key:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
import javax.sql.DataSource; import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
@ -888,8 +1020,10 @@ The following example updates a column for a certain primary key:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
import javax.sql.DataSource import javax.sql.DataSource
import org.springframework.jdbc.core.JdbcTemplate import org.springframework.jdbc.core.JdbcTemplate
@ -903,6 +1037,7 @@ The following example updates a column for a certain primary key:
} }
} }
---- ----
======
In the preceding example, In the preceding example,
an SQL statement has placeholders for row parameters. You can pass the parameter values an SQL statement has placeholders for row parameters. You can pass the parameter values
@ -922,8 +1057,11 @@ update. There is no standard single way to create an appropriate `PreparedStatem
(which explains why the method signature is the way it is). The following example works (which explains why the method signature is the way it is). The following example works
on Oracle but may not work on other platforms: on Oracle but may not work on other platforms:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
final String INSERT_SQL = "insert into my_test (name) values(?)"; final String INSERT_SQL = "insert into my_test (name) values(?)";
final String name = "Rob"; final String name = "Rob";
@ -937,8 +1075,10 @@ on Oracle but may not work on other platforms:
// keyHolder.getKey() now contains the generated key // keyHolder.getKey() now contains the generated key
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val INSERT_SQL = "insert into my_test (name) values(?)" val INSERT_SQL = "insert into my_test (name) values(?)"
val name = "Rob" val name = "Rob"
@ -950,6 +1090,7 @@ on Oracle but may not work on other platforms:
// keyHolder.getKey() now contains the generated key // keyHolder.getKey() now contains the generated key
---- ----
======

View File

@ -44,8 +44,11 @@ The `EmbeddedDatabaseBuilder` class provides a fluent API for constructing an em
database programmatically. You can use this when you need to create an embedded database in a database programmatically. You can use this when you need to create an embedded database in a
stand-alone environment or in a stand-alone integration test, as in the following example: stand-alone environment or in a stand-alone integration test, as in the following example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
EmbeddedDatabase db = new EmbeddedDatabaseBuilder() EmbeddedDatabase db = new EmbeddedDatabaseBuilder()
.generateUniqueName(true) .generateUniqueName(true)
@ -60,8 +63,10 @@ stand-alone environment or in a stand-alone integration test, as in the followin
db.shutdown() db.shutdown()
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val db = EmbeddedDatabaseBuilder() val db = EmbeddedDatabaseBuilder()
.generateUniqueName(true) .generateUniqueName(true)
@ -76,6 +81,7 @@ stand-alone environment or in a stand-alone integration test, as in the followin
db.shutdown() db.shutdown()
---- ----
======
See the {api-spring-framework}/jdbc/datasource/embedded/EmbeddedDatabaseBuilder.html[javadoc for `EmbeddedDatabaseBuilder`] See the {api-spring-framework}/jdbc/datasource/embedded/EmbeddedDatabaseBuilder.html[javadoc for `EmbeddedDatabaseBuilder`]
for further details on all supported options. for further details on all supported options.
@ -83,8 +89,11 @@ for further details on all supported options.
You can also use the `EmbeddedDatabaseBuilder` to create an embedded database by using Java You can also use the `EmbeddedDatabaseBuilder` to create an embedded database by using Java
configuration, as the following example shows: configuration, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Configuration @Configuration
public class DataSourceConfig { public class DataSourceConfig {
@ -102,8 +111,10 @@ configuration, as the following example shows:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Configuration @Configuration
class DataSourceConfig { class DataSourceConfig {
@ -121,6 +132,7 @@ configuration, as the following example shows:
} }
} }
---- ----
======
[[jdbc-embedded-database-types]] [[jdbc-embedded-database-types]]
@ -168,8 +180,11 @@ configuring the embedded database as a bean in the Spring `ApplicationContext` a
in xref:data-access/jdbc/embedded-database-support.adoc#jdbc-embedded-database-xml[Creating an Embedded Database by Using Spring XML] and xref:data-access/jdbc/embedded-database-support.adoc#jdbc-embedded-database-java[Creating an Embedded Database Programmatically]. The following listing in xref:data-access/jdbc/embedded-database-support.adoc#jdbc-embedded-database-xml[Creating an Embedded Database by Using Spring XML] and xref:data-access/jdbc/embedded-database-support.adoc#jdbc-embedded-database-java[Creating an Embedded Database Programmatically]. The following listing
shows the test template: shows the test template:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class DataAccessIntegrationTestTemplate { public class DataAccessIntegrationTestTemplate {
@ -198,8 +213,10 @@ shows the test template:
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class DataAccessIntegrationTestTemplate { class DataAccessIntegrationTestTemplate {
@ -227,6 +244,7 @@ shows the test template:
} }
} }
---- ----
======
[[jdbc-embedded-database-unique-names]] [[jdbc-embedded-database-unique-names]]

View File

@ -40,8 +40,11 @@ abstract `mapRow(..)` method to convert each row of the supplied `ResultSet` int
object of the type specified. The following example shows a custom query that maps the object of the type specified. The following example shows a custom query that maps the
data from the `t_actor` relation to an instance of the `Actor` class: data from the `t_actor` relation to an instance of the `Actor` class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class ActorMappingQuery extends MappingSqlQuery<Actor> { public class ActorMappingQuery extends MappingSqlQuery<Actor> {
@ -61,8 +64,10 @@ data from the `t_actor` relation to an instance of the `Actor` class:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class ActorMappingQuery(ds: DataSource) : MappingSqlQuery<Actor>(ds, "select id, first_name, last_name from t_actor where id = ?") { class ActorMappingQuery(ds: DataSource) : MappingSqlQuery<Actor>(ds, "select id, first_name, last_name from t_actor where id = ?") {
@ -79,6 +84,7 @@ data from the `t_actor` relation to an instance of the `Actor` class:
} }
---- ----
======
The class extends `MappingSqlQuery` parameterized with the `Actor` type. The constructor The class extends `MappingSqlQuery` parameterized with the `Actor` type. The constructor
for this customer query takes a `DataSource` as the only parameter. In this for this customer query takes a `DataSource` as the only parameter. In this
@ -93,8 +99,11 @@ thread-safe after it is compiled, so, as long as these instances are created whe
is initialized, they can be kept as instance variables and be reused. The following is initialized, they can be kept as instance variables and be reused. The following
example shows how to define such a class: example shows how to define such a class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
private ActorMappingQuery actorMappingQuery; private ActorMappingQuery actorMappingQuery;
@ -107,13 +116,16 @@ example shows how to define such a class:
return actorMappingQuery.findObject(id); return actorMappingQuery.findObject(id);
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
private val actorMappingQuery = ActorMappingQuery(dataSource) private val actorMappingQuery = ActorMappingQuery(dataSource)
fun getCustomer(id: Long) = actorMappingQuery.findObject(id) fun getCustomer(id: Long) = actorMappingQuery.findObject(id)
---- ----
======
The method in the preceding example retrieves the customer with the `id` that is passed in as the The method in the preceding example retrieves the customer with the `id` that is passed in as the
only parameter. Since we want only one object to be returned, we call the `findObject` convenience only parameter. Since we want only one object to be returned, we call the `findObject` convenience
@ -122,19 +134,25 @@ list of objects and took additional parameters, we would use one of the `execute
methods that takes an array of parameter values passed in as varargs. The following methods that takes an array of parameter values passed in as varargs. The following
example shows such a method: example shows such a method:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public List<Actor> searchForActors(int age, String namePattern) { public List<Actor> searchForActors(int age, String namePattern) {
return actorSearchMappingQuery.execute(age, namePattern); return actorSearchMappingQuery.execute(age, namePattern);
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
fun searchForActors(age: Int, namePattern: String) = fun searchForActors(age: Int, namePattern: String) =
actorSearchMappingQuery.execute(age, namePattern) actorSearchMappingQuery.execute(age, namePattern)
---- ----
======
[[jdbc-SqlUpdate]] [[jdbc-SqlUpdate]]
@ -149,8 +167,11 @@ However, you do not have to subclass the `SqlUpdate`
class, since it can easily be parameterized by setting SQL and declaring parameters. class, since it can easily be parameterized by setting SQL and declaring parameters.
The following example creates a custom update method named `execute`: The following example creates a custom update method named `execute`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
import java.sql.Types; import java.sql.Types;
import javax.sql.DataSource; import javax.sql.DataSource;
@ -177,8 +198,10 @@ The following example creates a custom update method named `execute`:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
import java.sql.Types import java.sql.Types
import javax.sql.DataSource import javax.sql.DataSource
@ -205,6 +228,7 @@ The following example creates a custom update method named `execute`:
} }
} }
---- ----
======
[[jdbc-StoredProcedure]] [[jdbc-StoredProcedure]]
@ -219,18 +243,24 @@ To define a parameter for the `StoredProcedure` class, you can use an `SqlParame
of its subclasses. You must specify the parameter name and SQL type in the constructor, of its subclasses. You must specify the parameter name and SQL type in the constructor,
as the following code snippet shows: as the following code snippet shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
new SqlParameter("in_id", Types.NUMERIC), new SqlParameter("in_id", Types.NUMERIC),
new SqlOutParameter("out_first_name", Types.VARCHAR), new SqlOutParameter("out_first_name", Types.VARCHAR),
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
SqlParameter("in_id", Types.NUMERIC), SqlParameter("in_id", Types.NUMERIC),
SqlOutParameter("out_first_name", Types.VARCHAR), SqlOutParameter("out_first_name", Types.VARCHAR),
---- ----
======
The SQL type is specified using the `java.sql.Types` constants. The SQL type is specified using the `java.sql.Types` constants.
@ -259,8 +289,11 @@ returned date from the results `Map`. The results `Map` has an entry for each de
output parameter (in this case, only one) by using the parameter name as the key. output parameter (in this case, only one) by using the parameter name as the key.
The following listing shows our custom StoredProcedure class: The following listing shows our custom StoredProcedure class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
import java.sql.Types; import java.sql.Types;
import java.util.Date; import java.util.Date;
@ -306,8 +339,10 @@ The following listing shows our custom StoredProcedure class:
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
import java.sql.Types import java.sql.Types
import java.util.Date import java.util.Date
@ -343,12 +378,16 @@ The following listing shows our custom StoredProcedure class:
} }
} }
---- ----
======
The following example of a `StoredProcedure` has two output parameters (in this case, The following example of a `StoredProcedure` has two output parameters (in this case,
Oracle REF cursors): Oracle REF cursors):
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -374,8 +413,10 @@ Oracle REF cursors):
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
import java.util.HashMap import java.util.HashMap
import javax.sql.DataSource import javax.sql.DataSource
@ -401,6 +442,7 @@ Oracle REF cursors):
} }
} }
---- ----
======
Notice how the overloaded variants of the `declareParameter(..)` method that have been Notice how the overloaded variants of the `declareParameter(..)` method that have been
used in the `TitlesAndGenresStoredProcedure` constructor are passed `RowMapper` used in the `TitlesAndGenresStoredProcedure` constructor are passed `RowMapper`
@ -410,8 +452,11 @@ functionality. The next two examples provide code for the two `RowMapper` implem
The `TitleMapper` class maps a `ResultSet` to a `Title` domain object for each row in The `TitleMapper` class maps a `ResultSet` to a `Title` domain object for each row in
the supplied `ResultSet`, as follows: the supplied `ResultSet`, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
@ -428,8 +473,10 @@ the supplied `ResultSet`, as follows:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
import java.sql.ResultSet import java.sql.ResultSet
import com.foo.domain.Title import com.foo.domain.Title
@ -441,12 +488,16 @@ the supplied `ResultSet`, as follows:
Title(rs.getLong("id"), rs.getString("name")) Title(rs.getLong("id"), rs.getString("name"))
} }
---- ----
======
The `GenreMapper` class maps a `ResultSet` to a `Genre` domain object for each row in The `GenreMapper` class maps a `ResultSet` to a `Genre` domain object for each row in
the supplied `ResultSet`, as follows: the supplied `ResultSet`, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
@ -460,8 +511,10 @@ the supplied `ResultSet`, as follows:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
import java.sql.ResultSet import java.sql.ResultSet
import com.foo.domain.Genre import com.foo.domain.Genre
@ -474,13 +527,17 @@ the supplied `ResultSet`, as follows:
} }
} }
---- ----
======
To pass parameters to a stored procedure that has one or more input parameters in its To pass parameters to a stored procedure that has one or more input parameters in its
definition in the RDBMS, you can code a strongly typed `execute(..)` method that would definition in the RDBMS, you can code a strongly typed `execute(..)` method that would
delegate to the untyped `execute(Map)` method in the superclass, as the following example shows: delegate to the untyped `execute(Map)` method in the superclass, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
import java.sql.Types; import java.sql.Types;
import java.util.Date; import java.util.Date;
@ -511,8 +568,10 @@ delegate to the untyped `execute(Map)` method in the superclass, as the followin
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
import java.sql.Types import java.sql.Types
import java.util.Date import java.util.Date
@ -539,6 +598,7 @@ delegate to the untyped `execute(Map)` method in the superclass, as the followin
mapOf<String, Any>(CUTOFF_DATE_PARAM to cutoffDate)) mapOf<String, Any>(CUTOFF_DATE_PARAM to cutoffDate))
} }
---- ----
======

View File

@ -63,8 +63,11 @@ dependency injection.
The following example shows how to create and insert a BLOB: The following example shows how to create and insert a BLOB:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
final File blobIn = new File("spring2004.jpg"); final File blobIn = new File("spring2004.jpg");
final InputStream blobIs = new FileInputStream(blobIn); final InputStream blobIs = new FileInputStream(blobIn);
@ -86,6 +89,7 @@ The following example shows how to create and insert a BLOB:
blobIs.close(); blobIs.close();
clobReader.close(); clobReader.close();
---- ----
======
<1> Pass in the `lobHandler` that (in this example) is a plain `DefaultLobHandler`. <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. <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. <3> Using the method `setBlobAsBinaryStream` to pass in the contents of the BLOB.
@ -134,8 +138,11 @@ Now it is time to read the LOB data from the database. Again, you use a `JdbcTem
with the same instance variable `lobHandler` and a reference to a `DefaultLobHandler`. with the same instance variable `lobHandler` and a reference to a `DefaultLobHandler`.
The following example shows how to do so: The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
List<Map<String, Object>> l = jdbcTemplate.query("select id, a_clob, a_blob from lob_table", List<Map<String, Object>> l = jdbcTemplate.query("select id, a_clob, a_blob from lob_table",
new RowMapper<Map<String, Object>>() { new RowMapper<Map<String, Object>>() {
@ -149,6 +156,7 @@ The following example shows how to do so:
} }
}); });
---- ----
======
<1> Using the method `getClobAsString` to retrieve the contents of the CLOB. <1> Using the method `getClobAsString` to retrieve the contents of the CLOB.
<2> Using the method `getBlobAsBytes` to retrieve the contents of the BLOB. <2> Using the method `getBlobAsBytes` to retrieve the contents of the BLOB.
@ -203,8 +211,11 @@ implemented. This interface is used as part of the declaration of an `SqlOutPara
The following example shows returning the value of an Oracle `STRUCT` object of the user The following example shows returning the value of an Oracle `STRUCT` object of the user
declared type `ITEM_TYPE`: declared type `ITEM_TYPE`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class TestItemStoredProcedure extends StoredProcedure { public class TestItemStoredProcedure extends StoredProcedure {
@ -223,8 +234,10 @@ declared type `ITEM_TYPE`:
// ... // ...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class TestItemStoredProcedure(dataSource: DataSource) : StoredProcedure() { class TestItemStoredProcedure(dataSource: DataSource) : StoredProcedure() {
@ -239,6 +252,7 @@ declared type `ITEM_TYPE`:
} }
} }
---- ----
======
You can use `SqlTypeValue` to pass the value of a Java object (such as `TestItem`) to a You can use `SqlTypeValue` to pass the value of a Java object (such as `TestItem`) to a
stored procedure. The `SqlTypeValue` interface has a single method (named stored procedure. The `SqlTypeValue` interface has a single method (named
@ -246,8 +260,11 @@ stored procedure. The `SqlTypeValue` interface has a single method (named
can use it to create database-specific objects, such as `StructDescriptor` instances can use it to create database-specific objects, such as `StructDescriptor` instances
or `ArrayDescriptor` instances. The following example creates a `StructDescriptor` instance: or `ArrayDescriptor` instances. The following example creates a `StructDescriptor` instance:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
final TestItem testItem = new TestItem(123L, "A test item", final TestItem testItem = new TestItem(123L, "A test item",
new SimpleDateFormat("yyyy-M-d").parse("2010-12-31")); new SimpleDateFormat("yyyy-M-d").parse("2010-12-31"));
@ -265,8 +282,10 @@ or `ArrayDescriptor` instances. The following example creates a `StructDescripto
} }
}; };
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val (id, description, expirationDate) = TestItem(123L, "A test item", val (id, description, expirationDate) = TestItem(123L, "A test item",
SimpleDateFormat("yyyy-M-d").parse("2010-12-31")) SimpleDateFormat("yyyy-M-d").parse("2010-12-31"))
@ -279,6 +298,7 @@ or `ArrayDescriptor` instances. The following example creates a `StructDescripto
} }
} }
---- ----
======
You can now add this `SqlTypeValue` to the `Map` that contains the input parameters for the You can now add this `SqlTypeValue` to the `Map` that contains the input parameters for the
`execute` call of the stored procedure. `execute` call of the stored procedure.
@ -288,8 +308,11 @@ procedure. Oracle has its own internal `ARRAY` class that must be used in this c
you can use the `SqlTypeValue` to create an instance of the Oracle `ARRAY` and populate you can use the `SqlTypeValue` to create an instance of the Oracle `ARRAY` and populate
it with values from the Java `ARRAY`, as the following example shows: it with values from the Java `ARRAY`, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
final Long[] ids = new Long[] {1L, 2L}; final Long[] ids = new Long[] {1L, 2L};
@ -301,8 +324,10 @@ it with values from the Java `ARRAY`, as the following example shows:
} }
}; };
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class TestItemStoredProcedure(dataSource: DataSource) : StoredProcedure() { class TestItemStoredProcedure(dataSource: DataSource) : StoredProcedure() {
@ -317,6 +342,7 @@ it with values from the Java `ARRAY`, as the following example shows:
} }
} }
---- ----
======

View File

@ -19,8 +19,11 @@ Configuration methods for this class follow the `fluid` style that returns the i
of the `SimpleJdbcInsert`, which lets you chain all configuration methods. The following of the `SimpleJdbcInsert`, which lets you chain all configuration methods. The following
example uses only one configuration method (we show examples of multiple methods later): example uses only one configuration method (we show examples of multiple methods later):
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class JdbcActorDao implements ActorDao { public class JdbcActorDao implements ActorDao {
@ -41,8 +44,10 @@ example uses only one configuration method (we show examples of multiple methods
// ... additional methods // ... additional methods
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class JdbcActorDao(dataSource: DataSource) : ActorDao { class JdbcActorDao(dataSource: DataSource) : ActorDao {
@ -59,6 +64,7 @@ example uses only one configuration method (we show examples of multiple methods
// ... additional methods // ... additional methods
} }
---- ----
======
The `execute` method used here takes a plain `java.util.Map` as its only parameter. The The `execute` method used here takes a plain `java.util.Map` as its only parameter. The
important thing to note here is that the keys used for the `Map` must match the column important thing to note here is that the keys used for the `Map` must match the column
@ -75,8 +81,11 @@ the `SimpleJdbcInsert`, in addition to specifying the table name, it specifies t
of the generated key column with the `usingGeneratedKeyColumns` method. The following of the generated key column with the `usingGeneratedKeyColumns` method. The following
listing shows how it works: listing shows how it works:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class JdbcActorDao implements ActorDao { public class JdbcActorDao implements ActorDao {
@ -99,8 +108,10 @@ listing shows how it works:
// ... additional methods // ... additional methods
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class JdbcActorDao(dataSource: DataSource) : ActorDao { class JdbcActorDao(dataSource: DataSource) : ActorDao {
@ -118,6 +129,7 @@ listing shows how it works:
// ... additional methods // ... additional methods
} }
---- ----
======
The main difference when you run the insert by using this second approach is that you do not The main difference when you run the insert by using this second approach is that you do not
add the `id` to the `Map`, and you call the `executeAndReturnKey` method. This returns a add the `id` to the `Map`, and you call the `executeAndReturnKey` method. This returns a
@ -134,8 +146,11 @@ use a `KeyHolder` that is returned from the `executeAndReturnKeyHolder` method.
You can limit the columns for an insert by specifying a list of column names with the You can limit the columns for an insert by specifying a list of column names with the
`usingColumns` method, as the following example shows: `usingColumns` method, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class JdbcActorDao implements ActorDao { public class JdbcActorDao implements ActorDao {
@ -159,8 +174,10 @@ You can limit the columns for an insert by specifying a list of column names wit
// ... additional methods // ... additional methods
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class JdbcActorDao(dataSource: DataSource) : ActorDao { class JdbcActorDao(dataSource: DataSource) : ActorDao {
@ -180,6 +197,7 @@ You can limit the columns for an insert by specifying a list of column names wit
// ... additional methods // ... additional methods
} }
---- ----
======
The execution of the insert is the same as if you had relied on the metadata to determine The execution of the insert is the same as if you had relied on the metadata to determine
which columns to use. which columns to use.
@ -195,8 +213,11 @@ which is a very convenient class if you have a JavaBean-compliant class that con
your values. It uses the corresponding getter method to extract the parameter your values. It uses the corresponding getter method to extract the parameter
values. The following example shows how to use `BeanPropertySqlParameterSource`: values. The following example shows how to use `BeanPropertySqlParameterSource`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class JdbcActorDao implements ActorDao { public class JdbcActorDao implements ActorDao {
@ -217,8 +238,10 @@ values. The following example shows how to use `BeanPropertySqlParameterSource`:
// ... additional methods // ... additional methods
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class JdbcActorDao(dataSource: DataSource) : ActorDao { class JdbcActorDao(dataSource: DataSource) : ActorDao {
@ -235,12 +258,16 @@ values. The following example shows how to use `BeanPropertySqlParameterSource`:
// ... additional methods // ... additional methods
} }
---- ----
======
Another option is the `MapSqlParameterSource` that resembles a `Map` but provides a more Another option is the `MapSqlParameterSource` that resembles a `Map` but provides a more
convenient `addValue` method that can be chained. The following example shows how to use it: convenient `addValue` method that can be chained. The following example shows how to use it:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class JdbcActorDao implements ActorDao { public class JdbcActorDao implements ActorDao {
@ -263,8 +290,10 @@ convenient `addValue` method that can be chained. The following example shows ho
// ... additional methods // ... additional methods
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class JdbcActorDao(dataSource: DataSource) : ActorDao { class JdbcActorDao(dataSource: DataSource) : ActorDao {
@ -283,6 +312,7 @@ convenient `addValue` method that can be chained. The following example shows ho
// ... additional methods // ... additional methods
} }
---- ----
======
As you can see, the configuration is the same. Only the executing code has to change to As you can see, the configuration is the same. Only the executing code has to change to
use these alternative input classes. use these alternative input classes.
@ -325,8 +355,11 @@ The following example of a `SimpleJdbcCall` configuration uses the preceding sto
procedure (the only configuration option, in addition to the `DataSource`, is the name procedure (the only configuration option, in addition to the `DataSource`, is the name
of the stored procedure): of the stored procedure):
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class JdbcActorDao implements ActorDao { public class JdbcActorDao implements ActorDao {
@ -352,8 +385,10 @@ of the stored procedure):
// ... additional methods // ... additional methods
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class JdbcActorDao(dataSource: DataSource) : ActorDao { class JdbcActorDao(dataSource: DataSource) : ActorDao {
@ -374,6 +409,7 @@ of the stored procedure):
// ... additional methods // ... additional methods
} }
---- ----
======
The code you write for the execution of the call involves creating an `SqlParameterSource` The code you write for the execution of the call involves creating an `SqlParameterSource`
containing the IN parameter. You must match the name provided for the input value containing the IN parameter. You must match the name provided for the input value
@ -397,8 +433,11 @@ To do the latter, you can create your own `JdbcTemplate` and set the `setResults
property to `true`. Then you can pass this customized `JdbcTemplate` instance into property to `true`. Then you can pass this customized `JdbcTemplate` instance into
the constructor of your `SimpleJdbcCall`. The following example shows this configuration: the constructor of your `SimpleJdbcCall`. The following example shows this configuration:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class JdbcActorDao implements ActorDao { public class JdbcActorDao implements ActorDao {
@ -414,8 +453,10 @@ the constructor of your `SimpleJdbcCall`. The following example shows this confi
// ... additional methods // ... additional methods
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class JdbcActorDao(dataSource: DataSource) : ActorDao { class JdbcActorDao(dataSource: DataSource) : ActorDao {
@ -426,6 +467,7 @@ the constructor of your `SimpleJdbcCall`. The following example shows this confi
// ... additional methods // ... additional methods
} }
---- ----
======
By taking this action, you avoid conflicts in the case used for the names of your By taking this action, you avoid conflicts in the case used for the names of your
returned `out` parameters. returned `out` parameters.
@ -456,8 +498,11 @@ of IN parameter names to include for a given signature.
The following example shows a fully declared procedure call and uses the information from The following example shows a fully declared procedure call and uses the information from
the preceding example: the preceding example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class JdbcActorDao implements ActorDao { public class JdbcActorDao implements ActorDao {
@ -481,8 +526,10 @@ the preceding example:
// ... additional methods // ... additional methods
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class JdbcActorDao(dataSource: DataSource) : ActorDao { class JdbcActorDao(dataSource: DataSource) : ActorDao {
@ -501,6 +548,7 @@ the preceding example:
// ... additional methods // ... additional methods
} }
---- ----
======
The execution and end results of the two examples are the same. The second example specifies all The execution and end results of the two examples are the same. The second example specifies all
details explicitly rather than relying on metadata. details explicitly rather than relying on metadata.
@ -515,18 +563,24 @@ To do so, you typically specify the parameter name and SQL type in the construct
is specified by using the `java.sql.Types` constants. Earlier in this chapter, we saw declarations is specified by using the `java.sql.Types` constants. Earlier in this chapter, we saw declarations
similar to the following: similar to the following:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
new SqlParameter("in_id", Types.NUMERIC), new SqlParameter("in_id", Types.NUMERIC),
new SqlOutParameter("out_first_name", Types.VARCHAR), new SqlOutParameter("out_first_name", Types.VARCHAR),
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
SqlParameter("in_id", Types.NUMERIC), SqlParameter("in_id", Types.NUMERIC),
SqlOutParameter("out_first_name", Types.VARCHAR), SqlOutParameter("out_first_name", Types.VARCHAR),
---- ----
======
The first line with the `SqlParameter` declares an IN parameter. You can use IN parameters The first line with the `SqlParameter` declares an IN parameter. You can use IN parameters
for both stored procedure calls and for queries by using the `SqlQuery` and its for both stored procedure calls and for queries by using the `SqlQuery` and its
@ -578,8 +632,11 @@ that returns an actor's full name:
To call this function, we again create a `SimpleJdbcCall` in the initialization method, To call this function, we again create a `SimpleJdbcCall` in the initialization method,
as the following example shows: as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class JdbcActorDao implements ActorDao { public class JdbcActorDao implements ActorDao {
@ -602,8 +659,10 @@ as the following example shows:
// ... additional methods // ... additional methods
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class JdbcActorDao(dataSource: DataSource) : ActorDao { class JdbcActorDao(dataSource: DataSource) : ActorDao {
@ -621,6 +680,7 @@ as the following example shows:
// ... additional methods // ... additional methods
} }
---- ----
======
The `executeFunction` method used returns a `String` that contains the return value from the The `executeFunction` method used returns a `String` that contains the return value from the
function call. function call.
@ -656,8 +716,11 @@ to map follows the JavaBean rules, you can use a `BeanPropertyRowMapper` that is
passing in the required class to map to in the `newInstance` method. passing in the required class to map to in the `newInstance` method.
The following example shows how to do so: The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class JdbcActorDao implements ActorDao { public class JdbcActorDao implements ActorDao {
@ -680,8 +743,10 @@ The following example shows how to do so:
// ... additional methods // ... additional methods
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class JdbcActorDao(dataSource: DataSource) : ActorDao { class JdbcActorDao(dataSource: DataSource) : ActorDao {
@ -699,6 +764,7 @@ The following example shows how to do so:
// ... additional methods // ... additional methods
} }
---- ----
======
The `execute` call passes in an empty `Map`, because this call does not take any parameters. The `execute` call passes in an empty `Map`, because this call does not take any parameters.
The list of actors is then retrieved from the results map and returned to the caller. The list of actors is then retrieved from the results map and returned to the caller.

View File

@ -61,8 +61,11 @@ do not need any special exception treatment (or both). However, Spring lets exce
translation be applied transparently through the `@Repository` annotation. The following translation be applied transparently through the `@Repository` annotation. The following
examples (one for Java configuration and one for XML configuration) show how to do so: examples (one for Java configuration and one for XML configuration) show how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Repository @Repository
public class ProductDaoImpl implements ProductDao { public class ProductDaoImpl implements ProductDao {
@ -71,8 +74,10 @@ examples (one for Java configuration and one for XML configuration) show how to
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Repository @Repository
class ProductDaoImpl : ProductDao { class ProductDaoImpl : ProductDao {
@ -81,6 +86,7 @@ examples (one for Java configuration and one for XML configuration) show how to
} }
---- ----
======
[source,xml,indent=0,subs="verbatim,quotes"] [source,xml,indent=0,subs="verbatim,quotes"]
---- ----

View File

@ -95,8 +95,11 @@ one current `Session` per transaction. This is roughly equivalent to Spring's
synchronization of one Hibernate `Session` per transaction. A corresponding DAO synchronization of one Hibernate `Session` per transaction. A corresponding DAO
implementation resembles the following example, based on the plain Hibernate API: implementation resembles the following example, based on the plain Hibernate API:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class ProductDaoImpl implements ProductDao { public class ProductDaoImpl implements ProductDao {
@ -114,8 +117,10 @@ implementation resembles the following example, based on the plain Hibernate API
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class ProductDaoImpl(private val sessionFactory: SessionFactory) : ProductDao { class ProductDaoImpl(private val sessionFactory: SessionFactory) : ProductDao {
@ -127,6 +132,7 @@ implementation resembles the following example, based on the plain Hibernate API
} }
} }
---- ----
======
This style is similar to that of the Hibernate reference documentation and examples, This style is similar to that of the Hibernate reference documentation and examples,
except for holding the `SessionFactory` in an instance variable. We strongly recommend except for holding the `SessionFactory` in an instance variable. We strongly recommend
@ -192,8 +198,11 @@ You can annotate the service layer with `@Transactional` annotations and instruc
Spring container to find these annotations and provide transactional semantics for Spring container to find these annotations and provide transactional semantics for
these annotated methods. The following example shows how to do so: these annotated methods. The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class ProductServiceImpl implements ProductService { public class ProductServiceImpl implements ProductService {
@ -215,8 +224,10 @@ these annotated methods. The following example shows how to do so:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class ProductServiceImpl(private val productDao: ProductDao) : ProductService { class ProductServiceImpl(private val productDao: ProductDao) : ProductService {
@ -230,6 +241,7 @@ these annotated methods. The following example shows how to do so:
fun findAllProducts() = productDao.findAllProducts() fun findAllProducts() = productDao.findAllProducts()
} }
---- ----
======
In the container, you need to set up the `PlatformTransactionManager` implementation In the container, you need to set up the `PlatformTransactionManager` implementation
(as a bean) and a `<tx:annotation-driven/>` entry, opting into `@Transactional` (as a bean) and a `<tx:annotation-driven/>` entry, opting into `@Transactional`
@ -295,8 +307,11 @@ and an example for a business method implementation:
</beans> </beans>
---- ----
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class ProductServiceImpl implements ProductService { public class ProductServiceImpl implements ProductService {
@ -321,8 +336,10 @@ and an example for a business method implementation:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class ProductServiceImpl(transactionManager: PlatformTransactionManager, class ProductServiceImpl(transactionManager: PlatformTransactionManager,
private val productDao: ProductDao) : ProductService { private val productDao: ProductDao) : ProductService {
@ -337,6 +354,7 @@ and an example for a business method implementation:
} }
} }
---- ----
======
Spring's `TransactionInterceptor` lets any checked application exception be thrown Spring's `TransactionInterceptor` lets any checked application exception be thrown
with the callback code, while `TransactionTemplate` is restricted to unchecked with the callback code, while `TransactionTemplate` is restricted to unchecked

View File

@ -293,8 +293,11 @@ using an injected `EntityManagerFactory` or `EntityManager`. Spring can understa
if a `PersistenceAnnotationBeanPostProcessor` is enabled. The following example shows a plain JPA DAO implementation if a `PersistenceAnnotationBeanPostProcessor` is enabled. The following example shows a plain JPA DAO implementation
that uses the `@PersistenceUnit` annotation: that uses the `@PersistenceUnit` annotation:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class ProductDaoImpl implements ProductDao { public class ProductDaoImpl implements ProductDao {
@ -320,8 +323,10 @@ that uses the `@PersistenceUnit` annotation:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class ProductDaoImpl : ProductDao { class ProductDaoImpl : ProductDao {
@ -340,6 +345,7 @@ that uses the `@PersistenceUnit` annotation:
} }
} }
---- ----
======
The preceding DAO has no dependency on Spring and still fits nicely into a Spring The preceding DAO has no dependency on Spring and still fits nicely into a Spring
application context. Moreover, the DAO takes advantage of annotations to require the application context. Moreover, the DAO takes advantage of annotations to require the
@ -382,8 +388,11 @@ the factory. You can avoid this by requesting a transactional `EntityManager` (a
called a "`shared EntityManager`" because it is a shared, thread-safe proxy for the actual called a "`shared EntityManager`" because it is a shared, thread-safe proxy for the actual
transactional EntityManager) to be injected instead of the factory. The following example shows how to do so: transactional EntityManager) to be injected instead of the factory. The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class ProductDaoImpl implements ProductDao { public class ProductDaoImpl implements ProductDao {
@ -397,8 +406,10 @@ transactional EntityManager) to be injected instead of the factory. The followin
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class ProductDaoImpl : ProductDao { class ProductDaoImpl : ProductDao {
@ -412,6 +423,7 @@ transactional EntityManager) to be injected instead of the factory. The followin
} }
} }
---- ----
======
The `@PersistenceContext` annotation has an optional attribute called `type`, which defaults to The `@PersistenceContext` annotation has an optional attribute called `type`, which defaults to
`PersistenceContextType.TRANSACTION`. You can use this default to receive a shared `PersistenceContextType.TRANSACTION`. You can use this default to receive a shared

View File

@ -171,8 +171,11 @@ You can use Spring's OXM for a wide variety of situations. In the following exam
use it to marshal the settings of a Spring-managed application as an XML file. In the following example, we use it to marshal the settings of a Spring-managed application as an XML file. In the following example, we
use a simple JavaBean to represent the settings: use a simple JavaBean to represent the settings:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class Settings { public class Settings {
@ -187,21 +190,27 @@ use a simple JavaBean to represent the settings:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class Settings { class Settings {
var isFooEnabled: Boolean = false var isFooEnabled: Boolean = false
} }
---- ----
======
The application class uses this bean to store its settings. Besides a main method, the The application class uses this bean to store its settings. Besides a main method, the
class has two methods: `saveSettings()` saves the settings bean to a file named class has two methods: `saveSettings()` saves the settings bean to a file named
`settings.xml`, and `loadSettings()` loads these settings again. The following `main()` method `settings.xml`, and `loadSettings()` loads these settings again. The following `main()` method
constructs a Spring application context and calls these two methods: constructs a Spring application context and calls these two methods:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
@ -249,8 +258,10 @@ constructs a Spring application context and calls these two methods:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class Application { class Application {
@ -276,6 +287,7 @@ constructs a Spring application context and calls these two methods:
application.loadSettings() application.loadSettings()
} }
---- ----
======
The `Application` requires both a `marshaller` and an `unmarshaller` property to be set. We The `Application` requires both a `marshaller` and an `unmarshaller` property to be set. We
can do so by using the following `applicationContext.xml`: can do so by using the following `applicationContext.xml`:

View File

@ -60,16 +60,22 @@ and give it to DAOs as a bean reference.
The simplest way to create a `DatabaseClient` object is through a static factory method, as follows: The simplest way to create a `DatabaseClient` object is through a static factory method, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
DatabaseClient client = DatabaseClient.create(connectionFactory); DatabaseClient client = DatabaseClient.create(connectionFactory);
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val client = DatabaseClient.create(connectionFactory) val client = DatabaseClient.create(connectionFactory)
---- ----
======
NOTE: The `ConnectionFactory` should always be configured as a bean in the Spring IoC NOTE: The `ConnectionFactory` should always be configured as a bean in the Spring IoC
container. container.
@ -119,18 +125,24 @@ See the attendant {api-spring-framework}/r2dbc/core/DatabaseClient.html[javadoc]
The following example shows what you need to include for minimal but fully functional The following example shows what you need to include for minimal but fully functional
code that creates a new table: code that creates a new table:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
Mono<Void> completion = client.sql("CREATE TABLE person (id VARCHAR(255) PRIMARY KEY, name VARCHAR(255), age INTEGER);") Mono<Void> completion = client.sql("CREATE TABLE person (id VARCHAR(255) PRIMARY KEY, name VARCHAR(255), age INTEGER);")
.then(); .then();
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
client.sql("CREATE TABLE person (id VARCHAR(255) PRIMARY KEY, name VARCHAR(255), age INTEGER);") client.sql("CREATE TABLE person (id VARCHAR(255) PRIMARY KEY, name VARCHAR(255), age INTEGER);")
.await() .await()
---- ----
======
`DatabaseClient` is designed for convenient, fluent usage. `DatabaseClient` is designed for convenient, fluent usage.
It exposes intermediate, continuation, and terminal methods at each stage of the It exposes intermediate, continuation, and terminal methods at each stage of the
@ -150,35 +162,47 @@ depending on the issued query.
The following query gets the `id` and `name` columns from a table: The following query gets the `id` and `name` columns from a table:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
Mono<Map<String, Object>> first = client.sql("SELECT id, name FROM person") Mono<Map<String, Object>> first = client.sql("SELECT id, name FROM person")
.fetch().first(); .fetch().first();
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val first = client.sql("SELECT id, name FROM person") val first = client.sql("SELECT id, name FROM person")
.fetch().awaitSingle() .fetch().awaitSingle()
---- ----
======
The following query uses a bind variable: The following query uses a bind variable:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
Mono<Map<String, Object>> first = client.sql("SELECT id, name FROM person WHERE first_name = :fn") Mono<Map<String, Object>> first = client.sql("SELECT id, name FROM person WHERE first_name = :fn")
.bind("fn", "Joe") .bind("fn", "Joe")
.fetch().first(); .fetch().first();
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val first = client.sql("SELECT id, name FROM person WHERE first_name = :fn") val first = client.sql("SELECT id, name FROM person WHERE first_name = :fn")
.bind("fn", "Joe") .bind("fn", "Joe")
.fetch().awaitSingle() .fetch().awaitSingle()
---- ----
======
You might have noticed the use of `fetch()` in the example above. `fetch()` is a You might have noticed the use of `fetch()` in the example above. `fetch()` is a
continuation operator that lets you specify how much data you want to consume. continuation operator that lets you specify how much data you want to consume.
@ -205,20 +229,26 @@ collections and maps, and objects).
The following example extracts the `name` column and emits its value: The following example extracts the `name` column and emits its value:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
Flux<String> names = client.sql("SELECT name FROM person") Flux<String> names = client.sql("SELECT name FROM person")
.map(row -> row.get("name", String.class)) .map(row -> row.get("name", String.class))
.all(); .all();
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val names = client.sql("SELECT name FROM person") val names = client.sql("SELECT name FROM person")
.map{ row: Row -> row.get("name", String.class) } .map{ row: Row -> row.get("name", String.class) }
.flow() .flow()
---- ----
======
[[r2dbc-DatabaseClient-mapping-null]] [[r2dbc-DatabaseClient-mapping-null]]
@ -242,20 +272,26 @@ do not return tabular data so you use `rowsUpdated()` to consume results.
The following example shows an `UPDATE` statement that returns the number The following example shows an `UPDATE` statement that returns the number
of updated rows: of updated rows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
Mono<Integer> affectedRows = client.sql("UPDATE person SET first_name = :fn") Mono<Integer> affectedRows = client.sql("UPDATE person SET first_name = :fn")
.bind("fn", "Joe") .bind("fn", "Joe")
.fetch().rowsUpdated(); .fetch().rowsUpdated();
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val affectedRows = client.sql("UPDATE person SET first_name = :fn") val affectedRows = client.sql("UPDATE person SET first_name = :fn")
.bind("fn", "Joe") .bind("fn", "Joe")
.fetch().awaitRowsUpdated() .fetch().awaitRowsUpdated()
---- ----
======
[[r2dbc-DatabaseClient-named-parameters]] [[r2dbc-DatabaseClient-named-parameters]]
==== Binding Values to Queries ==== Binding Values to Queries
@ -277,7 +313,6 @@ Parameter binding supports two binding strategies:
The following example shows parameter binding for a query: The following example shows parameter binding for a query:
====
[source,java] [source,java]
---- ----
db.sql("INSERT INTO person (id, name, age) VALUES(:id, :name, :age)") db.sql("INSERT INTO person (id, name, age) VALUES(:id, :name, :age)")
@ -285,7 +320,6 @@ db.sql("INSERT INTO person (id, name, age) VALUES(:id, :name, :age)")
.bind("name", "Joe") .bind("name", "Joe")
.bind("age", 34); .bind("age", 34);
---- ----
====
.R2DBC Native Bind Markers .R2DBC Native Bind Markers
**** ****
@ -318,8 +352,11 @@ SELECT id, name, state FROM table WHERE (name, age) IN (('John', 35), ('Ann', 50
The preceding query can be parameterized and run as follows: The preceding query can be parameterized and run as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
List<Object[]> tuples = new ArrayList<>(); List<Object[]> tuples = new ArrayList<>();
tuples.add(new Object[] {"John", 35}); tuples.add(new Object[] {"John", 35});
@ -328,8 +365,10 @@ The preceding query can be parameterized and run as follows:
client.sql("SELECT id, name, state FROM table WHERE (name, age) IN (:tuples)") client.sql("SELECT id, name, state FROM table WHERE (name, age) IN (:tuples)")
.bind("tuples", tuples); .bind("tuples", tuples);
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val tuples: MutableList<Array<Any>> = ArrayList() val tuples: MutableList<Array<Any>> = ArrayList()
tuples.add(arrayOf("John", 35)) tuples.add(arrayOf("John", 35))
@ -338,19 +377,25 @@ The preceding query can be parameterized and run as follows:
client.sql("SELECT id, name, state FROM table WHERE (name, age) IN (:tuples)") client.sql("SELECT id, name, state FROM table WHERE (name, age) IN (:tuples)")
.bind("tuples", tuples) .bind("tuples", tuples)
---- ----
======
NOTE: Usage of select lists is vendor-dependent. NOTE: Usage of select lists is vendor-dependent.
The following example shows a simpler variant using `IN` predicates: The following example shows a simpler variant using `IN` predicates:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
client.sql("SELECT id, name, state FROM table WHERE age IN (:ages)") client.sql("SELECT id, name, state FROM table WHERE age IN (:ages)")
.bind("ages", Arrays.asList(35, 50)); .bind("ages", Arrays.asList(35, 50));
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val tuples: MutableList<Array<Any>> = ArrayList() val tuples: MutableList<Array<Any>> = ArrayList()
tuples.add(arrayOf("John", 35)) tuples.add(arrayOf("John", 35))
@ -359,6 +404,7 @@ The following example shows a simpler variant using `IN` predicates:
client.sql("SELECT id, name, state FROM table WHERE age IN (:ages)") client.sql("SELECT id, name, state FROM table WHERE age IN (:ages)")
.bind("tuples", arrayOf(35, 50)) .bind("tuples", arrayOf(35, 50))
---- ----
======
NOTE: R2DBC itself does not support Collection-like values. Nevertheless, NOTE: R2DBC itself does not support Collection-like values. Nevertheless,
expanding a given `List` in the example above works for named parameters expanding a given `List` in the example above works for named parameters
@ -376,27 +422,36 @@ before it gets run. Register a `Statement` filter
(`StatementFilterFunction`) through `DatabaseClient` to intercept and (`StatementFilterFunction`) through `DatabaseClient` to intercept and
modify statements in their execution, as the following example shows: modify statements in their execution, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
client.sql("INSERT INTO table (name, state) VALUES(:name, :state)") client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
.filter((s, next) -> next.execute(s.returnGeneratedValues("id"))) .filter((s, next) -> next.execute(s.returnGeneratedValues("id")))
.bind("name", …) .bind("name", …)
.bind("state", …); .bind("state", …);
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
client.sql("INSERT INTO table (name, state) VALUES(:name, :state)") client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
.filter { s: Statement, next: ExecuteFunction -> next.execute(s.returnGeneratedValues("id")) } .filter { s: Statement, next: ExecuteFunction -> next.execute(s.returnGeneratedValues("id")) }
.bind("name", …) .bind("name", …)
.bind("state", …) .bind("state", …)
---- ----
======
`DatabaseClient` exposes also simplified `filter(…)` overload accepting `Function<Statement, Statement>`: `DatabaseClient` exposes also simplified `filter(…)` overload accepting `Function<Statement, Statement>`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
client.sql("INSERT INTO table (name, state) VALUES(:name, :state)") client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
.filter(statement -> s.returnGeneratedValues("id")); .filter(statement -> s.returnGeneratedValues("id"));
@ -404,8 +459,10 @@ modify statements in their execution, as the following example shows:
client.sql("SELECT id, name, state FROM table") client.sql("SELECT id, name, state FROM table")
.filter(statement -> s.fetchSize(25)); .filter(statement -> s.fetchSize(25));
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
client.sql("INSERT INTO table (name, state) VALUES(:name, :state)") client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
.filter { statement -> s.returnGeneratedValues("id") } .filter { statement -> s.returnGeneratedValues("id") }
@ -413,6 +470,7 @@ modify statements in their execution, as the following example shows:
client.sql("SELECT id, name, state FROM table") client.sql("SELECT id, name, state FROM table")
.filter { statement -> s.fetchSize(25) } .filter { statement -> s.fetchSize(25) }
---- ----
======
`StatementFilterFunction` implementations allow filtering of the `StatementFilterFunction` implementations allow filtering of the
`Statement` and filtering of `Result` objects. `Statement` and filtering of `Result` objects.
@ -432,8 +490,11 @@ that shared `ConnectionFactory` bean into your DAO classes. The `DatabaseClient`
the setter for the `ConnectionFactory`. This leads to DAOs that resemble the following: the setter for the `ConnectionFactory`. This leads to DAOs that resemble the following:
-- --
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class R2dbcCorporateEventDao implements CorporateEventDao { public class R2dbcCorporateEventDao implements CorporateEventDao {
@ -446,8 +507,10 @@ the setter for the `ConnectionFactory`. This leads to DAOs that resemble the fol
// R2DBC-backed implementations of the methods on the CorporateEventDao follow... // R2DBC-backed implementations of the methods on the CorporateEventDao follow...
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class R2dbcCorporateEventDao(connectionFactory: ConnectionFactory) : CorporateEventDao { class R2dbcCorporateEventDao(connectionFactory: ConnectionFactory) : CorporateEventDao {
@ -456,6 +519,7 @@ the setter for the `ConnectionFactory`. This leads to DAOs that resemble the fol
// R2DBC-backed implementations of the methods on the CorporateEventDao follow... // R2DBC-backed implementations of the methods on the CorporateEventDao follow...
} }
---- ----
======
-- --
An alternative to explicit configuration is to use component-scanning and annotation An alternative to explicit configuration is to use component-scanning and annotation
@ -464,8 +528,11 @@ support for dependency injection. In this case, you can annotate the class with
method with `@Autowired`. The following example shows how to do so: method with `@Autowired`. The following example shows how to do so:
-- --
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Component // <1> @Component // <1>
public class R2dbcCorporateEventDao implements CorporateEventDao { public class R2dbcCorporateEventDao implements CorporateEventDao {
@ -480,6 +547,7 @@ method with `@Autowired`. The following example shows how to do so:
// R2DBC-backed implementations of the methods on the CorporateEventDao follow... // R2DBC-backed implementations of the methods on the CorporateEventDao follow...
} }
---- ----
======
<1> Annotate the class with `@Component`. <1> Annotate the class with `@Component`.
<2> Annotate the `ConnectionFactory` setter method with `@Autowired`. <2> Annotate the `ConnectionFactory` setter method with `@Autowired`.
<3> Create a new `DatabaseClient` with the `ConnectionFactory`. <3> Create a new `DatabaseClient` with the `ConnectionFactory`.
@ -516,8 +584,11 @@ that defines an auto-increment or identity column. To get full control over
the column name to generate, simply register a `StatementFilterFunction` that the column name to generate, simply register a `StatementFilterFunction` that
requests the generated key for the desired column. requests the generated key for the desired column.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
Mono<Integer> generatedId = client.sql("INSERT INTO table (name, state) VALUES(:name, :state)") Mono<Integer> generatedId = client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
.filter(statement -> s.returnGeneratedValues("id")) .filter(statement -> s.returnGeneratedValues("id"))
@ -526,8 +597,10 @@ requests the generated key for the desired column.
// generatedId emits the generated key once the INSERT statement has finished // generatedId emits the generated key once the INSERT statement has finished
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val generatedId = client.sql("INSERT INTO table (name, state) VALUES(:name, :state)") val generatedId = client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
.filter { statement -> s.returnGeneratedValues("id") } .filter { statement -> s.returnGeneratedValues("id") }
@ -536,6 +609,7 @@ requests the generated key for the desired column.
// generatedId emits the generated key once the INSERT statement has finished // generatedId emits the generated key once the INSERT statement has finished
---- ----
======
[[r2dbc-connections]] [[r2dbc-connections]]
@ -575,16 +649,22 @@ To configure a `ConnectionFactory`:
The following example shows how to configure a `ConnectionFactory`: The following example shows how to configure a `ConnectionFactory`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
ConnectionFactory factory = ConnectionFactories.get("r2dbc:h2:mem:///test?options=DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE"); ConnectionFactory factory = ConnectionFactories.get("r2dbc:h2:mem:///test?options=DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE");
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val factory = ConnectionFactories.get("r2dbc:h2:mem:///test?options=DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE"); val factory = ConnectionFactories.get("r2dbc:h2:mem:///test?options=DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE");
---- ----
======
[[r2dbc-ConnectionFactoryUtils]] [[r2dbc-ConnectionFactoryUtils]]

View File

@ -15,8 +15,11 @@ The ease-of-use afforded by the use of the `@Transactional` annotation is best
illustrated with an example, which is explained in the text that follows. illustrated with an example, which is explained in the text that follows.
Consider the following class definition: Consider the following class definition:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
// the service class that we want to make transactional // the service class that we want to make transactional
@Transactional @Transactional
@ -43,8 +46,10 @@ Consider the following class definition:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
// the service class that we want to make transactional // the service class that we want to make transactional
@Transactional @Transactional
@ -67,6 +72,7 @@ Consider the following class definition:
} }
} }
---- ----
======
Used at the class level as above, the annotation indicates a default for all methods of Used at the class level as above, the annotation indicates a default for all methods of
the declaring class (as well as its subclasses). Alternatively, each method can be the declaring class (as well as its subclasses). Alternatively, each method can be
@ -128,8 +134,11 @@ preceding example.
Reactive transactional methods use reactive return types in contrast to imperative Reactive transactional methods use reactive return types in contrast to imperative
programming arrangements as the following listing shows: programming arrangements as the following listing shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
// the reactive service class that we want to make transactional // the reactive service class that we want to make transactional
@Transactional @Transactional
@ -156,8 +165,10 @@ programming arrangements as the following listing shows:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
// the reactive service class that we want to make transactional // the reactive service class that we want to make transactional
@Transactional @Transactional
@ -180,6 +191,7 @@ programming arrangements as the following listing shows:
} }
} }
---- ----
======
Note that there are special considerations for the returned `Publisher` with regards to Note that there are special considerations for the returned `Publisher` with regards to
Reactive Streams cancellation signals. See the xref:data-access/transaction/programmatic.adoc#tx-prog-operator-cancel[Cancel Signals] section under Reactive Streams cancellation signals. See the xref:data-access/transaction/programmatic.adoc#tx-prog-operator-cancel[Cancel Signals] section under
@ -322,8 +334,11 @@ annotated at the class level with the settings for a read-only transaction, but
`@Transactional` annotation on the `updateFoo(Foo)` method in the same class takes `@Transactional` annotation on the `updateFoo(Foo)` method in the same class takes
precedence over the transactional settings defined at the class level. precedence over the transactional settings defined at the class level.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Transactional(readOnly = true) @Transactional(readOnly = true)
public class DefaultFooService implements FooService { public class DefaultFooService implements FooService {
@ -339,8 +354,10 @@ precedence over the transactional settings defined at the class level.
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
@Transactional(readOnly = true) @Transactional(readOnly = true)
class DefaultFooService : FooService { class DefaultFooService : FooService {
@ -356,6 +373,7 @@ precedence over the transactional settings defined at the class level.
} }
} }
---- ----
======
[[transaction-declarative-attransactional-settings]] [[transaction-declarative-attransactional-settings]]
@ -455,8 +473,11 @@ of the transaction manager bean. For example, using the qualifier notation, you
combine the following Java code with the following transaction manager bean declarations combine the following Java code with the following transaction manager bean declarations
in the application context: in the application context:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class TransactionalService { public class TransactionalService {
@ -470,8 +491,10 @@ in the application context:
public Mono<Void> doSomethingReactive() { ... } public Mono<Void> doSomethingReactive() { ... }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
class TransactionalService { class TransactionalService {
@ -491,6 +514,7 @@ in the application context:
} }
} }
---- ----
======
The following listing shows the bean declarations: The following listing shows the bean declarations:
@ -527,8 +551,11 @@ methods, xref:core/beans/classpath-scanning.adoc#beans-meta-annotations[Spring's
define custom composed annotations for your specific use cases. For example, consider the define custom composed annotations for your specific use cases. For example, consider the
following annotation definitions: following annotation definitions:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Target({ElementType.METHOD, ElementType.TYPE}) @Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@ -542,8 +569,10 @@ following annotation definitions:
public @interface AccountTx { public @interface AccountTx {
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.TYPE) @Target(AnnotationTarget.FUNCTION, AnnotationTarget.TYPE)
@Retention(AnnotationRetention.RUNTIME) @Retention(AnnotationRetention.RUNTIME)
@ -555,11 +584,15 @@ following annotation definitions:
@Transactional(transactionManager = "account", label = ["retryable"]) @Transactional(transactionManager = "account", label = ["retryable"])
annotation class AccountTx annotation class AccountTx
---- ----
======
The preceding annotations let us write the example from the previous section as follows: The preceding annotations let us write the example from the previous section as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class TransactionalService { public class TransactionalService {
@ -574,8 +607,10 @@ The preceding annotations let us write the example from the previous section as
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"] [source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
---- ----
class TransactionalService { class TransactionalService {
@ -590,6 +625,7 @@ The preceding annotations let us write the example from the previous section as
} }
} }
---- ----
======
In the preceding example, we used the syntax to define the transaction manager qualifier In the preceding example, we used the syntax to define the transaction manager qualifier
and transactional labels, but we could also have included propagation behavior, and transactional labels, but we could also have included propagation behavior,

View File

@ -18,8 +18,11 @@ configuration and AOP in general.
The following code shows the simple profiling aspect discussed earlier: The following code shows the simple profiling aspect discussed earlier:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"] [source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
---- ----
package x.y; package x.y;
@ -55,8 +58,10 @@ The following code shows the simple profiling aspect discussed earlier:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages"] [source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages"]
.Kotlin
---- ----
package x.y package x.y
@ -92,6 +97,7 @@ The following code shows the simple profiling aspect discussed earlier:
} }
} }
---- ----
======
The ordering of advice The ordering of advice
is controlled through the `Ordered` interface. For full details on advice ordering, see is controlled through the `Ordered` interface. For full details on advice ordering, see

View File

@ -20,8 +20,11 @@ xref:core/aop.adoc[AOP] respectively.
The following example shows how to create a transaction manager and configure the The following example shows how to create a transaction manager and configure the
`AnnotationTransactionAspect` to use it: `AnnotationTransactionAspect` to use it:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
// construct an appropriate transaction manager // construct an appropriate transaction manager
DataSourceTransactionManager txManager = new DataSourceTransactionManager(getDataSource()); DataSourceTransactionManager txManager = new DataSourceTransactionManager(getDataSource());
@ -29,8 +32,10 @@ The following example shows how to create a transaction manager and configure th
// configure the AnnotationTransactionAspect to use it; this must be done before executing any transactional methods // configure the AnnotationTransactionAspect to use it; this must be done before executing any transactional methods
AnnotationTransactionAspect.aspectOf().setTransactionManager(txManager); AnnotationTransactionAspect.aspectOf().setTransactionManager(txManager);
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
// construct an appropriate transaction manager // construct an appropriate transaction manager
val txManager = DataSourceTransactionManager(getDataSource()) val txManager = DataSourceTransactionManager(getDataSource())
@ -38,6 +43,7 @@ The following example shows how to create a transaction manager and configure th
// configure the AnnotationTransactionAspect to use it; this must be done before executing any transactional methods // configure the AnnotationTransactionAspect to use it; this must be done before executing any transactional methods
AnnotationTransactionAspect.aspectOf().transactionManager = txManager AnnotationTransactionAspect.aspectOf().transactionManager = txManager
---- ----
======
NOTE: When you use this aspect, you must annotate the implementation class (or the methods NOTE: When you use this aspect, you must annotate the implementation class (or the methods
within that class or both), not the interface (if any) that the class implements. AspectJ within that class or both), not the interface (if any) that the class implements. AspectJ

View File

@ -10,8 +10,11 @@ transactions being created and then rolled back in response to the
`UnsupportedOperationException` instance. The following listing shows the `FooService` `UnsupportedOperationException` instance. The following listing shows the `FooService`
interface: interface:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"] [source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
---- ----
// the service interface that we want to make transactional // the service interface that we want to make transactional
@ -29,8 +32,10 @@ interface:
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
---- ----
// the service interface that we want to make transactional // the service interface that we want to make transactional
@ -47,11 +52,15 @@ interface:
fun updateFoo(foo: Foo) fun updateFoo(foo: Foo)
} }
---- ----
======
The following example shows an implementation of the preceding interface: The following example shows an implementation of the preceding interface:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"] [source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
---- ----
package x.y.service; package x.y.service;
@ -78,8 +87,10 @@ The following example shows an implementation of the preceding interface:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
---- ----
package x.y.service package x.y.service
@ -102,6 +113,7 @@ The following example shows an implementation of the preceding interface:
} }
} }
---- ----
======
Assume that the first two methods of the `FooService` interface, `getFoo(String)` and Assume that the first two methods of the `FooService` interface, `getFoo(String)` and
`getFoo(String, String)`, must run in the context of a transaction with read-only `getFoo(String, String)`, must run in the context of a transaction with read-only
@ -215,8 +227,11 @@ a transaction is started, suspended, marked as read-only, and so on, depending o
transaction configuration associated with that method. Consider the following program transaction configuration associated with that method. Consider the following program
that test drives the configuration shown earlier: that test drives the configuration shown earlier:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public final class Boot { public final class Boot {
@ -227,8 +242,10 @@ that test drives the configuration shown earlier:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
import org.springframework.beans.factory.getBean import org.springframework.beans.factory.getBean
@ -238,6 +255,7 @@ that test drives the configuration shown earlier:
fooService.insertFoo(Foo()) fooService.insertFoo(Foo())
} }
---- ----
======
The output from running the preceding program should resemble the following (the Log4J The output from running the preceding program should resemble the following (the Log4J
output and the stack trace from the `UnsupportedOperationException` thrown by the output and the stack trace from the `UnsupportedOperationException` thrown by the
@ -281,8 +299,11 @@ return type is reactive.
The following listing shows a modified version of the previously used `FooService`, but The following listing shows a modified version of the previously used `FooService`, but
this time the code uses reactive types: this time the code uses reactive types:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"] [source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
---- ----
// the reactive service interface that we want to make transactional // the reactive service interface that we want to make transactional
@ -300,8 +321,10 @@ this time the code uses reactive types:
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
---- ----
// the reactive service interface that we want to make transactional // the reactive service interface that we want to make transactional
@ -318,11 +341,15 @@ this time the code uses reactive types:
fun updateFoo(foo: Foo) : Mono<Void> fun updateFoo(foo: Foo) : Mono<Void>
} }
---- ----
======
The following example shows an implementation of the preceding interface: The following example shows an implementation of the preceding interface:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"] [source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
---- ----
package x.y.service; package x.y.service;
@ -349,8 +376,10 @@ The following example shows an implementation of the preceding interface:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
---- ----
package x.y.service package x.y.service
@ -373,6 +402,7 @@ The following example shows an implementation of the preceding interface:
} }
} }
---- ----
======
Imperative and reactive transaction management share the same semantics for transaction Imperative and reactive transaction management share the same semantics for transaction
boundary and transaction attribute definitions. The main difference between imperative boundary and transaction attribute definitions. The main difference between imperative

View File

@ -26,8 +26,11 @@ automatically rolled back in case of a failure. For more information on Vavr's T
refer to the [official Vavr documentation](https://www.vavr.io/vavr-docs/#_try). refer to the [official Vavr documentation](https://www.vavr.io/vavr-docs/#_try).
Here's an example of how to use Vavr's Try with a transactional method: Here's an example of how to use Vavr's Try with a transactional method:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Transactional @Transactional
public Try<String> myTransactionalMethod() { public Try<String> myTransactionalMethod() {
@ -37,6 +40,7 @@ Here's an example of how to use Vavr's Try with a transactional method:
return Try.of(delegate::myDataAccessOperation); return Try.of(delegate::myDataAccessOperation);
} }
---- ----
======
Checked exceptions that are thrown from a transactional method do not result in a rollback Checked exceptions that are thrown from a transactional method do not result in a rollback
in the default configuration. You can configure exactly which `Exception` types mark a in the default configuration. You can configure exactly which `Exception` types mark a
@ -138,8 +142,11 @@ is quite invasive and tightly couples your code to the Spring Framework's transa
infrastructure. The following example shows how to programmatically indicate a required infrastructure. The following example shows how to programmatically indicate a required
rollback: rollback:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public void resolvePosition() { public void resolvePosition() {
try { try {
@ -150,8 +157,10 @@ rollback:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
fun resolvePosition() { fun resolvePosition() {
try { try {
@ -162,6 +171,7 @@ rollback:
} }
} }
---- ----
======
You are strongly encouraged to use the declarative approach to rollback, if at all You are strongly encouraged to use the declarative approach to rollback, if at all
possible. Programmatic rollback is available should you absolutely need it, but its possible. Programmatic rollback is available should you absolutely need it, but its

View File

@ -15,8 +15,11 @@ event and that we want to define a listener that should only handle that event o
transaction in which it has been published has committed successfully. The following transaction in which it has been published has committed successfully. The following
example sets up such an event listener: example sets up such an event listener:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
@Component @Component
public class MyComponent { public class MyComponent {
@ -27,8 +30,10 @@ example sets up such an event listener:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
@Component @Component
class MyComponent { class MyComponent {
@ -39,6 +44,7 @@ example sets up such an event listener:
} }
} }
---- ----
======
The `@TransactionalEventListener` annotation exposes a `phase` attribute that lets you The `@TransactionalEventListener` annotation exposes a `phase` attribute that lets you
customize the phase of the transaction to which the listener should be bound. customize the phase of the transaction to which the listener should be bound.

View File

@ -33,8 +33,11 @@ anonymous inner class) that contains the code that you need to run in the contex
a transaction. You can then pass an instance of your custom `TransactionCallback` to the a transaction. You can then pass an instance of your custom `TransactionCallback` to the
`execute(..)` method exposed on the `TransactionTemplate`. The following example shows how to do so: `execute(..)` method exposed on the `TransactionTemplate`. The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class SimpleService implements Service { public class SimpleService implements Service {
@ -57,8 +60,10 @@ a transaction. You can then pass an instance of your custom `TransactionCallback
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
// use constructor-injection to supply the PlatformTransactionManager // use constructor-injection to supply the PlatformTransactionManager
class SimpleService(transactionManager: PlatformTransactionManager) : Service { class SimpleService(transactionManager: PlatformTransactionManager) : Service {
@ -72,13 +77,17 @@ a transaction. You can then pass an instance of your custom `TransactionCallback
} }
} }
---- ----
======
If there is no return value, you can use the convenient `TransactionCallbackWithoutResult` class If there is no return value, you can use the convenient `TransactionCallbackWithoutResult` class
with an anonymous class, as follows: with an anonymous class, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
transactionTemplate.execute(new TransactionCallbackWithoutResult() { transactionTemplate.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) { protected void doInTransactionWithoutResult(TransactionStatus status) {
@ -87,8 +96,10 @@ with an anonymous class, as follows:
} }
}); });
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
transactionTemplate.execute(object : TransactionCallbackWithoutResult() { transactionTemplate.execute(object : TransactionCallbackWithoutResult() {
override fun doInTransactionWithoutResult(status: TransactionStatus) { override fun doInTransactionWithoutResult(status: TransactionStatus) {
@ -97,13 +108,17 @@ with an anonymous class, as follows:
} }
}) })
---- ----
======
Code within the callback can roll the transaction back by calling the Code within the callback can roll the transaction back by calling the
`setRollbackOnly()` method on the supplied `TransactionStatus` object, as follows: `setRollbackOnly()` method on the supplied `TransactionStatus` object, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
transactionTemplate.execute(new TransactionCallbackWithoutResult() { transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@ -117,8 +132,10 @@ Code within the callback can roll the transaction back by calling the
} }
}); });
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
transactionTemplate.execute(object : TransactionCallbackWithoutResult() { transactionTemplate.execute(object : TransactionCallbackWithoutResult() {
@ -132,6 +149,7 @@ Code within the callback can roll the transaction back by calling the
} }
}) })
---- ----
======
[[tx-prog-template-settings]] [[tx-prog-template-settings]]
=== Specifying Transaction Settings === Specifying Transaction Settings
@ -143,8 +161,11 @@ xref:data-access/transaction/declarative/txadvice-settings.adoc[default transact
following example shows the programmatic customization of the transactional settings for following example shows the programmatic customization of the transactional settings for
a specific `TransactionTemplate:` a specific `TransactionTemplate:`
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class SimpleService implements Service { public class SimpleService implements Service {
@ -160,8 +181,10 @@ a specific `TransactionTemplate:`
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class SimpleService(transactionManager: PlatformTransactionManager) : Service { class SimpleService(transactionManager: PlatformTransactionManager) : Service {
@ -173,6 +196,7 @@ a specific `TransactionTemplate:`
} }
} }
---- ----
======
The following example defines a `TransactionTemplate` with some custom transactional The following example defines a `TransactionTemplate` with some custom transactional
settings by using Spring XML configuration: settings by using Spring XML configuration:
@ -212,8 +236,11 @@ to make yourself.
Application code that must run in a transactional context and that explicitly uses Application code that must run in a transactional context and that explicitly uses
the `TransactionalOperator` resembles the next example: the `TransactionalOperator` resembles the next example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class SimpleService implements Service { public class SimpleService implements Service {
@ -235,8 +262,10 @@ the `TransactionalOperator` resembles the next example:
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
// use constructor-injection to supply the ReactiveTransactionManager // use constructor-injection to supply the ReactiveTransactionManager
class SimpleService(transactionManager: ReactiveTransactionManager) : Service { class SimpleService(transactionManager: ReactiveTransactionManager) : Service {
@ -250,6 +279,7 @@ the `TransactionalOperator` resembles the next example:
} }
} }
---- ----
======
`TransactionalOperator` can be used in two ways: `TransactionalOperator` can be used in two ways:
@ -259,8 +289,11 @@ the `TransactionalOperator` resembles the next example:
Code within the callback can roll the transaction back by calling the `setRollbackOnly()` Code within the callback can roll the transaction back by calling the `setRollbackOnly()`
method on the supplied `ReactiveTransaction` object, as follows: method on the supplied `ReactiveTransaction` object, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
transactionalOperator.execute(new TransactionCallback<>() { transactionalOperator.execute(new TransactionCallback<>() {
@ -271,8 +304,10 @@ method on the supplied `ReactiveTransaction` object, as follows:
} }
}); });
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
transactionalOperator.execute(object : TransactionCallback() { transactionalOperator.execute(object : TransactionCallback() {
@ -282,6 +317,7 @@ method on the supplied `ReactiveTransaction` object, as follows:
} }
}) })
---- ----
======
[[tx-prog-operator-cancel]] [[tx-prog-operator-cancel]]
=== Cancel Signals === Cancel Signals
@ -306,8 +342,11 @@ xref:data-access/transaction/declarative/txadvice-settings.adoc[default transact
following example shows customization of the transactional settings for a specific following example shows customization of the transactional settings for a specific
`TransactionalOperator:` `TransactionalOperator:`
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
public class SimpleService implements Service { public class SimpleService implements Service {
@ -325,8 +364,10 @@ following example shows customization of the transactional settings for a specif
} }
} }
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
class SimpleService(transactionManager: ReactiveTransactionManager) : Service { class SimpleService(transactionManager: ReactiveTransactionManager) : Service {
@ -339,6 +380,7 @@ following example shows customization of the transactional settings for a specif
private val transactionalOperator = TransactionalOperator(transactionManager, definition) private val transactionalOperator = TransactionalOperator(transactionManager, definition)
} }
---- ----
======
[[transaction-programmatic-tm]] [[transaction-programmatic-tm]]
== Using the `TransactionManager` == Using the `TransactionManager`
@ -356,8 +398,11 @@ use to your bean through a bean reference. Then, by using the `TransactionDefini
`TransactionStatus` objects, you can initiate transactions, roll back, and commit. The `TransactionStatus` objects, you can initiate transactions, roll back, and commit. The
following example shows how to do so: following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
DefaultTransactionDefinition def = new DefaultTransactionDefinition(); DefaultTransactionDefinition def = new DefaultTransactionDefinition();
// explicitly setting the transaction name is something that can be done only programmatically // explicitly setting the transaction name is something that can be done only programmatically
@ -373,8 +418,10 @@ following example shows how to do so:
} }
txManager.commit(status); txManager.commit(status);
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val def = DefaultTransactionDefinition() val def = DefaultTransactionDefinition()
// explicitly setting the transaction name is something that can be done only programmatically // explicitly setting the transaction name is something that can be done only programmatically
@ -391,6 +438,7 @@ following example shows how to do so:
txManager.commit(status) txManager.commit(status)
---- ----
======
[[transaction-programmatic-rtm]] [[transaction-programmatic-rtm]]
@ -403,8 +451,11 @@ use to your bean through a bean reference. Then, by using the `TransactionDefini
`ReactiveTransaction` objects, you can initiate transactions, roll back, and commit. The `ReactiveTransaction` objects, you can initiate transactions, roll back, and commit. The
following example shows how to do so: following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"] [source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
---- ----
DefaultTransactionDefinition def = new DefaultTransactionDefinition(); DefaultTransactionDefinition def = new DefaultTransactionDefinition();
// explicitly setting the transaction name is something that can be done only programmatically // explicitly setting the transaction name is something that can be done only programmatically
@ -421,8 +472,10 @@ following example shows how to do so:
.onErrorResume(ex -> txManager.rollback(status).then(Mono.error(ex))); .onErrorResume(ex -> txManager.rollback(status).then(Mono.error(ex)));
}); });
---- ----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
---- ----
val def = DefaultTransactionDefinition() val def = DefaultTransactionDefinition()
// explicitly setting the transaction name is something that can be done only programmatically // explicitly setting the transaction name is something that can be done only programmatically
@ -438,5 +491,6 @@ following example shows how to do so:
.onErrorResume { ex -> txManager.rollback(status).then(Mono.error(ex)) } .onErrorResume { ex -> txManager.rollback(status).then(Mono.error(ex)) }
} }
---- ----
======

View File

@ -38,12 +38,10 @@ you can instead write the following:
registerBean { Bar(it.getBean()) } registerBean { Bar(it.getBean()) }
} }
---- ----
====
When the class `Bar` has a single constructor, you can even just specify the bean class, When the class `Bar` has a single constructor, you can even just specify the bean class,
the constructor parameters will be autowired by type: the constructor parameters will be autowired by type:
====
[source,kotlin,indent=0] [source,kotlin,indent=0]
---- ----
val context = GenericApplicationContext().apply { val context = GenericApplicationContext().apply {

View File

@ -256,7 +256,6 @@ in order to use `val` instead of `lateinit var`. You can use
{api-spring-framework}/test/context/TestConstructor.html[`@TestConstructor(autowireMode = AutowireMode.ALL)`] {api-spring-framework}/test/context/TestConstructor.html[`@TestConstructor(autowireMode = AutowireMode.ALL)`]
to enable autowiring for all parameters. to enable autowiring for all parameters.
====
[source,kotlin,indent=0] [source,kotlin,indent=0]
---- ----
@SpringJUnitConfig(TestConfig::class) @SpringJUnitConfig(TestConfig::class)
@ -267,7 +266,6 @@ class OrderServiceIntegrationTests(val orderService: OrderService,
// tests that use the injected OrderService and CustomerService // tests that use the injected OrderService and CustomerService
} }
---- ----
====
[[per_class-lifecycle]] [[per_class-lifecycle]]

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