Remove admonitions surrounding code snippets
This commit is contained in:
parent
8c768e48fa
commit
33cbe2e77a
|
@ -29,7 +29,6 @@ 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"]
|
||||
----
|
||||
|
@ -41,7 +40,6 @@ target advices to particular classes and methods. The complete interface follows
|
|||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
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`"
|
||||
|
@ -51,7 +49,6 @@ 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"]
|
||||
----
|
||||
|
@ -60,11 +57,9 @@ matched. The following listing shows the `ClassFilter` interface definition:
|
|||
boolean matches(Class clazz);
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The `MethodMatcher` interface is normally more important. The complete interface follows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -77,7 +72,6 @@ The `MethodMatcher` interface is normally more important. The complete interface
|
|||
boolean matches(Method m, Class targetClass, Object[] args);
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
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
|
||||
|
@ -153,7 +147,6 @@ effectively the union of these pointcuts.)
|
|||
|
||||
The following example shows how to use `JdkRegexpMethodPointcut`:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim"]
|
||||
----
|
||||
|
@ -167,7 +160,6 @@ The following example shows how to use `JdkRegexpMethodPointcut`:
|
|||
</property>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
Spring provides a convenience class named `RegexpMethodPointcutAdvisor`, which lets us
|
||||
also reference an `Advice` (remember that an `Advice` can be an interceptor, before advice,
|
||||
|
@ -175,7 +167,6 @@ 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"]
|
||||
----
|
||||
|
@ -192,7 +183,6 @@ pointcut and advice, as the following example shows:
|
|||
</property>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
You can use `RegexpMethodPointcutAdvisor` with any `Advice` type.
|
||||
|
||||
|
@ -239,7 +229,6 @@ 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"]
|
||||
----
|
||||
|
@ -250,7 +239,6 @@ following example shows how to subclass `StaticMethodMatcherPointcut`:
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
There are also superclasses for dynamic pointcuts.
|
||||
|
||||
|
@ -313,7 +301,6 @@ 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"]
|
||||
----
|
||||
|
@ -322,7 +309,6 @@ following interface:
|
|||
Object invoke(MethodInvocation invocation) throws Throwable;
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
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
|
||||
|
@ -331,7 +317,6 @@ point.
|
|||
|
||||
The following example shows a simple `MethodInterceptor` implementation:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -345,7 +330,6 @@ The following example shows a simple `MethodInterceptor` implementation:
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Note the call to the `proceed()` method of `MethodInvocation`. This proceeds down the
|
||||
interceptor chain towards the join point. Most interceptors invoke this method and
|
||||
|
@ -374,7 +358,6 @@ interceptor chain.
|
|||
|
||||
The following listing shows the `MethodBeforeAdvice` interface:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -383,7 +366,6 @@ The following listing shows the `MethodBeforeAdvice` interface:
|
|||
void before(Method m, Object[] args, Object target) throws Throwable;
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
(Spring's API design would allow for
|
||||
field before advice, although the usual objects apply to field interception and it is
|
||||
|
@ -398,7 +380,6 @@ 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"]
|
||||
----
|
||||
|
@ -415,7 +396,6 @@ The following example shows a before advice in Spring, which counts all method i
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
TIP: Before advice can be used with any pointcut.
|
||||
|
||||
|
@ -429,13 +409,11 @@ 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"]
|
||||
----
|
||||
afterThrowing([Method, args, target], subclassOfThrowable)
|
||||
----
|
||||
====
|
||||
|
||||
Only the last argument is required. The method signatures may have either one or four
|
||||
arguments, depending on whether the advice method is interested in the method and
|
||||
|
@ -443,7 +421,6 @@ 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"]
|
||||
----
|
||||
|
@ -454,13 +431,11 @@ The following advice is invoked if a `RemoteException` is thrown (including from
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Unlike the preceding
|
||||
advice, the next example declares four arguments, so that it has access to the invoked method, method
|
||||
arguments, and target object. The following advice is invoked if a `ServletException` is thrown:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -471,13 +446,11 @@ arguments, and target object. The following advice is invoked if a `ServletExcep
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The final example illustrates how these two methods could be used in a single class
|
||||
that handles both `RemoteException` and `ServletException`. Any number of throws advice
|
||||
methods can be combined in a single class. The following listing shows the final example:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -492,7 +465,6 @@ methods can be combined in a single class. The following listing shows the final
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: If a throws-advice method throws an exception itself, it overrides the
|
||||
original exception (that is, it changes the exception thrown to the user). The overriding
|
||||
|
@ -511,7 +483,6 @@ 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"]
|
||||
----
|
||||
|
@ -521,7 +492,6 @@ An after returning advice in Spring must implement the
|
|||
throws Throwable;
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
An after returning advice has access to the return value (which it cannot modify),
|
||||
the invoked method, the method's arguments, and the target.
|
||||
|
@ -529,7 +499,6 @@ 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"]
|
||||
----
|
||||
|
@ -547,7 +516,6 @@ not thrown exceptions:
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
This advice does not change the execution path. If it throws an exception, it is
|
||||
thrown up the interceptor chain instead of the return value.
|
||||
|
@ -563,7 +531,6 @@ 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"]
|
||||
----
|
||||
|
@ -572,7 +539,6 @@ implement the following interface:
|
|||
boolean implementsInterface(Class intf);
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The `invoke()` method inherited from the AOP Alliance `MethodInterceptor` interface must
|
||||
implement the introduction. That is, if the invoked method is on an introduced
|
||||
|
@ -583,7 +549,6 @@ 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"]
|
||||
----
|
||||
|
@ -599,7 +564,6 @@ rather than the method, level. You can only use introduction advice with the
|
|||
Class[] getInterfaces();
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
There is no `MethodMatcher` and, hence, no `Pointcut` associated with introduction
|
||||
advice. Only class filtering is logical.
|
||||
|
@ -612,7 +576,6 @@ 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"]
|
||||
----
|
||||
|
@ -622,7 +585,6 @@ introduce the following interface to one or more objects:
|
|||
boolean locked();
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
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
|
||||
|
@ -659,7 +621,6 @@ to that held in the target object.
|
|||
|
||||
The following example shows the example `LockMixin` class:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -688,7 +649,6 @@ The following example shows the example `LockMixin` class:
|
|||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Often, you need not override the `invoke()` method. The
|
||||
`DelegatingIntroductionInterceptor` implementation (which calls the `delegate` method if
|
||||
|
@ -703,7 +663,6 @@ 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"]
|
||||
----
|
||||
|
@ -714,7 +673,6 @@ The following example shows our `LockMixinAdvisor` class:
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
We can apply this advisor very simply, because it requires no configuration. (However, it
|
||||
is impossible to use an `IntroductionInterceptor` without an
|
||||
|
@ -904,7 +862,6 @@ Consider a simple example of `ProxyFactoryBean` in action. This example involves
|
|||
|
||||
The following listing shows the example:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -933,7 +890,6 @@ The following listing shows the example:
|
|||
</property>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
Note that the `interceptorNames` property takes a list of `String`, which holds the bean names of the
|
||||
interceptors or advisors in the current factory. You can use advisors, interceptors, before, after
|
||||
|
@ -948,18 +904,15 @@ 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"]
|
||||
----
|
||||
Person person = (Person) factory.getBean("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"]
|
||||
----
|
||||
|
@ -967,7 +920,6 @@ with an ordinary Java object. The following example shows how to do so:
|
|||
<property name="person"><ref bean="person"/></property>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
The `PersonUser` class in this example exposes a property of type `Person`. As far as
|
||||
it is concerned, the AOP proxy can be used transparently in place of a "`real`" person
|
||||
|
@ -979,7 +931,6 @@ 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"]
|
||||
----
|
||||
|
@ -1006,7 +957,6 @@ anonymous inner bean:
|
|||
</property>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
Using an anonymous inner bean has the advantage that there is only one object of type `Person`. This is useful if we want
|
||||
to prevent users of the application context from obtaining a reference to the un-advised
|
||||
|
@ -1060,7 +1010,6 @@ 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"]
|
||||
----
|
||||
|
@ -1076,7 +1025,6 @@ two global advisors:
|
|||
<bean id="global_debug" class="org.springframework.aop.interceptor.DebugInterceptor"/>
|
||||
<bean id="global_performance" class="org.springframework.aop.interceptor.PerformanceMonitorInterceptor"/>
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -1090,7 +1038,6 @@ 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"]
|
||||
----
|
||||
|
@ -1104,14 +1051,12 @@ First, we create a parent, template, bean definition for the proxy, as follows:
|
|||
</property>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
This is never instantiated itself, so it can actually be incomplete. Then, each proxy
|
||||
that needs to be created is a child bean definition, which wraps the target of the
|
||||
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"]
|
||||
----
|
||||
|
@ -1122,12 +1067,10 @@ The following example shows such a child bean:
|
|||
</property>
|
||||
</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"]
|
||||
----
|
||||
|
@ -1146,7 +1089,6 @@ we override the transaction propagation settings:
|
|||
</property>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
Note that in the parent bean example, we explicitly marked the parent bean definition as
|
||||
being abstract by setting the `abstract` attribute to `true`, as described
|
||||
|
@ -1171,7 +1113,6 @@ 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"]
|
||||
----
|
||||
|
@ -1180,7 +1121,6 @@ interceptor and one advisor:
|
|||
factory.addAdvisor(myAdvisor);
|
||||
MyBusinessInterface tb = (MyBusinessInterface) factory.getProxy();
|
||||
----
|
||||
====
|
||||
|
||||
The first step is to construct an object of type
|
||||
`org.springframework.aop.framework.ProxyFactory`. You can create this with a target
|
||||
|
@ -1211,7 +1151,6 @@ 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"]
|
||||
----
|
||||
|
@ -1235,7 +1174,6 @@ following methods:
|
|||
|
||||
boolean isFrozen();
|
||||
----
|
||||
====
|
||||
|
||||
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
|
||||
|
@ -1257,7 +1195,6 @@ 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"]
|
||||
----
|
||||
|
@ -1276,7 +1213,6 @@ manipulating its advice:
|
|||
|
||||
assertEquals("Added two advisors", oldAdvisorCount + 2, advised.getAdvisors().length);
|
||||
----
|
||||
====
|
||||
|
||||
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.
|
||||
|
@ -1333,7 +1269,6 @@ 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"]
|
||||
----
|
||||
|
@ -1346,7 +1281,6 @@ example shows how to create a `BeanNameAutoProxyCreator` bean:
|
|||
</property>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
As with `ProxyFactoryBean`, there is an `interceptorNames` property rather than a list
|
||||
of interceptors, to allow correct behavior for prototype advisors. Named "`interceptors`"
|
||||
|
@ -1398,7 +1332,6 @@ 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"]
|
||||
----
|
||||
|
@ -1416,7 +1349,6 @@ elements discussed in this section:
|
|||
|
||||
<bean id="businessObject2" class="com.mycompany.BusinessObject2"/>
|
||||
----
|
||||
====
|
||||
|
||||
The `DefaultAdvisorAutoProxyCreator` is very useful if you want to apply the same advice
|
||||
consistently to many business objects. Once the infrastructure definitions are in place,
|
||||
|
@ -1470,18 +1402,15 @@ 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"]
|
||||
----
|
||||
HotSwappableTargetSource swapper = (HotSwappableTargetSource) beanFactory.getBean("swapper");
|
||||
Object oldTarget = swapper.swap(newTarget);
|
||||
----
|
||||
====
|
||||
|
||||
The following example shows the required XML definitions:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1495,7 +1424,6 @@ The following example shows the required XML definitions:
|
|||
<property name="targetSource" ref="swapper"/>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
The preceding `swap()` call changes the target of the swappable bean. Clients that hold a
|
||||
reference to that bean are unaware of the change but immediately start hitting
|
||||
|
@ -1528,7 +1456,6 @@ 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"]
|
||||
----
|
||||
|
@ -1547,7 +1474,6 @@ The following listing shows an example configuration:
|
|||
<property name="interceptorNames" value="myInterceptor"/>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
Note that the target object (`businessObjectTarget` in the preceding example) must be a
|
||||
prototype. This lets the `PoolingTargetSource` implementation create new instances
|
||||
|
@ -1565,7 +1491,6 @@ 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"]
|
||||
----
|
||||
|
@ -1574,7 +1499,6 @@ need to define an advisor similar to the following:
|
|||
<property name="targetMethod" value="getPoolingConfigMixin"/>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
This advisor is obtained by calling a convenience method on the
|
||||
`AbstractPoolingTargetSource` class, hence the use of `MethodInvokingFactoryBean`. This
|
||||
|
@ -1583,14 +1507,12 @@ the `ProxyFactoryBean` that exposes the pooled object.
|
|||
|
||||
The cast is defined as follows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
PoolingConfig conf = (PoolingConfig) beanFactory.getBean("businessObject");
|
||||
System.out.println("Max pool size is " + conf.getMaxSize());
|
||||
----
|
||||
====
|
||||
|
||||
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
|
||||
|
@ -1613,7 +1535,6 @@ 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"]
|
||||
----
|
||||
|
@ -1621,7 +1542,6 @@ To do this, you could modify the `poolTargetSource` definition shown earlier as
|
|||
<property name="targetBeanName" ref="businessObjectTarget"/>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
The only property is the name of the target bean. Inheritance is used in the
|
||||
`TargetSource` implementations to ensure consistent naming. As with the pooling target
|
||||
|
@ -1638,7 +1558,6 @@ 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"]
|
||||
----
|
||||
|
@ -1646,7 +1565,6 @@ of target source, as the following example shows:
|
|||
<property name="targetBeanName" value="businessObjectTarget"/>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: `ThreadLocal` instances come with serious issues (potentially resulting in memory leaks) when
|
||||
incorrectly using them in multi-threaded and multi-classloader environments. You
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -21,7 +21,6 @@ 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"]
|
||||
----
|
||||
|
@ -35,7 +34,6 @@ correct schema so that the tags in the `util` namespace are available to you):
|
|||
<!-- bean definitions here -->
|
||||
</beans>
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -44,7 +42,6 @@ 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"]
|
||||
----
|
||||
|
@ -55,7 +52,6 @@ Consider the following bean definition:
|
|||
</property>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
The preceding configuration uses a Spring `FactoryBean` implementation (the
|
||||
`FieldRetrievingFactoryBean`) to set the value of the `isolation` property on a bean
|
||||
|
@ -66,7 +62,6 @@ 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"]
|
||||
----
|
||||
|
@ -76,7 +71,6 @@ developer's intent ("`inject this constant value`"), and it reads better:
|
|||
</property>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -92,7 +86,6 @@ 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"]
|
||||
----
|
||||
|
@ -101,26 +94,22 @@ property:
|
|||
<property name="staticField" value="java.sql.Connection.TRANSACTION_SERIALIZABLE"/>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
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"]
|
||||
----
|
||||
<bean id="java.sql.Connection.TRANSACTION_SERIALIZABLE"
|
||||
class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"/>
|
||||
----
|
||||
====
|
||||
|
||||
This does mean that there is no longer any choice in what the bean `id` is (so any other
|
||||
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"]
|
||||
----
|
||||
|
@ -131,7 +120,6 @@ to be specified for the bean reference, as the following example shows:
|
|||
</property>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
You can also access a non-static (instance) field of another bean, as
|
||||
described in the API documentation for the
|
||||
|
@ -144,7 +132,6 @@ anything about 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"]
|
||||
----
|
||||
|
@ -156,11 +143,9 @@ enum value is:
|
|||
EXTENDED
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Now consider the following setter of type `PersistenceContextType` and the corresponding bean definition:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -183,7 +168,6 @@ Now consider the following setter of type `PersistenceContextType` and the corre
|
|||
<property name="persistenceContextType" value="TRANSACTION"/>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -192,7 +176,6 @@ Now consider the following setter of type `PersistenceContextType` and the corre
|
|||
|
||||
Consider the following example:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -209,7 +192,6 @@ Consider the following example:
|
|||
<!-- results in 10, which is the value of property 'age' of bean 'testBean' -->
|
||||
<bean id="testBean.age" class="org.springframework.beans.factory.config.PropertyPathFactoryBean"/>
|
||||
----
|
||||
====
|
||||
|
||||
The preceding configuration uses a Spring `FactoryBean` implementation (the
|
||||
`PropertyPathFactoryBean`) to create a bean (of type `int`) called `testBean.age` that
|
||||
|
@ -217,7 +199,6 @@ 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"]
|
||||
----
|
||||
|
@ -234,7 +215,6 @@ Now consider the following example, which adds a `<util:property-path/>` element
|
|||
<!-- results in 10, which is the value of property 'age' of bean 'testBean' -->
|
||||
<util:property-path id="name" path="testBean.age"/>
|
||||
----
|
||||
====
|
||||
|
||||
The value of the `path` attribute of the `<property-path/>` element follows the form of
|
||||
`beanName.beanProperty`. In this case, it picks up the `age` property of the bean named
|
||||
|
@ -250,7 +230,6 @@ argument.
|
|||
|
||||
The following example shows a path being used against another bean, by name:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -271,11 +250,9 @@ The following example shows a path being used against another bean, by name:
|
|||
<property name="propertyPath" value="spouse.age"/>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
In the following example, a path is evaluated against an inner bean:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -290,12 +267,10 @@ In the following example, a path is evaluated against an inner bean:
|
|||
<property name="propertyPath" value="age"/>
|
||||
</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"]
|
||||
----
|
||||
|
@ -303,13 +278,11 @@ The following example shows the shortcut form:
|
|||
<bean id="person.age"
|
||||
class="org.springframework.beans.factory.config.PropertyPathFactoryBean"/>
|
||||
----
|
||||
====
|
||||
|
||||
This form does mean that there is no choice in the name of the bean. Any reference to it
|
||||
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"]
|
||||
----
|
||||
|
@ -320,7 +293,6 @@ bean, there is no need to refer to it at all, as the following example shows:
|
|||
</property>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
You can specifically set the result type in the actual definition. This is not necessary
|
||||
for most use cases, but it can sometimes be useful. See the javadoc for more info on
|
||||
|
@ -332,7 +304,6 @@ this feature.
|
|||
|
||||
Consider the following example:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -341,7 +312,6 @@ Consider the following example:
|
|||
<property name="location" value="classpath:com/foo/jdbc-production.properties"/>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
The preceding configuration uses a Spring `FactoryBean` implementation (the
|
||||
`PropertiesFactoryBean`) to instantiate a `java.util.Properties` instance with values
|
||||
|
@ -349,14 +319,12 @@ 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"]
|
||||
----
|
||||
<!-- creates a java.util.Properties instance with values loaded from the supplied location -->
|
||||
<util:properties id="jdbcConfiguration" location="classpath:com/foo/jdbc-production.properties"/>
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -365,7 +333,6 @@ 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"]
|
||||
----
|
||||
|
@ -381,7 +348,6 @@ Consider the following example:
|
|||
</property>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
The preceding configuration uses a Spring `FactoryBean` implementation (the
|
||||
`ListFactoryBean`) to create a `java.util.List` instance and initialize it with values taken
|
||||
|
@ -389,7 +355,6 @@ 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"]
|
||||
----
|
||||
|
@ -401,14 +366,12 @@ The following example uses a `<util:list/>` element to make a more concise repre
|
|||
<value>porfiry@gov.org</value>
|
||||
</util:list>
|
||||
----
|
||||
====
|
||||
|
||||
You can also explicitly control the exact type of `List` that is instantiated and
|
||||
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"]
|
||||
----
|
||||
|
@ -419,7 +382,6 @@ following configuration:
|
|||
<value>d'Arcachon@nemesis.org</value>
|
||||
</util:list>
|
||||
----
|
||||
====
|
||||
|
||||
If no `list-class` attribute is supplied, the container chooses a `List` implementation.
|
||||
|
||||
|
@ -430,7 +392,6 @@ If no `list-class` attribute is supplied, the container chooses a `List` impleme
|
|||
|
||||
Consider the following example:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -446,7 +407,6 @@ Consider the following example:
|
|||
</property>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
The preceding configuration uses a Spring `FactoryBean` implementation (the
|
||||
`MapFactoryBean`) to create a `java.util.Map` instance initialized with key-value pairs
|
||||
|
@ -454,7 +414,6 @@ 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"]
|
||||
----
|
||||
|
@ -466,14 +425,12 @@ The following example uses a `<util:map/>` element to make a more concise repres
|
|||
<entry key="porfiry" value="porfiry@gov.org"/>
|
||||
</util:map>
|
||||
----
|
||||
====
|
||||
|
||||
You can also explicitly control the exact type of `Map` that is instantiated and
|
||||
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"]
|
||||
----
|
||||
|
@ -484,7 +441,6 @@ following configuration:
|
|||
<entry key="porfiry" value="porfiry@gov.org"/>
|
||||
</util:map>
|
||||
----
|
||||
====
|
||||
|
||||
If no `'map-class'` attribute is supplied, the container chooses a `Map` implementation.
|
||||
|
||||
|
@ -495,7 +451,6 @@ If no `'map-class'` attribute is supplied, the container chooses a `Map` impleme
|
|||
|
||||
Consider the following example:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -511,7 +466,6 @@ Consider the following example:
|
|||
</property>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
The preceding configuration uses a Spring `FactoryBean` implementation (the
|
||||
`SetFactoryBean`) to create a `java.util.Set` instance initialized with values taken
|
||||
|
@ -519,7 +473,6 @@ 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"]
|
||||
----
|
||||
|
@ -531,14 +484,12 @@ The following example uses a `<util:set/>` element to make a more concise repres
|
|||
<value>porfiry@gov.org</value>
|
||||
</util:set>
|
||||
----
|
||||
====
|
||||
|
||||
You can also explicitly control the exact type of `Set` that is instantiated and
|
||||
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"]
|
||||
----
|
||||
|
@ -549,7 +500,6 @@ following configuration:
|
|||
<value>porfiry@gov.org</value>
|
||||
</util:set>
|
||||
----
|
||||
====
|
||||
|
||||
If no `set-class` attribute is supplied, the container chooses a `Set` implementation.
|
||||
|
||||
|
@ -568,7 +518,6 @@ 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"]
|
||||
----
|
||||
|
@ -582,7 +531,6 @@ are available to you):
|
|||
<!-- bean definitions here -->
|
||||
</beans>
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -595,7 +543,6 @@ 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"]
|
||||
----
|
||||
|
@ -609,7 +556,6 @@ available to you:
|
|||
<!-- bean definitions here -->
|
||||
</beans>
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -696,7 +642,6 @@ 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"]
|
||||
----
|
||||
|
@ -714,7 +659,7 @@ as it stands).
|
|||
</beans>
|
||||
----
|
||||
<1> This is the example `meta` element
|
||||
====
|
||||
|
||||
|
||||
In the case of the preceding example, you could assume that there is some logic that
|
||||
consumes the bean definition and sets up some caching infrastructure that uses the supplied
|
||||
|
@ -749,7 +694,6 @@ XML extension (a custom XML element) that lets us configure objects of the type
|
|||
`SimpleDateFormat` (from the `java.text` package). When we are done,
|
||||
we will be able to define bean definitions of type `SimpleDateFormat` as follows:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -757,7 +701,6 @@ we will be able to define bean definitions of type `SimpleDateFormat` as follows
|
|||
pattern="yyyy-MM-dd HH:mm"
|
||||
lenient="true"/>
|
||||
----
|
||||
====
|
||||
|
||||
(We include much more detailed
|
||||
examples follow later in this appendix. The intent of this first simple example is to walk you
|
||||
|
@ -772,7 +715,6 @@ 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"]
|
||||
----
|
||||
|
@ -804,14 +746,12 @@ to configure `SimpleDateFormat` objects:
|
|||
(meaning they have an `id` attribute that we can use as the bean identifier in the
|
||||
container). We can use this attribute because we imported the Spring-provided
|
||||
`beans` namespace.
|
||||
====
|
||||
|
||||
|
||||
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"]
|
||||
----
|
||||
|
@ -819,12 +759,10 @@ example shows:
|
|||
pattern="yyyy-MM-dd HH:mm"
|
||||
lenient="true"/>
|
||||
----
|
||||
====
|
||||
|
||||
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"]
|
||||
----
|
||||
|
@ -833,7 +771,6 @@ essentially the same as the following XML snippet:
|
|||
<property name="lenient" value="true"/>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
The second of the two preceding snippets
|
||||
creates a bean in the container (identified by the name `dateFormat` of type
|
||||
|
@ -876,7 +813,6 @@ 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"]
|
||||
----
|
||||
|
@ -892,7 +828,6 @@ use the `NamespaceHandlerSupport` class:
|
|||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
You may notice that there is not actually a whole lot of parsing logic
|
||||
in this class. Indeed, the `NamespaceHandlerSupport` class has a built-in notion of
|
||||
|
@ -916,7 +851,6 @@ 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]
|
||||
----
|
||||
package org.springframework.samples.xml;
|
||||
|
@ -954,7 +888,7 @@ the basic grunt work of creating a single `BeanDefinition`.
|
|||
|
||||
<2> We supply the `AbstractSingleBeanDefinitionParser` superclass with the type that our
|
||||
single `BeanDefinition` represents.
|
||||
====
|
||||
|
||||
|
||||
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
|
||||
|
@ -980,13 +914,11 @@ 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"]
|
||||
----
|
||||
http\://www.mycompany.com/schema/myns=org.springframework.samples.xml.MyNamespaceHandler
|
||||
----
|
||||
====
|
||||
|
||||
(The `:` character is a valid delimiter in the Java properties format, so
|
||||
`:` character in the URI needs to be escaped with a backslash.)
|
||||
|
@ -1009,13 +941,11 @@ 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"]
|
||||
----
|
||||
http\://www.mycompany.com/schema/myns/myns.xsd=org/springframework/samples/xml/myns.xsd
|
||||
----
|
||||
====
|
||||
|
||||
(Remember that the `:` character must be escaped.)
|
||||
|
||||
|
@ -1032,7 +962,6 @@ 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"]
|
||||
----
|
||||
|
@ -1057,7 +986,6 @@ in a Spring XML configuration file:
|
|||
</beans>
|
||||
----
|
||||
<1> Our custom bean.
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -1074,7 +1002,6 @@ 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"]
|
||||
----
|
||||
|
@ -1096,7 +1023,6 @@ to satisfy a target of the following configuration:
|
|||
|
||||
</beans>
|
||||
----
|
||||
====
|
||||
|
||||
The preceding configuration nests custom extensions within each other. The class
|
||||
that is actually configured by the `<foo:component/>` element is the `Component`
|
||||
|
@ -1105,7 +1031,6 @@ 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"]
|
||||
----
|
||||
|
@ -1138,12 +1063,11 @@ The following listing shows the `Component` class:
|
|||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The typical solution to this issue is to create a custom `FactoryBean` that exposes a
|
||||
setter property for the `components` property. The following listing shows such a custom
|
||||
`FactoryBean`:
|
||||
====
|
||||
|
||||
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
|
@ -1186,7 +1110,6 @@ setter property for the `components` property. The following listing shows such
|
|||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
This works nicely, but it exposes a lot of Spring plumbing to the
|
||||
end user. What we are going to do is write a custom extension that hides away all of
|
||||
|
@ -1194,7 +1117,6 @@ this Spring plumbing. If we stick to <<xsd-custom-introduction,the steps describ
|
|||
previously>>, we start off 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"]
|
||||
----
|
||||
|
@ -1218,11 +1140,9 @@ custom tag, as the following listing shows:
|
|||
|
||||
</xsd:schema>
|
||||
----
|
||||
====
|
||||
|
||||
Again following <<xsd-custom-introduction,the process described earlier>>, we then create a custom `NamespaceHandler`:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1238,13 +1158,11 @@ Again following <<xsd-custom-introduction,the process described earlier>>, we th
|
|||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Next up is the custom `BeanDefinitionParser`. Remember that we are creating
|
||||
`BeanDefinition` that describes a `ComponentFactoryBean`. The following listing shows our
|
||||
custom `BeanDefinitionParser`:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1295,12 +1213,10 @@ custom `BeanDefinitionParser`:
|
|||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
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"]
|
||||
----
|
||||
|
@ -1314,7 +1230,6 @@ http\://www.foo.com/schema/component=com.foo.ComponentNamespaceHandler
|
|||
# in 'META-INF/spring.schemas'
|
||||
http\://www.foo.com/schema/component/component.xsd=com/foo/component.xsd
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -1333,7 +1248,6 @@ http://jcp.org/en/jsr/detail?id=107[JCache], and you want to ensure that the nam
|
|||
JCache instance is eagerly started within the surrounding cluster. The following
|
||||
listing shows such a definition:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1342,7 +1256,6 @@ listing shows such a definition:
|
|||
<!-- other dependencies here... -->
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
We can then create another `BeanDefinition` when the
|
||||
`'jcache:cache-name'` attribute is parsed. This `BeanDefinition` then initializes
|
||||
|
@ -1350,7 +1263,6 @@ 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"]
|
||||
----
|
||||
|
@ -1370,12 +1282,10 @@ JCache-initializing `BeanDefinition`. The following listing shows our `JCacheIni
|
|||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Now we can move onto the custom extension. First, we need to author the XSD schema that describes the
|
||||
custom attribute, as follows:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1390,11 +1300,9 @@ custom attribute, as follows:
|
|||
|
||||
</xsd:schema>
|
||||
----
|
||||
====
|
||||
|
||||
Next, we need to create the associated `NamespaceHandler`, as follows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1411,13 +1319,11 @@ Next, we need to create the associated `NamespaceHandler`, as follows:
|
|||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Next, we need to create the parser. Note that, in this case, because we are going to parse an XML
|
||||
attribute, we write a `BeanDefinitionDecorator` rather than a `BeanDefinitionParser`.
|
||||
The following listing shows our `BeanDefinitionDecorator`:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1473,12 +1379,10 @@ The following listing shows our `BeanDefinitionDecorator`:
|
|||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
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"]
|
||||
----
|
||||
|
@ -1492,4 +1396,3 @@ http\://www.foo.com/schema/jcache=com.foo.JCacheNamespaceHandler
|
|||
# in 'META-INF/spring.schemas'
|
||||
http\://www.foo.com/schema/jcache/jcache.xsd=com/foo/jcache.xsd
|
||||
----
|
||||
====
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -141,7 +141,6 @@ 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"]
|
||||
----
|
||||
|
@ -158,7 +157,6 @@ a serialization error occurs while populating the buffer with data. For example:
|
|||
}
|
||||
return buffer;
|
||||
----
|
||||
====
|
||||
|
||||
The consumer of an `Encoder` is responsible for releasing the data buffers it receives.
|
||||
In a WebFlux application, the output of the `Encoder` is used to write to the HTTP server
|
||||
|
|
|
@ -65,7 +65,6 @@ The complete language reference can be found in
|
|||
The following code introduces the SpEL API to evaluate the literal string expression,
|
||||
`Hello World`.
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -74,7 +73,7 @@ The following code introduces the SpEL API to evaluate the literal string expres
|
|||
String message = (String) exp.getValue();
|
||||
----
|
||||
<1> The value of the message variable is `'Hello World'`.
|
||||
====
|
||||
|
||||
|
||||
The SpEL classes and interfaces you are most likely to use are located in the
|
||||
`org.springframework.expression` package and its sub-packages, such as `spel.support`.
|
||||
|
@ -91,7 +90,6 @@ and calling constructors.
|
|||
|
||||
In the following example of method invocation, we call the `concat` method on the string literal:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -100,11 +98,10 @@ In the following example of method invocation, we call the `concat` method on th
|
|||
String message = (String) exp.getValue();
|
||||
----
|
||||
<1> The value of `message` is now 'Hello World!'.
|
||||
====
|
||||
|
||||
|
||||
The following example of calling a JavaBean property calls the `String` property `Bytes`:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -115,13 +112,12 @@ The following example of calling a JavaBean property calls the `String` property
|
|||
byte[] bytes = (byte[]) exp.getValue();
|
||||
----
|
||||
<1> This line converts the literal to a byte array.
|
||||
====
|
||||
|
||||
|
||||
SpEL also supports nested properties by using standard dot notation (such as
|
||||
`prop1.prop2.prop3`) and the setting of property values. Public fields may also be accessed.
|
||||
The following example shows how to use dot notation to get the length of a literal:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -132,12 +128,11 @@ The following example shows how to use dot notation to get the length of a liter
|
|||
int length = (Integer) exp.getValue();
|
||||
----
|
||||
<1> `'Hello World'.bytes.length` gives the length of the literal.
|
||||
====
|
||||
|
||||
|
||||
The String's constructor can be called instead of using a string literal, as the following
|
||||
example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -146,7 +141,7 @@ example shows:
|
|||
String message = exp.getValue(String.class);
|
||||
----
|
||||
<1> Construct a new `String` from the literal and make it be upper case.
|
||||
====
|
||||
|
||||
|
||||
Note the use of the generic method: `public <T> T getValue(Class<T> desiredResultType)`.
|
||||
Using this method removes the need to cast the value of the expression to the desired
|
||||
|
@ -158,7 +153,6 @@ against a specific object instance (called the root object). The following examp
|
|||
how to retrieve the `name` property from an instance of the `Inventor` class or
|
||||
create a boolean condition:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -180,7 +174,6 @@ create a boolean condition:
|
|||
// result == true
|
||||
----
|
||||
<1> Parse `name` as an expression.
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -228,7 +221,6 @@ to set a `List` property. The type of the property is actually `List<Boolean>`.
|
|||
recognizes that the elements of the list need to be converted to `Boolean` before
|
||||
being placed in it. The following example shows how to do so:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -248,7 +240,6 @@ being placed in it. The following example shows how to do so:
|
|||
// b is false
|
||||
Boolean b = simple.booleanList.get(0);
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -265,7 +256,6 @@ and specifying an index that is beyond the end of the current size of the array
|
|||
list, you can automatically grow the array or list to accommodate that index. The following
|
||||
example demonstrates how to automatically grow the list:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -289,7 +279,6 @@ example demonstrates how to automatically grow the list:
|
|||
// demo.list will now be a real collection of 4 entries
|
||||
// Each entry is a new empty String
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -317,11 +306,9 @@ on repeated evaluations.
|
|||
|
||||
Consider the following basic expression:
|
||||
|
||||
====
|
||||
----
|
||||
someArray[0].someProperty.someOtherProperty < 0.1
|
||||
----
|
||||
====
|
||||
|
||||
Because the preceding expression involves array access, some property de-referencing,
|
||||
and numeric operations, the performance gain can be very noticeable. In an example
|
||||
|
@ -361,7 +348,6 @@ since part of the expression may be running twice.
|
|||
After selecting a mode, use the `SpelParserConfiguration` to configure the parser. The
|
||||
following example shows how to do so:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -376,7 +362,6 @@ following example shows how to do so:
|
|||
|
||||
Object payload = expr.getValue(message);
|
||||
----
|
||||
====
|
||||
|
||||
When you specify the compiler mode, you can also specify a classloader (passing null is allowed).
|
||||
Compiled expressions are defined in a child classloader created under any that is supplied.
|
||||
|
@ -425,7 +410,6 @@ form `#{ <expression string> }`.
|
|||
A property or constructor argument value can be set by using expressions, as the following
|
||||
example shows:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim"]
|
||||
----
|
||||
|
@ -435,12 +419,10 @@ example shows:
|
|||
<!-- other properties -->
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
The `systemProperties` variable is predefined, so you can use it in your expressions, as
|
||||
the following example shows:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim"]
|
||||
----
|
||||
|
@ -450,14 +432,12 @@ the following example shows:
|
|||
<!-- other properties -->
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
Note that you do not have to prefix the predefined variable with the `#`
|
||||
symbol in this context.
|
||||
|
||||
You can also refer to other bean properties by name, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim"]
|
||||
----
|
||||
|
@ -473,7 +453,6 @@ You can also refer to other bean properties by name, as the following example sh
|
|||
<!-- other properties -->
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -485,7 +464,6 @@ parameters.
|
|||
|
||||
The following example sets the default value of a field variable:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -504,11 +482,9 @@ The following example sets the default value of a field variable:
|
|||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The following example shows the equivalent but on a property setter method:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -527,12 +503,10 @@ The following example shows the equivalent but on a property setter method:
|
|||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Autowired methods and constructors can also use the `@Value` annotation, as the following
|
||||
examples show:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -571,7 +545,6 @@ examples show:
|
|||
// ...
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -611,7 +584,6 @@ The following listing shows simple usage of literals. Typically, they are not us
|
|||
in isolation like this but, rather, as part of a more complex expression -- for example,
|
||||
using a literal on one side of a logical comparison operator.
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -629,7 +601,6 @@ using a literal on one side of a logical comparison operator.
|
|||
|
||||
Object nullValue = parser.parseExpression("null").getValue();
|
||||
----
|
||||
====
|
||||
|
||||
Numbers support the use of the negative sign, exponential notation, and decimal points.
|
||||
By default, real numbers are parsed by using Double.parseDouble().
|
||||
|
@ -645,7 +616,6 @@ data listed in the <<expressions-example-classes,Classes used in the examples>>
|
|||
To navigate "`down`" and get Tesla's year of birth and Pupin's city of birth, we use the following
|
||||
expressions:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -654,13 +624,11 @@ expressions:
|
|||
|
||||
String city = (String) parser.parseExpression("placeOfBirth.City").getValue(context);
|
||||
----
|
||||
====
|
||||
|
||||
Case insensitivity is allowed for the first letter of property names. The contents of
|
||||
arrays and lists are obtained by using square bracket notation, as the following example
|
||||
shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -684,13 +652,11 @@ shows:
|
|||
String invention = parser.parseExpression("Members[0].Inventions[6]").getValue(
|
||||
context, ieee, String.class);
|
||||
----
|
||||
====
|
||||
|
||||
The contents of maps are obtained by specifying the literal key value within the
|
||||
brackets. In the following example, because keys for the `Officers` map are strings, we can specify
|
||||
string literals:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -707,7 +673,6 @@ string literals:
|
|||
parser.parseExpression("Officers['advisors'][0].PlaceOfBirth.Country").setValue(
|
||||
societyContext, "Croatia");
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -716,7 +681,6 @@ string literals:
|
|||
|
||||
You can directly express lists in an expression by using `{}` notation.
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -725,7 +689,6 @@ You can directly express lists in an expression by using `{}` notation.
|
|||
|
||||
List listOfLists = (List) parser.parseExpression("{{'a','b'},{'x','y'}}").getValue(context);
|
||||
----
|
||||
====
|
||||
|
||||
`{}` by itself means an empty list. For performance reasons, if the list is itself
|
||||
entirely composed of fixed literals, a constant list is created to represent the
|
||||
|
@ -739,7 +702,6 @@ expression (rather than building a new list on each evaluation).
|
|||
You can also directly express maps in an expression by using `{key:value}` notation. The
|
||||
following example shows how to do so:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -748,7 +710,6 @@ following example shows how to do so:
|
|||
|
||||
Map mapOfMaps = (Map) parser.parseExpression("{name:{first:'Nikola',last:'Tesla'},dob:{day:10,month:'July',year:1856}}").getValue(context);
|
||||
----
|
||||
====
|
||||
|
||||
`{:}` by itself means an empty map. For performance reasons, if the map is itself composed
|
||||
of fixed literals or other nested constant structures (lists or maps), a constant map is created
|
||||
|
@ -763,7 +724,6 @@ is optional. The examples above do not use quoted keys.
|
|||
You can build arrays by using the familiar Java syntax, optionally supplying an initializer
|
||||
to have the array populated at construction time. The following example shows how to do so:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -775,7 +735,6 @@ to have the array populated at construction time. The following example shows ho
|
|||
// Multi dimensional array
|
||||
int[][] numbers3 = (int[][]) parser.parseExpression("new int[4][5]").getValue(context);
|
||||
----
|
||||
====
|
||||
|
||||
You cannot currently supply an initializer when you construct
|
||||
multi-dimensional array.
|
||||
|
@ -789,7 +748,6 @@ You can invoke methods by using typical Java programming syntax. You can also in
|
|||
on literals. Variable arguments are also supported. The following examples show how to
|
||||
invoke methods:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -800,7 +758,6 @@ invoke methods:
|
|||
boolean isMember = parser.parseExpression("isMember('Mihajlo Pupin')").getValue(
|
||||
societyContext, Boolean.class);
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -822,7 +779,6 @@ The relational operators (equal, not equal, less than, less than or equal, great
|
|||
and greater than or equal) are supported by using standard operator notation. The
|
||||
following listing shows a few examples of operators:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -835,7 +791,6 @@ following listing shows a few examples of operators:
|
|||
// evaluates to true
|
||||
boolean trueValue = parser.parseExpression("'black' < 'block'").getValue(Boolean.class);
|
||||
----
|
||||
====
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
|
@ -851,7 +806,6 @@ in favor of comparisons against zero (for example, `X > 0` or `X < 0`).
|
|||
In addition to the standard relational operators, SpEL supports the `instanceof` and regular
|
||||
expression-based `matches` operator. The following listing shows examples of both:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -867,7 +821,6 @@ expression-based `matches` operator. The following listing shows examples of bot
|
|||
boolean falseValue = parser.parseExpression(
|
||||
"'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);
|
||||
----
|
||||
====
|
||||
|
||||
CAUTION: Be careful with primitive types, as they are immediately boxed up to the wrapper type,
|
||||
so `1 instanceof T(int)` evaluates to `false` while `1 instanceof T(Integer)`
|
||||
|
@ -901,7 +854,6 @@ SpEL supports the following logical operators:
|
|||
|
||||
The following example shows how to use the logical operators
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -932,7 +884,6 @@ The following example shows how to use the logical operators
|
|||
String expression = "isMember('Nikola Tesla') and !isMember('Mihajlo Pupin')";
|
||||
boolean falseValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
[[expressions-operators-mathematical]]
|
||||
|
@ -943,7 +894,6 @@ and division operators only on numbers. You can also use
|
|||
the modulus (%) and exponential power (^) operators. Standard operator precedence is enforced. The
|
||||
following example shows the mathematical operators in use:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -976,7 +926,6 @@ following example shows the mathematical operators in use:
|
|||
// Operator precedence
|
||||
int minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Integer.class); // -21
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
[[expressions-assignment]]
|
||||
|
@ -986,7 +935,6 @@ To setting a property, use the assignment operator (`=`). This is typically
|
|||
done within a call to `setValue` but can also be done inside a call to `getValue`. The
|
||||
following listing shows both ways to use the assignment operator:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -999,7 +947,6 @@ following listing shows both ways to use the assignment operator:
|
|||
String aleks = parser.parseExpression(
|
||||
"Name = 'Aleksandar Seovic'").getValue(context, inventor, String.class);
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -1014,7 +961,6 @@ type). Static methods are invoked by using this operator as well. The
|
|||
fully qualified, but all other type references must be. The following example shows how
|
||||
to use the `T` operator:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1026,7 +972,6 @@ to use the `T` operator:
|
|||
"T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR")
|
||||
.getValue(Boolean.class);
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -1037,7 +982,6 @@ You can invoke constructors by using the `new` operator. You should use the full
|
|||
for all but the primitive types (`int`, `float`, and so on) and String. The following
|
||||
example shows how to use the `new` operator to invoke constructors:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1050,7 +994,6 @@ example shows how to use the `new` operator to invoke constructors:
|
|||
"Members.add(new org.spring.samples.spel.inventor.Inventor(
|
||||
'Albert Einstein', 'German'))").getValue(societyContext);
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -1061,7 +1004,6 @@ You can reference variables in the expression by using the `#variableName` synta
|
|||
are set by using the `setVariable` method on `EvaluationContext` implementations. The
|
||||
following example shows how to use variables:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1073,7 +1015,6 @@ following example shows how to use variables:
|
|||
parser.parseExpression("Name = #newName").getValue(context, tesla);
|
||||
System.out.println(tesla.getName()) // "Mike Tesla"
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
[[expressions-this-root]]
|
||||
|
@ -1085,7 +1026,6 @@ defined and refers to the root context object. Although `#this` may vary as comp
|
|||
an expression are evaluated, `#root` always refers to the root. The following examples
|
||||
show how to use the `#this` and `#root` variables:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1103,7 +1043,6 @@ show how to use the `#this` and `#root` variables:
|
|||
List<Integer> primesGreaterThanTen = (List<Integer>) parser.parseExpression(
|
||||
"#primes.?[#this>10]").getValue(context);
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -1114,7 +1053,6 @@ You can extend SpEL by registering user-defined functions that can be called wit
|
|||
expression string. The function is registered through the `EvaluationContext`. The
|
||||
following example shows how to register a user-defined function:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1123,11 +1061,9 @@ following example shows how to register a user-defined function:
|
|||
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
|
||||
context.setVariable("myFunction", method);
|
||||
----
|
||||
====
|
||||
|
||||
For example, consider the following utility method that reverses a string:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1142,11 +1078,9 @@ For example, consider the following utility method that reverses a string:
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
You can then register and use the preceding method, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1159,7 +1093,6 @@ You can then register and use the preceding method, as the following example sho
|
|||
String helloWorldReversed = parser.parseExpression(
|
||||
"#reverseString('hello')").getValue(context, String.class);
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -1170,7 +1103,6 @@ If the evaluation context has been configured with a bean resolver, you can
|
|||
look up beans from an expression by using the `@` symbol. The following example shows how
|
||||
to do so:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1181,12 +1113,10 @@ to do so:
|
|||
// This will end up calling resolve(context,"something") on MyBeanResolver during evaluation
|
||||
Object bean = parser.parseExpression("@something").getValue(context);
|
||||
----
|
||||
====
|
||||
|
||||
To access a factory bean itself, you should instead prefix the bean name with an `&` symbol.
|
||||
The following example shows how to do so:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1197,7 +1127,6 @@ The following example shows how to do so:
|
|||
// This will end up calling resolve(context,"&foo") on MyBeanResolver during evaluation
|
||||
Object bean = parser.parseExpression("&foo").getValue(context);
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -1207,19 +1136,16 @@ The following example shows how to do so:
|
|||
You can use the ternary operator for performing if-then-else conditional logic inside
|
||||
the expression. The following listing shows a minimal example:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
String falseString = parser.parseExpression(
|
||||
"false ? 'trueExp' : 'falseExp'").getValue(String.class);
|
||||
----
|
||||
====
|
||||
|
||||
In this case, the boolean `false` results in returning the string value `'falseExp'`. A more
|
||||
realistic example follows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1233,7 +1159,6 @@ realistic example follows:
|
|||
.getValue(societyContext, String.class);
|
||||
// queryResultString = "Nikola Tesla is a member of the IEEE Society"
|
||||
----
|
||||
====
|
||||
|
||||
See the next section on the Elvis operator for an even shorter syntax for the
|
||||
ternary operator.
|
||||
|
@ -1248,19 +1173,16 @@ http://www.groovy-lang.org/operators.html#_elvis_operator[Groovy] language.
|
|||
With the ternary operator syntax, you usually have to repeat a variable twice, as the
|
||||
following example shows:
|
||||
|
||||
====
|
||||
[source,groovy,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
String name = "Elvis Presley";
|
||||
String displayName = (name != null ? name : "Unknown");
|
||||
----
|
||||
====
|
||||
|
||||
Instead, you can use the Elvis operator (named for the resemblance to Elvis' hair style).
|
||||
The following example shows how to use the Elvis operator:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1269,11 +1191,9 @@ The following example shows how to use the Elvis operator:
|
|||
String name = parser.parseExpression("name?:'Unknown'").getValue(String.class);
|
||||
System.out.println(name); // 'Unknown'
|
||||
----
|
||||
====
|
||||
|
||||
The following listing shows A more complex example:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1288,14 +1208,12 @@ The following listing shows A more complex example:
|
|||
name = parser.parseExpression("Name?:'Elvis Presley'").getValue(context, tesla, String.class);
|
||||
System.out.println(name); // Elvis Presley
|
||||
----
|
||||
====
|
||||
|
||||
[NOTE]
|
||||
=====
|
||||
You can use the Elvis operator to apply default values in expressions. The folloiwng
|
||||
example shows how to use the Elvis operator in a `@Value` expression:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1303,11 +1221,9 @@ example shows how to use the Elvis operator in a `@Value` expression:
|
|||
----
|
||||
|
||||
This will inject a system property `pop3.port` if it is defined or 25 if not.
|
||||
====
|
||||
=====
|
||||
|
||||
|
||||
|
||||
[[expressions-operator-safe-navigation]]
|
||||
=== Safe Navigation Operator
|
||||
|
||||
|
@ -1318,7 +1234,6 @@ it is not null before accessing methods or properties of the object. To avoid th
|
|||
safe navigation operator returns null instead of throwing an exception. The following
|
||||
example shows how to use the safe navigation operator:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1335,7 +1250,6 @@ example shows how to use the safe navigation operator:
|
|||
city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, tesla, String.class);
|
||||
System.out.println(city); // null - does not throw NullPointerException!!!
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -1349,14 +1263,12 @@ Selection uses a syntax of `.?[selectionExpression]`. It filters the collection
|
|||
returns a new collection that contain a subset of the original elements. For example,
|
||||
selection lets us easily get a list of Serbian inventors, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
List<Inventor> list = (List<Inventor>) parser.parseExpression(
|
||||
"Members.?[Nationality == 'Serbian']").getValue(societyContext);
|
||||
----
|
||||
====
|
||||
|
||||
Selection is possible upon both lists and maps. For a list, the selection
|
||||
criteria is evaluated against each individual list element. Against a map, the
|
||||
|
@ -1367,13 +1279,11 @@ the selection.
|
|||
The following expression returns a new map that consists of those elements of the original map
|
||||
where the entry value is less than 27:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
Map newMap = parser.parseExpression("map.?[value<27]").getValue();
|
||||
----
|
||||
====
|
||||
|
||||
In addition to returning all the selected elements, you can retrieve only the
|
||||
first or the last value. To obtain the first entry matching the selection, the syntax is
|
||||
|
@ -1391,14 +1301,12 @@ example, suppose we have a list of inventors but want the list of
|
|||
cities where they were born. Effectively, we want to evaluate 'placeOfBirth.city' for
|
||||
every entry in the inventor list. The following example uses projection to do so:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
// returns ['Smiljan', 'Idvor' ]
|
||||
List placesOfBirth = (List)parser.parseExpression("Members.![placeOfBirth.city]");
|
||||
----
|
||||
====
|
||||
|
||||
You can also use a map to drive projection and, in this case, the projection expression is
|
||||
evaluated against each entry in the map (represented as a Java `Map.Entry`). The result
|
||||
|
@ -1415,7 +1323,6 @@ Each evaluation block is delimited with prefix and suffix characters that you ca
|
|||
define. A common choice is to use `#{ }` as the delimiters, as the following example
|
||||
shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1425,7 +1332,6 @@ shows:
|
|||
|
||||
// evaluates to "random number is 0.7038186818312008"
|
||||
----
|
||||
====
|
||||
|
||||
The string is evaluated by concatenating the literal text `'random number is '` with the
|
||||
result of evaluating the expression inside the `#{ }` delimiter (in this case, the result
|
||||
|
@ -1434,7 +1340,6 @@ is of the type `ParserContext`. The `ParserContext` interface is used to influen
|
|||
the expression is parsed in order to support the expression templating functionality.
|
||||
The definition of `TemplateParserContext` follows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1453,7 +1358,6 @@ The definition of `TemplateParserContext` follows:
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -1464,7 +1368,6 @@ The definition of `TemplateParserContext` follows:
|
|||
This section lists the classes used in the examples throughout this chapter.
|
||||
|
||||
.Inventor.java
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1538,10 +1441,8 @@ This section lists the classes used in the examples throughout this chapter.
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
.PlaceOfBirth.java
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1579,10 +1480,8 @@ This section lists the classes used in the examples throughout this chapter.
|
|||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
.Society.java
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1627,4 +1526,3 @@ This section lists the classes used in the examples throughout this chapter.
|
|||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
|
|
@ -37,7 +37,6 @@ 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"]
|
||||
----
|
||||
|
@ -59,13 +58,11 @@ definition:
|
|||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
As the definition of the `Resource` interface shows, it extends the `InputStreamSource`
|
||||
interface. The following listing shows the definition of the `InputStreamSource`
|
||||
interface:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -75,7 +72,6 @@ interface:
|
|||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Some of the most important methods from the `Resource` interface are:
|
||||
|
||||
|
@ -227,7 +223,6 @@ 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"]
|
||||
----
|
||||
|
@ -237,7 +232,6 @@ interface definition:
|
|||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
All application contexts implement the `ResourceLoader` interface. Therefore, all
|
||||
application contexts may be used to obtain `Resource` instances.
|
||||
|
@ -247,13 +241,11 @@ 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"]
|
||||
----
|
||||
Resource 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
|
||||
|
@ -267,19 +259,16 @@ 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"]
|
||||
----
|
||||
Resource 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"]
|
||||
----
|
||||
|
@ -291,7 +280,6 @@ prefixes:
|
|||
----
|
||||
Resource template = ctx.getResource("http://myhost.com/resource/path/myTemplate.txt");
|
||||
----
|
||||
====
|
||||
|
||||
The following table summarizes the strategy for converting `String` objects to `Resource` objects:
|
||||
|
||||
|
@ -327,7 +315,6 @@ The `ResourceLoaderAware` interface is a special marker interface that identifie
|
|||
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"]
|
||||
----
|
||||
|
@ -336,7 +323,6 @@ the definition of the `ResourceLoaderAware` interface:
|
|||
void 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
|
||||
|
@ -381,7 +367,6 @@ 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"]
|
||||
----
|
||||
|
@ -389,7 +374,6 @@ be configured with a simple string for that resource, as the following example s
|
|||
<property name="template" value="some/resource/path/myTemplate.txt"/>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
Note that the resource path has no prefix. Consequently, because the application context itself is
|
||||
going to be used as the `ResourceLoader`, the resource itself is loaded through a
|
||||
|
@ -400,7 +384,6 @@ 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"]
|
||||
----
|
||||
|
@ -412,7 +395,6 @@ The following two examples show how to force a `ClassPathResource` and a
|
|||
----
|
||||
<property name="template" value="file:///some/resource/path/myTemplate.txt"/>
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -437,25 +419,21 @@ 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"]
|
||||
----
|
||||
ApplicationContext ctx = new 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"]
|
||||
----
|
||||
ApplicationContext ctx =
|
||||
new FileSystemXmlApplicationContext("conf/appContext.xml");
|
||||
----
|
||||
====
|
||||
|
||||
Now the bean definition is loaded from a filesystem location (in this case, relative to
|
||||
the current working directory).
|
||||
|
@ -487,7 +465,6 @@ then derives the path information from the supplied class.
|
|||
|
||||
Consider the following directory layout:
|
||||
|
||||
====
|
||||
[literal]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -497,19 +474,16 @@ com/
|
|||
daos.xml
|
||||
MessengerService.class
|
||||
----
|
||||
====
|
||||
|
||||
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"]
|
||||
----
|
||||
ApplicationContext ctx = new ClassPathXmlApplicationContext(
|
||||
new String[] {"services.xml", "daos.xml"}, MessengerService.class);
|
||||
----
|
||||
====
|
||||
|
||||
See the {api-spring-framework}/jca/context/SpringContextResourceAdapter.html[`ClassPathXmlApplicationContext`]
|
||||
javadoc for details on the various constructors.
|
||||
|
@ -542,7 +516,6 @@ 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"]
|
||||
----
|
||||
|
@ -551,7 +524,6 @@ com/mycompany/**/applicationContext.xml
|
|||
file:C:/some/path/*-context.xml
|
||||
classpath:com/mycompany/**/applicationContext.xml
|
||||
----
|
||||
====
|
||||
|
||||
When the path location contains an Ant-style pattern, the resolver follows a more complex procedure to try to resolve the
|
||||
wildcard. It produces a `Resource` for the path up to the last non-wildcard segment and
|
||||
|
@ -590,14 +562,12 @@ 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"]
|
||||
----
|
||||
ApplicationContext ctx =
|
||||
new 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
|
||||
|
@ -655,13 +625,11 @@ 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"]
|
||||
----
|
||||
com/mycompany/package1/service-context.xml
|
||||
----
|
||||
====
|
||||
|
||||
Now consider an Ant-style path that someone might use to try to find that file:
|
||||
|
||||
|
@ -695,7 +663,6 @@ 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"]
|
||||
----
|
||||
|
@ -709,12 +676,10 @@ In practice, this means the following examples are equivalent:
|
|||
ApplicationContext ctx =
|
||||
new 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"]
|
||||
----
|
||||
|
@ -728,14 +693,12 @@ case is relative and the other absolute):
|
|||
FileSystemXmlApplicationContext ctx = ...;
|
||||
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"]
|
||||
----
|
||||
|
@ -750,4 +713,3 @@ show how to do so:
|
|||
ApplicationContext ctx =
|
||||
new FileSystemXmlApplicationContext("file:///conf/context.xml");
|
||||
----
|
||||
====
|
||||
|
|
|
@ -53,7 +53,6 @@ validators can report validation failures to the `Errors` object.
|
|||
|
||||
Consider the following example of a small data object:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -65,7 +64,6 @@ Consider the following example of a small data object:
|
|||
// the usual getters and setters...
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The next example provides validation behavior for the `Person` class by implementing the
|
||||
following two methods of the `org.springframework.validation.Validator` interface:
|
||||
|
@ -78,7 +76,6 @@ Implementing a `Validator` is fairly straightforward, especially when you know o
|
|||
`ValidationUtils` helper class that the Spring Framework also provides. The following
|
||||
example implements `Validator` for `Person` instances:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim"]
|
||||
----
|
||||
|
@ -102,7 +99,6 @@ example implements `Validator` for `Person` instances:
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The `static` `rejectIfEmpty(..)` method on the `ValidationUtils` class is used to
|
||||
reject the `name` property if it is `null` or the empty string. Have a look at the
|
||||
|
@ -120,7 +116,6 @@ within the `AddressValidator` class without resorting to copy-and-paste, you can
|
|||
dependency-inject or instantiate an `AddressValidator` within your `CustomerValidator`,
|
||||
as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -160,7 +155,6 @@ as the following example shows:
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Validation errors are reported to the `Errors` object passed to the validator. In the case
|
||||
of Spring Web MVC, you can use the `<spring:bind/>` tag to inspect the error messages, but
|
||||
|
@ -265,7 +259,6 @@ and their default implementations, you should skip ahead to the <<beans-beans-co
|
|||
The following two example classes use the `BeanWrapper` to get and set
|
||||
properties:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -318,12 +311,10 @@ properties:
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The following code snippets show some examples of how to retrieve and manipulate some of
|
||||
the properties of instantiated `Companies` and `Employees`:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -342,7 +333,6 @@ the properties of instantiated `Companies` and `Employees`:
|
|||
// retrieving the salary of the managingDirector through the company
|
||||
Float salary = (Float) company.getPropertyValue("managingDirector.salary");
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -453,7 +443,6 @@ name as that class, with `Editor` appended. For example, one could have the foll
|
|||
class and package structure, which would be sufficient for the `SomethingEditor` class to be
|
||||
recognized and used as the `PropertyEditor` for `Something`-typed properties.
|
||||
|
||||
====
|
||||
[literal]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -463,7 +452,6 @@ com
|
|||
Something
|
||||
SomethingEditor // the PropertyEditor for the Something class
|
||||
----
|
||||
====
|
||||
|
||||
Note that you can also use the standard `BeanInfo` JavaBeans mechanism here as well
|
||||
(described to some extent
|
||||
|
@ -472,7 +460,6 @@ here]). The following example use the `BeanInfo` mechanism to
|
|||
explicitly register one or more `PropertyEditor` instances with the properties of an
|
||||
associated class:
|
||||
|
||||
====
|
||||
[literal]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -482,12 +469,10 @@ com
|
|||
Something
|
||||
SomethingBeanInfo // the BeanInfo for the Something class
|
||||
----
|
||||
====
|
||||
|
||||
The following Java source code for the referenced `SomethingBeanInfo` class
|
||||
associates a `CustomNumberEditor` with the `age` property of the `Something` class:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -509,7 +494,6 @@ associates a `CustomNumberEditor` with the `age` property of the `Something` cla
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
[[beans-beans-conversion-customeditor-registration]]
|
||||
|
@ -549,7 +533,6 @@ support for additional `PropertyEditor` instances to an `ApplicationContext`.
|
|||
Consider the following example, which defines a user class called `ExoticType` and another class called `DependsOnExoticType`, which needs
|
||||
`ExoticType` set as a property:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -573,13 +556,11 @@ Consider the following example, which defines a user class called `ExoticType` a
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
When things are properly set up, we want to be able to assign the type property as a
|
||||
string, which a `PropertyEditor` converts into an actual
|
||||
`ExoticType` instance. The following bean definition shows how to set up this relationship:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -587,11 +568,9 @@ string, which a `PropertyEditor` converts into an actual
|
|||
<property name="type" value="aNameForExoticType"/>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
The `PropertyEditor` implementation could look similar to the following:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -605,12 +584,10 @@ The `PropertyEditor` implementation could look similar to the following:
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Finally, the following example shows how to use `CustomEditorConfigurer` to register the new `PropertyEditor` with the
|
||||
`ApplicationContext`, which will then be able to use it as needed:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -622,7 +599,6 @@ Finally, the following example shows how to use `CustomEditorConfigurer` to regi
|
|||
</property>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
[[beans-beans-conversion-customeditor-registration-per]]
|
||||
===== Using `PropertyEditorRegistrar`
|
||||
|
@ -643,7 +619,6 @@ instances for each bean creation attempt.
|
|||
|
||||
The following example shows how to create your own `PropertyEditorRegistrar` implementation:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -660,7 +635,6 @@ The following example shows how to create your own `PropertyEditorRegistrar` imp
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
See also the `org.springframework.beans.support.ResourceEditorRegistrar` for an example
|
||||
`PropertyEditorRegistrar` implementation. Notice how in its implementation of the
|
||||
|
@ -669,7 +643,6 @@ See also the `org.springframework.beans.support.ResourceEditorRegistrar` for an
|
|||
The next example shows how to configure a `CustomEditorConfigurer` and inject an instance of our
|
||||
`CustomPropertyEditorRegistrar` into it:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -684,7 +657,6 @@ The next example shows how to configure a `CustomEditorConfigurer` and inject an
|
|||
<bean id="customPropertyEditorRegistrar"
|
||||
class="com.foo.editors.spring.CustomPropertyEditorRegistrar"/>
|
||||
----
|
||||
====
|
||||
|
||||
Finally (and in a bit of a departure from the focus of this chapter for those of you
|
||||
using <<web.adoc#mvc,Spring's MVC web framework>>), using `PropertyEditorRegistrars` in
|
||||
|
@ -692,7 +664,6 @@ conjunction with data-binding `Controllers` (such as `SimpleFormController`) can
|
|||
convenient. The following example uses a `PropertyEditorRegistrar` in the
|
||||
implementation of an `initBinder(..)` method:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -712,7 +683,6 @@ implementation of an `initBinder(..)` method:
|
|||
// other methods to do with registering a User
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
This style of `PropertyEditor` registration can lead to concise code (the implementation
|
||||
of `initBinder(..)` is only one line long) and lets common `PropertyEditor`
|
||||
|
@ -740,7 +710,6 @@ application where type conversion is needed.
|
|||
The SPI to implement type conversion logic is simple and strongly typed, as the following
|
||||
interface definition shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -751,7 +720,6 @@ interface definition shows:
|
|||
T convert(S source);
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
To create your own converter, implement the `Converter` interface and parameterize `S`
|
||||
as the type you are converting from and `T` as the type you are converting to. You can also transparently apply such a
|
||||
|
@ -768,7 +736,6 @@ Several converter implementations are provided in the `core.convert.support` pac
|
|||
a convenience. These include converters from strings to numbers and other common types.
|
||||
The following listing shows the `StringToInteger` class, which is a typical `Converter` implementation:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -781,7 +748,6 @@ The following listing shows the `StringToInteger` class, which is a typical `Con
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -792,7 +758,6 @@ When you need to centralize the conversion logic for an entire class hierarchy
|
|||
(for example, when converting from `String` to `Enum` objects), you can implement
|
||||
`ConverterFactory`, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -803,7 +768,6 @@ When you need to centralize the conversion logic for an entire class hierarchy
|
|||
<T extends R> Converter<S, T> getConverter(Class<T> targetType);
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Parameterize S to be the type you are converting from and R to be the base type defining
|
||||
the __range__ of classes you can convert to. Then implement `getConverter(Class<T>)`,
|
||||
|
@ -811,7 +775,6 @@ where T is a subclass of R.
|
|||
|
||||
Consider the `StringToEnumConverterFactory` as an example:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -837,7 +800,6 @@ Consider the `StringToEnumConverterFactory` as an example:
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -852,7 +814,6 @@ context that you can use when you implement your conversion logic. Such context
|
|||
type conversion be driven by a field annotation or by generic information declared on a
|
||||
field signature. The following listing shows the interface definition of `GenericConverter`:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -865,7 +826,6 @@ field signature. The following listing shows the interface definition of `Generi
|
|||
Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
To implement a `GenericConverter`, have `getConvertibleTypes()` return the supported
|
||||
source->target type pairs. Then implement `convert(Object, TypeDescriptor,
|
||||
|
@ -894,7 +854,6 @@ on the target field, or you might want to run a `Converter` only if a specific m
|
|||
`ConditionalGenericConverter` is the union of the `GenericConverter` and
|
||||
`ConditionalConverter` interfaces that lets you define such custom matching criteria:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -906,7 +865,6 @@ on the target field, or you might want to run a `Converter` only if a specific m
|
|||
public interface ConditionalGenericConverter extends GenericConverter, ConditionalConverter {
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
A good example of a `ConditionalGenericConverter` is an `EntityConverter` that converts
|
||||
between a persistent entity identifier and an entity reference. Such an `EntityConverter`
|
||||
|
@ -922,7 +880,6 @@ might match only if the target entity type declares a static finder method (for
|
|||
`ConversionService` defines a unified API for executing type conversion logic at
|
||||
runtime. Converters are often executed behind the following facade interface:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -940,7 +897,6 @@ runtime. Converters are often executed behind the following facade interface:
|
|||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Most `ConversionService` implementations also implement `ConverterRegistry`, which
|
||||
provides an SPI for registering converters. Internally, a `ConversionService`
|
||||
|
@ -969,21 +925,18 @@ system is used.
|
|||
To register a default `ConversionService` with Spring, add the following bean definition
|
||||
with an `id` of `conversionService`:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
<bean id="conversionService"
|
||||
class="org.springframework.context.support.ConversionServiceFactoryBean"/>
|
||||
----
|
||||
====
|
||||
|
||||
A default `ConversionService` can convert between strings, numbers, enums, collections,
|
||||
maps, and other common types. To supplement or override the default converters with your
|
||||
own custom converters, set the `converters` property. Property values can implement
|
||||
any of the `Converter`, `ConverterFactory`, or `GenericConverter` interfaces.
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -996,7 +949,6 @@ any of the `Converter`, `ConverterFactory`, or `GenericConverter` interfaces.
|
|||
</property>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
It is also common to use a `ConversionService` within a Spring MVC application. See
|
||||
<<web.adoc#mvc-config-conversion, Conversion and Formatting>> in the Spring MVC chapter.
|
||||
|
@ -1013,7 +965,6 @@ In certain situations, you may wish to apply formatting during conversion. See
|
|||
To work with a `ConversionService` instance programmatically, you can inject a reference to
|
||||
it like you would for any other bean. The following example shows how to do so:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1030,7 +981,6 @@ it like you would for any other bean. The following example shows how to do so:
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
For most use cases, you can use the `convert` method that specifies the `targetType`, but it
|
||||
does not work with more complex types, such as a collection of a parameterized element.
|
||||
|
@ -1040,7 +990,6 @@ you need to provide a formal definition of the source and target types.
|
|||
Fortunately, `TypeDescriptor` provides various options to make doing so straightforward,
|
||||
as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1051,7 +1000,6 @@ as the following example shows:
|
|||
TypeDescriptor.forObject(input), // List<Integer> type descriptor
|
||||
TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(String.class)));
|
||||
----
|
||||
====
|
||||
|
||||
Note that `DefaultConversionService` automatically registers converters that are
|
||||
appropriate for most environments. This includes collection converters, scalar
|
||||
|
@ -1100,7 +1048,6 @@ provides a unified type conversion API for both SPIs.
|
|||
The `Formatter` SPI to implement field formatting logic is simple and strongly typed. The
|
||||
following listing shows the `Formatter` interface definition:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1109,12 +1056,10 @@ following listing shows the `Formatter` interface definition:
|
|||
public interface Formatter<T> extends Printer<T>, Parser<T> {
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
`Formatter` extends from the `Printer` and `Parser` building-block interfaces. The
|
||||
following listing shows the definitions of those two interfaces:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1134,7 +1079,6 @@ following listing shows the definitions of those two interfaces:
|
|||
T parse(String clientValue, Locale locale) throws ParseException;
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
To create your own `Formatter`, implement the `Formatter` interface shown earlier.
|
||||
Parameterize `T` to be the type of object you wish to format -- for example,
|
||||
|
@ -1153,7 +1097,6 @@ formatting support based on the http://joda-time.sourceforge.net[Joda-Time libra
|
|||
|
||||
The following `DateFormatter` is an example `Formatter` implementation:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1188,7 +1131,6 @@ The following `DateFormatter` is an example `Formatter` implementation:
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The Spring team welcomes community-driven `Formatter` contributionsSee
|
||||
https://jira.spring.io/browse/SPR[jira.spring.io] to contribute.
|
||||
|
@ -1202,7 +1144,6 @@ Field formatting can be configured by field type or annotation. To bind
|
|||
an annotation to a `Formatter`, implement `AnnotationFormatterFactory`. The following
|
||||
listing shows the definition of the `AnnotationFormatterFactory` interface:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1217,7 +1158,6 @@ listing shows the definition of the `AnnotationFormatterFactory` interface:
|
|||
Parser<?> getParser(A annotation, Class<?> fieldType);
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
To create an implementation:
|
||||
. Parameterize A to be the field `annotationType` with which you wish to associate
|
||||
|
@ -1230,7 +1170,6 @@ The following example `AnnotationFormatterFactory` implementation binds the `@Nu
|
|||
annotation to a formatter to let a number style or pattern be
|
||||
specified:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1267,12 +1206,10 @@ specified:
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
To trigger formatting, you can annotate fields with @NumberFormat, as the following
|
||||
example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1282,7 +1219,6 @@ example shows:
|
|||
private BigDecimal decimal;
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -1297,7 +1233,6 @@ package. You can use `@NumberFormat` to format `Number` fields such as `Double`
|
|||
The following example uses `@DateTimeFormat` to format a `java.util.Date` as an ISO Date
|
||||
(yyyy-MM-dd):
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1307,7 +1242,6 @@ The following example uses `@DateTimeFormat` to format a `java.util.Date` as an
|
|||
private Date date;
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -1323,7 +1257,6 @@ for use with Spring's `DataBinder` and the Spring Expression Language (SpEL).
|
|||
|
||||
The following listing shows the `FormatterRegistry` SPI:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1340,7 +1273,6 @@ The following listing shows the `FormatterRegistry` SPI:
|
|||
void addFormatterForAnnotation(AnnotationFormatterFactory<?, ?> factory);
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
As shown in the preceding listing, you can register formatters by field type or by annotation.
|
||||
|
||||
|
@ -1358,7 +1290,6 @@ these rules once, and they are applied whenever formatting is needed.
|
|||
`FormatterRegistrar` is an SPI for registering formatters and converters through the
|
||||
FormatterRegistry. The following listing shows its interface definition:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1369,7 +1300,6 @@ FormatterRegistry. The following listing shows its interface definition:
|
|||
void registerFormatters(FormatterRegistry registry);
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
A `FormatterRegistrar` is useful when registering multiple related converters and
|
||||
formatters for a given formatting category, such as date formatting. It can also be
|
||||
|
@ -1404,7 +1334,6 @@ you use the Joda-Time library.
|
|||
For example, the following Java configuration registers a global `yyyyMMdd`
|
||||
format (this example does not depend on the Joda-Time library):
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1429,13 +1358,11 @@ format (this example does not depend on the Joda-Time library):
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
If you prefer XML-based configuration, you can use a
|
||||
`FormattingConversionServiceFactoryBean`. The following example shows how to do so (this time using Joda
|
||||
Time):
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1467,7 +1394,6 @@ Time):
|
|||
</bean>
|
||||
</beans>
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: Joda-Time provides separate distinct types to represent `date`, `time`, and `date-time`
|
||||
values. The `dateFormatter`, `timeFormatter`, and `dateTimeFormatter` properties of the
|
||||
|
@ -1504,7 +1430,6 @@ constraints. You can also define your own custom constraints.
|
|||
|
||||
Consider the following example, which shows a simple `PersonForm` model with two properties:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1513,12 +1438,10 @@ Consider the following example, which shows a simple `PersonForm` model with two
|
|||
private int age;
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
JSR-303 lets you define declarative validation constraints against such properties, as the
|
||||
following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1532,7 +1455,6 @@ following example shows:
|
|||
private int age;
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
When a JSR-303 Validator validates an instance of this class, these constraints
|
||||
are enforced.
|
||||
|
@ -1556,14 +1478,12 @@ wherever validation is needed in your application.
|
|||
You can use the `LocalValidatorFactoryBean` to configure a default Validator as a Spring bean,
|
||||
as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
<bean id="validator"
|
||||
class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
|
||||
----
|
||||
====
|
||||
|
||||
The basic configuration in the preceding example triggers bean validation to initialize by using its
|
||||
default bootstrap mechanism. A JSR-303 or JSR-349 provider, such as the Hibernate Validator,
|
||||
|
@ -1581,7 +1501,6 @@ these interfaces into beans that need to invoke validation logic.
|
|||
You can inject a reference to `javax.validation.Validator` if you prefer to work with the Bean
|
||||
Validation API directly, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1593,12 +1512,10 @@ Validation API directly, as the following example shows:
|
|||
@Autowired
|
||||
private Validator validator;
|
||||
----
|
||||
====
|
||||
|
||||
You can inject a reference to `org.springframework.validation.Validator` if your bean requires
|
||||
the Spring Validation API, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1611,7 +1528,6 @@ the Spring Validation API, as the following example shows:
|
|||
private Validator validator;
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
[[validation-beanvalidation-spring-constraints]]
|
||||
|
@ -1636,7 +1552,6 @@ that uses Spring to create `ConstraintValidator` instances. This lets your custo
|
|||
The following example shows a custom `@Constraint` declaration followed by an associated
|
||||
`ConstraintValidator` implementation that uses Spring for dependency injection:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1660,7 +1575,6 @@ The following example shows a custom `@Constraint` declaration followed by an as
|
|||
...
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
As the preceding example shows, a `ConstraintValidator` implementation can have its dependencies
|
||||
`@Autowired` as any other Spring bean.
|
||||
|
@ -1673,13 +1587,11 @@ You can integrate the method validation feature supported by Bean Validation 1.1
|
|||
extension, also by Hibernate Validator 4.3) into a Spring context
|
||||
through a `MethodValidationPostProcessor` bean definition, as follows:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"/>
|
||||
----
|
||||
====
|
||||
|
||||
To be eligible for Spring-driven method validation, all target classes need to be annotated with
|
||||
Spring's `@Validated` annotation. (Optionally, you can also declare the validation groups to use.)
|
||||
|
@ -1708,7 +1620,6 @@ configured, you can invoke the `Validator` by calling `binder.validate()`. Any v
|
|||
The following example shows how to use a `DataBinder` programmatically to invoke validation
|
||||
logic after binding to a target object:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1725,7 +1636,6 @@ logic after binding to a target object:
|
|||
// get BindingResult that includes any validation errors
|
||||
BindingResult results = binder.getBindingResult();
|
||||
----
|
||||
====
|
||||
|
||||
You can also configure a `DataBinder` with multiple `Validator` instances through
|
||||
`dataBinder.addValidators` and `dataBinder.replaceValidators`. This is useful when
|
||||
|
|
|
@ -32,7 +32,6 @@ the following preamble at the top of your Spring XML configuration file. The tex
|
|||
following snippet references the correct schema so that the tags in the `tx` namespace
|
||||
are available to you:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -52,7 +51,6 @@ are available to you:
|
|||
----
|
||||
<1> Declare usage of the `tx` namespace.
|
||||
<2> Specify the location (with other schema locations).
|
||||
====
|
||||
|
||||
NOTE: Often, when you use the elements in the `tx` namespace, you are also using the
|
||||
elements from the `aop` namespace (since the declarative transaction support in Spring is
|
||||
|
@ -74,7 +72,6 @@ To use the elements in the `jdbc` schema, you need to have the following preambl
|
|||
top of your Spring XML configuration file. The text in the following snippet references
|
||||
the correct schema so that the elements in the `jdbc` namespace are available to you:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -92,4 +89,3 @@ the correct schema so that the elements in the `jdbc` namespace are available to
|
|||
----
|
||||
<1> Declare usage of the `jdbc` namespace.
|
||||
<2> Specify the location (with other schema locations).
|
||||
====
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -20,7 +20,6 @@ To use the elements in the `jee` schema, you need to have the following preamble
|
|||
of your Spring XML configuration file. The text in the following snippet references the
|
||||
correct schema so that the elements in the `jee` namespace are available to you:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -34,7 +33,6 @@ correct schema so that the elements in the `jee` namespace are available to you:
|
|||
<!-- bean definitions here -->
|
||||
</beans>
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -43,7 +41,6 @@ correct schema so that the elements in the `jee` namespace are available to you:
|
|||
|
||||
The following example shows how to use JNDI to look up a data source without the `jee` schema:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -55,12 +52,10 @@ The following example shows how to use JNDI to look up a data source without the
|
|||
<property name="dataSource" ref="**dataSource**"/>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
The following example shows how to use JNDI to look up a data source with the `jee`
|
||||
schema:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -71,7 +66,6 @@ schema:
|
|||
<property name="dataSource" ref="**dataSource**"/>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -81,7 +75,6 @@ schema:
|
|||
The following example shows how to use JNDI to look up an environment variable without
|
||||
`jee`:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -94,7 +87,6 @@ The following example shows how to use JNDI to look up an environment variable w
|
|||
</property>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
The following example shows how to use JNDI to look up an environment variable with `jee`:
|
||||
|
||||
|
@ -113,7 +105,6 @@ The following example shows how to use JNDI to look up an environment variable w
|
|||
The following example shows how to use JNDI to look up multiple environment variables
|
||||
without `jee`:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -127,12 +118,10 @@ without `jee`:
|
|||
</property>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
The following example shows how to use JNDI to look up multiple environment variables with
|
||||
`jee`:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -144,7 +133,6 @@ The following example shows how to use JNDI to look up multiple environment vari
|
|||
</jee:environment>
|
||||
</jee:jndi-lookup>
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
[[xsd-schemas-jee-jndi-lookup-complex]]
|
||||
|
@ -153,7 +141,6 @@ The following example shows how to use JNDI to look up multiple environment vari
|
|||
The following example shows how to use JNDI to look up a data source and a number of
|
||||
different properties without `jee`:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -166,12 +153,10 @@ different properties without `jee`:
|
|||
<property name="proxyInterface" value="com.myapp.Thing"/>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
The following example shows how to use JNDI to look up a data source and a number of
|
||||
different properties with `jee`:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -183,7 +168,6 @@ different properties with `jee`:
|
|||
expected-type="com.myapp.DefaultThing"
|
||||
proxy-interface="com.myapp.Thing"/>
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -195,7 +179,6 @@ The `<jee:local-slsb/>` element configures a reference to a local EJB Stateless
|
|||
The following example shows how to configures a reference to a local EJB Stateless
|
||||
SessionBean without `jee`:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -205,19 +188,16 @@ SessionBean without `jee`:
|
|||
<property name="businessInterface" value="com.foo.service.RentalService"/>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
The following example shows how to configures a reference to a local EJB Stateless
|
||||
SessionBean with `jee`:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
<jee:local-slsb id="simpleSlsb" jndi-name="ejb/RentalServiceBean"
|
||||
business-interface="com.foo.service.RentalService"/>
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -229,7 +209,6 @@ The `<jee:local-slsb/>` element configures a reference to a local EJB Stateless
|
|||
The following example shows how to configures a reference to a local EJB Stateless
|
||||
SessionBean and a number of properties without `jee`:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -242,12 +221,10 @@ SessionBean and a number of properties without `jee`:
|
|||
<property name="resourceRef" value="true"/>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
The following example shows how to configures a reference to a local EJB Stateless
|
||||
SessionBean and a number of properties with `jee`:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -258,7 +235,6 @@ SessionBean and a number of properties with `jee`:
|
|||
lookup-home-on-startup="true"
|
||||
resource-ref="true">
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
[[xsd-schemas-jee-remote-slsb]]
|
||||
|
@ -270,7 +246,6 @@ SessionBean.
|
|||
The following example shows how to configures a reference to a remote EJB Stateless
|
||||
SessionBean without `jee`:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -285,12 +260,10 @@ SessionBean without `jee`:
|
|||
<property name="refreshHomeOnConnectFailure" value="true"/>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
The following example shows how to configures a reference to a remote EJB Stateless
|
||||
SessionBean with `jee`:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -303,7 +276,6 @@ SessionBean with `jee`:
|
|||
home-interface="com.foo.service.RentalService"
|
||||
refresh-home-on-connect-failure="true">
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -321,7 +293,6 @@ the following preamble at the top of your Spring XML configuration file. The tex
|
|||
following snippet references the correct schema so that the elements in the `jms` namespace
|
||||
are available to you:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -335,7 +306,6 @@ are available to you:
|
|||
<!-- bean definitions here -->
|
||||
</beans>
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -359,7 +329,6 @@ To use the elements in the `cache` schema, you need to have the following preamb
|
|||
top of your Spring XML configuration file. The text in the following snippet references
|
||||
the correct schema so that the elements in the `cache` namespace are available to you:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -373,4 +342,3 @@ the correct schema so that the elements in the `cache` namespace are available t
|
|||
<!-- bean definitions here -->
|
||||
</beans>
|
||||
----
|
||||
====
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -42,7 +42,6 @@ Note that this interface is defined in plain Java. Dependent objects that are
|
|||
injected with a reference to the `Messenger` do not know that the underlying
|
||||
implementation is a Groovy script. The following listing shows the `Messenger` interface:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -54,11 +53,9 @@ implementation is a Groovy script. The following listing shows the `Messenger` i
|
|||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The following example defines a class that has a dependency on the `Messenger` interface:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -78,11 +75,9 @@ The following example defines a class that has a dependency on the `Messenger` i
|
|||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The following example implements the `Messenger` interface in Groovy:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -99,7 +94,6 @@ The following example implements the `Messenger` interface in Groovy:
|
|||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
|
@ -118,7 +112,6 @@ Finally, the following example shows the bean definitions that effect the inject
|
|||
Groovy-defined `Messenger` implementation into an instance of the
|
||||
`DefaultBookingService` class:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -141,7 +134,6 @@ Groovy-defined `Messenger` implementation into an instance of the
|
|||
|
||||
</beans>
|
||||
----
|
||||
====
|
||||
|
||||
The `bookingService` bean (a `DefaultBookingService`) can now use its private
|
||||
`messenger` member variable as normal, because the `Messenger` instance that was injected
|
||||
|
@ -244,7 +236,6 @@ So, if we stick with <<dynamic-language-a-first-example,the example>> from earli
|
|||
chapter, the following example shows what we would change in the Spring XML configuration to effect
|
||||
refreshable beans:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -263,7 +254,6 @@ refreshable beans:
|
|||
|
||||
</beans>
|
||||
----
|
||||
====
|
||||
|
||||
That really is all you have to do. The `refresh-check-delay` attribute defined on the
|
||||
`messenger` bean definition is the number of milliseconds after which the bean is
|
||||
|
@ -281,7 +271,6 @@ the program resumes execution.
|
|||
|
||||
The following listing shows this sample application:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -301,14 +290,12 @@ The following listing shows this sample application:
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Assume then, for the purposes of this example, that all calls to the
|
||||
`getMessage()` method of `Messenger` implementations have to be changed such that the
|
||||
message is surrounded by quotation marks. The following listing shows the changes that you (the developer) should make to the
|
||||
`Messenger.groovy` source file when the execution of the program is paused:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -328,7 +315,6 @@ message is surrounded by quotation marks. The following listing shows the change
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
When the program runs, the output before the input pause will be `I Can Do The
|
||||
Frug`. After the change to the source file is made and saved and the program resumes
|
||||
|
@ -363,7 +349,6 @@ embedded directly in Spring bean definitions. More specifically, the
|
|||
inside a Spring configuration file. An example might clarify how the inline script
|
||||
feature works:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -382,7 +367,6 @@ feature works:
|
|||
<lang:property name="message" value="I Can Do The Frug" />
|
||||
</lang:groovy>
|
||||
----
|
||||
====
|
||||
|
||||
If we put to one side the issues surrounding whether it is good practice to define
|
||||
dynamic language source inside a Spring configuration file, the `<lang:inline-script/>`
|
||||
|
@ -404,7 +388,6 @@ constructors and properties 100% clear, the following mixture of code and config
|
|||
does not work:
|
||||
|
||||
.An approach that cannot work
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -443,7 +426,6 @@ does not work:
|
|||
|
||||
</lang>
|
||||
----
|
||||
====
|
||||
|
||||
In practice this limitation is not as significant as it first appears, since setter
|
||||
injection is the injection style favored by the overwhelming majority of developers
|
||||
|
@ -477,7 +459,6 @@ If you have read this chapter straight from the top, you have already
|
|||
<<dynamic-language-a-first-example,seen an example>> of a Groovy-dynamic-language-backed
|
||||
bean. Now consider another example (again using an example from the Spring test suite):
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -489,11 +470,9 @@ bean. Now consider another example (again using an example from the Spring test
|
|||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The following example implements the `Calculator` interface in Groovy:
|
||||
|
||||
====
|
||||
[source,groovy,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -508,11 +487,9 @@ The following example implements the `Calculator` interface in Groovy:
|
|||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The following bean definition uses the calculator defined in Groovy:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -521,11 +498,9 @@ The following bean definition uses the calculator defined in Groovy:
|
|||
<lang:groovy id="calculator" script-source="classpath:calculator.groovy"/>
|
||||
</beans>
|
||||
----
|
||||
====
|
||||
|
||||
Finally, the following small application exercises the preceding configuration:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -543,7 +518,6 @@ Finally, the following small application exercises the preceding configuration:
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The resulting output from running the above program is (unsurprisingly) `10`.
|
||||
(For more interesting examples,
|
||||
|
@ -566,7 +540,6 @@ implementations of this interface could invoke any required initialization metho
|
|||
set some default property values, or specify a custom `MetaClass`. The following listing
|
||||
shows the `GroovyObjectCustomizer` interface definition:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -575,7 +548,6 @@ shows the `GroovyObjectCustomizer` interface definition:
|
|||
void customize(GroovyObject goo);
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The Spring Framework instantiates an instance of your Groovy-backed bean and
|
||||
then passes the created `GroovyObject` to the specified `GroovyObjectCustomizer` (if one
|
||||
|
@ -583,7 +555,6 @@ has been defined). You can do whatever you like with the supplied `GroovyObject`
|
|||
reference. We expect that most people want to set a custom `MetaClass` with this callback,
|
||||
and the following example shows how to do so:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -603,7 +574,6 @@ and the following example shows how to do so:
|
|||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
A full discussion of meta-programming in Groovy is beyond the scope of the Spring
|
||||
reference manual. See the relevant section of the Groovy reference manual or do a
|
||||
|
@ -611,7 +581,6 @@ search online. Plenty of articles address this topic. Actually, making use
|
|||
of a `GroovyObjectCustomizer` is easy if you use the Spring namespace support, as the
|
||||
following example shows:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -623,12 +592,10 @@ following example shows:
|
|||
script-source="classpath:org/springframework/scripting/groovy/Calculator.groovy"
|
||||
customizer-ref="tracingCustomizer"/>
|
||||
----
|
||||
====
|
||||
|
||||
If you do not use the Spring namespace support, you can still use the
|
||||
`GroovyObjectCustomizer` functionality, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -642,7 +609,6 @@ If you do not use the Spring namespace support, you can still use the
|
|||
|
||||
<bean class="org.springframework.scripting.support.ScriptFactoryPostProcessor"/>
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: As of Spring Framework 4.3.3, you may also specify a Groovy `CompilationCustomizer`
|
||||
(such as an `ImportCustomizer`) or even a full Groovy `CompilerConfiguration` object
|
||||
|
@ -684,7 +650,6 @@ Now we can show a fully working example of using a BeanShell-based bean that imp
|
|||
the `Messenger` interface that was defined earlier in this chapter. We again show the
|
||||
definition of the `Messenger` interface:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -696,12 +661,10 @@ definition of the `Messenger` interface:
|
|||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The following example shows the BeanShell "`implementation`" (we use the term loosely here) of the
|
||||
`Messenger` interface:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -715,12 +678,10 @@ The following example shows the BeanShell "`implementation`" (we use the term lo
|
|||
message = aMessage;
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The following example shows the Spring XML that defines an "`instance`" of the above "`class`" (again,
|
||||
we use these terms very loosely here):
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -730,7 +691,6 @@ we use these terms very loosely here):
|
|||
<lang:property name="message" value="Hello World!" />
|
||||
</lang:bsh>
|
||||
----
|
||||
====
|
||||
|
||||
See <<dynamic-language-scenarios>> for some scenarios where you might want to use
|
||||
BeanShell-based beans.
|
||||
|
@ -773,7 +733,6 @@ beans, you have to enable the "`refreshable beans`" functionality. See
|
|||
The following example shows an `org.springframework.web.servlet.mvc.Controller` implemented
|
||||
by using the Groovy dynamic language:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -809,7 +768,6 @@ by using the Groovy dynamic language:
|
|||
<lang:property name="fortuneService" ref="fortuneService"/>
|
||||
</lang:groovy>
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -836,7 +794,6 @@ by using the Groovy dynamic language (see <<core.adoc#validator,
|
|||
Validation using Spring’s Validator interface>> for a discussion of the
|
||||
`Validator` interface):
|
||||
|
||||
====
|
||||
[source,groovy,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -859,7 +816,6 @@ Validation using Spring’s Validator interface>> for a discussion of the
|
|||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -899,7 +855,6 @@ with "`regular`" beans.)
|
|||
The following example uses the `scope` attribute to define a Groovy bean scoped as
|
||||
a <<core.adoc#beans-factory-scopes-prototype,prototype>>:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -920,7 +875,6 @@ a <<core.adoc#beans-factory-scopes-prototype,prototype>>:
|
|||
|
||||
</beans>
|
||||
----
|
||||
====
|
||||
|
||||
See <<core.adoc#beans-factory-scopes,Bean scopes>> in <<core.adoc#beans,The IoC container>>
|
||||
for a full discussion of the scoping support in the Spring Framework.
|
||||
|
@ -943,7 +897,6 @@ the following preamble at the top of your Spring XML configuration file. The tex
|
|||
following snippet references the correct schema so that the tags in the `lang` namespace
|
||||
are available to you:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -958,7 +911,6 @@ are available to you:
|
|||
|
||||
</beans>
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -56,23 +56,19 @@ for their APIs, thus giving a better Kotlin development experience overall.
|
|||
|
||||
To retrieve a list of `User` objects in Java, you would normally write the following:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
----
|
||||
Flux<User> users = client.get().retrieve().bodyToFlux(User.class)
|
||||
----
|
||||
====
|
||||
|
||||
With Kotlin and the Spring Framework extensions, you can instead write the following:
|
||||
|
||||
====
|
||||
[source,kotlin,indent=0]
|
||||
----
|
||||
val users = client.get().retrieve().bodyToFlux<User>()
|
||||
// or (both are equivalent)
|
||||
val users : Flux<User> = client.get().retrieve().bodyToFlux()
|
||||
----
|
||||
====
|
||||
|
||||
As in Java, `users` in Kotlin is strongly typed, but Kotlin's clever type inference allows
|
||||
for shorter syntax.
|
||||
|
@ -176,7 +172,6 @@ This mechanism is very efficient, as it does not require any reflection or CGLIB
|
|||
|
||||
In Java, you can, for example, write the following:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
----
|
||||
GenericApplicationContext context = new GenericApplicationContext();
|
||||
|
@ -184,12 +179,10 @@ In Java, you can, for example, write the following:
|
|||
context.registerBean(Bar.class, () -> new Bar(context.getBean(Foo.class))
|
||||
);
|
||||
----
|
||||
====
|
||||
|
||||
In Kotlin, with reified type parameters and `GenericApplicationContext`
|
||||
Kotlin extensions, you can instead write the following:
|
||||
|
||||
====
|
||||
[source,kotlin,indent=0]
|
||||
----
|
||||
val context = GenericApplicationContext().apply {
|
||||
|
@ -197,7 +190,6 @@ Kotlin extensions, you can instead write the following:
|
|||
registerBean { Bar(it.getBean<Foo>()) }
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
In order to allow a more declarative approach and cleaner syntax, Spring Framework provides
|
||||
a {doc-root}/spring-framework/docs/{spring-version}/kdoc-api/spring-framework/org.springframework.context.support/-bean-definition-dsl/[Kotlin bean definition DSL]
|
||||
|
@ -205,7 +197,6 @@ It declares an `ApplicationContextInitializer` through a clean declarative API,
|
|||
which lets you deal with profiles and `Environment` for customizing
|
||||
how beans are registered. The following example creates a `Play` profile:
|
||||
|
||||
====
|
||||
[source,kotlin,indent=0]
|
||||
----
|
||||
fun beans() = beans {
|
||||
|
@ -237,7 +228,6 @@ how beans are registered. The following example creates a `Play` profile:
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
In the preceding example, `bean<Routes>()` uses autowiring by constructor, and `ref<Routes>()`
|
||||
is a shortcut for `applicationContext.getBean(Routes::class.java)`.
|
||||
|
@ -245,7 +235,6 @@ is a shortcut for `applicationContext.getBean(Routes::class.java)`.
|
|||
You can then use this `beans()` function to register beans on the application context,
|
||||
as the following example shows:
|
||||
|
||||
====
|
||||
[source,kotlin,indent=0]
|
||||
----
|
||||
val context = GenericApplicationContext().apply {
|
||||
|
@ -253,7 +242,6 @@ as the following example shows:
|
|||
refresh()
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: This DSL is programmatic, meaning it allows custom registration logic of beans
|
||||
through an `if` expression, a `for` loop, or any other Kotlin constructs.
|
||||
|
@ -281,7 +269,6 @@ Spring Framework now comes with a
|
|||
that lets you use the <<web-reactive#webflux-fn,WebFlux functional
|
||||
API>> to write clean and idiomatic Kotlin code, as the following example shows:
|
||||
|
||||
====
|
||||
[source,kotlin,indent=0]
|
||||
----
|
||||
router {
|
||||
|
@ -301,7 +288,6 @@ API>> to write clean and idiomatic Kotlin code, as the following example shows:
|
|||
resources("/**", ClassPathResource("static/"))
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: This DSL is programmatic, meaning that it allows custom registration logic of beans
|
||||
through an `if` expression, a `for` loop, or any other Kotlin constructs. That can be useful when you need to register routes
|
||||
|
@ -330,7 +316,6 @@ https://github.com/Kotlin/kotlinx.html[kotlinx.html] DSL or by a using Kotlin mu
|
|||
This can let you write Kotlin templates with full autocompletion and
|
||||
refactoring support in a supported IDE, as the following example shows:
|
||||
|
||||
====
|
||||
[source,kotlin,indent=0]
|
||||
----
|
||||
import io.spring.demo.*
|
||||
|
@ -344,7 +329,6 @@ refactoring support in a supported IDE, as the following example shows:
|
|||
${include("footer")}
|
||||
"""
|
||||
----
|
||||
====
|
||||
|
||||
See the https://github.com/sdeleuze/kotlin-script-templating[kotlin-script-templating] example
|
||||
project for more details.
|
||||
|
@ -399,12 +383,10 @@ you can write your Kotlin beans without any additional `open` keyword, as in Jav
|
|||
In Kotlin, it is convenient and considered to be a best practice to declare read-only properties
|
||||
within the primary constructor, as in the following example:
|
||||
|
||||
====
|
||||
[source,kotlin,indent=0]
|
||||
----
|
||||
class Person(val name: String, val age: Int)
|
||||
----
|
||||
====
|
||||
|
||||
You can optionally add https://kotlinlang.org/docs/reference/data-classes.html[the `data` keyword]
|
||||
to make the compiler automatically derive the following members from all properties declared
|
||||
|
@ -417,7 +399,6 @@ in the primary constructor:
|
|||
|
||||
As the following example shows, this allows for easy changes to individual properties, even if `Person` properties are read-only:
|
||||
|
||||
====
|
||||
[source,kotlin,indent=0]
|
||||
----
|
||||
data class Person(val name: String, val age: Int)
|
||||
|
@ -425,7 +406,6 @@ As the following example shows, this allows for easy changes to individual prope
|
|||
val jack = Person(name = "Jack", age = 1)
|
||||
val olderJack = jack.copy(age = 2)
|
||||
----
|
||||
====
|
||||
|
||||
Common persistence technologies (such as JPA) require a default constructor, preventing this
|
||||
kind of design. Fortunately, there is now a workaround for this
|
||||
|
@ -448,7 +428,6 @@ mappings (such as MongoDB, Redis, Cassandra, and others).
|
|||
Our recommendation is to try and favor constructor injection with `val` read-only (and non-nullable when possible)
|
||||
https://kotlinlang.org/docs/reference/properties.html[properties], as the following example shows:
|
||||
|
||||
====
|
||||
[source,kotlin,indent=0]
|
||||
----
|
||||
@Component
|
||||
|
@ -457,7 +436,6 @@ https://kotlinlang.org/docs/reference/properties.html[properties], as the follow
|
|||
private val solrClient: SolrClient
|
||||
)
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: As of Spring Framework 4.3, classes with a single constructor have their
|
||||
parameters automatically autowired, that's why there is no need for an
|
||||
|
@ -466,7 +444,6 @@ explicit `@Autowired constructor` in the example shown above.
|
|||
If you really need to use field injection, you can use the `lateinit var` construct,
|
||||
as the following example shows:
|
||||
|
||||
====
|
||||
[source,kotlin,indent=0]
|
||||
----
|
||||
@Component
|
||||
|
@ -479,7 +456,6 @@ as the following example shows:
|
|||
lateinit var solrClient: SolrClient
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -494,7 +470,6 @@ character by writing `@Value("\${property}")`.
|
|||
As an alternative, you can customize the properties placeholder prefix by declaring
|
||||
the following configuration beans:
|
||||
|
||||
====
|
||||
[source,kotlin,indent=0]
|
||||
----
|
||||
@Bean
|
||||
|
@ -502,12 +477,10 @@ the following configuration beans:
|
|||
setPlaceholderPrefix("%{")
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
You can customize existing code (such as Spring Boot actuators or `@LocalServerPort`) that uses the `${...}` syntax,
|
||||
with configuration beans, as the following example shows:
|
||||
|
||||
====
|
||||
[source,kotlin,indent=0]
|
||||
----
|
||||
@Bean
|
||||
|
@ -519,7 +492,6 @@ with configuration beans, as the following example shows:
|
|||
@Bean
|
||||
fun defaultPropertyConfigurer() = PropertySourcesPlaceholderConfigurer()
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: If you use Spring Boot, you can use
|
||||
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-typesafe-configuration-properties[`@ConfigurationProperties`]
|
||||
|
@ -543,7 +515,6 @@ specify it as a `vararg` parameter.
|
|||
To understand what that means, consider `@RequestMapping` (which is one
|
||||
of the most widely used Spring annotations) as an example. This Java annotation is declared as follows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
----
|
||||
public @interface RequestMapping {
|
||||
|
@ -559,7 +530,6 @@ of the most widely used Spring annotations) as an example. This Java annotation
|
|||
// ...
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The typical use case for `@RequestMapping` is to map a handler method to a specific path
|
||||
and method. In Java, you can specify a single value for the
|
||||
|
@ -600,7 +570,6 @@ You can now change the default behavior to `PER_CLASS` thanks to a
|
|||
|
||||
The following example `@BeforeAll` and `@AfterAll` annotations on non-static methods:
|
||||
|
||||
====
|
||||
[source]
|
||||
----
|
||||
class IntegrationTests {
|
||||
|
@ -630,7 +599,6 @@ class IntegrationTests {
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -639,7 +607,6 @@ class IntegrationTests {
|
|||
You can create specification-like tests with JUnit 5 and Kotlin.
|
||||
The following example shows how to do so:
|
||||
|
||||
====
|
||||
[source]
|
||||
----
|
||||
class SpecificationLikeTests {
|
||||
|
@ -663,7 +630,6 @@ class SpecificationLikeTests {
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -27,13 +27,11 @@ a URL to connect to a running server.
|
|||
|
||||
The following example shows how to create a server setup to test one `@Controller` at a time:
|
||||
|
||||
====
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
client = WebTestClient.bindToController(new TestController()).build();
|
||||
----
|
||||
====
|
||||
|
||||
The preceding example loads the <<web-reactive.adoc#webflux-config,WebFlux Java configuration>> and
|
||||
registers the given controller. The resulting WebFlux application is tested
|
||||
|
@ -48,14 +46,12 @@ on the builder to customize the default WebFlux Java configuration.
|
|||
The following example shows how to set up a server from a
|
||||
<<web-reactive.adoc#webflux-fn,RouterFunction>>:
|
||||
|
||||
====
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
RouterFunction<?> route = ...
|
||||
client = WebTestClient.bindToRouterFunction(route).build();
|
||||
----
|
||||
====
|
||||
|
||||
Internally, the configuration is passed to `RouterFunctions.toWebHandler`.
|
||||
The resulting WebFlux application is tested without an HTTP server by using mock
|
||||
|
@ -69,7 +65,6 @@ request and response objects.
|
|||
The following example shows how to setup a server from the Spring configuration of your application or
|
||||
some subset of it:
|
||||
|
||||
====
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -92,7 +87,6 @@ some subset of it:
|
|||
<1> Specify the configuration to load
|
||||
<2> Inject the configuration
|
||||
<3> Create the `WebTestClient`
|
||||
====
|
||||
|
||||
Internally, the configuration is passed to `WebHttpHandlerBuilder` to set up
|
||||
the request processing chain. See
|
||||
|
@ -107,13 +101,11 @@ and response objects.
|
|||
|
||||
The following server setup option lets you connect to a running server:
|
||||
|
||||
====
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
client = WebTestClient.bindToServer().baseUrl("http://localhost:8080").build();
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -125,7 +117,6 @@ options, including base URL, default headers, client filters, and others. These
|
|||
are readily available following `bindToServer`. For all others, you need to use
|
||||
`configureClient()` to transition from server to client configuration, as follows:
|
||||
|
||||
====
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -134,7 +125,6 @@ are readily available following `bindToServer`. For all others, you need to use
|
|||
.baseUrl("/test")
|
||||
.build();
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -148,7 +138,6 @@ up to the point of performing a request by using `exchange()`. What follows afte
|
|||
|
||||
Typically, you start by asserting the response status and headers, as follows:
|
||||
|
||||
====
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -159,7 +148,6 @@ Typically, you start by asserting the response status and headers, as follows:
|
|||
.expectHeader().contentType(MediaType.APPLICATION_JSON_UTF8)
|
||||
// ...
|
||||
----
|
||||
====
|
||||
|
||||
Then you specify how to decode and consume the response body:
|
||||
|
||||
|
@ -169,7 +157,6 @@ Then you specify how to decode and consume the response body:
|
|||
|
||||
Then you can use built-in assertions for the body. The following example shows one way to do so:
|
||||
|
||||
====
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -178,11 +165,9 @@ Then you can use built-in assertions for the body. The following example shows o
|
|||
.expectStatus().isOk()
|
||||
.expectBodyList(Person.class).hasSize(3).contains(person);
|
||||
----
|
||||
====
|
||||
|
||||
You can also go beyond the built-in assertions and create your own, as the following example shows:
|
||||
|
||||
====
|
||||
----
|
||||
client.get().uri("/persons/1")
|
||||
.exchange()
|
||||
|
@ -192,11 +177,9 @@ You can also go beyond the built-in assertions and create your own, as the follo
|
|||
// custom assertions (e.g. AssertJ)...
|
||||
});
|
||||
----
|
||||
====
|
||||
|
||||
You can also exit the workflow and get a result, as follows:
|
||||
|
||||
====
|
||||
----
|
||||
EntityExchangeResult<Person> result = client.get().uri("/persons/1")
|
||||
.exchange()
|
||||
|
@ -204,7 +187,6 @@ You can also exit the workflow and get a result, as follows:
|
|||
.expectBody(Person.class)
|
||||
.returnResult();
|
||||
----
|
||||
====
|
||||
|
||||
TIP: When you need to decode to a target type with generics, look for the overloaded methods
|
||||
that accept
|
||||
|
@ -219,7 +201,6 @@ instead of `Class<T>`.
|
|||
If the response has no content (or you do not care if it does) use `Void.class`, which ensures
|
||||
that resources are released. The following example shows how to do so:
|
||||
|
||||
====
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -228,11 +209,9 @@ that resources are released. The following example shows how to do so:
|
|||
.expectStatus().isNotFound()
|
||||
.expectBody(Void.class);
|
||||
----
|
||||
====
|
||||
|
||||
Alternatively, if you want to assert there is no response content, you can use code similar to the following:
|
||||
|
||||
====
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -242,7 +221,6 @@ Alternatively, if you want to assert there is no response content, you can use c
|
|||
.expectStatus().isCreated()
|
||||
.expectBody().isEmpty();
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -253,7 +231,6 @@ When you use `expectBody()`, the response is consumed as a `byte[]`. This is use
|
|||
raw content assertions. For example, you can use
|
||||
http://jsonassert.skyscreamer.org[JSONAssert] to verify JSON content, as follows:
|
||||
|
||||
====
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -263,11 +240,9 @@ http://jsonassert.skyscreamer.org[JSONAssert] to verify JSON content, as follows
|
|||
.expectBody()
|
||||
.json("{\"name\":\"Jane\"}")
|
||||
----
|
||||
====
|
||||
|
||||
You can also use https://github.com/jayway/JsonPath[JSONPath] expressions, as follows:
|
||||
|
||||
====
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -278,7 +253,6 @@ You can also use https://github.com/jayway/JsonPath[JSONPath] expressions, as fo
|
|||
.jsonPath("$[0].name").isEqualTo("Jane")
|
||||
.jsonPath("$[1].name").isEqualTo("Jason");
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -289,7 +263,6 @@ To test infinite streams (for example, `"text/event-stream"` or `"application/st
|
|||
you need to exit the chained API (by using `returnResult`), immediately after the response status
|
||||
and header assertions, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -300,13 +273,11 @@ and header assertions, as the following example shows:
|
|||
.returnResult(MyEvent.class);
|
||||
|
||||
----
|
||||
====
|
||||
|
||||
Now you can consume the `Flux<T>`, assert decoded objects as they come, and then
|
||||
cancel at some point when test objectives are met. We recommend using the `StepVerifier`
|
||||
from the `reactor-test` module to do that, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -319,7 +290,6 @@ from the `reactor-test` module to do that, as the following example shows:
|
|||
.thenCancel()
|
||||
.verify();
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -43,7 +43,6 @@ set of Spring XML configuration files to load.
|
|||
|
||||
Consider the following `<listener/>` configuration:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -51,11 +50,9 @@ Consider the following `<listener/>` configuration:
|
|||
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
|
||||
</listener>
|
||||
----
|
||||
====
|
||||
|
||||
Further consider the following `<context-param/>` configuration:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -64,7 +61,6 @@ Further consider the following `<context-param/>` configuration:
|
|||
<param-value>/WEB-INF/applicationContext*.xml</param-value>
|
||||
</context-param>
|
||||
----
|
||||
====
|
||||
|
||||
If you do not specify the `contextConfigLocation` context parameter, the
|
||||
`ContextLoaderListener` looks for a file called `/WEB-INF/applicationContext.xml` to
|
||||
|
@ -79,13 +75,11 @@ created by the `ContextLoaderListener`.
|
|||
|
||||
The following example shows how to get the `WebApplicationContext`:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(servletContext);
|
||||
----
|
||||
====
|
||||
|
||||
The
|
||||
{api-spring-framework}/web/context/support/WebApplicationContextUtils.html[`WebApplicationContextUtils`]
|
||||
|
@ -140,7 +134,6 @@ implementation.
|
|||
Configuration-wise, you can define `SpringBeanFacesELResolver` in your JSF
|
||||
`faces-context.xml` file, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -151,7 +144,6 @@ Configuration-wise, you can define `SpringBeanFacesELResolver` in your JSF
|
|||
</application>
|
||||
</faces-config>
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -166,13 +158,11 @@ takes a `FacesContext` parameter rather than a `ServletContext` parameter.
|
|||
|
||||
The following example shows how to use `FacesContextUtils`:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
ApplicationContext ctx = FacesContextUtils.getWebApplicationContext(FacesContext.getCurrentInstance());
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
`UriComponentsBuilder` helps to build URI's from URI templates with variables, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -21,12 +20,11 @@
|
|||
<3> Request to have the URI template and URI variables encoded.
|
||||
<4> Build a `UriComponents`.
|
||||
<5> Expand variables and obtain the `URI`.
|
||||
====
|
||||
|
||||
|
||||
The preceding example can be consolidated into one chain and shortened with `buildAndExpand`,
|
||||
as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -37,12 +35,10 @@ as the following example shows:
|
|||
.buildAndExpand("Westin", "123")
|
||||
.toUri();
|
||||
----
|
||||
====
|
||||
|
||||
You can shorten it further by going directly to a URI (which implies encoding),
|
||||
as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -51,11 +47,9 @@ as the following example shows:
|
|||
.queryParam("q", "{q}")
|
||||
.build("Westin", "123");
|
||||
----
|
||||
====
|
||||
|
||||
You shorter it further still with a full URI template, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -63,7 +57,6 @@ You shorter it further still with a full URI template, as the following example
|
|||
.fromUriString("http://example.com/hotels/{hotel}?q={q}")
|
||||
.build("Westin", "123");
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -83,7 +76,6 @@ exposes shared configuration options.
|
|||
|
||||
The following example shows how to configure a `RestTemplate`:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -96,11 +88,9 @@ The following example shows how to configure a `RestTemplate`:
|
|||
RestTemplate restTemplate = new RestTemplate();
|
||||
restTemplate.setUriTemplateHandler(factory);
|
||||
----
|
||||
====
|
||||
|
||||
The following example configures a `WebClient`:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -112,13 +102,11 @@ The following example configures a `WebClient`:
|
|||
|
||||
WebClient client = WebClient.builder().uriBuilderFactory(factory).build();
|
||||
----
|
||||
====
|
||||
|
||||
In addition, you can also use `DefaultUriBuilderFactory` directly. It is similar to using
|
||||
`UriComponentsBuilder` but, instead of static factory methods, it is an actual instance
|
||||
that holds configuration and preferences, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -129,7 +117,6 @@ that holds configuration and preferences, as the following example shows:
|
|||
.queryParam("q", "{q}")
|
||||
.build("Westin", "123");
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -157,7 +144,6 @@ URI variables intentionally contain reserved characters.
|
|||
|
||||
The following example uses the first option:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -169,12 +155,10 @@ URI uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
|
|||
|
||||
// Result is "/hotel%20list/New%20York?q=foo%2Bbar"
|
||||
----
|
||||
====
|
||||
|
||||
You can shorten the preceding example by going directly to the URI (which implies encoding),
|
||||
as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -182,24 +166,20 @@ URI uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
|
|||
.queryParam("q", "{q}")
|
||||
.build("New York", "foo+bar")
|
||||
----
|
||||
====
|
||||
|
||||
You can shorten it further still with a full URI template, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
URI uri = UriComponentsBuilder.fromPath("/hotel list/{city}?q={q}")
|
||||
.build("New York", "foo+bar")
|
||||
----
|
||||
====
|
||||
|
||||
The `WebClient` and the `RestTemplate` expand and encode URI templates internally through
|
||||
the `UriBuilderFactory` strategy. Both can be configured with a custom strategy.
|
||||
as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -214,7 +194,6 @@ as the following example shows:
|
|||
// Customize the WebClient..
|
||||
WebClient client = WebClient.builder().uriBuilderFactory(factory).build();
|
||||
----
|
||||
====
|
||||
|
||||
The `DefaultUriBuilderFactory` implementation uses `UriComponentsBuilder` internally to
|
||||
expand and encode URI templates. As a factory, it provides a single place to configure
|
||||
|
|
|
@ -83,7 +83,6 @@ The {api-spring-framework}/web/bind/annotation/CrossOrigin.html[`@CrossOrigin`]
|
|||
annotation enables cross-origin requests on annotated controller methods, as the
|
||||
following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -103,7 +102,6 @@ public class AccountController {
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
By default, `@CrossOrigin` allows:
|
||||
|
||||
|
@ -121,7 +119,6 @@ should be used only where appropriate.
|
|||
`@CrossOrigin` is supported at the class level, too, and inherited by all methods.
|
||||
The following example specifies a certain domain and sets `maxAge` to an hour:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -141,11 +138,9 @@ public class AccountController {
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
You can use `@CrossOrigin` at both the class and the method level,
|
||||
as the following example shows:
|
||||
====
|
||||
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
|
@ -169,8 +164,6 @@ public class AccountController {
|
|||
----
|
||||
<1> Using `@CrossOrigin` at the class level.
|
||||
<2> Using `@CrossOrigin` at the method level.
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -198,7 +191,6 @@ should be used only where appropriate.
|
|||
To enable CORS in the WebFlux Java configuration, you can use the `CorsRegistry` callback,
|
||||
as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -220,7 +212,6 @@ public class WebConfig implements WebFluxConfigurer {
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -236,7 +227,6 @@ good fit with <<webflux-fn,functional endpoints>>.
|
|||
To configure the filter, you can declare a `CorsWebFilter` bean and pass a
|
||||
`CorsConfigurationSource` to its constructor, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim"]
|
||||
----
|
||||
|
@ -259,4 +249,3 @@ CorsWebFilter corsFilter() {
|
|||
return new CorsWebFilter(source);
|
||||
}
|
||||
----
|
||||
====
|
||||
|
|
|
@ -28,7 +28,6 @@ difference that router functions provide not just data, but also behavior.
|
|||
`RouterFunctions.route()` provides a router builder that facilitates the creation of routers,
|
||||
as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -63,7 +62,6 @@ public class PersonHandler {
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
One way to run a `RouterFunction` is to turn it into an `HttpHandler` and install it
|
||||
through one of the built-in <<web-reactive.adoc#webflux-httphandler,server adapters>>:
|
||||
|
@ -97,62 +95,50 @@ while access to the body is provided through the `body` methods.
|
|||
|
||||
The following example extracts the request body to a `Mono<String>`:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
Mono<String> string = request.bodyToMono(String.class);
|
||||
----
|
||||
====
|
||||
|
||||
The following example extracts the body to a `Flux<Person>`, where `Person` objects are decoded from some
|
||||
serialized form, such as JSON or XML:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
Flux<Person> people = request.bodyToFlux(Person.class);
|
||||
----
|
||||
====
|
||||
|
||||
The preceding examples are shortcuts that use the more general `ServerRequest.body(BodyExtractor)`,
|
||||
which accepts the `BodyExtractor` functional strategy interface. The utility class
|
||||
`BodyExtractors` provides access to a number of instances. For example, the preceding examples can
|
||||
also be written as follows:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
Mono<String> string = request.body(BodyExtractors.toMono(String.class));
|
||||
Flux<Person> people = request.body(BodyExtractors.toFlux(Person.class));
|
||||
----
|
||||
====
|
||||
|
||||
The following example shows how to access form data:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
Mono<MultiValueMap<String, String> map = request.body(BodyExtractors.toFormData());
|
||||
----
|
||||
====
|
||||
|
||||
The following example shows how to access multipart data as a map:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
Mono<MultiValueMap<String, Part> map = request.body(BodyExtractors.toMultipartData());
|
||||
----
|
||||
====
|
||||
|
||||
The following example shows how to access multiparts, one at a time, in streaming fashion:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
Flux<Part> parts = request.body(BodyExtractos.toParts());
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -164,23 +150,19 @@ a `build` method to create it. You can use the builder to set the response statu
|
|||
headers, or to provide a body. The following example creates a 200 (OK) response with JSON
|
||||
content:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
Mono<Person> person = ...
|
||||
ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(person, Person.class);
|
||||
----
|
||||
====
|
||||
|
||||
The following example shows how to build a 201 (CREATED) response with a `Location` header and no body:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
URI location = ...
|
||||
ServerResponse.created(location).build();
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -189,14 +171,12 @@ ServerResponse.created(location).build();
|
|||
|
||||
We can write a handler function as a lambda, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
HandlerFunction<ServerResponse> helloWorld =
|
||||
request -> ServerResponse.ok().body(fromObject("Hello World"));
|
||||
----
|
||||
====
|
||||
|
||||
That is convenient, but in an application we need multiple functions, and multiple inline
|
||||
lambda's can get messy.
|
||||
|
@ -204,7 +184,6 @@ Therefore, it is useful to group related handler functions together into a handl
|
|||
has a similar role as `@Controller` in an annotation-based application.
|
||||
For example, the following class exposes a reactive `Person` repository:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -248,7 +227,6 @@ when the `Person` has been saved).
|
|||
<3> `getPerson` is a handler function that returns a single person, identified by the `id` path
|
||||
variable. We retrieve that `Person` from the repository and create a JSON response, if it is
|
||||
found. If it is not found, we use `switchIfEmpty(Mono<T>)` to return a 404 Not Found response.
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -324,7 +302,6 @@ and so on.
|
|||
The following example uses a request predicate to create a constraint based on the `Accept`
|
||||
header:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -332,7 +309,6 @@ RouterFunction<ServerResponse> route = RouterFunctions.route()
|
|||
.GET("/hello-world", accept(MediaType.TEXT_PLAIN),
|
||||
request -> Response.ok().body(fromObject("Hello World")));
|
||||
----
|
||||
====
|
||||
|
||||
You can compose multiple request predicates together by using:
|
||||
|
||||
|
@ -368,7 +344,6 @@ There are also other ways to compose multiple router functions together:
|
|||
The following example shows the composition of four routes:
|
||||
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -395,7 +370,6 @@ RouterFunction<ServerResponse> route = route()
|
|||
`PersonHandler.createPerson`, and
|
||||
<4> `otherRoute` is a router function that is created elsewhere, and added to the route built.
|
||||
|
||||
====
|
||||
|
||||
|
||||
=== Nested Routes
|
||||
|
@ -409,7 +383,6 @@ When using annotations, you would remove this duplication by using a type-level
|
|||
In WebFlux.fn, path predicates can be shared through the `path` method on the router function builder.
|
||||
For instance, the last few lines of the example above can be improved in the following way by using nested routes:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -420,7 +393,6 @@ RouterFunction<ServerResponse> route = route()
|
|||
.POST("/person", handler::createPerson))
|
||||
.build();
|
||||
----
|
||||
====
|
||||
|
||||
Note that second parameter of `path` is a consumer that takes the a router builder.
|
||||
|
||||
|
@ -429,7 +401,6 @@ the `nest` method on the builder.
|
|||
The above still contains some duplication in the form of the shared `Accept`-header predicate.
|
||||
We can further improve by using the `nest` method together with `accept`:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -441,7 +412,6 @@ RouterFunction<ServerResponse> route = route()
|
|||
.POST("/person", handler::createPerson))
|
||||
.build();
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
[[webflux-fn-running]]
|
||||
|
@ -478,7 +448,6 @@ starter.
|
|||
The following example shows a WebFlux Java configuration (see
|
||||
<<web-reactive.adoc#webflux-dispatcher-handler,DispatcherHandler>> for how to run it):
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -514,7 +483,6 @@ public class WebConfig implements WebFluxConfigurer {
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -529,7 +497,6 @@ The filter will apply to all routes that are built by the builder.
|
|||
This means that filters defined in nested routes do not apply to "top-level" routes.
|
||||
For instance, consider the following example:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -547,7 +514,7 @@ RouterFunction<ServerResponse> route = route()
|
|||
----
|
||||
<1> The `before` filter that adds a custom request header is only applied to the two GET routes.
|
||||
<2> The `after` filter that logs the response is applied to all routes, including the nested ones.
|
||||
====
|
||||
|
||||
|
||||
The `filter` method on the router builder takes a `HandlerFilterFunction`: a
|
||||
function that takes a `ServerRequest` and `HandlerFunction` and returns a `ServerResponse`.
|
||||
|
@ -559,7 +526,6 @@ Now we can add a simple security filter to our route, assuming that we have a `S
|
|||
can determine whether a particular path is allowed.
|
||||
The following example shows how to do so:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -582,7 +548,6 @@ RouterFunction<ServerResponse> route = route()
|
|||
})
|
||||
.build();
|
||||
----
|
||||
====
|
||||
|
||||
The preceding example demonstrates that invoking the `next.handle(ServerRequest)` is optional.
|
||||
We allow only the handler function to be executed when access is allowed.
|
||||
|
|
|
@ -47,7 +47,6 @@ integration for using Spring WebFlux with FreeMarker templates.
|
|||
|
||||
The following example shows how to configure FreeMarker as a view technology:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -70,7 +69,6 @@ The following example shows how to configure FreeMarker as a view technology:
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Your templates need to be stored in the directory specified by the `FreeMarkerConfigurer`,
|
||||
shown in the preceding example. Given the preceding configuration, if your controller returns the view name,
|
||||
|
@ -89,7 +87,6 @@ the `FreeMarkerConfigurer` bean. The `freemarkerSettings` property requires a
|
|||
`java.util.Properties` object, and the `freemarkerVariables` property requires a
|
||||
`java.util.Map`. The following example shows how to use a `FreeMarkerConfigurer`:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -111,7 +108,6 @@ the `FreeMarkerConfigurer` bean. The `freemarkerSettings` property requires a
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
See the FreeMarker documentation for details of settings and variables as they apply to
|
||||
the `Configuration` object.
|
||||
|
@ -173,7 +169,6 @@ You can declare a `ScriptTemplateConfigurer` bean to specify the script engine t
|
|||
the script files to load, what function to call to render templates, and so on.
|
||||
The following example uses Mustache templates and the Nashorn JavaScript engine:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -197,7 +192,6 @@ The following example uses Mustache templates and the Nashorn JavaScript engine:
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The `render` function is called with the following parameters:
|
||||
|
||||
|
@ -217,7 +211,6 @@ http://en.wikipedia.org/wiki/Polyfill[polyfill] in order to emulate some
|
|||
browser facilities not available in the server-side script engine.
|
||||
The following example shows how to set a custom render function:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -241,7 +234,6 @@ The following example shows how to set a custom render function:
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: Setting the `sharedEngine` property to `false` is required when using non-thread-safe
|
||||
script engines with templating libraries not designed for concurrency, such as Handlebars or
|
||||
|
@ -251,13 +243,11 @@ to https://bugs.openjdk.java.net/browse/JDK-8076099[this bug].
|
|||
`polyfill.js` defines only the `window` object needed by Handlebars to run properly,
|
||||
as the following snippet shows:
|
||||
|
||||
====
|
||||
[source,javascript,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
var window = {};
|
||||
----
|
||||
====
|
||||
|
||||
This basic `render.js` implementation compiles the template before using it. A production
|
||||
ready implementation should also store and reused cached templates or pre-compiled templates.
|
||||
|
@ -265,7 +255,6 @@ This can be done on the script side, as well as any customization you need (mana
|
|||
template engine configuration for example).
|
||||
The following example shows how compile a template:
|
||||
|
||||
====
|
||||
[source,javascript,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -274,7 +263,6 @@ The following example shows how compile a template:
|
|||
return compiledTemplate(model);
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Check out the Spring Framework unit tests,
|
||||
https://github.com/spring-projects/spring-framework/tree/master/spring-webflux/src/test/java/org/springframework/web/reactive/result/view/script[Java], and
|
||||
|
|
|
@ -38,7 +38,6 @@ You can also use `WebClient.builder()` with further options:
|
|||
|
||||
The following example configures <<web-reactive.adoc#webflux-codecs,HTTP codecs>>:
|
||||
|
||||
====
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -52,12 +51,10 @@ The following example configures <<web-reactive.adoc#webflux-codecs,HTTP codecs>
|
|||
.exchangeStrategies(strategies)
|
||||
.build();
|
||||
----
|
||||
====
|
||||
|
||||
Once built, a `WebClient` instance is immutable. However, you can clone it and build a
|
||||
modified copy without affecting the original instance, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -71,7 +68,6 @@ modified copy without affecting the original instance, as the following example
|
|||
|
||||
// client2 has filterA, filterB, filterC, filterD
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -80,7 +76,6 @@ modified copy without affecting the original instance, as the following example
|
|||
|
||||
To customize Reactor Netty settings, simple provide a pre-configured `HttpClient`:
|
||||
|
||||
====
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -90,7 +85,6 @@ To customize Reactor Netty settings, simple provide a pre-configured `HttpClient
|
|||
.clientConnector(new ReactorClientHttpConnector(httpClient))
|
||||
.build();
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
[[webflux-client-builder-reactor-resources]]
|
||||
|
@ -108,7 +102,6 @@ application deployed as a WAR), you can declare a Spring-managed bean of type
|
|||
Netty global resources are shut down when the Spring `ApplicationContext` is closed,
|
||||
as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -117,13 +110,11 @@ as the following example shows:
|
|||
return new ReactorResourceFactory();
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
You can also choose not to participate in the global Reactor Netty resources. However,
|
||||
in this mode, the burden is on you to ensure that all Reactor Netty client and server
|
||||
instances use shared resources, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -150,7 +141,6 @@ instances use shared resources, as the following example shows:
|
|||
<1> Create resources independent of global ones.
|
||||
<2> Use the `ReactorClientHttpConnector` constructor with resource factory.
|
||||
<3> Plug the connector into the `WebClient.Builder`.
|
||||
====
|
||||
|
||||
|
||||
[[webflux-client-builder-reactor-timeout]]
|
||||
|
@ -158,7 +148,6 @@ instances use shared resources, as the following example shows:
|
|||
|
||||
To configure a connection timeout:
|
||||
|
||||
====
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -168,11 +157,9 @@ HttpClient httpClient = HttpClient.create()
|
|||
.tcpConfiguration(client ->
|
||||
client.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000));
|
||||
----
|
||||
====
|
||||
|
||||
To configure a read and/or write timeout values:
|
||||
|
||||
====
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -185,7 +172,6 @@ HttpClient httpClient = HttpClient.create()
|
|||
.addHandlerLast(new ReadTimeoutHandler(10))
|
||||
.addHandlerLast(new WriteTimeoutHandler(10))));
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -194,7 +180,6 @@ HttpClient httpClient = HttpClient.create()
|
|||
|
||||
The following example shows how to customize Jetty `HttpClient` settings:
|
||||
|
||||
====
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -204,7 +189,6 @@ The following example shows how to customize Jetty `HttpClient` settings:
|
|||
|
||||
WebClient webClient = WebClient.builder().clientConnector(connector).build();
|
||||
----
|
||||
====
|
||||
|
||||
By default, `HttpClient` creates its own resources (`Executor`, `ByteBufferPool`, `Scheduler`),
|
||||
which remain active until the process exits or `stop()` is called.
|
||||
|
@ -214,7 +198,6 @@ ensure that the resources are shut down when the Spring `ApplicationContext` is
|
|||
declaring a Spring-managed bean of type `JettyResourceFactory`, as the following example
|
||||
shows:
|
||||
|
||||
====
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -238,7 +221,6 @@ shows:
|
|||
----
|
||||
<1> Use the `JettyClientHttpConnector` constructor with resource factory.
|
||||
<2> Plug the connector into the `WebClient.Builder`.
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -249,7 +231,6 @@ shows:
|
|||
The `retrieve()` method is the easiest way to get a response body and decode it.
|
||||
The following example shows how to do so:
|
||||
|
||||
====
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -260,11 +241,9 @@ The following example shows how to do so:
|
|||
.retrieve()
|
||||
.bodyToMono(Person.class);
|
||||
----
|
||||
====
|
||||
|
||||
You can also get a stream of objects decoded from the response, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -273,7 +252,6 @@ You can also get a stream of objects decoded from the response, as the following
|
|||
.retrieve()
|
||||
.bodyToFlux(Quote.class);
|
||||
----
|
||||
====
|
||||
|
||||
By default, responses with 4xx or 5xx status codes result in an
|
||||
`WebClientResponseException` or one of its HTTP status specific sub-classes, such as
|
||||
|
@ -281,7 +259,6 @@ By default, responses with 4xx or 5xx status codes result in an
|
|||
You can also use the `onStatus` method to customize the resulting exception,
|
||||
as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -292,7 +269,6 @@ as the following example shows:
|
|||
.onStatus(HttpStatus::is5xxServerError, response -> ...)
|
||||
.bodyToMono(Person.class);
|
||||
----
|
||||
====
|
||||
|
||||
When `onStatus` is used, if the response is expected to have content, then the `onStatus`
|
||||
callback should consume it. If not, the content will be automatically drained to ensure
|
||||
|
@ -307,7 +283,6 @@ resources are released.
|
|||
The `exchange()` method provides more control than the `retrieve` method. The following example is equivalent
|
||||
to `retrieve()` but also provides access to the `ClientResponse`:
|
||||
|
||||
====
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -316,11 +291,9 @@ to `retrieve()` but also provides access to the `ClientResponse`:
|
|||
.exchange()
|
||||
.flatMap(response -> response.bodyToMono(Person.class));
|
||||
----
|
||||
====
|
||||
|
||||
At this level, you can also create a full `ResponseEntity`:
|
||||
|
||||
====
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -329,7 +302,6 @@ At this level, you can also create a full `ResponseEntity`:
|
|||
.exchange()
|
||||
.flatMap(response -> response.toEntity(Person.class));
|
||||
----
|
||||
====
|
||||
|
||||
Note that (unlike `retrieve()`), with `exchange()`, there are no automatic error signals for
|
||||
4xx and 5xx responses. You have to check the status code and decide how to proceed.
|
||||
|
@ -348,7 +320,6 @@ is closed and is not placed back in the pool.
|
|||
|
||||
The request body can be encoded from an `Object`, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -361,11 +332,9 @@ The request body can be encoded from an `Object`, as the following example shows
|
|||
.retrieve()
|
||||
.bodyToMono(Void.class);
|
||||
----
|
||||
====
|
||||
|
||||
You can also have a stream of objects be encoded, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -378,12 +347,10 @@ You can also have a stream of objects be encoded, as the following example shows
|
|||
.retrieve()
|
||||
.bodyToMono(Void.class);
|
||||
----
|
||||
====
|
||||
|
||||
Alternatively, if you have the actual value, you can use the `syncBody` shortcut method,
|
||||
as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -396,7 +363,6 @@ as the following example shows:
|
|||
.retrieve()
|
||||
.bodyToMono(Void.class);
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -407,7 +373,6 @@ To send form data, you can provide a `MultiValueMap<String, String>` as the body
|
|||
content is automatically set to `application/x-www-form-urlencoded` by the
|
||||
`FormHttpMessageWriter`. The following example shows how to use `MultiValueMap<String, String>`:
|
||||
|
||||
====
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -419,11 +384,9 @@ content is automatically set to `application/x-www-form-urlencoded` by the
|
|||
.retrieve()
|
||||
.bodyToMono(Void.class);
|
||||
----
|
||||
====
|
||||
|
||||
You can also supply form data in-line by using `BodyInserters`, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -435,7 +398,6 @@ You can also supply form data in-line by using `BodyInserters`, as the following
|
|||
.retrieve()
|
||||
.bodyToMono(Void.class);
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -467,7 +429,6 @@ builder `part` methods.
|
|||
Once a `MultiValueMap` is prepared, the easiest way to pass it to the the `WebClient` is
|
||||
through the `syncBody` method, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -479,7 +440,6 @@ through the `syncBody` method, as the following example shows:
|
|||
.retrieve()
|
||||
.bodyToMono(Void.class);
|
||||
----
|
||||
====
|
||||
|
||||
If the `MultiValueMap` contains at least one non-`String` value, which could also
|
||||
represent regular form data (that is, `application/x-www-form-urlencoded`), you need not
|
||||
|
@ -489,7 +449,6 @@ set the `Content-Type` to `multipart/form-data`. This is always the case when us
|
|||
As an alternative to `MultipartBodyBuilder`, you can also provide multipart content,
|
||||
inline-style, through the built-in `BodyInserters`, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -501,7 +460,6 @@ inline-style, through the built-in `BodyInserters`, as the following example sho
|
|||
.retrieve()
|
||||
.bodyToMono(Void.class);
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -512,7 +470,6 @@ inline-style, through the built-in `BodyInserters`, as the following example sho
|
|||
You can register a client filter (`ExchangeFilterFunction`) through the `WebClient.Builder`
|
||||
in order to intercept and modify requests, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -527,12 +484,10 @@ WebClient client = WebClient.builder()
|
|||
})
|
||||
.build();
|
||||
----
|
||||
====
|
||||
|
||||
This can be used for cross-cutting concerns, such as authentication. The following example uses
|
||||
a filter for basic authentication through a static factory method:
|
||||
|
||||
====
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -543,13 +498,11 @@ WebClient client = WebClient.builder()
|
|||
.filter(basicAuthentication("user", "password"))
|
||||
.build();
|
||||
----
|
||||
====
|
||||
|
||||
Filters apply globally to every request. To change a filter's behavior for a specific
|
||||
request, you can add request attributes to the `ClientRequest` that can then be accessed
|
||||
by all filters in the chain, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -567,13 +520,11 @@ client.get().uri("http://example.org/")
|
|||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
You can also replicate an existing `WebClient`, insert new filters, or remove already
|
||||
registered filters. The following example, inserts a basic authentication filter at
|
||||
index 0:
|
||||
|
||||
====
|
||||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -586,7 +537,6 @@ WebClient client = webClient.mutate()
|
|||
})
|
||||
.build();
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -26,7 +26,6 @@ server-side applications that handle WebSocket messages.
|
|||
To create a WebSocket server, you can first create a `WebSocketHandler`.
|
||||
The following example shows how to do so:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -41,11 +40,9 @@ The following example shows how to do so:
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Then you can map it to a URL and add a `WebSocketHandlerAdapter`, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -69,7 +66,6 @@ Then you can map it to a URL and add a `WebSocketHandlerAdapter`, as the followi
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -110,7 +106,6 @@ receives a cancellation signal.
|
|||
The most basic implementation of a handler is one that handles the inbound stream. The
|
||||
following example shows such an implementation:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -133,7 +128,7 @@ class ExampleHandler implements WebSocketHandler {
|
|||
<2> Do something with each message.
|
||||
<3> Perform nested asynchronous operations that use the message content.
|
||||
<4> Return a `Mono<Void>` that completes when receiving completes.
|
||||
====
|
||||
|
||||
|
||||
TIP: For nested, asynchronous operations, you may need to call `message.retain()` on underlying
|
||||
servers that use pooled data buffers (for example, Netty). Otherwise, the data buffer may be
|
||||
|
@ -142,7 +137,6 @@ released before you have had a chance to read the data. For more background, see
|
|||
|
||||
The following implementation combines the inbound and outbound streams:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -167,12 +161,11 @@ class ExampleHandler implements WebSocketHandler {
|
|||
<1> Handle the inbound message stream.
|
||||
<2> Create the outbound message, producing a combined flow.
|
||||
<3> Return a `Mono<Void>` that does not complete while we continue to receive.
|
||||
====
|
||||
|
||||
|
||||
Inbound and outbound streams can be independent and be joined only for completion,
|
||||
as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -200,7 +193,6 @@ class ExampleHandler implements WebSocketHandler {
|
|||
<1> Handle inbound message stream.
|
||||
<2> Send outgoing messages.
|
||||
<3> Join the streams and return a `Mono<Void>` that completes when either stream ends.
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -243,7 +235,6 @@ The `RequestUpgradeStrategy` for each server exposes WebSocket-related configura
|
|||
options available for the underlying WebSocket engine. The following example sets
|
||||
WebSocket options when running on Tomcat:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -263,7 +254,6 @@ WebSocket options when running on Tomcat:
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Check the upgrade strategy for your server to see what options are available. Currently,
|
||||
only Tomcat and Jetty expose such options.
|
||||
|
@ -296,7 +286,6 @@ API to suspend receiving messages for back pressure.
|
|||
To start a WebSocket session, you can create an instance of the client and use its `execute`
|
||||
methods:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -308,7 +297,6 @@ client.execute(url, session ->
|
|||
.doOnNext(System.out::println)
|
||||
.then());
|
||||
----
|
||||
====
|
||||
|
||||
Some clients, such as Jetty, implement `Lifecycle` and need to be stopped and started
|
||||
before you can use them. All clients have constructor options related to configuration
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -83,7 +83,6 @@ The {api-spring-framework}/web/bind/annotation/CrossOrigin.html[`@CrossOrigin`]
|
|||
annotation enables cross-origin requests on annotated controller methods,
|
||||
as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -103,7 +102,6 @@ public class AccountController {
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
By default, `@CrossOrigin` allows:
|
||||
|
||||
|
@ -120,7 +118,6 @@ should only be used where appropriate.
|
|||
`@CrossOrigin` is supported at the class level, too, and is inherited by all methods,
|
||||
as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -140,12 +137,10 @@ public class AccountController {
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
You can use `@CrossOrigin` at both the class level and the method level,
|
||||
as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -166,7 +161,6 @@ public class AccountController {
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -202,7 +196,6 @@ should only be used where appropriate.
|
|||
To enable CORS in the MVC Java config, you can use the `CorsRegistry` callback,
|
||||
as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -224,7 +217,6 @@ public class WebConfig implements WebMvcConfigurer {
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -234,7 +226,6 @@ public class WebConfig implements WebMvcConfigurer {
|
|||
To enable CORS in the XML namespace, you can use the `<mvc:cors>` element,
|
||||
as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim"]
|
||||
----
|
||||
|
@ -252,7 +243,6 @@ as the following example shows:
|
|||
|
||||
</mvc:cors>
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -272,7 +262,6 @@ for CORS.
|
|||
To configure the filter, pass a
|
||||
`CorsConfigurationSource` to its constructor, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim"]
|
||||
----
|
||||
|
@ -291,4 +280,3 @@ source.registerCorsConfiguration("/**", config);
|
|||
|
||||
CorsFilter filter = new CorsFilter(source);
|
||||
----
|
||||
====
|
||||
|
|
|
@ -46,7 +46,6 @@ integration for using Spring MVC with FreeMarker templates.
|
|||
|
||||
The following example shows how to configure FreeMarker as a view technology:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -69,11 +68,9 @@ The following example shows how to configure FreeMarker as a view technology:
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The following example shows how to configure the same in XML:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -88,12 +85,10 @@ The following example shows how to configure the same in XML:
|
|||
<mvc:template-loader-path location="/WEB-INF/freemarker"/>
|
||||
</mvc:freemarker-configurer>
|
||||
----
|
||||
====
|
||||
|
||||
Alternatively, you can also declare the `FreeMarkerConfigurer` bean for full control over all
|
||||
properties, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -101,7 +96,6 @@ properties, as the following example shows:
|
|||
<property name="templateLoaderPath" value="/WEB-INF/freemarker/"/>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
Your templates need to be stored in the directory specified by the `FreeMarkerConfigurer`
|
||||
shown in the preceding example. Given the preceding configuration, if your controller returns a view name
|
||||
|
@ -119,7 +113,6 @@ the `FreeMarkerConfigurer` bean. The `freemarkerSettings` property requires a
|
|||
`java.util.Properties` object, and the `freemarkerVariables` property requires a
|
||||
`java.util.Map`. The following example shows how to do so:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -134,7 +127,6 @@ the `FreeMarkerConfigurer` bean. The `freemarkerSettings` property requires a
|
|||
|
||||
<bean id="fmXmlEscape" class="freemarker.template.utility.XmlEscape"/>
|
||||
----
|
||||
====
|
||||
|
||||
See the FreeMarker documentation for details of settings and variables as they apply to
|
||||
the `Configuration` object.
|
||||
|
@ -173,7 +165,6 @@ controller, you can use code similar to the next example to bind to field values
|
|||
display error messages for each input field in similar fashion to the JSP equivalent.
|
||||
The following example shows the `personForm` view that was configured earlier:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -196,7 +187,6 @@ The following example shows the `personForm` view that was configured earlier:
|
|||
...
|
||||
</html>
|
||||
----
|
||||
====
|
||||
|
||||
`<@spring.bind>` requires a 'path' argument, which consists of the name of your command
|
||||
object (it is 'command', unless you changed it in your `FormController` properties)
|
||||
|
@ -318,14 +308,12 @@ time, a class name or style attribute. Note that FreeMarker can specify default
|
|||
values for the attributes parameter. The following example shows how to use the `formInput`
|
||||
and `showWErrors` macros:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
<@spring.formInput "command.name"/>
|
||||
<@spring.showErrors "<br>"/>
|
||||
----
|
||||
====
|
||||
|
||||
The next example shows the output of the form fragment, generating the name field and displaying a
|
||||
validation error after the form was submitted with no value in the field. Validation
|
||||
|
@ -333,7 +321,6 @@ occurs through Spring's Validation framework.
|
|||
|
||||
The generated HTML resembles the following example:
|
||||
|
||||
====
|
||||
[source,jsp,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -344,7 +331,6 @@ The generated HTML resembles the following example:
|
|||
<br>
|
||||
<br>
|
||||
----
|
||||
====
|
||||
|
||||
The `formTextarea` macro works the same way as the `formInput` macro and accepts the same
|
||||
parameter list. Commonly, the second parameter (attributes) is used to pass style
|
||||
|
@ -370,7 +356,6 @@ value of 'London' for this field, so no validation is necessary. When the form i
|
|||
rendered, the entire list of cities to choose from is supplied as reference data in the
|
||||
model under the name 'cityMap'. The following listing shows the example:
|
||||
|
||||
====
|
||||
[source,jsp,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -378,7 +363,6 @@ model under the name 'cityMap'. The following listing shows the example:
|
|||
Town:
|
||||
<@spring.formRadioButtons "command.address.town", cityMap, ""/><br><br>
|
||||
----
|
||||
====
|
||||
|
||||
The preceding listing renders a line of radio buttons, one for each value in `cityMap`, and uses a
|
||||
separator of `""`. No additional attributes are supplied (the last parameter to the macro is
|
||||
|
@ -387,7 +371,6 @@ keys are what the form actually submits as POSTed request parameters. The map va
|
|||
labels that the user sees. In the preceding example, given a list of three well known cities
|
||||
and a default value in the form backing object, the HTML resembles the following:
|
||||
|
||||
====
|
||||
[source,jsp,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -396,12 +379,10 @@ and a default value in the form backing object, the HTML resembles the following
|
|||
<input type="radio" name="address.town" value="Paris" checked="checked">Paris</input>
|
||||
<input type="radio" name="address.town" value="New York">New York</input>
|
||||
----
|
||||
====
|
||||
|
||||
If your application expects to handle cities by internal codes (for example), you can create the map of
|
||||
codes with suitable keys, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -416,12 +397,10 @@ codes with suitable keys, as the following example shows:
|
|||
return model;
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The code now produces output where the radio values are the relevant codes, but the
|
||||
user still sees the more user-friendly city names, as follows:
|
||||
|
||||
====
|
||||
[source,jsp,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -430,7 +409,6 @@ user still sees the more user-friendly city names, as follows:
|
|||
<input type="radio" name="address.town" value="PRS" checked="checked">Paris</input>
|
||||
<input type="radio" name="address.town" value="NYC">New York</input>
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
[[mvc-views-form-macros-html-escaping]]
|
||||
|
@ -447,21 +425,18 @@ template processing to provide different behavior for different fields in your f
|
|||
To switch to XHTML compliance for your tags, specify a value of `true` for a
|
||||
model or context variable named `xhtmlCompliant`, as the following example shows:
|
||||
|
||||
====
|
||||
[source,jsp,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
<#-- for FreeMarker -->
|
||||
<#assign xhtmlCompliant = true>
|
||||
----
|
||||
====
|
||||
|
||||
After processing
|
||||
this directive, any elements generated by the Spring macros are now XHTML compliant.
|
||||
|
||||
In similar fashion, you can specify HTML escaping per field, as the following example shows:
|
||||
|
||||
====
|
||||
[source,jsp,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -474,7 +449,6 @@ In similar fashion, you can specify HTML escaping per field, as the following ex
|
|||
<#assign htmlEscape = false in spring>
|
||||
<#-- all future fields will be bound with HTML escaping off -->
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -496,7 +470,6 @@ NOTE: The Groovy Markup Template engine requires Groovy 2.3.1+.
|
|||
|
||||
The following example shows how to configure the Groovy Markup Template Engine:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -519,11 +492,9 @@ The following example shows how to configure the Groovy Markup Template Engine:
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The following example shows how to configure the same in XML:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -536,7 +507,6 @@ The following example shows how to configure the same in XML:
|
|||
<!-- Configure the Groovy Markup Template Engine... -->
|
||||
<mvc:groovy-configurer resource-loader-path="/WEB-INF/"/>
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -546,7 +516,6 @@ The following example shows how to configure the same in XML:
|
|||
Unlike traditional template engines, Groovy Markup relies on a DSL that uses a builder
|
||||
syntax. The following example shows a sample template for an HTML page:
|
||||
|
||||
====
|
||||
[source,groovy,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -561,7 +530,6 @@ syntax. The following example shows a sample template for an HTML page:
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -620,7 +588,6 @@ You can declare a `ScriptTemplateConfigurer` bean to specify the script engine t
|
|||
the script files to load, what function to call to render templates, and so on.
|
||||
The following example uses Mustache templates and the Nashorn JavaScript engine:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -644,11 +611,9 @@ The following example uses Mustache templates and the Nashorn JavaScript engine:
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The following example shows the same arrangement in XML:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -662,11 +627,9 @@ The following example shows the same arrangement in XML:
|
|||
<mvc:script location="mustache.js"/>
|
||||
</mvc:script-template-configurer>
|
||||
----
|
||||
====
|
||||
|
||||
The controller would look no different for the Java and XML configurations, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -681,11 +644,9 @@ The controller would look no different for the Java and XML configurations, as t
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The following example shows the Mustache template:
|
||||
|
||||
====
|
||||
[source,html,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -698,7 +659,6 @@ The following example shows the Mustache template:
|
|||
</body>
|
||||
</html>
|
||||
----
|
||||
====
|
||||
|
||||
The render function is called with the following parameters:
|
||||
|
||||
|
@ -719,7 +679,6 @@ browser facilities that are not available in the server-side script engine.
|
|||
|
||||
The following example shows how to do so:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -743,7 +702,6 @@ The following example shows how to do so:
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: Setting the `sharedEngine` property to `false` is required when you use non-thread-safe
|
||||
script engines with templating libraries not designed for concurrency, such as Handlebars or
|
||||
|
@ -752,20 +710,17 @@ to https://bugs.openjdk.java.net/browse/JDK-8076099[this bug].
|
|||
|
||||
`polyfill.js` defines only the `window` object needed by Handlebars to run properly, as follows:
|
||||
|
||||
====
|
||||
[source,javascript,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
var window = {};
|
||||
----
|
||||
====
|
||||
|
||||
This basic `render.js` implementation compiles the template before using it. A production-ready
|
||||
implementation should also store any reused cached templates or pre-compiled templates.
|
||||
You can do so on the script side (and handle any customization you need -- managing
|
||||
template engine configuration, for example). The following example shows how to do so:
|
||||
|
||||
====
|
||||
[source,javascript,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -774,7 +729,6 @@ template engine configuration, for example). The following example shows how to
|
|||
return compiledTemplate(model);
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Check out the Spring Framework unit tests,
|
||||
https://github.com/spring-projects/spring-framework/tree/master/spring-webmvc/src/test/java/org/springframework/web/servlet/view/script[Java], and
|
||||
|
@ -801,7 +755,6 @@ When developing with JSPs, you can declare a `InternalResourceViewResolver` or a
|
|||
mapped to a class and a URL. With a `ResourceBundleViewResolver`, you
|
||||
can mix different types of views byusing only one resolver, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -831,7 +784,6 @@ directory so there can be no direct access by clients.
|
|||
<property name="suffix" value=".jsp"/>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -884,14 +836,12 @@ called `spring-form.tld`.
|
|||
To use the tags from this library, add the following directive to the top of your JSP
|
||||
page:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
|
||||
----
|
||||
where `form` is the tag name prefix you want to use for the tags from this library.
|
||||
====
|
||||
|
||||
|
||||
[[mvc-view-jsp-formtaglib-formtag]]
|
||||
|
@ -907,7 +857,6 @@ such as `firstName` and `lastName`. We can use it as the form-backing object of
|
|||
form controller, which returns `form.jsp`. The following example shows what `form.jsp` could
|
||||
look like:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -929,7 +878,6 @@ look like:
|
|||
</table>
|
||||
</form:form>
|
||||
----
|
||||
====
|
||||
|
||||
The `firstName` and `lastName` values are retrieved from the command object placed in
|
||||
the `PageContext` by the page controller. Keep reading to see more complex examples of
|
||||
|
@ -937,7 +885,6 @@ how inner tags are used with the `form` tag.
|
|||
|
||||
The following listing shows the generated HTML, which looks like a standard form:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -959,14 +906,12 @@ The following listing shows the generated HTML, which looks like a standard form
|
|||
</table>
|
||||
</form>
|
||||
----
|
||||
====
|
||||
|
||||
The preceding JSP assumes that the variable name of the form-backing object is
|
||||
`command`. If you have put the form-backing object into the model under another name
|
||||
(definitely a best practice), you can bind the form to the named variable, as the
|
||||
following example shows:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -988,7 +933,6 @@ following example shows:
|
|||
</table>
|
||||
</form:form>
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
[[mvc-view-jsp-formtaglib-inputtag]]
|
||||
|
@ -1008,7 +952,6 @@ This tag renders an HTML `input` tag with the `type` set to `checkbox`.
|
|||
Assume that our `User` has preferences such as newsletter subscription and a list of
|
||||
hobbies. The following example shows the `Preferences` class:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1043,11 +986,9 @@ hobbies. The following example shows the `Preferences` class:
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The corresponding `form.jsp` could then resemble the following:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1079,7 +1020,6 @@ The corresponding `form.jsp` could then resemble the following:
|
|||
</table>
|
||||
</form:form>
|
||||
----
|
||||
====
|
||||
|
||||
There are three approaches to the `checkbox` tag, which should meet all your checkbox needs.
|
||||
|
||||
|
@ -1095,7 +1035,6 @@ There are three approaches to the `checkbox` tag, which should meet all your che
|
|||
Note that, regardless of the approach, the same HTML structure is generated. The following
|
||||
HTML snippet defines some checkboxes:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1111,7 +1050,6 @@ HTML snippet defines some checkboxes:
|
|||
</td>
|
||||
</tr>
|
||||
----
|
||||
====
|
||||
|
||||
You might not expect to see the additional hidden field after each checkbox.
|
||||
When a checkbox in an HTML page is not checked, its value is not sent to the
|
||||
|
@ -1137,7 +1075,6 @@ the available options in the `items` property. Typically, the bound property is
|
|||
collection so that it can hold multiple values selected by the user. The following example
|
||||
shows a JSP that uses this tag:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1153,7 +1090,6 @@ shows a JSP that uses this tag:
|
|||
</table>
|
||||
</form:form>
|
||||
----
|
||||
====
|
||||
|
||||
This example assumes that the `interestList` is a `List` available as a model attribute
|
||||
that contains strings of the values to be selected from. If you use a `Map`,
|
||||
|
@ -1171,7 +1107,6 @@ This tag renders an HTML `input` element with the `type` set to `radio`.
|
|||
A typical usage pattern involves multiple tag instances bound to the same property
|
||||
but with different values, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1183,7 +1118,6 @@ but with different values, as the following example shows:
|
|||
</td>
|
||||
</tr>
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -1200,7 +1134,6 @@ entry's value are used as the label to be displayed. You can also use a custom
|
|||
object where you can provide the property names for the value by using `itemValue` and the
|
||||
label by using `itemLabel`, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1209,7 +1142,6 @@ label by using `itemLabel`, as the following example shows:
|
|||
<td><form:radiobuttons path="sex" items="${sexOptions}"/></td>
|
||||
</tr>
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
[[mvc-view-jsp-formtaglib-passwordtag]]
|
||||
|
@ -1217,7 +1149,6 @@ label by using `itemLabel`, as the following example shows:
|
|||
|
||||
This tag renders an HTML `input` tag with the type set to `password` with the bound value.
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1228,13 +1159,11 @@ This tag renders an HTML `input` tag with the type set to `password` with the bo
|
|||
</td>
|
||||
</tr>
|
||||
----
|
||||
====
|
||||
|
||||
Note that, by default, the password value is not shown. If you do want the
|
||||
password value to be shown, you can set the value of the `showPassword` attribute to
|
||||
`true`, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1245,7 +1174,6 @@ password value to be shown, you can set the value of the `showPassword` attribut
|
|||
</td>
|
||||
</tr>
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
[[mvc-view-jsp-formtaglib-selecttag]]
|
||||
|
@ -1256,7 +1184,6 @@ option as well as the use of nested `option` and `options` tags.
|
|||
|
||||
Assume that a `User` has a list of skills. The corresponding HTML could be as follows:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1265,12 +1192,10 @@ Assume that a `User` has a list of skills. The corresponding HTML could be as fo
|
|||
<td><form:select path="skills" items="${skills}"/></td>
|
||||
</tr>
|
||||
----
|
||||
====
|
||||
|
||||
If the `User's` skill are in Herbology, the HTML source of the 'Skills' row could be
|
||||
as follows:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1285,7 +1210,6 @@ as follows:
|
|||
</td>
|
||||
</tr>
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -1295,7 +1219,6 @@ as follows:
|
|||
This tag renders an HTML `option` element. It sets `selected`, based on the bound
|
||||
value. The following HTML shows typical output for it:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1311,12 +1234,10 @@ value. The following HTML shows typical output for it:
|
|||
</td>
|
||||
</tr>
|
||||
----
|
||||
====
|
||||
|
||||
If the `User's` house was in Gryffindor, the HTML source of the 'House' row would be
|
||||
as follows:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1333,7 +1254,6 @@ as follows:
|
|||
</tr>
|
||||
----
|
||||
<1> Note the addition of a `selected` attribute.
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -1343,7 +1263,6 @@ as follows:
|
|||
This tag renders a list of HTML `option` elements. It sets the `selected` attribute,
|
||||
based on the bound value. The following HTML shows typical output for it:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1357,11 +1276,9 @@ based on the bound value. The following HTML shows typical output for it:
|
|||
</td>
|
||||
</tr>
|
||||
----
|
||||
====
|
||||
|
||||
If the `User` lived in the UK, the HTML source of the 'Country' row would be as follows:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1378,7 +1295,7 @@ If the `User` lived in the UK, the HTML source of the 'Country' row would be as
|
|||
</tr>
|
||||
----
|
||||
<1> Note the addition of a `selected` attribute.
|
||||
====
|
||||
|
||||
|
||||
As the preceding example shows, the combined usage of an `option` tag with the `options` tag
|
||||
generates the same standard HTML but lets you explicitly specify a value in the
|
||||
|
@ -1400,7 +1317,6 @@ the item label property applies to the map value.
|
|||
|
||||
This tag renders an HTML `textarea` element. The following HTML shows typical output for it:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1410,7 +1326,6 @@ This tag renders an HTML `textarea` element. The following HTML shows typical ou
|
|||
<td><form:errors path="notes"/></td>
|
||||
</tr>
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
[[mvc-view-jsp-formtaglib-hiddeninputtag]]
|
||||
|
@ -1420,24 +1335,20 @@ This tag renders an HTML `input` tag with the `type` set to `hidden` with the bo
|
|||
an unbound hidden value, use the HTML `input` tag with the `type` set to `hidden`.
|
||||
The following HTML shows typical output for it:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
<form:hidden path="house"/>
|
||||
----
|
||||
====
|
||||
|
||||
If we choose to submit the `house` value as a hidden one, the HTML would be as follows:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
<input name="house" type="hidden" value="Gryffindor"/>
|
||||
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
[[mvc-view-jsp-formtaglib-errorstag]]
|
||||
|
@ -1451,7 +1362,6 @@ Assume that we want to display all error messages for the `firstName` and `lastN
|
|||
fields once we submit the form. We have a validator for instances of the `User` class
|
||||
called `UserValidator`, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1467,11 +1377,9 @@ called `UserValidator`, as the following example shows:
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The `form.jsp` could be as follows:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1498,12 +1406,10 @@ The `form.jsp` could be as follows:
|
|||
</table>
|
||||
</form:form>
|
||||
----
|
||||
====
|
||||
|
||||
If we submit a form with empty values in the `firstName` and `lastName` fields,
|
||||
the HTML would be as follows:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1530,7 +1436,6 @@ the HTML would be as follows:
|
|||
</table>
|
||||
</form>
|
||||
----
|
||||
====
|
||||
|
||||
What if we want to display the entire list of errors for a given page? The next example
|
||||
shows that the `errors` tag also supports some basic wildcarding functionality.
|
||||
|
@ -1542,7 +1447,6 @@ shows that the `errors` tag also supports some basic wildcarding functionality.
|
|||
The following example displays a list of errors at the top of the page, followed by
|
||||
field-specific errors next to the fields:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1567,11 +1471,9 @@ field-specific errors next to the fields:
|
|||
</table>
|
||||
</form:form>
|
||||
----
|
||||
====
|
||||
|
||||
The HTML would be as follows:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1597,7 +1499,6 @@ The HTML would be as follows:
|
|||
</table>
|
||||
</form>
|
||||
----
|
||||
====
|
||||
|
||||
The `spring-form.tld` tag library descriptor (TLD) is included in the `spring-webmvc.jar`.
|
||||
For a comprehensive reference on individual tags, browse the
|
||||
|
@ -1628,7 +1529,6 @@ To support HTTP method conversion, the Spring MVC form tag was updated to suppor
|
|||
the HTTP method. For example, the following snippet comes from the Pet Clinic
|
||||
sample:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1636,13 +1536,11 @@ sample:
|
|||
<p class="submit"><input type="submit" value="Delete Pet"/></p>
|
||||
</form:form>
|
||||
----
|
||||
====
|
||||
|
||||
The preceding example perform an HTTP POST, with the "`real`" DELETE method hidden behind a
|
||||
request parameter. It is picked up by the `HiddenHttpMethodFilter`, which is defined in
|
||||
web.xml, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1656,11 +1554,9 @@ web.xml, as the following example shows:
|
|||
<servlet-name>petclinic</servlet-name>
|
||||
</filter-mapping>
|
||||
----
|
||||
====
|
||||
|
||||
The following example shows the corresponding `@Controller` method:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1670,7 +1566,6 @@ The following example shows the corresponding `@Controller` method:
|
|||
return "redirect:/owners/" + ownerId;
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
[[mvc-view-jsp-formtaglib-html5]]
|
||||
|
@ -1715,7 +1610,6 @@ To be able to use Tiles, you have to configure it by using files that contain de
|
|||
http://tiles.apache.org[]). In Spring, this is done by using the `TilesConfigurer`.
|
||||
The following example `ApplicationContext` configuration shows how to do so:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1731,7 +1625,6 @@ The following example `ApplicationContext` configuration shows how to do so:
|
|||
</property>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
The preceding example defines five files that contain definitions. The files are all located in
|
||||
the `WEB-INF/defs` directory. At initialization of the `WebApplicationContext`, the
|
||||
|
@ -1744,7 +1637,6 @@ implementations, the `UrlBasedViewResolver` and the `ResourceBundleViewResolver`
|
|||
You can specify locale-specific Tiles definitions by adding an underscore and then
|
||||
the locale, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1757,7 +1649,6 @@ the locale, as the following example shows:
|
|||
</property>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
With the preceding configuration, `tiles_fr_FR.xml` is used for requests with the `fr_FR` locale,
|
||||
and `tiles.xml` is used by default.
|
||||
|
@ -1773,7 +1664,6 @@ them otherwise in the file names for Tiles definitions.
|
|||
The `UrlBasedViewResolver` instantiates the given `viewClass` for each view it has to
|
||||
resolve. The following bean defines a `UrlBasedViewResolver`:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1781,7 +1671,6 @@ resolve. The following bean defines a `UrlBasedViewResolver`:
|
|||
<property name="viewClass" value="org.springframework.web.servlet.view.tiles3.TilesView"/>
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
[[mvc-view-tiles-resource]]
|
||||
|
@ -1792,7 +1681,6 @@ view names and view classes that the resolver can use. The following example sho
|
|||
definition for a `ResourceBundleViewResolver` and the corresponding view names and view
|
||||
classes (taken from the Pet Clinic sample):
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1815,7 +1703,6 @@ classes (taken from the Pet Clinic sample):
|
|||
findOwnersForm.url=/WEB-INF/jsp/findOwners.jsp
|
||||
...
|
||||
----
|
||||
====
|
||||
|
||||
When you use the `ResourceBundleViewResolver`, you can easily mix
|
||||
different view technologies.
|
||||
|
@ -1845,7 +1732,6 @@ configuration, scoped beans, and so on. Note that you need to define one Spring
|
|||
for each preparer name (as used in your Tiles definitions). The following example shows
|
||||
how to define a set a `SpringBeanPreparerFactory` property on a `TilesConfigurer` bean:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1866,7 +1752,6 @@ how to define a set a `SpringBeanPreparerFactory` property on a `TilesConfigurer
|
|||
|
||||
</bean>
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -1883,7 +1768,6 @@ package `org.springframework.web.servlet.view.feed`.
|
|||
optionally override the `buildFeedMetadata()` method (the default implementation is
|
||||
empty). The following example shows how to do so:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1903,11 +1787,9 @@ empty). The following example shows how to do so:
|
|||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Similar requirements apply for implementing `AbstractRssFeedView`, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1926,7 +1808,6 @@ Similar requirements apply for implementing `AbstractRssFeedView`, as the follow
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The `buildFeedItems()` and `buildFeedEntries()` methods pass in the HTTP request, in case
|
||||
you need to access the Locale. The HTTP response is passed in only for the setting of
|
||||
|
@ -1973,7 +1854,6 @@ A simple PDF view for a word list could extend
|
|||
`org.springframework.web.servlet.view.document.AbstractPdfView` and implement the
|
||||
`buildPdfDocument()` method, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1989,7 +1869,6 @@ A simple PDF view for a word list could extend
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
A controller can return such a view either from an external view definition
|
||||
(referencing it by name) or as a `View` instance from the handler method.
|
||||
|
@ -2094,7 +1973,6 @@ Configuration is standard for a simple Spring web application: The MVC configura
|
|||
has to define an `XsltViewResolver` bean and regular MVC annotation configuration.
|
||||
The following example shows how to do so:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -2112,7 +1990,6 @@ public class WebConfig implements WebMvcConfigurer {
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -2124,7 +2001,6 @@ We also need a Controller that encapsulates our word-generation logic.
|
|||
The controller logic is encapsulated in a `@Controller` class, with the
|
||||
handler method being defined as follows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -2149,7 +2025,6 @@ handler method being defined as follows:
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
So far, we have only created a DOM document and added it to the Model map. Note that you
|
||||
can also load an XML file as a `Resource` and use it instead of a custom DOM document.
|
||||
|
@ -2172,7 +2047,6 @@ and end with an `xslt` file extension.
|
|||
|
||||
The following example shows an XSLT transform:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -2199,11 +2073,9 @@ The following example shows an XSLT transform:
|
|||
|
||||
</xsl:stylesheet>
|
||||
----
|
||||
====
|
||||
|
||||
The preceding transform is rendered as the following HTML:
|
||||
|
||||
====
|
||||
[source,html,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -2222,4 +2094,3 @@ The preceding transform is rendered as the following HTML:
|
|||
</body>
|
||||
</html>
|
||||
----
|
||||
====
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -10,7 +10,6 @@ A WebSocket interaction begins with an HTTP request that uses the HTTP `Upgrade`
|
|||
to upgrade or, in this case, to switch to the WebSocket protocol. The following example
|
||||
shows such an interaction:
|
||||
|
||||
====
|
||||
[source,yaml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -25,12 +24,11 @@ shows such an interaction:
|
|||
----
|
||||
<1> The `Upgrade` header.
|
||||
<2> Using the `Upgrade` connection.
|
||||
====
|
||||
|
||||
|
||||
Instead of the usual 200 status code, a server with WebSocket support returns output
|
||||
similar to the following:
|
||||
|
||||
====
|
||||
[source,yaml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -41,7 +39,7 @@ similar to the following:
|
|||
Sec-WebSocket-Protocol: v10.stomp
|
||||
----
|
||||
<1> Protocol switch
|
||||
====
|
||||
|
||||
|
||||
After a successful handshake, the TCP socket underlying the HTTP upgrade request remains
|
||||
open for both the client and the server to continue to send and receive messages.
|
||||
|
|
|
@ -29,7 +29,6 @@ Creating a WebSocket server is as simple as implementing `WebSocketHandler` or,
|
|||
likely, extending either `TextWebSocketHandler` or `BinaryWebSocketHandler`. The following
|
||||
example uses `TextWebSocketHandler`:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -46,12 +45,10 @@ example uses `TextWebSocketHandler`:
|
|||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
There is dedicated WebSocket Java configuration and XML namespace support for mapping the preceding
|
||||
WebSocket handler to a specific URL, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -75,11 +72,9 @@ WebSocket handler to a specific URL, as the following example shows:
|
|||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The following example shows the XML configuration equivalent of the preceding example:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes,attributes"]
|
||||
----
|
||||
|
@ -100,7 +95,6 @@ The following example shows the XML configuration equivalent of the preceding ex
|
|||
|
||||
</beans>
|
||||
----
|
||||
====
|
||||
|
||||
The preceding example is for use in Spring MVC applications and should be included in the
|
||||
configuration of a <<mvc-servlet,`DispatcherServlet`>>. However, Spring's WebSocket
|
||||
|
@ -126,7 +120,6 @@ You can use such an interceptor to preclude the handshake or to make any attribu
|
|||
available to the `WebSocketSession`. The following example uses a built-in interceptor
|
||||
to pass HTTP session attributes to the WebSocket session:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -142,11 +135,9 @@ to pass HTTP session attributes to the WebSocket session:
|
|||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The following example shows the XML configuration equivalent of the preceding example:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes,attributes"]
|
||||
----
|
||||
|
@ -170,7 +161,6 @@ The following example shows the XML configuration equivalent of the preceding ex
|
|||
|
||||
</beans>
|
||||
----
|
||||
====
|
||||
|
||||
A more advanced option is to extend the `DefaultHandshakeHandler` that performs
|
||||
the steps of the WebSocket handshake, including validating the client origin,
|
||||
|
@ -228,7 +218,6 @@ upgrade to a Servlet container version with JSR-356 support, it should
|
|||
be possible to selectively enable or disable web fragments (and SCI scanning)
|
||||
through the use of the `<absolute-ordering />` element in `web.xml`, as the following example shows:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes,attributes"]
|
||||
----
|
||||
|
@ -243,13 +232,11 @@ through the use of the `<absolute-ordering />` element in `web.xml`, as the foll
|
|||
|
||||
</web-app>
|
||||
----
|
||||
====
|
||||
|
||||
You can then selectively enable web fragments by name, such as Spring's own
|
||||
`SpringServletContainerInitializer` that provides support for the Servlet 3
|
||||
Java initialization API. The following example shows how to do so:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes,attributes"]
|
||||
----
|
||||
|
@ -266,7 +253,6 @@ Java initialization API. The following example shows how to do so:
|
|||
|
||||
</web-app>
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -281,7 +267,6 @@ and others.
|
|||
For Tomcat, WildFly, and GlassFish, you can add a `ServletServerContainerFactoryBean` to your
|
||||
WebSocket Java config, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -299,11 +284,9 @@ WebSocket Java config, as the following example shows:
|
|||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The following example shows the XML configuration equivalent of the preceding example:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes,attributes"]
|
||||
----
|
||||
|
@ -323,7 +306,6 @@ The following example shows the XML configuration equivalent of the preceding ex
|
|||
|
||||
</beans>
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: For client-side WebSocket configuration, you should use `WebSocketContainerFactoryBean`
|
||||
(XML) or `ContainerProvider.getWebSocketContainer()` (Java configuration).
|
||||
|
@ -332,7 +314,6 @@ For Jetty, you need to supply a pre-configured Jetty `WebSocketServerFactory` an
|
|||
that into Spring's `DefaultHandshakeHandler` through your WebSocket Java config.
|
||||
The following example shows how to do so:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -359,11 +340,9 @@ The following example shows how to do so:
|
|||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The following example shows the XML configuration equivalent of the preceding example:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes,attributes"]
|
||||
----
|
||||
|
@ -401,7 +380,6 @@ The following example shows the XML configuration equivalent of the preceding ex
|
|||
|
||||
</beans>
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -430,7 +408,6 @@ The three possible behaviors are:
|
|||
|
||||
You can configure WebSocket and SockJS allowed origins, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -454,11 +431,9 @@ You can configure WebSocket and SockJS allowed origins, as the following example
|
|||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The following example shows the XML configuration equivalent of the preceding example:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes,attributes"]
|
||||
----
|
||||
|
@ -479,7 +454,6 @@ The following example shows the XML configuration equivalent of the preceding ex
|
|||
|
||||
</beans>
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -532,7 +506,6 @@ polling is used.
|
|||
|
||||
All transport requests have the following URL structure:
|
||||
|
||||
====
|
||||
----
|
||||
http://host:port/myApp/myEndpoint/{server-id}/{session-id}/{transport}
|
||||
----
|
||||
|
@ -542,7 +515,6 @@ where:
|
|||
* `{server-id}` is useful for routing requests in a cluster but is not used otherwise.
|
||||
* `{session-id}` correlates HTTP requests belonging to a SockJS session.
|
||||
* `{transport}` indicates the transport type (for example, `websocket`, `xhr-streaming`, and others).
|
||||
====
|
||||
|
||||
The WebSocket transport needs only a single HTTP request to do the WebSocket handshake.
|
||||
All messages thereafter are exchanged on that socket.
|
||||
|
@ -572,7 +544,6 @@ http://sockjs.github.io/sockjs-protocol/sockjs-protocol-0.3.3.html[narrated test
|
|||
|
||||
You can enable SockJS through Java configuration, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -592,11 +563,9 @@ You can enable SockJS through Java configuration, as the following example shows
|
|||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The following example shows the XML configuration equivalent of the preceding example:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes,attributes"]
|
||||
----
|
||||
|
@ -618,7 +587,6 @@ The following example shows the XML configuration equivalent of the preceding ex
|
|||
|
||||
</beans>
|
||||
----
|
||||
====
|
||||
|
||||
The preceding example is for use in Spring MVC applications and should be included in the
|
||||
configuration of a <<mvc-servlet,`DispatcherServlet`>>. However, Spring's WebSocket
|
||||
|
@ -690,7 +658,6 @@ a URL from the same origin as the application.
|
|||
|
||||
The following example shows how to do so in Java configuration:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -708,7 +675,6 @@ The following example shows how to do so in Java configuration:
|
|||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The XML namespace provides a similar option through the `<websocket:sockjs>` element.
|
||||
|
||||
|
@ -821,7 +787,6 @@ to the server. At present there are two implementations:
|
|||
|
||||
The following example shows how to create a SockJS client and connect to a SockJS endpoint:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -832,7 +797,6 @@ The following example shows how to create a SockJS client and connect to a SockJ
|
|||
SockJsClient sockJsClient = new SockJsClient(transports);
|
||||
sockJsClient.doHandshake(new MyWebSocketHandler(), "ws://example.com:8080/sockjs");
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: SockJS uses JSON formatted arrays for messages. By default, Jackson 2 is used and needs
|
||||
to be on the classpath. Alternatively, you can configure a custom implementation of
|
||||
|
@ -842,7 +806,6 @@ To use `SockJsClient` to simulate a large number of concurrent users, you
|
|||
need to configure the underlying HTTP client (for XHR transports) to allow a sufficient
|
||||
number of connections and threads. The following example shows how to do so with Jetty:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -850,12 +813,10 @@ HttpClient jettyHttpClient = new HttpClient();
|
|||
jettyHttpClient.setMaxConnectionsPerDestination(1000);
|
||||
jettyHttpClient.setExecutor(new QueuedThreadPool(1000));
|
||||
----
|
||||
====
|
||||
|
||||
The following example shows the server-side SockJS-related properties (see javadoc for details)
|
||||
that you should also consider customizing:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -877,7 +838,6 @@ that you should also consider customizing:
|
|||
<2> Set the `httpMessageCacheSize` property to 1,000 (the default is `100`).
|
||||
<3> Set the `disconnectDelay` property to 30 property seconds (the default is five seconds
|
||||
-- `5 * 1000`).
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -908,7 +868,6 @@ either text or binary.
|
|||
STOMP is a frame-based protocol whose frames are modeled on HTTP. The following listing shows the structure
|
||||
of a STOMP frame:
|
||||
|
||||
====
|
||||
----
|
||||
COMMAND
|
||||
header1:value1
|
||||
|
@ -916,7 +875,6 @@ header2:value2
|
|||
|
||||
Body^@
|
||||
----
|
||||
====
|
||||
|
||||
Clients can use the `SEND` or `SUBSCRIBE` commands to send or subscribe for
|
||||
messages, along with a `destination` header that describes what the
|
||||
|
@ -940,7 +898,6 @@ The following example shows a client subscribing to receive stock quotes, which
|
|||
the server may emit periodically (for example, via a scheduled task that sends messages
|
||||
through a `SimpMessagingTemplate` to the broker):
|
||||
|
||||
====
|
||||
----
|
||||
SUBSCRIBE
|
||||
id:sub-1
|
||||
|
@ -948,12 +905,10 @@ destination:/topic/price.stock.*
|
|||
|
||||
^@
|
||||
----
|
||||
====
|
||||
|
||||
The following example shows a client that sends a trade request, which the server
|
||||
can handle through an `@MessageMapping` method:
|
||||
|
||||
====
|
||||
----
|
||||
SEND
|
||||
destination:/queue/trade
|
||||
|
@ -962,7 +917,6 @@ content-length:44
|
|||
|
||||
{"action":"BUY","ticker":"MMM","shares",44}^@
|
||||
----
|
||||
====
|
||||
|
||||
After the execution, the server can
|
||||
broadcast a trade confirmation message and details down to the client.
|
||||
|
@ -977,7 +931,6 @@ exchanges.
|
|||
STOMP servers can use the `MESSAGE` command to broadcast messages to all subscribers.
|
||||
The following example shows a server sending a stock quote to a subscribed client:
|
||||
|
||||
====
|
||||
----
|
||||
MESSAGE
|
||||
message-id:nxahklf6-1
|
||||
|
@ -986,7 +939,6 @@ destination:/topic/price.stock.MMM
|
|||
|
||||
{"ticker":"MMM","price":129.45}^@
|
||||
----
|
||||
====
|
||||
|
||||
A server cannot send unsolicited messages. All messages
|
||||
from a server must be in response to a specific client subscription, and the
|
||||
|
@ -1026,7 +978,6 @@ STOMP over WebSocket support is available in the `spring-messaging` and
|
|||
`spring-websocket` modules. Once you have those dependencies, you can expose a STOMP
|
||||
endpoints, over WebSocket with <<websocket-fallback>>, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1056,11 +1007,10 @@ client needs to connect for the WebSocket handshake.
|
|||
`@MessageMapping` methods in `@Controller` classes.
|
||||
<3> Use the built-in message broker for subscriptions and broadcasting and
|
||||
route messages whose destination header begins with `/topic `or `/queue` to the broker.
|
||||
====
|
||||
|
||||
|
||||
The following example shows the XML configuration equivalent of the preceding example:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes,attributes"]
|
||||
----
|
||||
|
@ -1082,7 +1032,6 @@ The following example shows the XML configuration equivalent of the preceding ex
|
|||
|
||||
</beans>
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: For the built-in simple broker, the `/topic` and `/queue` prefixes do not have any special
|
||||
meaning. They are merely a convention to differentiate between pub-sub versus point-to-point
|
||||
|
@ -1099,7 +1048,6 @@ https://github.com/JSteunou/webstomp-client[JSteunou/webstomp-client] is the mos
|
|||
actively maintained and evolving successor of that library. The following example code
|
||||
is based on it:
|
||||
|
||||
====
|
||||
[source,javascript,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1109,11 +1057,9 @@ is based on it:
|
|||
stompClient.connect({}, function(frame) {
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Alternatively, if you connect through WebSocket (without SockJS), you can use the following code:
|
||||
|
||||
====
|
||||
[source,javascript,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1123,7 +1069,6 @@ Alternatively, if you connect through WebSocket (without SockJS), you can use th
|
|||
stompClient.connect({}, function(frame) {
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Note that `stompClient` in the preceding example does not need to specify `login` and `passcode` headers.
|
||||
Even if it did, they would be ignored (or, rather, overridden) on the server side. See
|
||||
|
@ -1146,7 +1091,6 @@ To configure the underlying WebSocket server, the information in
|
|||
<<websocket-server-runtime-configuration>> applies. For Jetty, however you need to set
|
||||
the `HandshakeHandler` and `WebSocketPolicy` through the `StompEndpointRegistry`:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1171,7 +1115,6 @@ the `HandshakeHandler` and `WebSocketPolicy` through the `StompEndpointRegistry`
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -1237,7 +1180,6 @@ to broadcast to subscribed clients.
|
|||
|
||||
We can trace the flow through a simple example. Consider the following example, which sets up a server:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1269,7 +1211,6 @@ We can trace the flow through a simple example. Consider the following example,
|
|||
}
|
||||
|
||||
----
|
||||
====
|
||||
|
||||
The preceding example supports the following flow:
|
||||
|
||||
|
@ -1419,7 +1360,6 @@ when a subscription is stored and ready for broadcasts, a client should ask for
|
|||
receipt if the server supports it (simple broker does not). For example, with the Java
|
||||
<<websocket-stomp-client, STOMP client>>, you could do the following to add a receipt:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1438,7 +1378,6 @@ receipt if the server supports it (simple broker does not). For example, with th
|
|||
// Subscription ready...
|
||||
});
|
||||
----
|
||||
====
|
||||
|
||||
A server side option is <<websocket-stomp-interceptors,to register>> an
|
||||
`ExecutorChannelInterceptor` on the `brokerChannel` and implement the `afterMessageHandled`
|
||||
|
@ -1453,7 +1392,6 @@ An application can use `@MessageExceptionHandler` methods to handle exceptions f
|
|||
itself or through a method argument if you want to get access to the exception instance.
|
||||
The following example declares an exception through a method argument:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1469,7 +1407,6 @@ The following example declares an exception through a method argument:
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
`@MessageExceptionHandler` methods support flexible method signatures and support the same
|
||||
method argument types and return values as <<websocket-stomp-message-mapping,`@MessageMapping`>> methods.
|
||||
|
@ -1490,7 +1427,6 @@ The easiest way to do so is to inject a `SimpMessagingTemplate` and
|
|||
use it to send messages. Typically, you would inject it by
|
||||
type, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1512,7 +1448,6 @@ type, as the following example shows:
|
|||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
However, you can also qualify it by its name (`brokerMessagingTemplate`), if another
|
||||
bean of the same type exists.
|
||||
|
@ -1535,7 +1470,6 @@ https://stomp.github.io/stomp-specification-1.2.html#Heart-beating[STOMP heartbe
|
|||
For that, you can declare your own scheduler or use the one that is automatically
|
||||
declared and used internally. The following example shows how to declare your own scheduler:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1561,7 +1495,6 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
@ -1582,7 +1515,6 @@ and run it with STOMP support enabled. Then you can enable the STOMP broker rela
|
|||
|
||||
The following example configuration enables a full-featured broker:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1603,11 +1535,9 @@ The following example configuration enables a full-featured broker:
|
|||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The following example shows the XML configuration equivalent of the preceding example:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes,attributes"]
|
||||
----
|
||||
|
@ -1629,7 +1559,6 @@ The following example shows the XML configuration equivalent of the preceding ex
|
|||
|
||||
</beans>
|
||||
----
|
||||
====
|
||||
|
||||
The STOMP broker relay in the preceding configuration is a Spring
|
||||
{api-spring-framework}/messaging/MessageHandler.html[`MessageHandler`]
|
||||
|
@ -1688,7 +1617,6 @@ connectivity is lost, to the same host and port. If you wish to supply multiple
|
|||
on each attempt to connect, you can configure a supplier of addresses, instead of a
|
||||
fixed host and port. The following example shows how to do that:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1711,7 +1639,6 @@ public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
You can also configure the STOMP broker relay with a `virtualHost` property.
|
||||
The value of this property is set as the `host` header of every `CONNECT` frame
|
||||
|
@ -1731,7 +1658,6 @@ you are more used to messaging conventions, you can switch to using dot (`.`) as
|
|||
|
||||
The following example shows how to do so in Java configuration:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1749,11 +1675,9 @@ The following example shows how to do so in Java configuration:
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The following example shows the XML configuration equivalent of the preceding example:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes,attributes"]
|
||||
----
|
||||
|
@ -1779,12 +1703,10 @@ The following example shows the XML configuration equivalent of the preceding ex
|
|||
|
||||
</beans>
|
||||
----
|
||||
====
|
||||
|
||||
After that, a controller can use a dot (`.`) as the separator in `@MessageMapping` methods,
|
||||
as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1798,7 +1720,6 @@ as the following example shows:
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The client can now send a message to `/app/red.blue.green123`.
|
||||
|
||||
|
@ -1896,7 +1817,6 @@ the user header on the CONNECT `Message`. Spring notes and saves the authenticat
|
|||
user and associate it with subsequent STOMP messages on the same session. The following
|
||||
example shows how register a custom authentication interceptor:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1921,7 +1841,6 @@ example shows how register a custom authentication interceptor:
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Also, note that, when you use Spring Security's authorization for messages, at present,
|
||||
you need to ensure that the authentication `ChannelInterceptor` config is ordered
|
||||
|
@ -1956,7 +1875,6 @@ A message-handling method can send messages to the user associated with
|
|||
the message being handled through the `@SendToUser` annotation (also supported on
|
||||
the class-level to share a common destination), as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1971,14 +1889,12 @@ the class-level to share a common destination), as the following example shows:
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
If the user has more than one session, by default, all of the sessions subscribed
|
||||
to the given destination are targeted. However, sometimes, it may be necessary to
|
||||
target only the session that sent the message being handled. You can do so by
|
||||
setting the `broadcast` attribute to false, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -1998,7 +1914,6 @@ setting the `broadcast` attribute to false, as the following example shows:
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: While user destinations generally imply an authenticated user, it is not strictly required.
|
||||
A WebSocket session that is not associated with an authenticated user
|
||||
|
@ -2011,7 +1926,6 @@ component by, for example, injecting the `SimpMessagingTemplate` created by the
|
|||
the XML namespace. (The bean name is `"brokerMessagingTemplate"` if required
|
||||
for qualification with `@Qualifier`.) The following example shows how to do so:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -2033,7 +1947,6 @@ public class TradeServiceImpl implements TradeService {
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: When you use user destinations with an external message broker, you should check the broker
|
||||
documentation on how to manage inactive queues, so that, when the user session is
|
||||
|
@ -2063,7 +1976,6 @@ not match the exact order of publication.
|
|||
|
||||
If this is an issue, enable the `setPreservePublishOrder` flag, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -2079,11 +1991,9 @@ If this is an issue, enable the `setPreservePublishOrder` flag, as the following
|
|||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The following example shows the XML configuration equivalent of the preceding example:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes,attributes"]
|
||||
----
|
||||
|
@ -2102,7 +2012,6 @@ The following example shows the XML configuration equivalent of the preceding ex
|
|||
|
||||
</beans>
|
||||
----
|
||||
====
|
||||
|
||||
When the flag is set, messages within the same client session are published to the
|
||||
`clientOutboundChannel` one at a time, so that the order of publication is guaranteed.
|
||||
|
@ -2158,7 +2067,6 @@ of a STOMP connection but not for every client message. Applications can also re
|
|||
`ChannelInterceptor` to intercept any message and in any part of the processing chain.
|
||||
The following example shows how to intercept inbound messages from clients:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -2172,12 +2080,10 @@ The following example shows how to intercept inbound messages from clients:
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
A custom `ChannelInterceptor` can use `StompHeaderAccessor` or `SimpMessageHeaderAccessor`
|
||||
to access information about the message, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -2192,7 +2098,6 @@ to access information about the message, as the following example shows:
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Applications can also implement `ExecutorChannelInterceptor`, which is a sub-interface
|
||||
of `ChannelInterceptor` with callbacks in the thread in which the messages are handled.
|
||||
|
@ -2215,7 +2120,6 @@ Spring provides a STOMP over WebSocket client and a STOMP over TCP client.
|
|||
|
||||
To begin, you can create and configure `WebSocketStompClient`, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -2224,7 +2128,6 @@ To begin, you can create and configure `WebSocketStompClient`, as the following
|
|||
stompClient.setMessageConverter(new StringMessageConverter());
|
||||
stompClient.setTaskScheduler(taskScheduler); // for heartbeats
|
||||
----
|
||||
====
|
||||
|
||||
In the preceding example, you could replace `StandardWebSocketClient` with `SockJsClient`,
|
||||
since that is also an implementation of `WebSocketClient`. The `SockJsClient` can
|
||||
|
@ -2233,7 +2136,6 @@ use WebSocket or HTTP-based transport as a fallback. For more details, see
|
|||
|
||||
Next, you can establish a connection and provide a handler for the STOMP session, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -2241,11 +2143,9 @@ Next, you can establish a connection and provide a handler for the STOMP session
|
|||
StompSessionHandler sessionHandler = new MyStompSessionHandler();
|
||||
stompClient.connect(url, sessionHandler);
|
||||
----
|
||||
====
|
||||
|
||||
When the session is ready for use, the handler is notified, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -2257,25 +2157,21 @@ public class MyStompSessionHandler extends StompSessionHandlerAdapter {
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Once the session is established, any payload can be sent and is
|
||||
serialized with the configured `MessageConverter`, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
session.send("/topic/something", "payload");
|
||||
----
|
||||
====
|
||||
|
||||
You can also subscribe to destinations. The `subscribe` methods require a handler
|
||||
for messages on the subscription and returns a `Subscription` handle that you can
|
||||
use to unsubscribe. For each received message, the handler can specify the target
|
||||
`Object` type to which the payload should be deserialized, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -2293,7 +2189,6 @@ session.subscribe("/topic/something", new StompFrameHandler() {
|
|||
|
||||
});
|
||||
----
|
||||
====
|
||||
|
||||
To enable STOMP heartbeat, you can configure `WebSocketStompClient` with a `TaskScheduler`
|
||||
and optionally customize the heartbeat intervals (10 seconds for write inactivity,
|
||||
|
@ -2329,7 +2224,6 @@ transport-level errors including `ConnectionLostException`.
|
|||
Each WebSocket session has a map of attributes. The map is attached as a header to
|
||||
inbound client messages and may be accessed from a controller method, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -2343,7 +2237,6 @@ public class MyController {
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
You can declare a Spring-managed bean in the `websocket` scope.
|
||||
You can inject WebSocket-scoped beans into controllers and any channel interceptors
|
||||
|
@ -2351,7 +2244,6 @@ registered on the `clientInboundChannel`. Those are typically singletons and liv
|
|||
longer than any individual WebSocket session. Therefore, you need to use a
|
||||
scope proxy mode for WebSocket-scoped beans, as the following example shows:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -2388,7 +2280,6 @@ scope proxy mode for WebSocket-scoped beans, as the following example shows:
|
|||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
As with any custom scope, Spring initializes a new `MyBean` instance the first
|
||||
time it is accessed from the controller and stores the instance in the WebSocket
|
||||
|
@ -2462,7 +2353,6 @@ documentation of the XML schema for important additional details.
|
|||
|
||||
The following example shows a possible configuration:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -2479,11 +2369,9 @@ The following example shows a possible configuration:
|
|||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The following example shows the XML configuration equivalent of the preceding example:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes,attributes"]
|
||||
----
|
||||
|
@ -2503,7 +2391,6 @@ The following example shows the XML configuration equivalent of the preceding ex
|
|||
|
||||
</beans>
|
||||
----
|
||||
====
|
||||
|
||||
You can also use the WebSocket transport configuration shown earlier to configure the
|
||||
maximum allowed size for incoming STOMP messages. In theory, a WebSocket
|
||||
|
@ -2521,7 +2408,6 @@ minimum.
|
|||
|
||||
The following example shows one possible configuration:
|
||||
|
||||
====
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
@ -2538,11 +2424,9 @@ The following example shows one possible configuration:
|
|||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The following example shows the XML configuration equivalent of the preceding example:
|
||||
|
||||
====
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes,attributes"]
|
||||
----
|
||||
|
@ -2562,7 +2446,6 @@ The following example shows the XML configuration equivalent of the preceding ex
|
|||
|
||||
</beans>
|
||||
----
|
||||
====
|
||||
|
||||
An important point about scaling involves using multiple application instances.
|
||||
Currently, you cannot do that with the simple broker.
|
||||
|
|
Loading…
Reference in New Issue