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:
extensions:
- '@antora/collector-extension'
- require: '@springio/antora-extensions/tabs-migration-extension'
unwrap_example_block: always
site:
title: Spring Framework Reference
url: https://https://rwinch.github.io/spring-framework/

View File

@ -54,8 +54,11 @@ point.
The following example shows a simple `MethodInterceptor` implementation:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
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
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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
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.
@ -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):
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
class RemoteThrowsAdvice : ThrowsAdvice {
@ -201,13 +218,17 @@ The following advice is invoked if a `RemoteException` is thrown (including from
}
}
----
======
Unlike the preceding
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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
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
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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
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
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
not thrown exceptions:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class CountingAfterReturningAdvice implements AfterReturningAdvice {
@ -309,8 +342,10 @@ not thrown exceptions:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
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
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
introduce the following interface to one or more objects:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public interface Lockable {
void lock();
@ -389,8 +428,10 @@ introduce the following interface to one or more objects:
boolean locked();
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
interface Lockable {
fun lock()
@ -398,6 +439,7 @@ introduce the following interface to one or more objects:
fun locked(): Boolean
}
----
======
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
@ -434,8 +476,11 @@ to that held in the target object.
The following example shows the example `LockMixin` class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
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
`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`.
The following example shows our `LockMixinAdvisor` class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
class LockMixinAdvisor : DefaultIntroductionAdvisor(LockMixin(), Lockable::class.java)
----
======
We can apply this advisor very simply, because it requires no configuration. (However, it
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
following methods:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Advisor[] getAdvisors();
@ -29,8 +32,10 @@ following methods:
boolean isFrozen();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
fun getAdvisors(): Array<Advisor>
@ -59,6 +64,7 @@ following methods:
fun isFrozen(): Boolean
----
======
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
@ -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
manipulating its advice:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Advised advised = (Advised) myObject;
Advisor[] advisors = advised.getAdvisors();
@ -98,8 +107,10 @@ manipulating its advice:
assertEquals("Added two advisors", oldAdvisorCount + 2, advised.getAdvisors().length);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val advised = myObject as Advised
val advisors = advised.advisors
@ -116,6 +127,7 @@ manipulating its advice:
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
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
follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Person person = (Person) factory.getBean("person");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val person = factory.getBean("person") as Person;
----
======
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:

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
following example shows how to subclass `StaticMethodMatcherPointcut`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
class TestStaticPointcut : StaticMethodMatcherPointcut() {
@ -228,6 +233,7 @@ following example shows how to subclass `StaticMethodMatcherPointcut`:
}
}
----
======
There are also superclasses for dynamic pointcuts.
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
interceptor and one advisor:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
ProxyFactory factory = new ProxyFactory(myBusinessInterfaceImpl);
factory.addAdvice(myMethodInterceptor);
factory.addAdvisor(myAdvisor);
MyBusinessInterface tb = (MyBusinessInterface) factory.getProxy();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val factory = ProxyFactory(myBusinessInterfaceImpl)
factory.addAdvice(myMethodInterceptor)
factory.addAdvisor(myAdvisor)
val tb = factory.proxy as MyBusinessInterface
----
======
The first step is to construct an object of type
`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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
HotSwappableTargetSource swapper = (HotSwappableTargetSource) beanFactory.getBean("swapper");
Object oldTarget = swapper.swap(newTarget);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val swapper = beanFactory.getBean("swapper") as HotSwappableTargetSource
val oldTarget = swapper.swap(newTarget)
----
======
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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
PoolingConfig conf = (PoolingConfig) beanFactory.getBean("businessObject");
System.out.println("Max pool size is " + conf.getMaxSize());
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val conf = beanFactory.getBean("businessObject") as PoolingConfig
println("Max pool size is " + conf.maxSize)
----
======
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

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.
The basic usage for this class is very simple, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
// create a factory that can generate a proxy for the given target object
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...
MyInterfaceType proxy = factory.getProxy();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
// create a factory that can generate a proxy for the given target object
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...
val proxy = factory.getProxy<Any>()
----
======
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.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
import org.aspectj.lang.annotation.Aspect;
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"]
.Kotlin
----
import org.aspectj.lang.annotation.Aspect
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
as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@ -62,8 +71,10 @@ as follows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Before
@ -77,6 +88,7 @@ as follows:
}
}
----
======
[[aop-advice-after-returning]]
@ -85,8 +97,11 @@ as follows:
After returning advice runs when a matched method execution returns normally.
You can declare it by using the `@AfterReturning` annotation.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
import org.aspectj.lang.annotation.Aspect;
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"]
.Kotlin
----
import org.aspectj.lang.annotation.Aspect
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),
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
access, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
import org.aspectj.lang.annotation.Aspect;
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"]
.Kotlin
----
import org.aspectj.lang.annotation.Aspect
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
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
following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;
@ -191,8 +218,10 @@ following example shows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
import org.aspectj.lang.annotation.Aspect
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,
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.
The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
import org.aspectj.lang.annotation.Aspect;
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"]
.Kotlin
----
import org.aspectj.lang.annotation.Aspect
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 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
purposes. The following example shows how to use after finally advice:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
import org.aspectj.lang.annotation.Aspect;
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"]
.Kotlin
----
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.After
@ -301,6 +342,7 @@ purposes. The following example shows how to use after finally advice:
}
}
----
======
[NOTE]
====
@ -371,8 +413,11 @@ value depending on the use case.
The following example shows how to use around advice:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
import org.aspectj.lang.annotation.Aspect;
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"]
.Kotlin
----
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Around
@ -409,6 +456,7 @@ The following example shows how to use around advice:
}
}
----
======
[[aop-ataspectj-advice-params]]
== 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.
You could write the following:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
@Before("execution(* com.xyz.dao.*.*(..)) && args(account,..)")
public void validateAccount(Account account) {
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
@Before("execution(* com.xyz.dao.*.*(..)) && args(account,..)")
fun validateAccount(account: Account) {
// ...
}
----
======
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
@ -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
from the advice. This would look as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
@Pointcut("execution(* com.xyz.dao.*.*(..)) && args(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"]
.Kotlin
----
@Pointcut("execution(* com.xyz.dao.*.*(..)) && args(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.
@ -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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@ -517,18 +580,24 @@ The following shows the definition of the `@Auditable` annotation:
AuditCode value();
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
annotation class Auditable(val value: AuditCode)
----
======
The following shows the advice that matches the execution of `@Auditable` methods:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
@Before("com.xyz.Pointcuts.publicMethod() && @annotation(auditable)") // <1>
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].
[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
you have a generic type like the following:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
public interface Sample<T> {
void sampleGenericMethod(T param);
void sampleGenericCollectionMethod(Collection<T> param);
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
interface Sample<T> {
fun sampleGenericMethod(param: T)
fun sampleGenericCollectionMethod(param: Collection<T>)
}
----
======
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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
@Before("execution(* ..Sample+.sampleGenericMethod(*)) && args(param)")
public void beforeSampleMethod(MyType param) {
// Advice implementation
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
@Before("execution(* ..Sample+.sampleGenericMethod(*)) && args(param)")
fun beforeSampleMethod(param: MyType) {
// Advice implementation
}
----
======
This approach does not work for generic collections. So you cannot define a
pointcut as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
@Before("execution(* ..Sample+.sampleGenericCollectionMethod(*)) && args(param)")
public void beforeSampleMethod(Collection<MyType> param) {
// Advice implementation
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
@Before("execution(* ..Sample+.sampleGenericCollectionMethod(*)) && args(param)")
fun beforeSampleMethod(param: Collection<MyType>) {
// Advice implementation
}
----
======
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
@ -668,8 +756,11 @@ needed information.
The following example shows how to use the `argNames` attribute:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
@Before(
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
}
----
======
<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.
@ -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
point object, the `argNames` attribute does not need to include it:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
@Before(
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
}
----
======
<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.
@ -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
the `argNames` attribute:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
@Before("com.xyz.Pointcuts.publicMethod()") // <1>
public void audit(JoinPoint jp) {
// ... use jp
}
----
======
<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"]
@ -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.
The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
@Around("execution(List<Account> find*(..)) && " +
"com.xyz.CommonPointcuts.inDataAccessLayer() && " +
@ -776,6 +879,7 @@ The following example shows how to do so:
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].
[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`
annotation, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
@Configuration
@EnableAspectJAutoProxy
class AppConfig
----
======
[[aop-enable-aspectj-xml]]
== 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
annotated with `@Aspect`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary",chomp="-packages",fold="none"]
.Java
----
package com.xyz;
@ -30,8 +33,10 @@ annotated with `@Aspect`:
public class NotVeryUsefulAspect {
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages",fold="none"]
.Kotlin
----
package com.xyz
@ -40,6 +45,7 @@ annotated with `@Aspect`:
@Aspect
class NotVeryUsefulAspect
----
======
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)

View File

@ -16,8 +16,11 @@ aspect.
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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
@Aspect
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].
[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
`Idempotent` annotation:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
@Retention(RetentionPolicy.RUNTIME)
// marker annotation
public @interface Idempotent {
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
@Retention(AnnotationRetention.RUNTIME)
// marker annotation
annotation class Idempotent
----
======
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
expression so that only `@Idempotent` operations match, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
@Around("execution(* com.xyz..service.*.*(..)) && " +
"@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"]
.Kotlin
----
@Around("execution(* com.xyz..service.*.*(..)) && " +
"@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`
annotation. Consider the following example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
@Aspect("perthis(execution(* com.xyz..service.*.*(..)))")
public class MyAspect {
@ -27,8 +30,10 @@ annotation. Consider the following example:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
@Aspect("perthis(execution(* com.xyz..service.*.*(..)))")
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
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
interfaces also implement the `UsageTracked` interface (e.g. for statistics via JMX):
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
@Aspect
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"]
.Kotlin
----
@Aspect
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
`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,
you would write the following:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
UsageTracked usageTracked = context.getBean("myService", UsageTracked.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
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
matches the execution of any method named `transfer`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
@Pointcut("execution(* transfer(..))") // the pointcut expression
private void anyOldTransfer() {} // the pointcut signature
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
@Pointcut("execution(* transfer(..))") // the pointcut expression
private fun anyOldTransfer() {} // the pointcut signature
----
======
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
@ -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
pointcut expressions by name. The following example shows three pointcut expressions:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary",chomp="-packages"]
.Java
----
package com.xyz;
@ -158,6 +167,7 @@ pointcut expressions by name. The following example shows three pointcut express
public void tradingOperation() {} // <3>
}
----
======
<1> `publicMethod` matches if a method execution join point represents the execution
of any public method.
<2> `inTrading` matches if a method execution is in the trading module.
@ -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
`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"]
.Java
----
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"]
.Kotlin
----
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
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.
For example, in the @AspectJ style you can write something like the following:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
@Pointcut("execution(* get*())")
public void propertyAccess() {}
@ -67,8 +70,10 @@ For example, in the @AspectJ style you can write something like the following:
@Pointcut("propertyAccess() && operationReturningAnAccount()")
public void accountPropertyAccess() {}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
@Pointcut("execution(* get*())")
fun propertyAccess() {}
@ -79,6 +84,7 @@ For example, in the @AspectJ style you can write something like the following:
@Pointcut("propertyAccess() && operationReturningAnAccount()")
fun accountPropertyAccess() {}
----
======
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
code snippet shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
public class SimplePojo implements Pojo {
@ -80,8 +83,10 @@ code snippet shows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
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
that object reference, as the following image and listing show:
image::aop-proxy-plain-pojo-call.png[]
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
public class Main {
@ -113,8 +122,10 @@ image::aop-proxy-plain-pojo-call.png[]
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
fun main() {
val pojo = SimplePojo()
@ -122,14 +133,18 @@ image::aop-proxy-plain-pojo-call.png[]
pojo.foo()
}
----
======
Things change slightly when the reference that client code has is a proxy. Consider the
following diagram and code snippet:
image::aop-proxy-call.png[]
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
public class Main {
@ -144,8 +159,10 @@ image::aop-proxy-call.png[]
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
fun main() {
val factory = ProxyFactory(SimplePojo())
@ -157,6 +174,7 @@ fun main() {
pojo.foo()
}
----
======
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
@ -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
within your class to Spring AOP, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
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"]
.Kotlin
----
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
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
following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
public class Main {
@ -228,8 +255,10 @@ following example shows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
fun main() {
val factory = ProxyFactory(SimplePojo())
@ -242,6 +271,7 @@ following example shows:
pojo.foo()
}
----
======
Finally, it must be noted that AspectJ does not have this self-invocation issue because
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
parameters of the matching names, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
public void monitor(Object service) {
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
fun monitor(service: Any) {
// ...
}
----
======
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;+`,
@ -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
example, you can declare the method signature as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
public void doAccessCheck(Object retVal) {...
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
fun doAccessCheck(retVal: Any) {...
----
======
[[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
`@AfterThrowing`. For example, the method signature may be declared as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
public void doRecoveryActions(DataAccessException dataAccessEx) {...
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
fun doRecoveryActions(dataAccessEx: DataAccessException) {...
----
======
[[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
@AspectJ example (minus the annotation, of course), as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
// start stopwatch
@ -409,8 +430,10 @@ The implementation of the `doBasicProfiling` advice can be exactly the same as i
return retVal;
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
fun doBasicProfiling(pjp: ProceedingJoinPoint): Any {
// start stopwatch
@ -419,6 +442,7 @@ The implementation of the `doBasicProfiling` advice can be exactly the same as i
return pjp.proceed()
}
----
======
[[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
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"]
.Java
----
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"]
.Kotlin
----
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
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
`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"]
.Java
----
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"]
.Kotlin
----
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
preceding advice for a particular join point:
@ -570,8 +606,11 @@ preceding advice for a particular join point:
Consider the following driver script:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
public class Boot {
@ -582,8 +621,10 @@ Consider the following driver script:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
fun main() {
val ctx = ClassPathXmlApplicationContext("beans.xml")
@ -591,6 +632,7 @@ Consider the following driver script:
person.getPerson("Pengo", 12)
}
----
======
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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
public void recordUsage(UsageTracked usageTracked) {
usageTracked.incrementUseCount();
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
fun recordUsage(usageTracked: UsageTracked) {
usageTracked.incrementUseCount()
}
----
======
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
@ -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
following:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
UsageTracked usageTracked = context.getBean("myService", UsageTracked.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
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
(which is a regular Java class that uses the schema support):
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
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"]
.Kotlin
----
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
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
to annotate the implementation of service operations, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
@Retention(RetentionPolicy.RUNTIME)
// marker annotation
public @interface Idempotent {
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
@Retention(AnnotationRetention.RUNTIME)
// marker annotation
annotation class Idempotent
----
======
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
following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary",chomp="-packages"]
.Java
----
package com.xyz.domain;
@ -44,8 +47,10 @@ following example shows:
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages"]
.Kotlin
----
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
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
can do so directly in the annotation, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary",chomp="-packages"]
.Java
----
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"]
.Kotlin
----
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
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
`@Configurable` declaration, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
@Configurable(preConstruction = true)
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
@Configurable(preConstruction = true)
----
======
You can find more information about the language semantics of the various pointcut
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
`@Configuration` class, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
@Configuration
@EnableSpringConfigured
public class AppConfig {
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
@Configuration
@EnableSpringConfigured
class AppConfig {
}
----
======
If you prefer XML based configuration, the Spring
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.
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"]
.Java
----
package com.xyz;
@ -448,8 +475,10 @@ It is a time-based profiler that uses the @AspectJ-style of aspect declaration:
public void methodsToBeProfiled(){}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages"]
.Kotlin
----
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 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
driver class with a `main(..)` method to demonstrate the LTW in action:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary",chomp="-packages"]
.Java
----
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"]
.Kotlin
----
package com.xyz
@ -573,6 +608,7 @@ driver class with a `main(..)` method to demonstrate the LTW in action:
service.calculateEntitlement()
}
----
======
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.
@ -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
result:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary",chomp="-packages"]
.Java
----
package com.xyz;
@ -632,8 +671,10 @@ result:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages"]
.Kotlin
----
package com.xyz
@ -648,6 +689,7 @@ result:
service.calculateEntitlement()
}
----
======
Notice how, in the preceding program, we bootstrap the Spring container and
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`,
which typically is done by using the `@EnableLoadTimeWeaving` annotation, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
@Configuration
@EnableLoadTimeWeaving
public class AppConfig {
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
@Configuration
@EnableLoadTimeWeaving
class AppConfig {
}
----
======
Alternatively, if you prefer XML-based configuration, use 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.
The following example specifies a `ReflectiveLoadTimeWeaver`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
@Configuration
@EnableLoadTimeWeaving
@ -817,8 +868,10 @@ The following example specifies a `ReflectiveLoadTimeWeaver`:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
@Configuration
@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
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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration(proxyBeanMethods = false)
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.
The AOT engine will convert the configuration class above to code similar to the following:
[tabs]
======
Java::
+
[source,java,indent=0,role="primary"]
.Java
----
/**
* 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.
@ -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 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"]
.Java
----
runtimeHints.resources().registerPattern("config/app.properties");
----
======
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).
@ -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.
The following example registers `Account` for serialization.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Component
public class OrderService {
@ -261,6 +276,7 @@ The following example registers `Account` for serialization.
}
----
======
[[aot.hints.testing]]
=== 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
use the `NamespaceHandlerSupport` class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
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"]
.Kotlin
----
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
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
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"]
.Java
----
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
the basic grunt work of creating a single `BeanDefinition`.
<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.
The following listing shows the `Component` class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
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"]
.Kotlin
----
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
setter property for the `components` property. The following listing shows such a custom
`FactoryBean`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
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"]
.Kotlin
----
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
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],
we then create a custom `NamespaceHandler`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
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"]
.Kotlin
----
package com.foo
@ -599,13 +626,17 @@ we then create a custom `NamespaceHandler`:
}
}
----
======
Next up is the custom `BeanDefinitionParser`. Remember that we are creating
a `BeanDefinition` that describes a `ComponentFactoryBean`. The following
listing shows our custom `BeanDefinitionParser` implementation:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
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"]
.Kotlin
----
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,
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
JCache-initializing `BeanDefinition`. The following listing shows our `JCacheInitializer`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
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"]
.Kotlin
----
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
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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
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"]
.Kotlin
----
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
an XML attribute, we write a `BeanDefinitionDecorator` rather than a `BeanDefinitionParser`.
The following listing shows our `BeanDefinitionDecorator` implementation:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
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"]
.Kotlin
----
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
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 following example enumeration shows how easy injecting an enum value is:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
package jakarta.persistence;
@ -128,8 +131,10 @@ The following example enumeration shows how easy injecting an enum value is:
EXTENDED
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
----
package jakarta.persistence
@ -139,11 +144,15 @@ The following example enumeration shows how easy injecting an enum value is:
EXTENDED
}
----
======
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"]
.Java
----
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"]
.Kotlin
----
package example
@ -166,6 +177,7 @@ Now consider the following setter of type `PersistenceContextType` and the corre
lateinit var persistenceContextType: PersistenceContextType
}
----
======
[source,xml,indent=0,subs="verbatim,quotes"]
----

View File

@ -11,8 +11,11 @@ autowired value.
Consider the following configuration that defines `firstMovieCatalog` as the
primary `MovieCatalog`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
public class MovieConfiguration {
@ -27,8 +30,10 @@ primary `MovieCatalog`:
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
class MovieConfiguration {
@ -43,12 +48,16 @@ primary `MovieCatalog`:
// ...
}
----
======
With the preceding configuration, the following `MovieRecommender` is autowired with the
`firstMovieCatalog`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
class MovieRecommender {
@ -69,6 +80,7 @@ class MovieRecommender {
// ...
}
----
======
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:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class MovieRecommender {
@ -21,8 +24,10 @@ shown in the following example:
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class MovieRecommender {
@ -33,14 +38,18 @@ shown in the following example:
// ...
}
----
======
--
You can also specify the `@Qualifier` annotation on individual constructor arguments or
method parameters, as shown in the following example:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
class MovieRecommender {
@ -77,6 +88,7 @@ method parameters, as shown in the following example:
// ...
}
----
======
--
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:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@ -205,22 +220,28 @@ provide the `@Qualifier` annotation within your definition, as the following exa
String value();
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
@Qualifier
annotation class Genre(val value: String)
----
======
--
Then you can provide the custom qualifier on autowired fields and parameters, as the
following example shows:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class MovieRecommender {
@ -238,8 +259,10 @@ following example shows:
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class MovieRecommender {
@ -257,6 +280,7 @@ following example shows:
// ...
}
----
======
--
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:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@ -315,22 +342,28 @@ the simple annotation, as the following example shows:
public @interface Offline {
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
@Qualifier
annotation class Offline
----
======
--
Then add the annotation to the field or property to be autowired, as shown in the
following example:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class MovieRecommender {
@ -341,6 +374,7 @@ following example:
// ...
}
----
======
<1> This line adds the `@Offline` annotation.
[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:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@ -392,41 +429,53 @@ consider the following annotation definition:
Format format();
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
@Qualifier
annotation class MovieQualifier(val genre: String, val format: Format)
----
======
--
In this case `Format` is an enum, defined as follows:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public enum Format {
VHS, DVD, BLURAY
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
enum class Format {
VHS, DVD, BLURAY
}
----
======
--
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:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
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

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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
class MovieRecommender @Autowired constructor(
private val customerPreferenceDao: CustomerPreferenceDao)
----
======
[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,
as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class SimpleMovieLister {
@ -59,8 +68,10 @@ as the following example shows:
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class SimpleMovieLister {
@ -71,12 +82,16 @@ as the following example shows:
}
----
======
You can also apply the annotation to methods with arbitrary names and multiple
arguments, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class MovieRecommender {
@ -94,8 +109,10 @@ arguments, as the following example shows:
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
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
following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class MovieRecommender {
@ -135,8 +156,10 @@ following example shows:
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class MovieRecommender @Autowired constructor(
private val customerPreferenceDao: CustomerPreferenceDao) {
@ -147,6 +170,7 @@ following example shows:
// ...
}
----
======
[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
expects an array of that type, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
class MovieRecommender {
@ -217,6 +252,7 @@ The same applies for typed collections, as the following example shows:
// ...
}
----
======
[[beans-factory-ordered]]
[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
corresponding bean names, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
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
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
non-required (i.e., by setting the `required` attribute in `@Autowired` to `false`):
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
class SimpleMovieLister {
@ -303,6 +350,7 @@ non-required (i.e., by setting the `required` attribute in `@Autowired` to `fals
// ...
}
----
======
[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`
attribute in `@Autowired` has a somewhat different meaning due to Spring's 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
Kotlin built-in null-safety support:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class SimpleMovieLister {
@ -375,8 +426,10 @@ Kotlin built-in null-safety support:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class SimpleMovieLister {
@ -386,6 +439,7 @@ Kotlin built-in null-safety support:
// ...
}
----
======
You can also use `@Autowired` for interfaces that are well-known resolvable
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
an `ApplicationContext` object:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class MovieRecommender {
@ -408,8 +465,10 @@ an `ApplicationContext` object:
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class MovieRecommender {
@ -419,6 +478,7 @@ class MovieRecommender {
// ...
}
----
======
[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
configuration:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
public class MyConfiguration {
@ -22,8 +25,10 @@ configuration:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
class MyConfiguration {
@ -35,13 +40,17 @@ configuration:
fun integerStore() = IntegerStore()
}
----
======
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
used as a qualifier, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Autowired
private Store<String> s1; // <String> qualifier, injects the stringStore bean
@ -49,8 +58,10 @@ used as a qualifier, as the following example shows:
@Autowired
private Store<Integer> s2; // <Integer> qualifier, injects the integerStore bean
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Autowired
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
private lateinit var s2: Store<Integer> // <Integer> qualifier, injects the integerStore bean
----
======
Generic qualifiers also apply when autowiring lists, `Map` instances and arrays. The
following example autowires a generic `List`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// Inject all Store beans as long as they have an <Integer> generic
// Store<String> beans will not appear in this list
@Autowired
private List<Store<Integer>> s;
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// Inject all Store beans as long as they have an <Integer> generic
// Store<String> beans will not appear in this list
@Autowired
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
cleared upon destruction:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class CachingMovieLister {
@ -29,8 +32,10 @@ cleared upon destruction:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class CachingMovieLister {
@ -45,6 +50,7 @@ cleared upon destruction:
}
}
----
======
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].

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:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class SimpleMovieLister {
@ -24,6 +27,7 @@ as demonstrated in the following example:
}
}
----
======
<1> This line injects a `@Resource`.
[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:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class SimpleMovieLister {
@ -58,8 +65,10 @@ named `movieFinder` injected into its setter method:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
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
@ -88,8 +98,11 @@ named "customerPreferenceDao" and then falls back to a primary type match for th
`CustomerPreferenceDao`:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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:
`ApplicationContext`.

View File

@ -3,8 +3,11 @@
`@Value` is typically used to inject externalized properties:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Component
public class MovieRecommender {
@ -16,29 +19,38 @@
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Component
class MovieRecommender(@Value("\${catalog.name}") private val catalog: String)
----
======
With the following configuration:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@PropertySource("classpath:application.properties")
public class AppConfig { }
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@PropertySource("classpath:application.properties")
class AppConfig
----
======
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
example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
public class AppConfig {
@ -67,8 +82,10 @@ example shows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
class AppConfig {
@ -77,6 +94,7 @@ example shows:
fun propertyPlaceholderConfigurer() = PropertySourcesPlaceholderConfigurer()
}
----
======
NOTE: When configuring a `PropertySourcesPlaceholderConfigurer` using JavaConfig, the
`@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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Component
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"]
.Kotlin
----
@Component
class MovieRecommender(@Value("\${catalog.name:defaultCatalog}") private val catalog: String)
----
======
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
provide conversion support for your own custom type, you can provide your own
`ConversionService` bean instance as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
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"]
.Kotlin
----
@Configuration
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
computed at runtime as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Component
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"]
.Kotlin
----
@Component
class MovieRecommender(
@Value("#{systemProperties['user.catalog'] + 'Catalog' }") private val catalog: String)
----
======
SpEL also enables the use of more complex data structures:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Component
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"]
.Kotlin
----
@Component
class MovieRecommender(
@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
as the local file system, the Java `CLASSPATH`, and so on.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
----
.Kotlin
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val context = ClassPathXmlApplicationContext("services.xml", "daos.xml")
----
======
[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
example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// create and configure beans
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
@ -307,7 +316,9 @@ example shows:
// use configured instance
List<String> userList = service.getUsernameList();
----
.Kotlin
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
import org.springframework.beans.factory.getBean
@ -321,58 +332,77 @@ example shows:
// use configured instance
var userList = service.getUsernameList()
----
======
With Groovy configuration, bootstrapping looks very similar. It has a different context
implementation class which is Groovy-aware (but also understands XML bean definitions).
The following example shows Groovy configuration:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
ApplicationContext context = new GenericGroovyApplicationContext("services.groovy", "daos.groovy");
----
.Kotlin
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val context = GenericGroovyApplicationContext("services.groovy", "daos.groovy")
----
======
The most flexible variant is `GenericApplicationContext` in combination with reader
delegates -- for example, with `XmlBeanDefinitionReader` for XML files, as the following
example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
GenericApplicationContext context = new GenericApplicationContext();
new XmlBeanDefinitionReader(context).loadBeanDefinitions("services.xml", "daos.xml");
context.refresh();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val context = GenericApplicationContext()
XmlBeanDefinitionReader(context).loadBeanDefinitions("services.xml", "daos.xml")
context.refresh()
----
======
You can also use the `GroovyBeanDefinitionReader` for Groovy files, as the following
example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
GenericApplicationContext context = new GenericApplicationContext();
new GroovyBeanDefinitionReader(context).loadBeanDefinitions("services.groovy", "daos.groovy");
context.refresh();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val context = GenericApplicationContext()
GroovyBeanDefinitionReader(context).loadBeanDefinitions("services.groovy", "daos.groovy")
context.refresh()
----
======
You can mix and match such reader delegates on the same `ApplicationContext`,
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`,
you need to programmatically call `addBeanPostProcessor`, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
// 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
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val factory = DefaultListableBeanFactory()
// 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
----
======
To apply a `BeanFactoryPostProcessor` to a plain `DefaultListableBeanFactory`,
you need to call its `postProcessBeanFactory` method, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
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
cfg.postProcessBeanFactory(factory);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val factory = DefaultListableBeanFactory()
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
cfg.postProcessBeanFactory(factory)
----
======
In both cases, the explicit registration steps are inconvenient, which is
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]
is meta-annotated with `@Component`, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Target(ElementType.TYPE)
@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`.
[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
`SessionScope` annotation:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Target({ElementType.TYPE, ElementType.METHOD})
@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"]
.Kotlin
----
@Target(AnnotationTarget.TYPE, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
@ -124,11 +133,15 @@ customization of the `proxyMode`. The following listing shows the definition of
val proxyMode: ScopedProxyMode = ScopedProxyMode.TARGET_CLASS
)
----
======
You can then use `@SessionScope` without declaring the `proxyMode` as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Service
@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"]
.Kotlin
----
@Service
@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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Service
@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"]
.Kotlin
----
@Service
@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
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
are eligible for such autodetection:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Service
public class SimpleMovieLister {
@ -193,29 +218,38 @@ are eligible for such autodetection:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Service
class SimpleMovieLister(private val movieFinder: MovieFinder)
----
======
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Repository
public class JpaMovieFinder implements MovieFinder {
// implementation elided for clarity
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Repository
class JpaMovieFinder : MovieFinder {
// implementation elided for clarity
}
----
======
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
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"]
.Java
----
@Configuration
@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"]
.Kotlin
----
@Configuration
@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
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
and using "`stub`" repositories instead:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@ComponentScan(basePackages = "org.example",
@ -346,8 +389,10 @@ and using "`stub`" repositories instead:
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@ComponentScan(basePackages = ["org.example"],
@ -357,6 +402,7 @@ and using "`stub`" repositories instead:
// ...
}
----
======
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`
annotated classes. The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Component
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"]
.Kotlin
----
@Component
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
`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
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"]
.Java
----
@Component
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"]
.Kotlin
----
@Component
class FactoryMethodComponent {
@ -504,6 +561,7 @@ support for autowiring of `@Bean` methods. The following example shows how to do
fun requestScopedInstance() = TestBean("requestScopedInstance", 3)
}
----
======
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
@ -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.
The following example shows how to use `InjectionPoint`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Component
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"]
.Kotlin
----
@Component
class FactoryMethodComponent {
@ -546,6 +609,7 @@ The following example shows how to use `InjectionPoint`:
TestBean("prototypeInstance for ${injectionPoint.member}")
}
----
======
The `@Bean` methods in a regular Spring component are processed differently than their
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
classes were detected, the names would be `myMovieLister` and `movieFinderImpl`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Service("myMovieLister")
public class SimpleMovieLister {
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Service("myMovieLister")
class SimpleMovieLister {
// ...
}
----
======
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Repository
public class MovieFinderImpl implements MovieFinder {
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Repository
class MovieFinderImpl : MovieFinder {
// ...
}
----
======
If you do not want to rely on the default bean-naming strategy, you can provide a custom
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
`org.springframework.context.annotation` can be used for such purposes.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@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"]
.Kotlin
----
@Configuration
@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"]
----
@ -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
scope within the annotation, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Scope("prototype")
@Repository
@ -707,8 +792,10 @@ scope within the annotation, as the following example shows:
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Scope("prototype")
@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
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
an annotation and a bean definition shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@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"]
.Kotlin
----
@Configuration
@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"]
----
@ -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,
the following configuration results in standard JDK dynamic proxies:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@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"]
.Kotlin
----
@Configuration
@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"]
----
@ -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
technique:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Component
@Qualifier("Action")
@ -817,16 +920,22 @@ technique:
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Component
@Qualifier("Action")
class ActionMovieCatalog : MovieCatalog
----
======
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Component
@Genre("Action")
@ -834,8 +943,10 @@ technique:
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Component
@Genre("Action")
@ -843,9 +954,13 @@ technique:
// ...
}
----
======
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Component
@Offline
@ -853,8 +968,10 @@ technique:
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Component
@Offline
@ -862,6 +979,7 @@ class CachingMovieCatalog : MovieCatalog {
// ...
}
----
======
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

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`
implementations and so can be cast to the `MessageSource` interface.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public static void main(String[] args) {
MessageSource resources = new ClassPathXmlApplicationContext("beans.xml");
@ -107,8 +110,10 @@ implementations and so can be cast to the `MessageSource` interface.
System.out.println(message);
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
fun main() {
val resources = ClassPathXmlApplicationContext("beans.xml")
@ -116,6 +121,7 @@ implementations and so can be cast to the `MessageSource` interface.
println(message)
}
----
======
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>
----
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
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:
@ -208,8 +220,11 @@ resolved is specified manually:
argument.required=Ebagum lad, the ''{0}'' argument is required, I say, required.
----
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public static void main(final String[] args) {
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);
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
fun main() {
val resources = ClassPathXmlApplicationContext("beans.xml")
@ -228,6 +245,7 @@ argument.required=Ebagum lad, the ''{0}'' argument is required, I say, required.
println(message)
}
----
======
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
simple class that extends Spring's `ApplicationEvent` base class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class BlockedListEvent extends ApplicationEvent {
@ -339,21 +360,27 @@ simple class that extends Spring's `ApplicationEvent` base class:
// accessor and other methods...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class BlockedListEvent(source: Any,
val address: String,
val content: String) : ApplicationEvent(source)
----
======
To publish a custom `ApplicationEvent`, call the `publishEvent()` method on an
`ApplicationEventPublisher`. Typically, this is done by creating a class that implements
`ApplicationEventPublisherAware` and registering it as a Spring bean. The following
example shows such a class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class EmailService implements ApplicationEventPublisherAware {
@ -377,8 +404,10 @@ example shows such a class:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class EmailService : ApplicationEventPublisherAware {
@ -402,6 +431,7 @@ example shows such a class:
}
}
----
======
At configuration time, the Spring container detects that `EmailService` implements
`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
shows such a class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class BlockedListNotifier implements ApplicationListener<BlockedListEvent> {
@ -429,8 +462,10 @@ shows such a class:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class BlockedListNotifier : ApplicationListener<BlockedListEvent> {
@ -441,6 +476,7 @@ shows such a class:
}
}
----
======
Notice that `ApplicationListener` is generically parameterized with the type of your
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
`@EventListener` annotation. The `BlockedListNotifier` can be rewritten as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
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,
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
following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class})
public void handleContextStart() {
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@EventListener(ContextStartedEvent::class, ContextRefreshedEvent::class)
fun handleContextStart() {
// ...
}
----
======
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
@ -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
`content` attribute of the event is equal to `my-event`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@EventListener(condition = "#blEvent.content == 'my-event'")
public void processBlockedListEvent(BlockedListEvent blEvent) {
// notify appropriate parties via notificationAddress...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@EventListener(condition = "#blEvent.content == 'my-event'")
fun processBlockedListEvent(blEvent: BlockedListEvent) {
// notify appropriate parties via notificationAddress...
}
----
======
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:
@ -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
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"]
.Java
----
@EventListener
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...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@EventListener
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...
}
----
======
NOTE: This feature is not supported for
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].
The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@EventListener
@Async
@ -654,8 +717,10 @@ The following example shows how to do so:
// BlockedListEvent is processed in a separate thread
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@EventListener
@Async
@ -663,6 +728,7 @@ The following example shows how to do so:
// BlockedListEvent is processed in a separate thread
}
----
======
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`
annotation to the method declaration, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@EventListener
@Order(42)
@ -691,8 +760,10 @@ annotation to the method declaration, as the following example shows:
// notify appropriate parties via notificationAddress...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@EventListener
@Order(42)
@ -700,6 +771,7 @@ annotation to the method declaration, as the following example shows:
// notify appropriate parties via notificationAddress...
}
----
======
[[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
`Person`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@EventListener
public void onPersonCreated(EntityCreatedEvent<Person> event) {
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@EventListener
fun onPersonCreated(event: EntityCreatedEvent<Person>) {
// ...
}
----
======
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
@ -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
environment provides. The following event shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
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
an event.
@ -819,8 +903,11 @@ The `AbstractApplicationContext` (and its subclasses) is instrumented with an
Here is an example of instrumentation in the `AnnotationConfigApplicationContext`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// create a startup step and start recording
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
scanPackages.end();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// create a startup step and start recording
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
scanPackages.end()
----
======
The application context is already instrumented with multiple steps.
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
`@Configuration` classes, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableLoadTimeWeaving
public class AppConfig {
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableLoadTimeWeaving
class AppConfig
----
======
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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class 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"]
.Kotlin
----
class ClientService private constructor() {
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
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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class DefaultServiceLocator {
@ -328,8 +337,10 @@ The following example shows the corresponding class:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class DefaultServiceLocator {
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:
@ -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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class DefaultServiceLocator {
@ -378,8 +393,10 @@ The following example shows the corresponding class:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class DefaultServiceLocator {
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
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
injection:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class SimpleMovieLister {
@ -46,14 +49,17 @@ injection:
// business logic that actually uses the injected MovieFinder is omitted...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// a constructor so that the Spring container can inject a MovieFinder
class SimpleMovieLister(private val movieFinder: MovieFinder) {
// business logic that actually uses the injected MovieFinder is omitted...
}
----
======
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.
@ -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
being instantiated. Consider the following class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
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"]
.Kotlin
----
package x.y
class ThingOne(thingTwo: ThingTwo, thingThree: ThingThree)
----
======
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
@ -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
by type without help. Consider the following class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
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"]
.Kotlin
----
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
)
----
======
.[[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
then have to look as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
package examples;
@ -211,8 +232,10 @@ then have to look as follows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
----
package examples
@ -220,6 +243,7 @@ then have to look as follows:
@ConstructorProperties("years", "ultimateAnswer")
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
on container specific interfaces, base classes, or annotations.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class SimpleMovieLister {
@ -261,6 +290,7 @@ class SimpleMovieLister {
// business logic that actually uses the injected MovieFinder is omitted...
}
----
======
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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
class ExampleBean {
lateinit var beanOne: AnotherBean
@ -436,6 +471,7 @@ class ExampleBean {
var i: Int = 0
}
----
======
In the preceding example, setters are declared to match against the properties specified
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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
class ExampleBean(
private val beanOne: AnotherBean,
private val beanTwo: YetAnotherBean,
private val i: Int)
----
======
The constructor arguments specified in the bean definition are used as arguments to
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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
class ExampleBean private constructor() {
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,
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
shows this approach:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages",fold="none"]
.Java
----
package fiona.apple;
@ -54,8 +57,10 @@ shows this approach:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages",fold="none"]
.Kotlin
----
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
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
the reworked example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages",fold="none"]
.Java
----
package fiona.apple;
@ -148,8 +157,10 @@ the reworked example shows:
protected abstract Command createCommand();
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages",fold="none"]
.Kotlin
----
package fiona.apple
@ -169,6 +180,7 @@ the reworked example shows:
protected abstract fun createCommand(): Command
}
----
======
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:
@ -204,8 +216,11 @@ bean is returned each time.
Alternatively, within the annotation-based component model, you can declare a lookup
method through the `@Lookup` annotation, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public abstract class CommandManager {
@ -219,8 +234,10 @@ method through the `@Lookup` annotation, as the following example shows:
protected abstract Command createCommand();
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
abstract class CommandManager {
@ -234,12 +251,16 @@ method through the `@Lookup` annotation, as the following example shows:
protected abstract fun createCommand(): Command
}
----
======
Or, more idiomatically, you can rely on the target bean getting resolved against the
declared return type of the lookup method:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public abstract class CommandManager {
@ -253,8 +274,10 @@ declared return type of the lookup method:
protected abstract Command createCommand();
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
abstract class CommandManager {
@ -268,6 +291,7 @@ declared return type of the lookup method:
protected abstract fun createCommand(): Command
}
----
======
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
@ -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
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"]
.Java
----
public class MyValueCalculator {
@ -308,8 +335,10 @@ the following class, which has a method called `computeValue` that we want to ov
// some other methods...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class MyValueCalculator {
@ -320,12 +349,16 @@ the following class, which has a method called `computeValue` that we want to ov
// some other methods...
}
----
======
A class that implements the `org.springframework.beans.factory.support.MethodReplacer`
interface provides the new method definition, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
/**
* 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"]
.Kotlin
----
/**
* 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`.
The following Java class and bean definition show how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
class SomeClass {
lateinit var accounts: Map<String, Float>
}
----
======
[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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
exampleBean.setEmail("");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
exampleBean.email = ""
----
======
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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
exampleBean.setEmail(null);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
exampleBean.email = null
----
======
[[beans-p-namespace]]

View File

@ -39,8 +39,11 @@ B deployments.
Consider the first use case in a practical application that requires a
`DataSource`. In a test environment, the configuration might resemble the following:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Bean
public DataSource dataSource() {
@ -51,8 +54,10 @@ Consider the first use case in a practical application that requires a
.build();
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Bean
fun dataSource(): DataSource {
@ -63,14 +68,18 @@ Consider the first use case in a practical application that requires a
.build()
}
----
======
Now consider how this application can be deployed into a QA or production
environment, assuming that the datasource for the application is registered
with the production application server's JNDI directory. Our `dataSource` bean
now looks like the following listing:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Bean(destroyMethod = "")
public DataSource dataSource() throws Exception {
@ -78,8 +87,10 @@ now looks like the following listing:
return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Bean(destroyMethod = "")
fun dataSource(): DataSource {
@ -87,6 +98,7 @@ now looks like the following listing:
return ctx.lookup("java:comp/env/jdbc/datasource") as DataSource
}
----
======
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
@ -112,8 +124,11 @@ when one or more specified profiles are active. Using our preceding example, we
can rewrite the `dataSource` configuration as follows:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@Profile("development")
@ -129,8 +144,10 @@ can rewrite the `dataSource` configuration as follows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@Profile("development")
@ -146,11 +163,15 @@ can rewrite the `dataSource` configuration as follows:
}
}
----
======
--
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@Profile("production")
@ -163,6 +184,7 @@ can rewrite the `dataSource` configuration as follows:
}
}
----
======
<1> `@Bean(destroyMethod = "")` disables default destroy method inference.
[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")`:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@ -215,14 +240,17 @@ of creating a custom composed annotation. The following example defines a custom
public @interface Production {
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@Profile("production")
annotation class Production
----
======
--
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:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
public class AppConfig {
@ -263,6 +294,7 @@ the following example shows:
}
}
----
======
<1> The `standaloneDataSource` method is available only in the `development` 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
`ApplicationContext`. The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("development");
ctx.register(SomeConfig.class, StandaloneDataConfig.class, JndiDataConfig.class);
ctx.refresh();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val ctx = AnnotationConfigApplicationContext().apply {
environment.setActiveProfiles("development")
@ -433,6 +470,7 @@ it programmatically against the `Environment` API which is available through an
refresh()
}
----
======
In addition, you can also declaratively activate profiles through the
`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
activates multiple profiles:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
ctx.getEnvironment().setActiveProfiles("profile1", "profile2");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
ctx.getEnvironment().setActiveProfiles("profile1", "profile2")
----
======
Declaratively, `spring.profiles.active` may accept a comma-separated list of profile names,
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
following example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@Profile("default")
@ -489,8 +536,10 @@ following example:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@Profile("default")
@ -505,6 +554,7 @@ following example:
}
}
----
======
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
@ -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
hierarchy of property sources. Consider the following listing:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
ApplicationContext ctx = new GenericApplicationContext();
Environment env = ctx.getEnvironment();
boolean containsMyProperty = env.containsProperty("my-property");
System.out.println("Does my environment contain the 'my-property' property? " + containsMyProperty);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val ctx = GenericApplicationContext()
val env = ctx.environment
val containsMyProperty = env.containsProperty("my-property")
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
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
current `Environment`. The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
ConfigurableApplicationContext ctx = new GenericApplicationContext();
MutablePropertySources sources = ctx.getEnvironment().getPropertySources();
sources.addFirst(new MyPropertySource());
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val ctx = GenericApplicationContext()
val sources = ctx.environment.propertySources
sources.addFirst(MyPropertySource())
----
======
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
@ -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
a call to `testBean.getName()` returns `myTestBean`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@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"]
.Kotlin
----
@Configuration
@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
resolved against the set of property sources already registered against the
environment, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@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"]
.Kotlin
----
@Configuration
@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
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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
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"]
.Kotlin
----
package scripting
@ -160,6 +165,7 @@ The following listing shows the custom `BeanPostProcessor` implementation class
}
}
----
======
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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import org.springframework.context.ApplicationContext;
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"]
.Kotlin
----
import org.springframework.beans.factory.getBean
@ -224,6 +235,7 @@ The following Java application runs the preceding code and configuration:
println(messenger)
}
----
======
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"/>
----
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
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
(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"/>
----
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
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.
@ -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"/>
----
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
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:
@ -174,8 +192,11 @@ The preceding definition has almost exactly the same effect as the following def
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
----
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
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.
@ -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
following example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class DefaultBlogService implements BlogService {
@ -248,8 +275,10 @@ following example:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class DefaultBlogService : BlogService {
@ -263,6 +292,7 @@ following example:
}
}
----
======
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
declared on the `ConfigurableApplicationContext` interface, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import org.springframework.context.ConfigurableApplicationContext;
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"]
.Kotlin
----
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...
}
----
======

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
to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@RequestScope
@Component
@ -256,8 +259,10 @@ to do so:
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@RequestScope
@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
`@SessionScope` annotation to assign a component to the `session` scope.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@SessionScope
@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"]
.Kotlin
----
@SessionScope
@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
following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@ApplicationScope
@Component
@ -344,8 +359,10 @@ following example shows how to do so:
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@ApplicationScope
@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
underlying scope:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Object get(String name, ObjectFactory<?> objectFactory)
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
fun get(name: String, objectFactory: ObjectFactory<*>): Any
----
======
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
object with the specified name is not found. The following method removes the object from
the underlying scope:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Object remove(String name)
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
fun remove(name: String): Any
----
======
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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
void registerDestructionCallback(String name, Runnable destructionCallback)
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
fun registerDestructionCallback(name: String, destructionCallback: Runnable)
----
======
See the {api-spring-framework}/beans/factory/config/Scope.html#registerDestructionCallback[javadoc]
or a Spring scope implementation for more information on destruction callbacks.
The following method obtains the conversation identifier for the underlying scope:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
String getConversationId()
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
fun getConversationId(): String
----
======
This identifier is different for each scope. For a session scoped implementation, this
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
method to register a new `Scope` with the Spring container:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
void registerScope(String scopeName, Scope scope);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
fun registerScope(scopeName: String, scope: Scope)
----
======
This method is declared on the `ConfigurableBeanFactory` interface, which is available
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`
implementations.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Scope threadScope = new SimpleThreadScope();
beanFactory.registerScope("thread", threadScope);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val threadScope = SimpleThreadScope()
beanFactory.registerScope("thread", threadScope)
----
======
You can then create bean definitions that adhere to the scoping rules of your custom
`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.
The simplest possible `@Configuration` class reads as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
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"]
.Kotlin
----
@Configuration
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:

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
the method name. The following example shows a `@Bean` method declaration:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
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"]
.Kotlin
----
@Configuration
class AppConfig {
@ -43,6 +48,7 @@ the method name. The following example shows a `@Bean` method declaration:
fun transferService() = TransferServiceImpl()
}
----
======
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
configurations by implementing interfaces with bean definitions on default methods.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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)
return type, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
public class AppConfig {
@ -97,8 +110,10 @@ return type, as the following example shows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
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
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
parameter, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
public class AppConfig {
@ -145,8 +164,10 @@ parameter, as the following example shows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
class AppConfig {
@ -157,6 +178,7 @@ parameter, as the following example shows:
}
}
----
======
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
on the `bean` element, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
class BeanOne {
@ -242,6 +269,7 @@ class AppConfig {
fun beanTwo() = BeanTwo()
}
----
======
[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
`DataSource`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Bean(destroyMethod = "")
public DataSource dataSource() throws NamingException {
return (DataSource) jndiTemplate.lookup("MyDS");
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Bean(destroyMethod = "")
fun dataSource(): DataSource {
return jndiTemplate.lookup("MyDS") as DataSource
}
----
======
Also, with `@Bean` methods, you typically use programmatic JNDI lookups, either by
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()`
method directly during construction, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
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"]
.Kotlin
----
@Configuration
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
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,
as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
public class MyConfiguration {
@ -349,8 +392,10 @@ as the following example shows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
class MyConfiguration {
@ -362,6 +407,7 @@ as the following example shows:
}
}
----
======
[[beans-java-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,
it resembles the following:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// an HTTP Session-scoped bean exposed as a proxy
@Bean
@ -397,8 +446,10 @@ it resembles the following:
return service;
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// an HTTP Session-scoped bean exposed as a proxy
@Bean
@ -413,6 +464,7 @@ it resembles the following:
}
}
----
======
[[beans-java-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,
as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
public class AppConfig {
@ -433,8 +488,10 @@ as the following example shows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
class AppConfig {
@ -443,6 +500,7 @@ as the following example shows:
fun thing() = Thing()
}
----
======
[[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
a number of aliases for a bean:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
public class AppConfig {
@ -465,8 +526,10 @@ a number of aliases for a bean:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
class AppConfig {
@ -477,6 +540,7 @@ a number of aliases for a bean:
}
}
----
======
[[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`]
annotation, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
public class AppConfig {
@ -502,8 +569,10 @@ annotation, as the following example shows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
class AppConfig {
@ -513,6 +582,7 @@ annotation, as the following example shows:
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
another configuration class, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
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"]
.Kotlin
----
@Configuration
class ConfigA {
@ -52,13 +57,17 @@ another configuration class, as the following example shows:
fun b() = B()
}
----
======
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
following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class);
@ -68,8 +77,10 @@ following example shows:
B b = ctx.getBean(B.class);
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import org.springframework.beans.factory.getBean
@ -81,6 +92,7 @@ following example shows:
val b = ctx.getBean<B>()
}
----
======
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
@ -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`
classes, each depending on beans declared in the others:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
public class ServiceConfig {
@ -144,8 +159,10 @@ classes, each depending on beans declared in the others:
transferService.transfer(100.00, "A123", "C456");
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
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")
}
----
======
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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
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");
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
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")
}
----
======
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
@ -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
configuration classes themselves. The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
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"]
.Kotlin
----
@Configuration
class ServiceConfig {
@ -350,14 +379,18 @@ class ServiceConfig {
}
}
----
======
In the preceding situation, where `AccountRepository` is defined is completely explicit.
However, `ServiceConfig` is now tightly coupled to `RepositoryConfig`. That is the
tradeoff. This tight coupling can be somewhat mitigated by using interface-based or
abstract class-based `@Configuration` classes. Consider the following example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
public class ServiceConfig {
@ -404,8 +437,10 @@ abstract class-based `@Configuration` classes. Consider the following example:
transferService.transfer(100.00, "A123", "C456");
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
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")
}
----
======
Now `ServiceConfig` is loosely coupled with respect to the concrete
`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
`Condition` implementation used for `@Profile`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Override
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;
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
override fun matches(context: ConditionContext, metadata: AnnotatedTypeMetadata): Boolean {
// Read the @Profile annotation attributes
@ -522,6 +563,7 @@ method that returns `true` or `false`. For example, the following listing shows
return true
}
----
======
See the {api-spring-framework}/context/annotation/Conditional.html[`@Conditional`]
javadoc for more detail.
@ -558,8 +600,11 @@ properly.
The following example shows an ordinary configuration class in Java:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
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"]
.Kotlin
----
@Configuration
class AppConfig {
@ -596,6 +643,7 @@ The following example shows an ordinary configuration class in Java:
fun transferService() = TransferService(accountRepository())
}
----
======
The following example shows part of a sample `system-test-config.xml` file:
@ -625,8 +673,11 @@ jdbc.username=sa
jdbc.password=
----
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public static void main(String[] args) {
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"]
.Kotlin
----
fun main() {
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`
@ -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
as needed:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@ImportResource("classpath:/com/acme/properties-config.xml")
@ -713,8 +770,10 @@ as needed:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@ImportResource("classpath:/com/acme/properties-config.xml")
@ -735,6 +794,7 @@ as needed:
}
}
----
======
[source,xml,indent=0,subs="verbatim,quotes"]
----
@ -752,8 +812,11 @@ jdbc.username=sa
jdbc.password=
----
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
@ -761,8 +824,10 @@ jdbc.password=
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
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
as having one bean method call another, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
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"]
.Kotlin
----
@Configuration
class AppConfig {
@ -43,6 +48,7 @@ as having one bean method call another, as the following example shows:
fun beanTwo() = BeanTwo()
}
----
======
In the preceding example, `beanOne` receives a reference to `beanTwo` through constructor
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
following example shows how to use lookup method injection:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public abstract class CommandManager {
public Object process(Object commandState) {
@ -78,8 +87,10 @@ following example shows how to use lookup method injection:
protected abstract Command createCommand();
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
abstract class CommandManager {
fun process(commandState: Any): Any {
@ -94,13 +105,17 @@ following example shows how to use lookup method injection:
protected abstract fun createCommand(): Command
}
----
======
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
(prototype) command object. The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Bean
@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"]
.Kotlin
----
@Bean
@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]]
@ -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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
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"]
.Kotlin
----
@Configuration
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()`.
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
XML-free usage of the Spring container, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
@ -32,8 +35,10 @@ XML-free usage of the Spring container, as the following example shows:
myService.doStuff();
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import org.springframework.beans.factory.getBean
@ -43,13 +48,17 @@ XML-free usage of the Spring container, as the following example shows:
myService.doStuff()
}
----
======
As mentioned earlier, `AnnotationConfigApplicationContext` is not limited to working only
with `@Configuration` classes. Any `@Component` or JSR-330 annotated class may be supplied
as input to the constructor, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public static void main(String[] args) {
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();
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import org.springframework.beans.factory.getBean
@ -68,6 +79,7 @@ as input to the constructor, as the following example shows:
myService.doStuff()
}
----
======
The preceding example assumes that `MyServiceImpl`, `Dependency1`, and `Dependency2` use Spring
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
example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
@ -93,8 +108,10 @@ example shows how to do so:
myService.doStuff();
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import org.springframework.beans.factory.getBean
@ -107,6 +124,7 @@ example shows how to do so:
myService.doStuff()
}
----
======
[[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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@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.
[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
following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
@ -166,8 +191,10 @@ following example shows:
MyService myService = ctx.getBean(MyService.class);
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
fun main() {
val ctx = AnnotationConfigApplicationContext()
@ -176,6 +203,7 @@ following example shows:
val myService = ctx.getBean<MyService>()
}
----
======
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,

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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
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
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
preceding example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import jakarta.inject.Inject;
import jakarta.inject.Provider;
@ -94,8 +103,10 @@ preceding example:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
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,
you should use the `@Named` annotation, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import jakarta.inject.Inject;
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"]
.Kotlin
----
import jakarta.inject.Inject
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
`@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"]
.Java
----
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"]
.Kotlin
----
class SimpleMovieLister {
@ -188,6 +211,7 @@ a `required` attribute. The following pair of examples show how to use `@Inject`
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`,
as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import jakarta.inject.Inject;
import jakarta.inject.Named;
@ -216,8 +243,10 @@ as the following example shows:
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import jakarta.inject.Inject
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.
`@Named` can be used in a similar fashion, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import jakarta.inject.Inject;
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"]
.Kotlin
----
import jakarta.inject.Inject
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
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"]
.Java
----
@Configuration
@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"]
.Kotlin
----
@Configuration
@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`
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
a serialization error occurs while populating the buffer with data. For example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
DataBuffer buffer = factory.allocateBuffer();
boolean release = true;
@ -156,8 +159,10 @@ a serialization error occurs while populating the buffer with data. For example:
}
return buffer;
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val buffer = factory.allocateBuffer()
var release = true
@ -171,6 +176,7 @@ a serialization error occurs while populating the buffer with data. For example:
}
return buffer
----
======
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

View File

@ -67,8 +67,11 @@ and method or constructor parameters.
The following example sets the default value of a field:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
class FieldValueTestBean {
@ -93,11 +98,15 @@ The following example sets the default value of a field:
var defaultLocale: String? = null
}
----
======
The following example shows the equivalent but on a property setter method:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
class PropertyValueTestBean {
@ -122,12 +133,16 @@ The following example shows the equivalent but on a property setter method:
var defaultLocale: String? = null
}
----
======
Autowired methods and constructors can also use the `@Value` annotation, as the following
examples show:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class SimpleMovieLister {
@ -144,8 +159,10 @@ examples show:
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class SimpleMovieLister {
@ -162,9 +179,13 @@ examples show:
// ...
}
----
======
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class MovieRecommender {
@ -181,14 +202,17 @@ examples show:
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class MovieRecommender(private val customerPreferenceDao: CustomerPreferenceDao,
@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,
`Hello World`.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("'Hello World'"); // <1>
String message = (String) exp.getValue();
----
======
<1> The value of the message variable is `'Hello World'`.
[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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("'Hello World'.concat('!')"); // <1>
String message = (String) exp.getValue();
----
======
<1> The value of `message` is now 'Hello World!'.
[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`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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>
byte[] bytes = (byte[]) exp.getValue();
----
======
<1> This line converts the literal to a byte array.
[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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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>
int length = (Integer) exp.getValue();
----
======
<1> `'Hello World'.bytes.length` gives the length of the literal.
[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
example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("new String('hello world').toUpperCase()"); // <1>
String message = exp.getValue(String.class);
----
======
<1> Construct a new `String` from the literal and make it be upper case.
[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
create a boolean condition:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// Create and set a calendar
GregorianCalendar c = new GregorianCalendar();
@ -164,8 +187,10 @@ create a boolean condition:
boolean result = exp.getValue(tesla, Boolean.class);
// result == true
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// Create and set a calendar
val c = GregorianCalendar()
@ -184,6 +209,7 @@ create a boolean condition:
val result = exp.getValue(tesla, Boolean::class.java)
// 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
being placed in it. The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
class Simple {
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
Boolean b = simple.booleanList.get(0);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class Simple {
var booleanList: MutableList<Boolean> = ArrayList()
@ -270,6 +301,7 @@ being placed in it. The following example shows how to do so:
// b is false
val b = simple.booleanList[0]
----
======
[[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
the list:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
class Demo {
public List<String> list;
@ -313,8 +348,10 @@ the list:
// demo.list will now be a real collection of 4 entries
// Each entry is a new empty String
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class Demo {
var list: List<String>? = null
@ -336,6 +373,7 @@ the list:
// demo.list will now be a real collection of 4 entries
// 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
following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
SpelParserConfiguration config = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE,
this.getClass().getClassLoader());
@ -418,8 +459,10 @@ following example shows how to do so:
Object payload = expr.getValue(message);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val config = SpelParserConfiguration(SpelCompilerMode.IMMEDIATE,
this.javaClass.classLoader)
@ -432,6 +475,7 @@ following example shows how to do so:
val payload = expr.getValue(message)
----
======
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.

View File

@ -3,8 +3,11 @@
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"]
.Inventor.Java
----
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"]
.Inventor.kt
----
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 placeOfBirth: PlaceOfBirth? = null)
----
======
[tabs]
======
PlaceOfBirth.java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.PlaceOfBirth.java
----
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"]
.PlaceOfBirth.kt
----
package org.spring.samples.spel.inventor
class PlaceOfBirth(var city: String, var country: String? = null) {
----
======
[tabs]
======
Society.java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Society.java
----
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"]
.Society.kt
----
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
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"]
.Java
----
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
int[][] numbers3 = (int[][]) parser.parseExpression("new int[4][5]").getValue(context);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
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
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.

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
to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
@ -15,8 +18,10 @@ to do so:
// This will end up calling resolve(context,"something") on MyBeanResolver during evaluation
Object bean = parser.parseExpression("@something").getValue(context);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val parser = SpelExpressionParser()
val context = StandardEvaluationContext()
@ -25,12 +30,16 @@ to do so:
// This will end up calling resolve(context,"something") on MyBeanResolver during evaluation
val bean = parser.parseExpression("@something").getValue(context)
----
======
To access a factory bean itself, you should instead prefix the bean name with an `&` symbol.
The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
ExpressionParser parser = new SpelExpressionParser();
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
Object bean = parser.parseExpression("&foo").getValue(context);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val parser = SpelExpressionParser()
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
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
list. The following example uses projection to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// returns ['Smiljan', 'Idvor' ]
List placesOfBirth = (List)parser.parseExpression("members.![placeOfBirth.city]");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// returns ['Smiljan', 'Idvor' ]
val placesOfBirth = parser.parseExpression("members.![placeOfBirth.city]") as List<*>
----
======
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

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,
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"]
.Java
----
List<Inventor> list = (List<Inventor>) parser.parseExpression(
"members.?[nationality == 'Serbian']").getValue(societyContext);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val list = parser.parseExpression(
"members.?[nationality == 'Serbian']").getValue(societyContext) as List<Inventor>
----
======
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
@ -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
original map where the entry's value is less than 27:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Map newMap = parser.parseExpression("map.?[value<27]").getValue();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val newMap = parser.parseExpression("map.?[value<27]").getValue()
----
======
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

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
`new` operator to invoke constructors:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Inventor einstein = p.parseExpression(
"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(
'Albert Einstein', 'German'))").getValue(societyContext);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val einstein = p.parseExpression(
"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'))")
.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
following example shows how to register a user-defined function:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Method method = ...;
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
context.setVariable("myFunction", method);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val method: Method = ...
val context = SimpleEvaluationContext.forReadOnlyDataBinding().build()
context.setVariable("myFunction", method)
----
======
For example, consider the following utility method that reverses a string:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
fun reverseString(input: String): String {
val backwards = StringBuilder(input.length)
@ -49,11 +60,15 @@ For example, consider the following utility method that reverses a string:
return backwards.toString()
}
----
======
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"]
.Java
----
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(
"#reverseString('hello')").getValue(context, String.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
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(
"#reverseString('hello')").getValue(context, String::class.java)
----
======

View File

@ -3,22 +3,28 @@
You can directly express lists in an expression by using `{}` notation.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// evaluates to a Java list containing the four numbers
List numbers = (List) parser.parseExpression("{1,2,3,4}").getValue(context);
List listOfLists = (List) parser.parseExpression("{{'a','b'},{'x','y'}}").getValue(context);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// evaluates to a Java list containing the four numbers
val numbers = parser.parseExpression("{1,2,3,4}").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
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
following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// evaluates to a Java map containing the two entries
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);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
// evaluates to a Java map containing the two entries
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<*, *>
----
======
`{:}` 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

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
method.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
ExpressionParser parser = new SpelExpressionParser();
@ -43,8 +46,10 @@ method.
Object nullValue = parser.parseExpression("null").getValue();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val parser = SpelExpressionParser()
@ -63,6 +68,7 @@ method.
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
invoke methods:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// string literal, evaluates to "bc"
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(
societyContext, Boolean.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// string literal, evaluates to "bc"
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(
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).
The following example shows how to use the Elvis operator:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
ExpressionParser parser = new SpelExpressionParser();
String name = parser.parseExpression("name?:'Unknown'").getValue(new Inventor(), String.class);
System.out.println(name); // 'Unknown'
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val parser = SpelExpressionParser()
val name = parser.parseExpression("name?:'Unknown'").getValue(Inventor(), String::class.java)
println(name) // 'Unknown'
----
======
The following listing shows a more complex example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
ExpressionParser parser = new SpelExpressionParser();
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);
System.out.println(name); // Elvis Presley
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val parser = SpelExpressionParser()
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)
println(name) // Elvis Presley
----
======
[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
example shows how to use the safe navigation operator:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
ExpressionParser parser = new SpelExpressionParser();
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);
System.out.println(city); // null - does not throw NullPointerException!!!
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val parser = SpelExpressionParser()
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)
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
the expression. The following listing shows a minimal example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
String falseString = parser.parseExpression(
"false ? 'trueExp' : 'falseExp'").getValue(String.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val falseString = parser.parseExpression(
"false ? 'trueExp' : 'falseExp'").getValue(String::class.java)
----
======
In this case, the boolean `false` results in returning the string value `'falseExp'`. A more
realistic example follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
parser.parseExpression("name").setValue(societyContext, "IEEE");
societyContext.setVariable("queryName", "Nikola Tesla");
@ -33,8 +42,10 @@ realistic example follows:
.getValue(societyContext, String.class);
// queryResultString = "Nikola Tesla is a member of the IEEE Society"
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
parser.parseExpression("name").setValue(societyContext, "IEEE")
societyContext.setVariable("queryName", "Nikola Tesla")
@ -45,6 +56,7 @@ realistic example follows:
.getValue(societyContext, String::class.java)
// 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
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`.
The following listing shows a few examples of operators:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// evaluates to true
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
boolean trueValue = parser.parseExpression("new CustomValue(1) < new CustomValue(2)").getValue(Boolean.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// evaluates to true
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
val trueValue = parser.parseExpression("new CustomValue(1) < new CustomValue(2)").getValue(Boolean::class.java);
----
======
[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
expression-based `matches` operator. The following listing shows examples of both:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// evaluates to false
boolean falseValue = parser.parseExpression(
@ -77,8 +86,10 @@ expression-based `matches` operator. The following listing shows examples of bot
boolean falseValue = parser.parseExpression(
"'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// evaluates to false
val falseValue = parser.parseExpression(
@ -92,6 +103,7 @@ expression-based `matches` operator. The following listing shows examples of bot
val falseValue = parser.parseExpression(
"'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean::class.java)
----
======
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
@ -125,8 +137,11 @@ SpEL supports the following logical operators:
The following example shows how to use the logical operators:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// -- AND --
@ -155,8 +170,10 @@ The following example shows how to use the logical operators:
String expression = "isMember('Nikola Tesla') and !isMember('Mihajlo Pupin')";
boolean falseValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// -- 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 falseValue = parser.parseExpression(expression).getValue(societyContext, Boolean::class.java)
----
======
[[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
operators in use:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// Addition
int two = parser.parseExpression("1 + 1").getValue(Integer.class); // 2
@ -228,8 +249,10 @@ operators in use:
// Operator precedence
int minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Integer.class); // -21
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// Addition
val two = parser.parseExpression("1 + 1").getValue(Int::class.java) // 2
@ -260,6 +283,7 @@ operators in use:
// Operator precedence
val minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Int::class.java) // -21
----
======
[[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
listing shows both ways to use the assignment operator:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Inventor inventor = new Inventor();
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();
@ -281,8 +308,10 @@ listing shows both ways to use the assignment operator:
String aleks = parser.parseExpression(
"name = 'Aleksandar Seovic'").getValue(context, inventor, String.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val inventor = Inventor()
val context = SimpleEvaluationContext.forReadWriteDataBinding().build()
@ -293,5 +322,6 @@ listing shows both ways to use the assignment operator:
val aleks = parser.parseExpression(
"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
Pupin's city of birth, we use the following expressions:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// evaluates to 1856
int year = (Integer) parser.parseExpression("birthdate.year + 1900").getValue(context);
String city = (String) parser.parseExpression("placeOfBirth.city").getValue(context);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// evaluates to 1856
val year = parser.parseExpression("birthdate.year + 1900").getValue(context) as Int
val city = parser.parseExpression("placeOfBirth.city").getValue(context) as String
----
======
[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
following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
@ -59,8 +68,10 @@ following example shows:
String invention = parser.parseExpression("members[0].inventions[6]").getValue(
context, ieee, String.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val parser = SpelExpressionParser()
val context = SimpleEvaluationContext.forReadOnlyDataBinding().build()
@ -82,13 +93,17 @@ following example shows:
val invention = parser.parseExpression("members[0].inventions[6]").getValue(
context, ieee, String::class.java)
----
======
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
string literals:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// Officer's Dictionary
@ -103,8 +118,10 @@ string literals:
parser.parseExpression("officers['advisors'][0].placeOfBirth.country").setValue(
societyContext, "Croatia");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// Officer's Dictionary
@ -119,6 +136,7 @@ string literals:
parser.parseExpression("officers['advisors'][0].placeOfBirth.country").setValue(
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
shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
String randomPhrase = parser.parseExpression(
"random number is #{T(java.lang.Math).random()}",
@ -15,8 +18,10 @@ shows:
// evaluates to "random number is 0.7038186818312008"
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val randomPhrase = parser.parseExpression(
"random number is #{T(java.lang.Math).random()}",
@ -24,6 +29,7 @@ shows:
// evaluates to "random number is 0.7038186818312008"
----
======
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
@ -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 definition of `TemplateParserContext` follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class TemplateParserContext implements ParserContext {
@ -50,8 +59,10 @@ The definition of `TemplateParserContext` follows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
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
following example shows how to use the `T` operator:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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")
.getValue(Boolean.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
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")
.getValue(Boolean::class.java)
----
======

View File

@ -17,8 +17,11 @@ characters.
The following example shows how to use variables.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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);
System.out.println(tesla.getName()) // "Mike Tesla"
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
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)
println(tesla.name) // "Mike Tesla"
----
======
[[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
show how to use the `#this` and `#root` variables:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// create an array of integers
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(
"#primes.?[#this>10]").getValue(context);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// create an array of integers
val primes = ArrayList<Int>()
@ -84,6 +95,7 @@ show how to use the `#this` and `#root` variables:
val primesGreaterThanTen = parser.parseExpression(
"#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
snippet of code was run against a `ClassPathXmlApplicationContext` instance:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Resource template = ctx.getResource("some/resource/path/myTemplate.txt");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val template = ctx.getResource("some/resource/path/myTemplate.txt")
----
======
Against a `ClassPathXmlApplicationContext`, that code returns a `ClassPathResource`. If
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
example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Resource template = ctx.getResource("classpath:some/resource/path/myTemplate.txt");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
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
`java.net.URL` prefixes. The following examples use the `file` and `https` prefixes:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Resource template = ctx.getResource("file:///some/resource/path/myTemplate.txt");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val template = ctx.getResource("file:///some/resource/path/myTemplate.txt")
----
======
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Resource template = ctx.getResource("https://myhost.com/resource/path/myTemplate.txt");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val template = ctx.getResource("https://myhost.com/resource/path/myTemplate.txt")
----
======
The following table summarizes the strategy for converting `String` objects to `Resource`
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`
property of type `Resource`.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
package example;
@ -493,11 +520,14 @@ property of type `Resource`.
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class MyBean(var template: Resource)
----
======
In an XML configuration file, the `template` property can be configured with a simple
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.
The following example demonstrates how to achieve this.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Component
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"]
.Kotlin
----
@Component
class MyBean(@Value("\${template.path}") private val template: Resource)
----
======
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
@ -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
can be injected into the `MyBean` constructor.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Component
public class MyBean {
@ -581,12 +620,15 @@ can be injected into the `MyBean` constructor.
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Component
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
`ClassPathXmlApplicationContext`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
ApplicationContext ctx = new ClassPathXmlApplicationContext("conf/appContext.xml");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val ctx = ClassPathXmlApplicationContext("conf/appContext.xml")
----
======
The bean definitions are loaded from the classpath, because a `ClassPathResource` is
used. However, consider the following example, which creates a `FileSystemXmlApplicationContext`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
ApplicationContext ctx =
new FileSystemXmlApplicationContext("conf/appContext.xml");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val ctx = FileSystemXmlApplicationContext("conf/appContext.xml")
----
======
Now the bean definitions are loaded from a filesystem location (in this case, relative to
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
definitions. Consider the following example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
ApplicationContext ctx =
new FileSystemXmlApplicationContext("classpath:conf/appContext.xml");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val ctx = FileSystemXmlApplicationContext("classpath:conf/appContext.xml")
----
======
Using `FileSystemXmlApplicationContext` loads the bean definitions from the classpath.
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
classpath) can be instantiated:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
ApplicationContext ctx = new ClassPathXmlApplicationContext(
new String[] {"services.xml", "repositories.xml"}, MessengerService.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val ctx = ClassPathXmlApplicationContext(arrayOf("services.xml", "repositories.xml"), MessengerService::class.java)
----
======
See the {api-spring-framework}/context/support/ClassPathXmlApplicationContext.html[`ClassPathXmlApplicationContext`]
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
special `classpath*:` prefix, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
ApplicationContext ctx =
new ClassPathXmlApplicationContext("classpath*:conf/appContext.xml");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val ctx = ClassPathXmlApplicationContext("classpath*:conf/appContext.xml")
----
======
This special prefix specifies that all classpath resources that match the given name
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.
In practice, this means the following examples are equivalent:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
ApplicationContext ctx =
new FileSystemXmlApplicationContext("conf/context.xml");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val ctx = FileSystemXmlApplicationContext("conf/context.xml")
----
======
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
ApplicationContext ctx =
new FileSystemXmlApplicationContext("/conf/context.xml");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
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
case is relative and the other absolute):
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
FileSystemXmlApplicationContext ctx = ...;
ctx.getResource("some/resource/path/myTemplate.txt");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val ctx: FileSystemXmlApplicationContext = ...
ctx.getResource("some/resource/path/myTemplate.txt")
----
======
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
FileSystemXmlApplicationContext ctx = ...;
ctx.getResource("/some/resource/path/myTemplate.txt");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val ctx: FileSystemXmlApplicationContext = ...
ctx.getResource("/some/resource/path/myTemplate.txt")
----
======
In practice, if you need true absolute filesystem paths, you should avoid using
absolute paths with `FileSystemResource` or `FileSystemXmlApplicationContext` and
force the use of a `UrlResource` by using the `file:` URL prefix. The following examples
show how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// actual context type doesn't matter, the Resource will always be UrlResource
ctx.getResource("file:///some/resource/path/myTemplate.txt");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// actual context type doesn't matter, the Resource will always be UrlResource
ctx.getResource("file:///some/resource/path/myTemplate.txt")
----
======
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// force this FileSystemXmlApplicationContext to load its definition via a UrlResource
ApplicationContext ctx =
new FileSystemXmlApplicationContext("file:///conf/context.xml");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// force this FileSystemXmlApplicationContext to load its definition via a UrlResource
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
the following example.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class MyBean {
private final Log log = LogFactory.getLog(getClass());
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class MyBean {
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
properties:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class Company {
@ -87,17 +90,23 @@ properties:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class Company {
var name: String? = null
var managingDirector: Employee? = null
}
----
======
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class Employee {
@ -122,20 +131,26 @@ properties:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class Employee {
var name: String? = null
var salary: Float? = null
}
----
======
The following code snippets show some examples of how to retrieve and manipulate some of
the properties of instantiated ``Company``s and ``Employee``s:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
BeanWrapper company = new BeanWrapperImpl(new Company());
// 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
Float salary = (Float) company.getPropertyValue("managingDirector.salary");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val company = BeanWrapperImpl(Company())
// 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
val salary = company.getPropertyValue("managingDirector.salary") as Float?
----
======
@ -308,8 +326,11 @@ com
The following Java source code for the referenced `SomethingBeanInfo` class
associates a `CustomNumberEditor` with the `age` property of the `Something` class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
class SomethingBeanInfo : SimpleBeanInfo() {
@ -351,6 +374,7 @@ associates a `CustomNumberEditor` with the `age` property of the `Something` cla
}
}
----
======
[[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
another class called `DependsOnExoticType`, which needs `ExoticType` set as a property:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
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"]
.Kotlin
----
package example
@ -425,6 +454,7 @@ another class called `DependsOnExoticType`, which needs `ExoticType` set as a pr
var type: ExoticType? = null
}
----
======
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
@ -439,8 +469,11 @@ string, which a `PropertyEditor` converts into an actual
The `PropertyEditor` implementation could look similar to the following:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
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"]
.Kotlin
----
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
`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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
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"]
.Kotlin
----
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
`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
example uses a `PropertyEditorRegistrar` in the implementation of an `@InitBinder` method:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Controller
public class RegisterUserController {
@ -586,8 +631,10 @@ example uses a `PropertyEditorRegistrar` in the implementation of an `@InitBinde
// other methods related to registering a User
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Controller
class RegisterUserController(
@ -601,6 +648,7 @@ example uses a `PropertyEditorRegistrar` in the implementation of an `@InitBinde
// other methods related to registering a User
}
----
======
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`

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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class PersonForm {
private String name;
private int age;
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class PersonForm(
private val name: String,
private val age: Int
)
----
======
Bean Validation lets you declare constraints as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class PersonForm {
@ -48,8 +57,10 @@ Bean Validation lets you declare constraints as the following example shows:
private int age;
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class PersonForm(
@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
)
----
======
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
@ -78,8 +90,11 @@ is needed in your application.
You can use the `LocalValidatorFactoryBean` to configure a default Validator as a Spring
bean, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.XML
----
<bean id="validator"
class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
----
======
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
@ -115,8 +133,11 @@ validation logic.
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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import jakarta.validation.Validator;
@ -127,20 +148,26 @@ Validation API directly, as the following example shows:
private Validator validator;
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import jakarta.validation.Validator;
@Service
class MyService(@Autowired private val validator: Validator)
----
======
You can inject a reference to `org.springframework.validation.Validator` if your bean
requires the Spring Validation API, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import org.springframework.validation.Validator;
@ -151,14 +178,17 @@ requires the Spring Validation API, as the following example shows:
private Validator validator;
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import org.springframework.validation.Validator
@Service
class MyService(@Autowired private val validator: Validator)
----
======
[[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
`ConstraintValidator` implementation that uses Spring for dependency injection:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@ -191,17 +224,23 @@ The following example shows a custom `@Constraint` declaration followed by an as
public @interface MyConstraint {
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.FIELD)
@Retention(AnnotationRetention.RUNTIME)
@Constraint(validatedBy = MyConstraintValidator::class)
annotation class MyConstraint
----
======
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
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
@ -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
`MethodValidationPostProcessor` bean definition:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.XML
----
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"/>
----
======
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
@ -296,8 +344,11 @@ automatically added to the binder's `BindingResult`.
The following example shows how to use a `DataBinder` programmatically to invoke validation
logic after binding to a target object:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Foo target = new Foo();
DataBinder binder = new DataBinder(target);
@ -312,8 +363,10 @@ logic after binding to a target object:
// get BindingResult that includes any validation errors
BindingResult results = binder.getBindingResult();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val target = Foo()
val binder = DataBinder(target)
@ -328,6 +381,7 @@ logic after binding to a target object:
// get BindingResult that includes any validation errors
val results = binder.bindingResult
----
======
You can also configure a `DataBinder` with multiple `Validator` instances through
`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
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"]
.Java
----
@Service
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"]
.Kotlin
----
@Service
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
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,
as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
DefaultConversionService cs = new DefaultConversionService();
@ -304,8 +313,10 @@ as the following example shows:
TypeDescriptor.forObject(input), // List<Integer> type descriptor
TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(String.class)));
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val cs = DefaultConversionService()
@ -314,6 +325,7 @@ as the following example shows:
TypeDescriptor.forObject(input), // List<Integer> type descriptor
TypeDescriptor.collection(List::class.java, TypeDescriptor.valueOf(String::class.java)))
----
======
Note that `DefaultConversionService` automatically registers converters that are
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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
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"]
.Kotlin
----
@Configuration
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
`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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
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"]
.Kotlin
----
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
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`
annotation to a formatter to let a number style or pattern be specified:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public final class NumberFormatAnnotationFormatterFactory
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"]
.Kotlin
----
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
example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class MyModel {
@ -248,13 +263,16 @@ example shows:
private BigDecimal decimal;
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class MyModel(
@field:NumberFormat(style = Style.CURRENCY) private val decimal: BigDecimal
)
----
======
[[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
(yyyy-MM-dd):
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class MyModel {
@ -277,13 +298,16 @@ The following example uses `@DateTimeFormat` to format a `java.util.Date` as an
private Date date;
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class MyModel(
@DateTimeFormat(iso=ISO.DATE) private val date: Date
)
----
======
[[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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class Person {
@ -18,11 +21,14 @@ Consider the following example of a small data object:
// the usual getters and setters...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class Person(val name: String, val age: Int)
----
======
The next example provides validation behavior for the `Person` class by implementing the
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
example implements `Validator` for `Person` instances:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
class PersonValidator : Validator {
@ -81,6 +92,7 @@ example implements `Validator` for `Person` instances:
}
}
----
======
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
@ -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`,
as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class CustomerValidator implements Validator {
@ -137,8 +152,10 @@ as the following example shows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
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
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
how to use the `@Repository` annotation:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Repository // <1>
public class SomeMovieFinder implements MovieFinder {
// ...
}
----
======
<1> The `@Repository` annotation.
[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`
annotations. The following example works for a JPA repository:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Repository
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"]
.Kotlin
----
@Repository
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
example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Repository
public class HibernateMovieFinder implements MovieFinder {
@ -126,22 +138,28 @@ example shows:
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Repository
class HibernateMovieFinder(private val sessionFactory: SessionFactory) : MovieFinder {
// ...
}
----
======
The last example we show here is for typical JDBC support. You could have the
`DataSource` injected into an initialization method or a constructor, where you would create a
`JdbcTemplate` and other data access support classes (such as `SimpleJdbcCall` and others) by using
this `DataSource`. The following example autowires a `DataSource`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Repository
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"]
.Kotlin
----
@Repository
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
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
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"]
.Java
----
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
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
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
}
----
======
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
@ -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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class JdbcActorDao implements ActorDao {
@ -114,8 +123,10 @@ The following example shows a batch update using named parameters:
// ... additional methods
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class JdbcActorDao(dataSource: DataSource) : ActorDao {
@ -130,6 +141,7 @@ The following example shows a batch update using named parameters:
// ... additional methods
}
----
======
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
@ -139,8 +151,11 @@ defined in the SQL statement.
The following example is the same as the preceding example, except that it uses classic
JDBC `?` placeholders:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class JdbcActorDao implements ActorDao {
@ -165,8 +180,10 @@ JDBC `?` placeholders:
// ... additional methods
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class JdbcActorDao(dataSource: DataSource) : ActorDao {
@ -184,6 +201,7 @@ JDBC `?` placeholders:
// ... additional methods
}
----
======
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
@ -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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
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
}
----
======
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.

View File

@ -48,8 +48,11 @@ To configure a `DriverManagerDataSource`:
The following example shows how to configure a `DriverManagerDataSource` in Java:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
@ -57,8 +60,10 @@ The following example shows how to configure a `DriverManagerDataSource` in Java
dataSource.setUsername("sa");
dataSource.setPassword("");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val dataSource = DriverManagerDataSource().apply {
setDriverClassName("org.hsqldb.jdbcDriver")
@ -67,6 +72,7 @@ The following example shows how to configure a `DriverManagerDataSource` in Java
password = ""
}
----
======
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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
int rowCount = this.jdbcTemplate.queryForObject("select count(*) from t_actor", Integer.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val rowCount = jdbcTemplate.queryForObject<Int>("select count(*) from t_actor")!!
----
======
The following query uses a bind variable:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
int countOfActorsNamedJoe = this.jdbcTemplate.queryForObject(
"select count(*) from t_actor where first_name = ?", Integer.class, "Joe");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val countOfActorsNamedJoe = jdbcTemplate.queryForObject<Int>(
"select count(*) from t_actor where first_name = ?", arrayOf("Joe"))!!
----
======
The following query looks for a `String`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
String lastName = this.jdbcTemplate.queryForObject(
"select last_name from t_actor where id = ?",
String.class, 1212L);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val lastName = this.jdbcTemplate.queryForObject<String>(
"select last_name from t_actor where id = ?",
arrayOf(1212L))!!
----
======
The following query finds and populates a single domain object:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Actor actor = jdbcTemplate.queryForObject(
"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);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val actor = jdbcTemplate.queryForObject(
"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"))
}
----
======
The following query finds and populates a list of domain objects:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
List<Actor> actors = this.jdbcTemplate.query(
"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;
});
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val actors = jdbcTemplate.query("select first_name, last_name from t_actor") { rs, _ ->
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
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.
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"]
.Java
----
private final RowMapper<Actor> actorRowMapper = (resultSet, rowNum) -> {
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);
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val actorMapper = RowMapper<Actor> { rs: ResultSet, rowNum: Int ->
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)
}
----
======
[[jdbc-JdbcTemplate-examples-update]]
=== 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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
this.jdbcTemplate.update(
"insert into t_actor (first_name, last_name) values (?, ?)",
"Leonor", "Watling");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
jdbcTemplate.update(
"insert into t_actor (first_name, last_name) values (?, ?)",
"Leonor", "Watling")
----
======
The following example updates an existing entry:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
this.jdbcTemplate.update(
"update t_actor set last_name = ? where id = ?",
"Banjo", 5276L);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
jdbcTemplate.update(
"update t_actor set last_name = ? where id = ?",
"Banjo", 5276L)
----
======
The following example deletes an entry:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
this.jdbcTemplate.update(
"delete from t_actor where id = ?",
Long.valueOf(actorId));
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
jdbcTemplate.update("delete from t_actor where id = ?", actorId.toLong())
----
======
[[jdbc-JdbcTemplate-examples-other]]
=== 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
table:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
jdbcTemplate.execute("create table mytable (id integer, name varchar(100))")
----
======
The following example invokes a stored procedure:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
this.jdbcTemplate.update(
"call SUPPORT.REFRESH_ACTORS_SUMMARY(?)",
Long.valueOf(unionId));
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
jdbcTemplate.update(
"call SUPPORT.REFRESH_ACTORS_SUMMARY(?)",
unionId.toLong())
----
======
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:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
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...
}
----
======
--
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:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Repository // <1>
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...
}
----
======
<1> Annotate the class with `@Repository`.
<2> Annotate the `DataSource` setter method with `@Autowired`.
<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
parameters. The following example shows how to use `NamedParameterJdbcTemplate`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// some JDBC-backed DAO class...
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"]
.Kotlin
----
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)!!
}
----
======
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`
@ -483,8 +564,11 @@ methods exposed by the `NamedParameterJdbcOperations` and implemented by the
The following example shows the use of the `Map`-based style:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// some JDBC-backed DAO class...
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);
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// some JDBC-backed DAO class...
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)!!
}
----
======
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
@ -531,8 +618,11 @@ of named parameter values.
The following example shows a typical JavaBean:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class Actor {
@ -556,17 +646,23 @@ The following example shows a typical JavaBean:
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
data class Actor(val id: Long, val firstName: String, val lastName: String)
----
======
The following example uses a `NamedParameterJdbcTemplate` to return the count of the
members of the class shown in the preceding example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// some JDBC-backed DAO class...
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
@ -585,8 +681,10 @@ members of the class shown in the preceding example:
return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// some JDBC-backed DAO class...
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)!!
}
----
======
Remember that the `NamedParameterJdbcTemplate` class wraps a classic `JdbcTemplate`
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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
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
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
translator:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
private JdbcTemplate jdbcTemplate;
@ -710,8 +818,10 @@ translator:
" where id = ?", pct, orderId);
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// create a JdbcTemplate and set data source
private val jdbcTemplate = JdbcTemplate(dataSource).apply {
@ -728,6 +838,7 @@ translator:
" where id = ?", pct, orderId)
}
----
======
The custom translator is passed a data source in order to look up the error codes in
`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
fully functional class that creates a new table:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import javax.sql.DataSource;
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"]
.Kotlin
----
import javax.sql.DataSource
import org.springframework.jdbc.core.JdbcTemplate
@ -775,6 +891,7 @@ fully functional class that creates a new table:
}
}
----
======
[[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
query methods, one for an `int` and one that queries for a `String`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import javax.sql.DataSource;
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"]
.Kotlin
----
import javax.sql.DataSource
import org.springframework.jdbc.core.JdbcTemplate
@ -826,6 +948,7 @@ class RunAQuery(dataSource: DataSource) {
get() = jdbcTemplate.queryForObject("select name from mytable")
}
----
======
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(..)`,
@ -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
list of all the rows, it might be as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
private JdbcTemplate jdbcTemplate;
@ -846,8 +972,10 @@ list of all the rows, it might be as follows:
return this.jdbcTemplate.queryForList("select * from mytable");
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
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")
}
----
======
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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import javax.sql.DataSource;
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"]
.Kotlin
----
import javax.sql.DataSource
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,
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
on Oracle but may not work on other platforms:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
final String INSERT_SQL = "insert into my_test (name) values(?)";
final String name = "Rob";
@ -937,8 +1075,10 @@ on Oracle but may not work on other platforms:
// keyHolder.getKey() now contains the generated key
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val INSERT_SQL = "insert into my_test (name) values(?)"
val name = "Rob"
@ -950,6 +1090,7 @@ on Oracle but may not work on other platforms:
// 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
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"]
.Java
----
EmbeddedDatabase db = new EmbeddedDatabaseBuilder()
.generateUniqueName(true)
@ -60,8 +63,10 @@ stand-alone environment or in a stand-alone integration test, as in the followin
db.shutdown()
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val db = EmbeddedDatabaseBuilder()
.generateUniqueName(true)
@ -76,6 +81,7 @@ stand-alone environment or in a stand-alone integration test, as in the followin
db.shutdown()
----
======
See the {api-spring-framework}/jdbc/datasource/embedded/EmbeddedDatabaseBuilder.html[javadoc for `EmbeddedDatabaseBuilder`]
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
configuration, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
public class DataSourceConfig {
@ -102,8 +111,10 @@ configuration, as the following example shows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
class DataSourceConfig {
@ -121,6 +132,7 @@ configuration, as the following example shows:
}
}
----
======
[[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
shows the test template:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class DataAccessIntegrationTestTemplate {
@ -198,8 +213,10 @@ shows the test template:
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class DataAccessIntegrationTestTemplate {
@ -227,6 +244,7 @@ shows the test template:
}
}
----
======
[[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
data from the `t_actor` relation to an instance of the `Actor` class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
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
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
example shows how to define such a class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
private ActorMappingQuery actorMappingQuery;
@ -107,13 +116,16 @@ example shows how to define such a class:
return actorMappingQuery.findObject(id);
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
private val actorMappingQuery = ActorMappingQuery(dataSource)
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
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
example shows such a method:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public List<Actor> searchForActors(int age, String namePattern) {
return actorSearchMappingQuery.execute(age, namePattern);
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
fun searchForActors(age: Int, namePattern: String) =
actorSearchMappingQuery.execute(age, namePattern)
----
======
[[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.
The following example creates a custom update method named `execute`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import java.sql.Types;
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"]
.Kotlin
----
import java.sql.Types
import javax.sql.DataSource
@ -205,6 +228,7 @@ The following example creates a custom update method named `execute`:
}
}
----
======
[[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,
as the following code snippet shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
new SqlParameter("in_id", Types.NUMERIC),
new SqlOutParameter("out_first_name", Types.VARCHAR),
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
SqlParameter("in_id", Types.NUMERIC),
SqlOutParameter("out_first_name", Types.VARCHAR),
----
======
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.
The following listing shows our custom StoredProcedure class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import java.sql.Types;
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"]
.Kotlin
----
import java.sql.Types
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,
Oracle REF cursors):
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import java.util.HashMap;
import java.util.Map;
@ -374,8 +413,10 @@ Oracle REF cursors):
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import java.util.HashMap
import javax.sql.DataSource
@ -401,6 +442,7 @@ Oracle REF cursors):
}
}
----
======
Notice how the overloaded variants of the `declareParameter(..)` method that have been
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 supplied `ResultSet`, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import java.sql.ResultSet;
import java.sql.SQLException;
@ -428,8 +473,10 @@ the supplied `ResultSet`, as follows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import java.sql.ResultSet
import com.foo.domain.Title
@ -441,12 +488,16 @@ the supplied `ResultSet`, as follows:
Title(rs.getLong("id"), rs.getString("name"))
}
----
======
The `GenreMapper` class maps a `ResultSet` to a `Genre` domain object for each row in
the supplied `ResultSet`, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import java.sql.ResultSet;
import java.sql.SQLException;
@ -460,8 +511,10 @@ the supplied `ResultSet`, as follows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import java.sql.ResultSet
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
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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import java.sql.Types;
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"]
.Kotlin
----
import java.sql.Types
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))
}
----
======

View File

@ -63,8 +63,11 @@ dependency injection.
The following example shows how to create and insert a BLOB:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
final File blobIn = new File("spring2004.jpg");
final InputStream blobIs = new FileInputStream(blobIn);
@ -86,6 +89,7 @@ The following example shows how to create and insert a BLOB:
blobIs.close();
clobReader.close();
----
======
<1> Pass in the `lobHandler` that (in this example) is a plain `DefaultLobHandler`.
<2> Using the method `setClobAsCharacterStream` to pass in the contents of the CLOB.
<3> Using the method `setBlobAsBinaryStream` to pass in the contents of the BLOB.
@ -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`.
The following example shows how to do so:
[tabs]
======
Java::
+
[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",
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.
<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
declared type `ITEM_TYPE`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class TestItemStoredProcedure extends StoredProcedure {
@ -223,8 +234,10 @@ declared type `ITEM_TYPE`:
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
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
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
or `ArrayDescriptor` instances. The following example creates a `StructDescriptor` instance:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
final TestItem testItem = new TestItem(123L, "A test item",
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"]
.Kotlin
----
val (id, description, expirationDate) = TestItem(123L, "A test item",
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
`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
it with values from the Java `ARRAY`, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
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
example uses only one configuration method (we show examples of multiple methods later):
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class JdbcActorDao implements ActorDao {
@ -41,8 +44,10 @@ example uses only one configuration method (we show examples of multiple methods
// ... additional methods
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class JdbcActorDao(dataSource: DataSource) : ActorDao {
@ -59,6 +64,7 @@ example uses only one configuration method (we show examples of multiple methods
// ... additional methods
}
----
======
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
@ -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
listing shows how it works:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class JdbcActorDao implements ActorDao {
@ -99,8 +108,10 @@ listing shows how it works:
// ... additional methods
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class JdbcActorDao(dataSource: DataSource) : ActorDao {
@ -118,6 +129,7 @@ listing shows how it works:
// ... additional methods
}
----
======
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
@ -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
`usingColumns` method, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
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
}
----
======
The execution of the insert is the same as if you had relied on the metadata to determine
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
values. The following example shows how to use `BeanPropertySqlParameterSource`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class JdbcActorDao implements ActorDao {
@ -217,8 +238,10 @@ values. The following example shows how to use `BeanPropertySqlParameterSource`:
// ... additional methods
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class JdbcActorDao(dataSource: DataSource) : ActorDao {
@ -235,12 +258,16 @@ values. The following example shows how to use `BeanPropertySqlParameterSource`:
// ... additional methods
}
----
======
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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class JdbcActorDao implements ActorDao {
@ -263,8 +290,10 @@ convenient `addValue` method that can be chained. The following example shows ho
// ... additional methods
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class JdbcActorDao(dataSource: DataSource) : ActorDao {
@ -283,6 +312,7 @@ convenient `addValue` method that can be chained. The following example shows ho
// ... additional methods
}
----
======
As you can see, the configuration is the same. Only the executing code has to change to
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
of the stored procedure):
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class JdbcActorDao implements ActorDao {
@ -352,8 +385,10 @@ of the stored procedure):
// ... additional methods
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class JdbcActorDao(dataSource: DataSource) : ActorDao {
@ -374,6 +409,7 @@ of the stored procedure):
// ... additional methods
}
----
======
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
@ -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
the constructor of your `SimpleJdbcCall`. The following example shows this configuration:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class JdbcActorDao implements ActorDao {
@ -414,8 +453,10 @@ the constructor of your `SimpleJdbcCall`. The following example shows this confi
// ... additional methods
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class JdbcActorDao(dataSource: DataSource) : ActorDao {
@ -426,6 +467,7 @@ the constructor of your `SimpleJdbcCall`. The following example shows this confi
// ... additional methods
}
----
======
By taking this action, you avoid conflicts in the case used for the names of your
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 preceding example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class JdbcActorDao implements ActorDao {
@ -481,8 +526,10 @@ the preceding example:
// ... additional methods
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class JdbcActorDao(dataSource: DataSource) : ActorDao {
@ -501,6 +548,7 @@ the preceding example:
// ... additional methods
}
----
======
The execution and end results of the two examples are the same. The second example specifies all
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
similar to the following:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
new SqlParameter("in_id", Types.NUMERIC),
new SqlOutParameter("out_first_name", Types.VARCHAR),
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
SqlParameter("in_id", Types.NUMERIC),
SqlOutParameter("out_first_name", Types.VARCHAR),
----
======
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
@ -578,8 +632,11 @@ that returns an actor's full name:
To call this function, we again create a `SimpleJdbcCall` in the initialization method,
as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class JdbcActorDao implements ActorDao {
@ -602,8 +659,10 @@ as the following example shows:
// ... additional methods
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class JdbcActorDao(dataSource: DataSource) : ActorDao {
@ -621,6 +680,7 @@ as the following example shows:
// ... additional methods
}
----
======
The `executeFunction` method used returns a `String` that contains the return value from the
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.
The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class JdbcActorDao implements ActorDao {
@ -680,8 +743,10 @@ The following example shows how to do so:
// ... additional methods
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class JdbcActorDao(dataSource: DataSource) : ActorDao {
@ -699,6 +764,7 @@ The following example shows how to do so:
// ... additional methods
}
----
======
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.

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
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"]
.Java
----
@Repository
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"]
.Kotlin
----
@Repository
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"]
----

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
implementation resembles the following example, based on the plain Hibernate API:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
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,
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
these annotated methods. The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
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()
}
----
======
In the container, you need to set up the `PlatformTransactionManager` implementation
(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>
----
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
class ProductServiceImpl(transactionManager: PlatformTransactionManager,
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
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
that uses the `@PersistenceUnit` annotation:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class ProductDaoImpl implements ProductDao {
@ -320,8 +323,10 @@ that uses the `@PersistenceUnit` annotation:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
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
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
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"]
.Java
----
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"]
.Kotlin
----
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
`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 a simple JavaBean to represent the settings:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
class Settings {
var isFooEnabled: Boolean = false
}
----
======
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
`settings.xml`, and `loadSettings()` loads these settings again. The following `main()` method
constructs a Spring application context and calls these two methods:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import java.io.FileInputStream;
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"]
.Kotlin
----
class Application {
@ -276,6 +287,7 @@ constructs a Spring application context and calls these two methods:
application.loadSettings()
}
----
======
The `Application` requires both a `marshaller` and an `unmarshaller` property to be set. We
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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
DatabaseClient client = DatabaseClient.create(connectionFactory);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val client = DatabaseClient.create(connectionFactory)
----
======
NOTE: The `ConnectionFactory` should always be configured as a bean in the Spring IoC
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
code that creates a new table:
[tabs]
======
Java::
+
[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);")
.then();
----
Kotlin::
+
[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);")
.await()
----
======
`DatabaseClient` is designed for convenient, fluent usage.
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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Mono<Map<String, Object>> first = client.sql("SELECT id, name FROM person")
.fetch().first();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val first = client.sql("SELECT id, name FROM person")
.fetch().awaitSingle()
----
======
The following query uses a bind variable:
[tabs]
======
Java::
+
[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")
.bind("fn", "Joe")
.fetch().first();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val first = client.sql("SELECT id, name FROM person WHERE first_name = :fn")
.bind("fn", "Joe")
.fetch().awaitSingle()
----
======
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.
@ -205,20 +229,26 @@ collections and maps, and objects).
The following example extracts the `name` column and emits its value:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Flux<String> names = client.sql("SELECT name FROM person")
.map(row -> row.get("name", String.class))
.all();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val names = client.sql("SELECT name FROM person")
.map{ row: Row -> row.get("name", String.class) }
.flow()
----
======
[[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
of updated rows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Mono<Integer> affectedRows = client.sql("UPDATE person SET first_name = :fn")
.bind("fn", "Joe")
.fetch().rowsUpdated();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val affectedRows = client.sql("UPDATE person SET first_name = :fn")
.bind("fn", "Joe")
.fetch().awaitRowsUpdated()
----
======
[[r2dbc-DatabaseClient-named-parameters]]
==== Binding Values to Queries
@ -277,7 +313,6 @@ Parameter binding supports two binding strategies:
The following example shows parameter binding for a query:
====
[source,java]
----
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("age", 34);
----
====
.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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
List<Object[]> tuples = new ArrayList<>();
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)")
.bind("tuples", tuples);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val tuples: MutableList<Array<Any>> = ArrayList()
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)")
.bind("tuples", tuples)
----
======
NOTE: Usage of select lists is vendor-dependent.
The following example shows a simpler variant using `IN` predicates:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
client.sql("SELECT id, name, state FROM table WHERE age IN (:ages)")
.bind("ages", Arrays.asList(35, 50));
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val tuples: MutableList<Array<Any>> = ArrayList()
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)")
.bind("tuples", arrayOf(35, 50))
----
======
NOTE: R2DBC itself does not support Collection-like values. Nevertheless,
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
modify statements in their execution, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
.filter((s, next) -> next.execute(s.returnGeneratedValues("id")))
.bind("name", …)
.bind("state", …);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
.filter { s: Statement, next: ExecuteFunction -> next.execute(s.returnGeneratedValues("id")) }
.bind("name", …)
.bind("state", …)
----
======
`DatabaseClient` exposes also simplified `filter(…)` overload accepting `Function<Statement, Statement>`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
.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")
.filter(statement -> s.fetchSize(25));
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
.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")
.filter { statement -> s.fetchSize(25) }
----
======
`StatementFilterFunction` implementations allow filtering of the
`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:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
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...
}
----
======
--
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:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Component // <1>
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...
}
----
======
<1> Annotate the class with `@Component`.
<2> Annotate the `ConnectionFactory` setter method with `@Autowired`.
<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
requests the generated key for the desired column.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Mono<Integer> generatedId = client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
.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
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val generatedId = client.sql("INSERT INTO table (name, state) VALUES(:name, :state)")
.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
----
======
[[r2dbc-connections]]
@ -575,16 +649,22 @@ To configure a `ConnectionFactory`:
The following example shows how to configure a `ConnectionFactory`:
[tabs]
======
Java::
+
[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");
----
Kotlin::
+
[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");
----
======
[[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.
Consider the following class definition:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// the service class that we want to make transactional
@Transactional
@ -43,8 +46,10 @@ Consider the following class definition:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// the service class that we want to make 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
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
programming arrangements as the following listing shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// the reactive service class that we want to make transactional
@Transactional
@ -156,8 +165,10 @@ programming arrangements as the following listing shows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// the reactive service class that we want to make 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
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
precedence over the transactional settings defined at the class level.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Transactional(readOnly = true)
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"]
.Kotlin
----
@Transactional(readOnly = true)
class DefaultFooService : FooService {
@ -356,6 +373,7 @@ precedence over the transactional settings defined at the class level.
}
}
----
======
[[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
in the application context:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class TransactionalService {
@ -470,8 +491,10 @@ in the application context:
public Mono<Void> doSomethingReactive() { ... }
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
class TransactionalService {
@ -491,6 +514,7 @@ in the application context:
}
}
----
======
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
following annotation definitions:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@ -542,8 +569,10 @@ following annotation definitions:
public @interface AccountTx {
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.TYPE)
@Retention(AnnotationRetention.RUNTIME)
@ -555,11 +584,15 @@ following annotation definitions:
@Transactional(transactionManager = "account", label = ["retryable"])
annotation class AccountTx
----
======
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"]
.Java
----
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"]
.Kotlin
----
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
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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
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"]
.Kotlin
----
package x.y
@ -92,6 +97,7 @@ The following code shows the simple profiling aspect discussed earlier:
}
}
----
======
The ordering of advice
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
`AnnotationTransactionAspect` to use it:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// construct an appropriate transaction manager
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
AnnotationTransactionAspect.aspectOf().setTransactionManager(txManager);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// construct an appropriate transaction manager
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
AnnotationTransactionAspect.aspectOf().transactionManager = txManager
----
======
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

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`
interface:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
// 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"]
.Kotlin
----
// the service interface that we want to make transactional
@ -47,11 +52,15 @@ interface:
fun updateFoo(foo: Foo)
}
----
======
The following example shows an implementation of the preceding interface:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
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"]
.Kotlin
----
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
`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
that test drives the configuration shown earlier:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
import org.springframework.beans.factory.getBean
@ -238,6 +255,7 @@ that test drives the configuration shown earlier:
fooService.insertFoo(Foo())
}
----
======
The output from running the preceding program should resemble the following (the Log4J
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
this time the code uses reactive types:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
// 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"]
.Kotlin
----
// 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>
}
----
======
The following example shows an implementation of the preceding interface:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
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"]
.Kotlin
----
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
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).
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"]
.Java
----
@Transactional
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);
}
----
======
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
@ -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
rollback:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public void resolvePosition() {
try {
@ -150,8 +157,10 @@ rollback:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
fun resolvePosition() {
try {
@ -162,6 +171,7 @@ rollback:
}
}
----
======
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

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
example sets up such an event listener:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Component
public class MyComponent {
@ -27,8 +30,10 @@ example sets up such an event listener:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Component
class MyComponent {
@ -39,6 +44,7 @@ example sets up such an event listener:
}
}
----
======
The `@TransactionalEventListener` annotation exposes a `phase` attribute that lets you
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
`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"]
.Java
----
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"]
.Kotlin
----
// use constructor-injection to supply the PlatformTransactionManager
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
with an anonymous class, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
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"]
.Kotlin
----
transactionTemplate.execute(object : TransactionCallbackWithoutResult() {
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
`setRollbackOnly()` method on the supplied `TransactionStatus` object, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
transactionTemplate.execute(object : TransactionCallbackWithoutResult() {
@ -132,6 +149,7 @@ Code within the callback can roll the transaction back by calling the
}
})
----
======
[[tx-prog-template-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
a specific `TransactionTemplate:`
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class SimpleService implements Service {
@ -160,8 +181,10 @@ a specific `TransactionTemplate:`
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class SimpleService(transactionManager: PlatformTransactionManager) : Service {
@ -173,6 +196,7 @@ a specific `TransactionTemplate:`
}
}
----
======
The following example defines a `TransactionTemplate` with some custom transactional
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
the `TransactionalOperator` resembles the next example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
// use constructor-injection to supply the ReactiveTransactionManager
class SimpleService(transactionManager: ReactiveTransactionManager) : Service {
@ -250,6 +279,7 @@ the `TransactionalOperator` resembles the next example:
}
}
----
======
`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()`
method on the supplied `ReactiveTransaction` object, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
transactionalOperator.execute(object : TransactionCallback() {
@ -282,6 +317,7 @@ method on the supplied `ReactiveTransaction` object, as follows:
}
})
----
======
[[tx-prog-operator-cancel]]
=== 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
`TransactionalOperator:`
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
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"]
.Kotlin
----
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)
}
----
======
[[transaction-programmatic-tm]]
== 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
following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
// 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);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val def = DefaultTransactionDefinition()
// 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)
----
======
[[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
following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
// 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)));
});
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val def = DefaultTransactionDefinition()
// 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)) }
}
----
======

View File

@ -38,12 +38,10 @@ you can instead write the following:
registerBean { Bar(it.getBean()) }
}
----
====
When the class `Bar` has a single constructor, you can even just specify the bean class,
the constructor parameters will be autowired by type:
====
[source,kotlin,indent=0]
----
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)`]
to enable autowiring for all parameters.
====
[source,kotlin,indent=0]
----
@SpringJUnitConfig(TestConfig::class)
@ -267,7 +266,6 @@ class OrderServiceIntegrationTests(val orderService: OrderService,
// tests that use the injected OrderService and CustomerService
}
----
====
[[per_class-lifecycle]]

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