Migrate to Asciidoctor Tabs
This commit is contained in:
parent
71154fd16b
commit
39146f9066
|
|
@ -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/
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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:
|
|||
// ...
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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, `+&&+` is awkward within an XML
|
||||
document, so you can use the `and`, `or`, and `not` keywords in place of `+&&+`,
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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/>`
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
----
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
====
|
||||
|
|
|
|||
|
|
@ -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>>
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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].
|
||||
|
|
|
|||
|
|
@ -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`.
|
||||
|
||||
|
|
|
|||
|
|
@ -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>)
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
|||
}
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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]]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -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...
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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=
|
|||
// ...
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
// ...
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
|||
}
|
||||
}
|
||||
----
|
||||
======
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
=====
|
||||
|
|
|
|||
|
|
@ -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!!!
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
|||
}
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
----
|
||||
======
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
// ...
|
||||
}
|
||||
----
|
||||
======
|
||||
|
|
|
|||
|
|
@ -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`
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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]]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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]]
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
|||
}
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
----
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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`:
|
||||
|
|
|
|||
|
|
@ -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]]
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)) }
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in New Issue