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:
parent
fcbca7d569
commit
96658235c8
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
@ -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
|
@ -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
|
@ -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
Loading…
Reference in New Issue