Add Kotlin code snippets to core refdoc

This commit introduces Kotlin code snippets, for now
in the core reference documentation. Other sections
will follow, as well as improvements like global
language switch.

See gh-21778
This commit is contained in:
Sebastien Deleuze 2019-08-12 15:39:49 +02:00
parent fcbca7d569
commit 96658235c8
10 changed files with 5758 additions and 1382 deletions

View File

@ -6,6 +6,7 @@ buildscript {
classpath("io.spring.gradle:propdeps-plugin:0.0.9.RELEASE")
classpath("io.spring.nohttp:nohttp-gradle:0.0.3.RELEASE")
classpath("org.asciidoctor:asciidoctorj-pdf:1.5.0-alpha.16")
classpath("io.spring.asciidoctor:spring-asciidoctor-extensions:0.1.3.RELEASE")
}
}
@ -303,6 +304,7 @@ configure(rootProject) {
testCompile("org.aspectj:aspectjweaver:${aspectjVersion}")
testCompile("org.hsqldb:hsqldb:${hsqldbVersion}")
testCompile("org.hibernate:hibernate-core:5.1.17.Final")
asciidoctor("io.spring.asciidoctor:spring-asciidoctor-extensions:0.1.3.RELEASE")
}
artifacts {

View File

@ -129,7 +129,7 @@ asciidoctor {
'highlightjsdir=js/highlight',
'highlightjs-theme=atom-one-dark-reasonable',
stylesdir: "css/",
stylesheet: 'spring.css',
stylesheet: 'stylesheet.css',
'spring-version': project.version
}

View File

@ -25,8 +25,8 @@ target different advice with the same pointcut.
The `org.springframework.aop.Pointcut` interface is the central interface, used to
target advices to particular classes and methods. The complete interface follows:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public interface Pointcut {
@ -36,6 +36,17 @@ target advices to particular classes and methods. The complete interface follows
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
interface Pointcut {
fun getClassFilter(): ClassFilter
fun getMethodMatcher(): MethodMatcher
}
----
Splitting the `Pointcut` interface into two parts allows reuse of class and method
matching parts and fine-grained composition operations (such as performing a "`union`"
@ -45,19 +56,27 @@ The `ClassFilter` interface is used to restrict the pointcut to a given set of t
classes. If the `matches()` method always returns true, all target classes are
matched. The following listing shows the `ClassFilter` interface definition:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public interface ClassFilter {
boolean matches(Class clazz);
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
interface ClassFilter {
fun matches(clazz: Class<*>): Boolean
}
----
The `MethodMatcher` interface is normally more important. The complete interface follows:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public interface MethodMatcher {
@ -68,6 +87,18 @@ The `MethodMatcher` interface is normally more important. The complete interface
boolean matches(Method m, Class targetClass, Object[] args);
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
interface MethodMatcher {
val isRuntime: Boolean
fun matches(m: Method, targetClass: Class<*>): Boolean
fun matches(m: Method, targetClass: Class<*>, args: Array<Any>): Boolean
}
----
The `matches(Method, Class)` method is used to test whether this pointcut ever
matches a given method on a target class. This evaluation can be performed when an AOP
@ -143,8 +174,7 @@ effectively the union of these pointcuts.)
The following example shows how to use `JdkRegexpMethodPointcut`:
[source,xml,indent=0]
[subs="verbatim"]
[source,xml,indent=0,subs="verbatim"]
----
<bean id="settersAndAbsquatulatePointcut"
class="org.springframework.aop.support.JdkRegexpMethodPointcut">
@ -163,8 +193,7 @@ throws advice, and others). Behind the scenes, Spring uses a `JdkRegexpMethodPoi
Using `RegexpMethodPointcutAdvisor` simplifies wiring, as the one bean encapsulates both
pointcut and advice, as the following example shows:
[source,xml,indent=0]
[subs="verbatim"]
[source,xml,indent=0,subs="verbatim"]
----
<bean id="settersAndAbsquatulateAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
@ -225,8 +254,8 @@ 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`:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
class TestStaticPointcut extends StaticMethodMatcherPointcut {
@ -235,6 +264,16 @@ following example shows how to subclass `StaticMethodMatcherPointcut`:
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class TestStaticPointcut : StaticMethodMatcherPointcut() {
override fun matches(method: Method, targetClass: Class<*>): Boolean {
// return true if custom criteria match
}
}
----
There are also superclasses for dynamic pointcuts.
You can use custom pointcuts with any advice type.
@ -296,14 +335,22 @@ Spring is compliant with the AOP `Alliance` interface for around advice that use
interception. Classes that implement `MethodInterceptor` and that implement around advice should also implement the
following interface:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public interface MethodInterceptor extends Interceptor {
Object invoke(MethodInvocation invocation) throws Throwable;
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
interface MethodInterceptor : Interceptor {
fun invoke(invocation: MethodInvocation) : Any
}
----
The `MethodInvocation` argument to the `invoke()` method exposes the method being
invoked, the target join point, the AOP proxy, and the arguments to the method. The
@ -312,8 +359,8 @@ point.
The following example shows a simple `MethodInterceptor` implementation:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class DebugInterceptor implements MethodInterceptor {
@ -325,6 +372,19 @@ The following example shows a simple `MethodInterceptor` implementation:
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class DebugInterceptor : MethodInterceptor {
override fun invoke(invocation: MethodInvocation): Any {
println("Before: invocation=[$invocation]")
val rval = invocation.proceed()
println("Invocation returned")
return rval
}
}
----
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
@ -353,14 +413,22 @@ interceptor chain.
The following listing shows the `MethodBeforeAdvice` interface:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public interface MethodBeforeAdvice extends BeforeAdvice {
void before(Method m, Object[] args, Object target) throws Throwable;
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
interface MethodBeforeAdvice : BeforeAdvice {
fun before(m: Method, args: Array<Any>, target: Any)
}
----
(Spring's API design would allow for
field before advice, although the usual objects apply to field interception and it is
@ -375,8 +443,8 @@ wrapped in an unchecked exception by the AOP proxy.
The following example shows a before advice in Spring, which counts all method invocations:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class CountingBeforeAdvice implements MethodBeforeAdvice {
@ -391,6 +459,18 @@ The following example shows a before advice in Spring, which counts all method i
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class CountingBeforeAdvice : MethodBeforeAdvice {
var count: Int = 0
override fun before(m: Method, args: Array<Any>, target: Any?) {
++count
}
}
----
TIP: Before advice can be used with any pointcut.
@ -404,8 +484,7 @@ an exception. Spring offers typed throws advice. Note that this means that the
tag interface identifying that the given object implements one or more typed throws
advice methods. These should be in the following form:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes"]
----
afterThrowing([Method, args, target], subclassOfThrowable)
----
@ -416,8 +495,8 @@ 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):
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class RemoteThrowsAdvice implements ThrowsAdvice {
@ -426,13 +505,23 @@ The following advice is invoked if a `RemoteException` is thrown (including from
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class RemoteThrowsAdvice : ThrowsAdvice {
fun afterThrowing(ex: RemoteException) {
// Do something with remote exception
}
}
----
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:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class ServletThrowsAdviceWithArguments implements ThrowsAdvice {
@ -441,13 +530,23 @@ arguments, and target object. The following advice is invoked if a `ServletExcep
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class ServletThrowsAdviceWithArguments : ThrowsAdvice {
fun afterThrowing(m: Method, args: Array<Any>, target: Any, ex: ServletException) {
// Do something with all arguments
}
}
----
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:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public static class CombinedThrowsAdvice implements ThrowsAdvice {
@ -460,6 +559,20 @@ methods can be combined in a single class. The following listing shows the final
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class CombinedThrowsAdvice : ThrowsAdvice {
fun afterThrowing(ex: RemoteException) {
// Do something with remote exception
}
fun afterThrowing(m: Method, args: Array<Any>, target: Any, ex: ServletException) {
// Do something with all arguments
}
}
----
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
@ -478,8 +591,8 @@ TIP: Throws advice can be used with any pointcut.
An after returning advice in Spring must implement the
`org.springframework.aop.AfterReturningAdvice` interface, which the following listing shows:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public interface AfterReturningAdvice extends Advice {
@ -487,6 +600,14 @@ An after returning advice in Spring must implement the
throws Throwable;
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
interface AfterReturningAdvice : Advice {
fun afterReturning(returnValue: Any, m: Method, args: Array<Any>, target: Any)
}
----
An after returning advice has access to the return value (which it cannot modify),
the invoked method, the method's arguments, and the target.
@ -494,8 +615,8 @@ 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:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class CountingAfterReturningAdvice implements AfterReturningAdvice {
@ -511,6 +632,19 @@ not thrown exceptions:
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class CountingAfterReturningAdvice : AfterReturningAdvice {
var count: Int = 0
private set
override fun afterReturning(returnValue: Any?, m: Method, args: Array<Any>, target: Any?) {
++count
}
}
----
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.
@ -526,14 +660,22 @@ Spring treats introduction advice as a special kind of interception advice.
Introduction requires an `IntroductionAdvisor` and an `IntroductionInterceptor` that
implement the following interface:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public interface IntroductionInterceptor extends MethodInterceptor {
boolean implementsInterface(Class intf);
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
interface IntroductionInterceptor : MethodInterceptor {
fun implementsInterface(intf: Class<*>): Boolean
}
----
The `invoke()` method inherited from the AOP Alliance `MethodInterceptor` interface must
implement the introduction. That is, if the invoked method is on an introduced
@ -544,8 +686,8 @@ Introduction advice cannot be used with any pointcut, as it applies only at the
rather than the method, level. You can only use introduction advice with the
`IntroductionAdvisor`, which has the following methods:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public interface IntroductionAdvisor extends Advisor, IntroductionInfo {
@ -559,6 +701,22 @@ rather than the method, level. You can only use introduction advice with the
Class[] getInterfaces();
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
interface IntroductionAdvisor : Advisor, IntroductionInfo {
val classFilter: ClassFilter
@Throws(IllegalArgumentException::class)
fun validateInterfaces()
}
interface IntroductionInfo {
val interfaces: Array<Class<*>>
}
----
There is no `MethodMatcher` and, hence, no `Pointcut` associated with introduction
advice. Only class filtering is logical.
@ -571,8 +729,8 @@ 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:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public interface Lockable {
void lock();
@ -580,6 +738,15 @@ introduce the following interface to one or more objects:
boolean locked();
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
interface Lockable {
fun lock()
fun unlock()
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
@ -616,8 +783,8 @@ to that held in the target object.
The following example shows the example `LockMixin` class:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class LockMixin extends DelegatingIntroductionInterceptor implements Lockable {
@ -644,6 +811,34 @@ The following example shows the example `LockMixin` class:
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class LockMixin : DelegatingIntroductionInterceptor(), Lockable {
private var locked: Boolean = false
fun lock() {
this.locked = true
}
fun unlock() {
this.locked = false
}
fun locked(): Boolean {
return this.locked
}
override fun invoke(invocation: MethodInvocation): Any? {
if (locked() && invocation.method.name.indexOf("set") == 0) {
throw LockedException()
}
return super.invoke(invocation)
}
}
----
Often, you need not override the `invoke()` method. The
`DelegatingIntroductionInterceptor` implementation (which calls the `delegate` method if
@ -658,8 +853,8 @@ 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:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class LockMixinAdvisor extends DefaultIntroductionAdvisor {
@ -668,6 +863,11 @@ The following example shows our `LockMixinAdvisor` class:
}
}
----
[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
@ -857,8 +1057,7 @@ Consider a simple example of `ProxyFactoryBean` in action. This example involves
The following listing shows the example:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="personTarget" class="com.mycompany.PersonImpl">
<property name="name" value="Tony"/>
@ -899,17 +1098,21 @@ 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:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Person person = (Person) factory.getBean("person");
----
[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:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="personUser" class="com.mycompany.PersonUser">
<property name="person"><ref bean="person"/></property>
@ -926,8 +1129,7 @@ inner bean. Only the `ProxyFactoryBean` definition is different. The
advice is included only for completeness. The following example shows how to use an
anonymous inner bean:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="myAdvisor" class="com.mycompany.MyAdvisor">
<property name="someProperty" value="Custom string property value"/>
@ -1004,8 +1206,7 @@ the part before the asterisk are added to the advisor chain. This can come in ha
if you need to add a standard set of "`global`" advisors. The following example defines
two global advisors:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="service"/>
@ -1032,8 +1233,7 @@ definitions, can result in much cleaner and more concise proxy definitions.
First, we create a parent, template, bean definition for the proxy, as follows:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="txProxyTemplate" abstract="true"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
@ -1051,8 +1251,7 @@ that needs to be created is a child bean definition, which wraps the target of t
proxy as an inner bean definition, since the target is never used on its own anyway.
The following example shows such a child bean:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="myService" parent="txProxyTemplate">
<property name="target">
@ -1065,8 +1264,7 @@ The following example shows such a child bean:
You can override properties from the parent template. In the following example,
we override the transaction propagation settings:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="mySpecialService" parent="txProxyTemplate">
<property name="target">
@ -1107,14 +1305,22 @@ 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:
[source,java,indent=0]
[subs="verbatim,quotes"]
[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();
----
[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
@ -1145,8 +1351,8 @@ 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:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Advisor[] getAdvisors();
@ -1168,6 +1374,36 @@ following methods:
boolean isFrozen();
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
fun getAdvisors(): Array<Advisor>
@Throws(AopConfigException::class)
fun addAdvice(advice: Advice)
@Throws(AopConfigException::class)
fun addAdvice(pos: Int, advice: Advice)
@Throws(AopConfigException::class)
fun addAdvisor(advisor: Advisor)
@Throws(AopConfigException::class)
fun addAdvisor(pos: Int, advisor: Advisor)
fun indexOf(advisor: Advisor): Int
@Throws(AopConfigException::class)
fun removeAdvisor(advisor: Advisor): Boolean
@Throws(AopConfigException::class)
fun removeAdvisor(index: Int)
@Throws(AopConfigException::class)
fun replaceAdvisor(a: Advisor, b: Advisor): Boolean
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
@ -1189,8 +1425,8 @@ 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:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Advised advised = (Advised) myObject;
Advisor[] advisors = advised.getAdvisors();
@ -1207,6 +1443,24 @@ manipulating its advice:
assertEquals("Added two advisors", oldAdvisorCount + 2, advised.getAdvisors().length);
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val advised = myObject as Advised
val advisors = advised.advisors
val oldAdvisorCount = advisors.size
println("$oldAdvisorCount advisors")
// Add an advice like an interceptor without a pointcut
// Will match all proxied methods
// Can use for interceptors, before, after returning or throws advice
advised.addAdvice(DebugInterceptor())
// Add selective advice using a pointcut
advised.addAdvisor(DefaultPointcutAdvisor(mySpecialPointcut, myAdvice))
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.
@ -1261,8 +1515,7 @@ The `BeanNameAutoProxyCreator` class is a `BeanPostProcessor` that automatically
AOP proxies for beans with names that match literal values or wildcards. The following
example shows how to create a `BeanNameAutoProxyCreator` bean:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames" value="jdk*,onlyJdk"/>
@ -1324,8 +1577,7 @@ bean`" idiom shown earlier also offers this benefit.)
The following example creates a `DefaultAdvisorAutoProxyCreator` bean and the other
elements discussed in this section:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
@ -1394,17 +1646,22 @@ 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:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
HotSwappableTargetSource swapper = (HotSwappableTargetSource) beanFactory.getBean("swapper");
Object oldTarget = swapper.swap(newTarget);
----
[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:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="initialTarget" class="mycompany.OldTarget"/>
@ -1448,8 +1705,7 @@ NOTE: Commons Pool 1.5+ is also supported but is deprecated as of Spring Framewo
The following listing shows an example configuration:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="businessObjectTarget" class="com.mycompany.MyBusinessObject"
scope="prototype">
@ -1483,8 +1739,7 @@ You can configure Spring to be able to cast any pooled object to the
about the configuration and current size of the pool through an introduction. You
need to define an advisor similar to the following:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="poolConfigAdvisor" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="poolTargetSource"/>
@ -1499,12 +1754,18 @@ the `ProxyFactoryBean` that exposes the pooled object.
The cast is defined as follows:
[source,java,indent=0]
[subs="verbatim,quotes"]
[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());
----
[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
@ -1527,8 +1788,7 @@ use this approach without very good reason.
To do this, you could modify the `poolTargetSource` definition shown earlier as follows
(we also changed the name, for clarity):
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="prototypeTargetSource" class="org.springframework.aop.target.PrototypeTargetSource">
<property name="targetBeanName" ref="businessObjectTarget"/>
@ -1550,8 +1810,7 @@ facility to transparently store a resource alongside a thread. Setting up a
`ThreadLocalTargetSource` is pretty much the same as was explained for the other types
of target source, as the following example shows:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="threadlocalTargetSource" class="org.springframework.aop.target.ThreadLocalTargetSource">
<property name="targetBeanName" value="businessObjectTarget"/>

File diff suppressed because it is too large Load Diff

View File

@ -22,8 +22,7 @@ To use the tags in the `util` schema, you need to have the following preamble at
of your Spring XML configuration file (the text in the snippet references the
correct schema so that the tags in the `util` namespace are available to you):
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
@ -44,8 +43,7 @@ correct schema so that the tags in the `util` namespace are available to you):
Consider the following bean definition:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="..." class="...">
<property name="isolation">
@ -64,8 +62,7 @@ plumbing to the end user.
The following XML Schema-based version is more concise, clearly expresses the
developer's intent ("`inject this constant value`"), and it reads better:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="..." class="...">
<property name="isolation">
@ -86,8 +83,7 @@ The following example shows how a `static` field is exposed, by using the
{api-spring-framework}/beans/factory/config/FieldRetrievingFactoryBean.html#setStaticField(java.lang.String)[`staticField`]
property:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="myField"
class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
@ -98,8 +94,7 @@ property:
There is also a convenience usage form where the `static` field is specified as the bean
name, as the following example shows:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="java.sql.Connection.TRANSACTION_SERIALIZABLE"
class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"/>
@ -110,8 +105,7 @@ bean that refers to it also has to use this longer name), but this form is very
concise to define and very convenient to use as an inner bean since the `id` does not have
to be specified for the bean reference, as the following example shows:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="..." class="...">
<property name="isolation">
@ -131,8 +125,8 @@ 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:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
package javax.persistence;
@ -142,11 +136,22 @@ The following example enumeration shows how easy injecting an enum value is:
EXTENDED
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
package javax.persistence
enum class PersistenceContextType {
TRANSACTION,
EXTENDED
}
----
Now consider the following setter of type `PersistenceContextType` and the corresponding bean definition:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
package example;
@ -159,9 +164,18 @@ Now consider the following setter of type `PersistenceContextType` and the corre
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
package example
[source,xml,indent=0]
[subs="verbatim,quotes"]
class Client {
lateinit var persistenceContextType: PersistenceContextType
}
----
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean class="example.Client">
<property name="persistenceContextType" value="TRANSACTION"/>
@ -174,8 +188,7 @@ Now consider the following setter of type `PersistenceContextType` and the corre
Consider the following example:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- target bean to be referenced by name -->
<bean id="testBean" class="org.springframework.beans.TestBean" scope="prototype">
@ -197,8 +210,7 @@ has a value equal to the `age` property of the `testBean` bean.
Now consider the following example, which adds a `<util:property-path/>` element:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- target bean to be referenced by name -->
<bean id="testBean" class="org.springframework.beans.TestBean" scope="prototype">
@ -228,8 +240,7 @@ argument.
The following example shows a path being used against another bean, by name:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
// target bean to be referenced by name
<bean id="person" class="org.springframework.beans.TestBean" scope="prototype">
@ -251,8 +262,7 @@ The following example shows a path being used against another bean, by name:
In the following example, a path is evaluated against an inner bean:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- results in 12, which is the value of property 'age' of the inner bean -->
<bean id="theAge"
@ -269,8 +279,7 @@ In the following example, a path is evaluated against an inner bean:
There is also a shortcut form, where the bean name is the property path.
The following example shows the shortcut form:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- results in 10, which is the value of property 'age' of bean 'person' -->
<bean id="person.age"
@ -281,8 +290,7 @@ This form does mean that there is no choice in the name of the bean. Any referen
also has to use the same `id`, which is the path. If used as an inner
bean, there is no need to refer to it at all, as the following example shows:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="..." class="...">
<property name="age">
@ -302,8 +310,7 @@ this feature.
Consider the following example:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- creates a java.util.Properties instance with values loaded from the supplied location -->
<bean id="jdbcConfiguration" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
@ -317,8 +324,7 @@ loaded from the supplied <<core.adoc#resources, `Resource`>> location).
The following example uses a `util:properties` element to make a more concise representation:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- creates a java.util.Properties instance with values loaded from the supplied location -->
<util:properties id="jdbcConfiguration" location="classpath:com/foo/jdbc-production.properties"/>
@ -330,8 +336,7 @@ The following example uses a `util:properties` element to make a more concise re
Consider the following example:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- creates a java.util.List instance with values loaded from the supplied 'sourceList' -->
<bean id="emails" class="org.springframework.beans.factory.config.ListFactoryBean">
@ -352,8 +357,7 @@ from the supplied `sourceList`.
The following example uses a `<util:list/>` element to make a more concise representation:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- creates a java.util.List instance with the supplied values -->
<util:list id="emails">
@ -369,8 +373,7 @@ populated by using the `list-class` attribute on the `<util:list/>` element. For
example, if we really need a `java.util.LinkedList` to be instantiated, we could use the
following configuration:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<util:list id="emails" list-class="java.util.LinkedList">
<value>jackshaftoe@vagabond.org</value>
@ -388,8 +391,7 @@ If no `list-class` attribute is supplied, the container chooses a `List` impleme
Consider the following example:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- creates a java.util.Map instance with values loaded from the supplied 'sourceMap' -->
<bean id="emails" class="org.springframework.beans.factory.config.MapFactoryBean">
@ -410,8 +412,7 @@ taken from the supplied `'sourceMap'`.
The following example uses a `<util:map/>` element to make a more concise representation:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- creates a java.util.Map instance with the supplied key-value pairs -->
<util:map id="emails">
@ -427,8 +428,7 @@ populated by using the `'map-class'` attribute on the `<util:map/>` element. For
example, if we really need a `java.util.TreeMap` to be instantiated, we could use the
following configuration:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<util:map id="emails" map-class="java.util.TreeMap">
<entry key="pechorin" value="pechorin@hero.org"/>
@ -446,8 +446,7 @@ If no `'map-class'` attribute is supplied, the container chooses a `Map` impleme
Consider the following example:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- creates a java.util.Set instance with values loaded from the supplied 'sourceSet' -->
<bean id="emails" class="org.springframework.beans.factory.config.SetFactoryBean">
@ -468,8 +467,7 @@ from the supplied `sourceSet`.
The following example uses a `<util:set/>` element to make a more concise representation:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- creates a java.util.Set instance with the supplied values -->
<util:set id="emails">
@ -485,8 +483,7 @@ populated by using the `set-class` attribute on the `<util:set/>` element. For
example, if we really need a `java.util.TreeSet` to be instantiated, we could use the
following configuration:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<util:set id="emails" set-class="java.util.TreeSet">
<value>pechorin@hero.org</value>
@ -513,8 +510,7 @@ the following preamble at the top of your Spring XML configuration file (the tex
snippet references the correct schema so that the tags in the `aop` namespace
are available to you):
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
@ -540,8 +536,7 @@ a lot of the "`grunt`" work in Spring, such as `BeanfactoryPostProcessors`. The
snippet references the correct schema so that the elements in the `context` namespace are
available to you:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
@ -636,8 +631,7 @@ The following example shows the `<meta/>` element in the context of a surroundin
(note that, without any logic to interpret it, the metadata is effectively useless
as it stands).
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
@ -708,8 +702,7 @@ Creating an XML configuration extension for use with Spring's IoC container star
authoring an XML Schema to describe the extension. For our example, we use the following schema
to configure `SimpleDateFormat` objects:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- myns.xsd (inside package org/springframework/samples/xml) -->
@ -745,8 +738,7 @@ The preceding schema lets us configure `SimpleDateFormat` objects directly in an
XML application context file by using the `<myns:dateformat/>` element, as the following
example shows:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<myns:dateformat id="dateFormat"
pattern="yyyy-MM-dd HH:mm"
@ -756,8 +748,7 @@ example shows:
Note that, after we have created the infrastructure classes, the preceding snippet of XML is
essentially the same as the following XML snippet:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="dateFormat" class="java.text.SimpleDateFormat">
<constructor-arg value="yyyy-HH-dd HH:mm"/>
@ -806,8 +797,8 @@ 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:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
package org.springframework.samples.xml;
@ -818,7 +809,20 @@ use the `NamespaceHandlerSupport` class:
public void init() {
registerBeanDefinitionParser("dateformat", new SimpleDateFormatBeanDefinitionParser());
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
package org.springframework.samples.xml
import org.springframework.beans.factory.xml.NamespaceHandlerSupport
class MyNamespaceHandler : NamespaceHandlerSupport {
override fun init() {
registerBeanDefinitionParser("dateformat", SimpleDateFormatBeanDefinitionParser())
}
}
----
@ -844,7 +848,8 @@ 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:
[source,java,indent=0]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
package org.springframework.samples.xml;
@ -855,16 +860,20 @@ we can parse our custom XML content, as you can see in the following example:
import java.text.SimpleDateFormat;
public class SimpleDateFormatBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { // <1>
// We use the Spring-provided AbstractSingleBeanDefinitionParser to handle a lot of
// the basic grunt work of creating a single BeanDefinition.
public class SimpleDateFormatBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
protected Class getBeanClass(Element element) {
return SimpleDateFormat.class; // <2>
// We supply the AbstractSingleBeanDefinitionParser superclass with the type that our
// single BeanDefinition represents.
return SimpleDateFormat.class;
}
protected void doParse(Element element, BeanDefinitionBuilder bean) {
// this will never be null since the schema explicitly requires that a value be supplied
String pattern = element.getAttribute("pattern");
bean.addConstructorArg(pattern);
bean.addConstructorArgValue(pattern);
// this however is an optional property
String lenient = element.getAttribute("lenient");
@ -875,12 +884,41 @@ we can parse our custom XML content, as you can see in the following example:
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
package org.springframework.samples.xml
<1> We use the Spring-provided `AbstractSingleBeanDefinitionParser` to handle a lot of
the basic grunt work of creating a single `BeanDefinition`.
import org.springframework.beans.factory.support.BeanDefinitionBuilder
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser
import org.springframework.util.StringUtils
import org.w3c.dom.Element
<2> We supply the `AbstractSingleBeanDefinitionParser` superclass with the type that our
single `BeanDefinition` represents.
import java.text.SimpleDateFormat
// We use the Spring-provided AbstractSingleBeanDefinitionParser to handle a lot of
// the basic grunt work of creating a single BeanDefinition.
class SimpleDateFormatBeanDefinitionParser : AbstractSingleBeanDefinitionParser() {
override fun getBeanClass(element: Element): Class<*>? {
// We supply the AbstractSingleBeanDefinitionParser superclass with the type that our
// single BeanDefinition represents.
return SimpleDateFormat::class.java
}
override fun doParse(element: Element, bean: BeanDefinitionBuilder) {
// this will never be null since the schema explicitly requires that a value be supplied
val pattern = element.getAttribute("pattern")
bean.addConstructorArgValue(pattern)
// this however is an optional property
val lenient = element.getAttribute("lenient")
if (StringUtils.hasText(lenient)) {
bean.addPropertyValue("lenient", java.lang.Boolean.valueOf(lenient))
}
}
}
----
In this simple case, this is all that we need to do. The creation of our single
`BeanDefinition` is handled by the `AbstractSingleBeanDefinitionParser` superclass, as
@ -906,8 +944,7 @@ these special properties files, the formats of which are detailed in the next tw
The properties file called `spring.handlers` contains a mapping of XML Schema URIs to
namespace handler classes. For our example, we need to write the following:
[literal]
[subs="verbatim,quotes"]
[literal,subs="verbatim,quotes"]
----
http\://www.mycompany.example/schema/myns=org.springframework.samples.xml.MyNamespaceHandler
----
@ -932,8 +969,7 @@ properties file, Spring searches for the schema (in this case,
`myns.xsd` in the `org.springframework.samples.xml` package) on the classpath.
The following snippet shows the line we need to add for our custom schema:
[literal]
[subs="verbatim,quotes"]
[literal,subs="verbatim,quotes"]
----
http\://www.mycompany.example/schema/myns/myns.xsd=org/springframework/samples/xml/myns.xsd
----
@ -953,8 +989,7 @@ one of the "`custom`" extensions that Spring provides. The following
example uses the custom `<dateformat/>` element developed in the previous steps
in a Spring XML configuration file:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
@ -992,8 +1027,7 @@ This section presents some more detailed examples of custom XML extensions.
The example presented in this section shows how you to write the various artifacts required
to satisfy a target of the following configuration:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
@ -1021,8 +1055,8 @@ 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:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
package com.foo;
@ -1052,13 +1086,35 @@ The following listing shows the `Component` class:
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
package com.foo
import java.util.ArrayList
class Component {
var name: String? = null
private val components = ArrayList<Component>()
// mmm, there is no setter method for the 'components'
fun addComponent(component: Component) {
this.components.add(component)
}
fun getComponents(): List<Component> {
return components
}
}
----
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`:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
package com.foo;
@ -1097,6 +1153,45 @@ setter property for the `components` property. The following listing shows such
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
package com.foo
import org.springframework.beans.factory.FactoryBean
import org.springframework.stereotype.Component
class ComponentFactoryBean : FactoryBean<Component> {
private var parent: Component? = null
private var children: List<Component>? = null
fun setParent(parent: Component) {
this.parent = parent
}
fun setChildren(children: List<Component>) {
this.children = children
}
override fun getObject(): Component? {
if (this.children != null && this.children!!.isNotEmpty()) {
for (child in children!!) {
this.parent!!.addComponent(child)
}
}
return this.parent
}
override fun getObjectType(): Class<Component>? {
return Component::class.java
}
override fun isSingleton(): Boolean {
return true
}
}
----
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.
@ -1104,8 +1199,7 @@ If we stick to <<xsd-custom-introduction, the steps described previously>>, we s
by creating the XSD schema to define the structure of our custom tag, as the following
listing shows:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
@ -1131,8 +1225,8 @@ listing shows:
Again following <<xsd-custom-introduction, the process described earlier>>,
we then create a custom `NamespaceHandler`:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
package com.foo;
@ -1145,13 +1239,27 @@ we then create a custom `NamespaceHandler`:
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
package com.foo
import org.springframework.beans.factory.xml.NamespaceHandlerSupport
class ComponentNamespaceHandler : NamespaceHandlerSupport() {
override fun init() {
registerBeanDefinitionParser("component", ComponentBeanDefinitionParser())
}
}
----
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:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
package com.foo;
@ -1199,19 +1307,66 @@ listing shows our custom `BeanDefinitionParser` implementation:
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
package com.foo
import org.springframework.beans.factory.config.BeanDefinition
import org.springframework.beans.factory.support.AbstractBeanDefinition
import org.springframework.beans.factory.support.BeanDefinitionBuilder
import org.springframework.beans.factory.support.ManagedList
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser
import org.springframework.beans.factory.xml.ParserContext
import org.springframework.util.xml.DomUtils
import org.w3c.dom.Element
import java.util.List
class ComponentBeanDefinitionParser : AbstractBeanDefinitionParser() {
override fun parseInternal(element: Element, parserContext: ParserContext): AbstractBeanDefinition? {
return parseComponentElement(element)
}
private fun parseComponentElement(element: Element): AbstractBeanDefinition {
val factory = BeanDefinitionBuilder.rootBeanDefinition(ComponentFactoryBean::class.java)
factory.addPropertyValue("parent", parseComponent(element))
val childElements = DomUtils.getChildElementsByTagName(element, "component")
if (childElements != null && childElements.size > 0) {
parseChildComponents(childElements, factory)
}
return factory.getBeanDefinition()
}
private fun parseComponent(element: Element): BeanDefinition {
val component = BeanDefinitionBuilder.rootBeanDefinition(Component::class.java)
component.addPropertyValue("name", element.getAttribute("name"))
return component.beanDefinition
}
private fun parseChildComponents(childElements: List<Element>, factory: BeanDefinitionBuilder) {
val children = ManagedList<BeanDefinition>(childElements.size)
for (element in childElements) {
children.add(parseComponentElement(element))
}
factory.addPropertyValue("children", children)
}
}
----
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:
[literal]
[subs="verbatim,quotes"]
[literal,subs="verbatim,quotes"]
----
# in 'META-INF/spring.handlers'
http\://www.foo.example/schema/component=com.foo.ComponentNamespaceHandler
----
[literal]
[subs="verbatim,quotes"]
[literal,subs="verbatim,quotes"]
----
# in 'META-INF/spring.schemas'
http\://www.foo.example/schema/component/component.xsd=com/foo/component.xsd
@ -1233,8 +1388,7 @@ https://jcp.org/en/jsr/detail?id=107[JCache], and you want to ensure that the
named JCache instance is eagerly started within the surrounding cluster.
The following listing shows such a definition:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="checkingAccountService" class="com.foo.DefaultCheckingAccountService"
jcache:cache-name="checking.account">
@ -1248,8 +1402,8 @@ 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`:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
package com.foo;
@ -1266,12 +1420,23 @@ JCache-initializing `BeanDefinition`. The following listing shows our `JCacheIni
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
package com.foo
class JCacheInitializer(private val name: String) {
fun initialize() {
// lots of JCache API calls to initialize the named cache...
}
}
----
Now we can move onto the custom extension. First, we need to author
the XSD schema that describes the custom attribute, as follows:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
@ -1287,8 +1452,8 @@ the XSD schema that describes the custom attribute, as follows:
Next, we need to create the associated `NamespaceHandler`, as follows:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
package com.foo;
@ -1303,13 +1468,29 @@ Next, we need to create the associated `NamespaceHandler`, as follows:
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
package com.foo
import org.springframework.beans.factory.xml.NamespaceHandlerSupport
class JCacheNamespaceHandler : NamespaceHandlerSupport() {
override fun init() {
super.registerBeanDefinitionDecoratorForAttribute("cache-name",
JCacheInitializingBeanDefinitionDecorator())
}
}
----
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:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
package com.foo;
@ -1362,19 +1543,67 @@ The following listing shows our `BeanDefinitionDecorator` implementation:
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
package com.foo
import org.springframework.beans.factory.config.BeanDefinitionHolder
import org.springframework.beans.factory.support.AbstractBeanDefinition
import org.springframework.beans.factory.support.BeanDefinitionBuilder
import org.springframework.beans.factory.xml.BeanDefinitionDecorator
import org.springframework.beans.factory.xml.ParserContext
import org.w3c.dom.Attr
import org.w3c.dom.Node
import java.util.ArrayList
class JCacheInitializingBeanDefinitionDecorator : BeanDefinitionDecorator {
override fun decorate(source: Node, holder: BeanDefinitionHolder,
ctx: ParserContext): BeanDefinitionHolder {
val initializerBeanName = registerJCacheInitializer(source, ctx)
createDependencyOnJCacheInitializer(holder, initializerBeanName)
return holder
}
private fun createDependencyOnJCacheInitializer(holder: BeanDefinitionHolder,
initializerBeanName: String) {
val definition = holder.beanDefinition as AbstractBeanDefinition
var dependsOn = definition.dependsOn
dependsOn = if (dependsOn == null) {
arrayOf(initializerBeanName)
} else {
val dependencies = ArrayList(listOf(*dependsOn))
dependencies.add(initializerBeanName)
dependencies.toTypedArray()
}
definition.setDependsOn(*dependsOn)
}
private fun registerJCacheInitializer(source: Node, ctx: ParserContext): String {
val cacheName = (source as Attr).value
val beanName = "$cacheName-initializer"
if (!ctx.registry.containsBeanDefinition(beanName)) {
val initializer = BeanDefinitionBuilder.rootBeanDefinition(JCacheInitializer::class.java)
initializer.addConstructorArg(cacheName)
ctx.registry.registerBeanDefinition(beanName, initializer.getBeanDefinition())
}
return beanName
}
}
----
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:
[literal]
[subs="verbatim,quotes"]
[literal,subs="verbatim,quotes"]
----
# in 'META-INF/spring.handlers'
http\://www.foo.example/schema/jcache=com.foo.JCacheNamespaceHandler
----
[literal]
[subs="verbatim,quotes"]
[literal,subs="verbatim,quotes"]
----
# in 'META-INF/spring.schemas'
http\://www.foo.example/schema/jcache/jcache.xsd=com/foo/jcache.xsd

File diff suppressed because it is too large Load Diff

View File

@ -140,8 +140,8 @@ 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:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
DataBuffer buffer = factory.allocateBuffer();
boolean release = true;
@ -156,6 +156,21 @@ a serialization error occurs while populating the buffer with data. For example:
}
return buffer;
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val buffer = factory.allocateBuffer()
var release = true
try {
// serialize and populate buffer..
release = false
} finally {
if (release) {
DataBufferUtils.release(buffer)
}
}
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

File diff suppressed because it is too large Load Diff

View File

@ -37,8 +37,8 @@ Spring's `Resource` interface is meant to be a more capable interface for abstra
access to low-level resources. The following listing shows the `Resource` interface
definition:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public interface Resource extends InputStreamSource {
@ -55,7 +55,27 @@ definition:
String getFilename();
String getDescription();
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
interface Resource : InputStreamSource {
fun exists(): Boolean
val isOpen: Boolean
val url: URL
val file: File
@Throws(IOException::class)
fun createRelative(relativePath: String): Resource
val filename: String
val description: String
}
----
@ -63,13 +83,20 @@ As the definition of the `Resource` interface shows, it extends the `InputStream
interface. The following listing shows the definition of the `InputStreamSource`
interface:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public interface InputStreamSource {
InputStream getInputStream() throws IOException;
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
interface InputStreamSource {
val inputStream: InputStream
}
----
@ -223,15 +250,22 @@ The `ResourceLoader` interface is meant to be implemented by objects that can re
(that is, load) `Resource` instances. The following listing shows the `ResourceLoader`
interface definition:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public interface ResourceLoader {
Resource getResource(String location);
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
interface ResourceLoader {
fun getResource(location: String): Resource
}
----
All application contexts implement the `ResourceLoader` interface. Therefore, all
application contexts may be used to obtain `Resource` instances.
@ -241,11 +275,16 @@ 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 executed against a `ClassPathXmlApplicationContext` instance:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Resource template = ctx.getResource("some/resource/path/myTemplate.txt");
----
[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 executed
against a `FileSystemXmlApplicationContext` instance, it would return a
@ -259,27 +298,42 @@ 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:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Resource template = ctx.getResource("classpath:some/resource/path/myTemplate.txt");
----
[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 pair of examples use the `file` and `http`
prefixes:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Resource template = ctx.getResource("file:///some/resource/path/myTemplate.txt");
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val template = ctx.getResource("file:///some/resource/path/myTemplate.txt")
----
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Resource template = ctx.getResource("https://myhost.com/resource/path/myTemplate.txt");
----
[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:
@ -315,14 +369,22 @@ The `ResourceLoaderAware` interface is a special callback interface which identi
components that expect to be provided with a `ResourceLoader` reference. The following
listing shows the definition of the `ResourceLoaderAware` interface:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public interface ResourceLoaderAware {
void setResourceLoader(ResourceLoader resourceLoader);
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
interface ResourceLoaderAware {
fun setResourceLoader(resourceLoader: ResourceLoader)
}
----
When a class implements `ResourceLoaderAware` and is deployed into an application context
(as a Spring-managed bean), it is recognized as `ResourceLoaderAware` by the application
@ -367,8 +429,7 @@ register and use a special JavaBeans `PropertyEditor`, which can convert `String
to `Resource` objects. So, if `myBean` has a template property of type `Resource`, it can
be configured with a simple string for that resource, as the following example shows:
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="myBean" class="...">
<property name="template" value="some/resource/path/myTemplate.txt"/>
@ -384,14 +445,12 @@ If you need to force a specific `Resource` type to be used, you can use a prefix
The following two examples show how to force a `ClassPathResource` and a
`UrlResource` (the latter being used to access a filesystem file):
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<property name="template" value="classpath:some/resource/path/myTemplate.txt">
----
[source,xml,indent=0]
[subs="verbatim,quotes"]
[source,xml,indent=0,subs="verbatim,quotes"]
----
<property name="template" value="file:///some/resource/path/myTemplate.txt"/>
----
@ -419,21 +478,31 @@ 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`:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
ApplicationContext ctx = new ClassPathXmlApplicationContext("conf/appContext.xml");
----
[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`:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
ApplicationContext ctx =
new FileSystemXmlApplicationContext("conf/appContext.xml");
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val ctx = FileSystemXmlApplicationContext("conf/appContext.xml")
----
Now the bean definition is loaded from a filesystem location (in this case, relative to
the current working directory).
@ -442,12 +511,17 @@ Note that the use of the special classpath prefix or a standard URL prefix on th
location path overrides the default type of `Resource` created to load the
definition. Consider the following example:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
ApplicationContext ctx =
new FileSystemXmlApplicationContext("classpath:conf/appContext.xml");
----
[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 `ResourceLoader`, any
@ -465,8 +539,7 @@ then derives the path information from the supplied class.
Consider the following directory layout:
[literal]
[subs="verbatim,quotes"]
[literal,subs="verbatim,quotes"]
----
com/
foo/
@ -478,12 +551,17 @@ com/
The following example shows how a `ClassPathXmlApplicationContext` instance composed of the beans defined in
files named `services.xml` and `daos.xml` (which are on the classpath) can be instantiated:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
ApplicationContext ctx = new ClassPathXmlApplicationContext(
new String[] {"services.xml", "daos.xml"}, MessengerService.class);
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val ctx = ClassPathXmlApplicationContext(arrayOf("services.xml", "daos.xml"), MessengerService::class.java)
----
See the {api-spring-framework}/jca/context/SpringContextResourceAdapter.html[`ClassPathXmlApplicationContext`]
javadoc for details on the various constructors.
@ -516,8 +594,7 @@ a resource points to just one resource at a time.
Path locations can contain Ant-style patterns, as the following example shows:
[literal]
[subs="verbatim"]
[literal,subs="verbatim,quotes"]
----
/WEB-INF/*-context.xml
com/mycompany/**/applicationContext.xml
@ -562,12 +639,17 @@ 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:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
ApplicationContext ctx =
new ClassPathXmlApplicationContext("classpath*:conf/appContext.xml");
----
[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
@ -625,16 +707,14 @@ Ant-style patterns with `classpath:` resources are not guaranteed to find matchi
resources if the root package to search is available in multiple class path locations.
Consider the following example of a resource location:
[literal]
[subs="verbatim,quotes"]
[literal,subs="verbatim,quotes"]
----
com/mycompany/package1/service-context.xml
----
Now consider an Ant-style path that someone might use to try to find that file:
[literal]
[subs="verbatim,quotes"]
[literal,subs="verbatim,quotes"]
----
classpath:com/mycompany/**/service-context.xml
----
@ -663,53 +743,87 @@ 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:
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
ApplicationContext ctx =
new FileSystemXmlApplicationContext("conf/context.xml");
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val ctx = FileSystemXmlApplicationContext("conf/context.xml")
----
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
ApplicationContext ctx =
new FileSystemXmlApplicationContext("/conf/context.xml");
----
[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):
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
FileSystemXmlApplicationContext ctx = ...;
ctx.getResource("some/resource/path/myTemplate.txt");
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val ctx: FileSystemXmlApplicationContext = ...
ctx.getResource("some/resource/path/myTemplate.txt")
----
[source,java,indent=0]
[subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
FileSystemXmlApplicationContext ctx = ...;
ctx.getResource("/some/resource/path/myTemplate.txt");
----
[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:
[source,java,indent=0]
[subs="verbatim,quotes"]
[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");
----
[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")
----
[source,java,indent=0]
[subs="verbatim,quotes"]
[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");
----
[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")
----

File diff suppressed because it is too large Load Diff