Skip to content

Commit

Permalink
* Polishing and Docs
Browse files Browse the repository at this point in the history
  • Loading branch information
artembilan committed Apr 15, 2019
1 parent ea56bc8 commit b5ccfab
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
Expand Down Expand Up @@ -235,7 +235,6 @@ else if (operation instanceof CacheEvictOperation) {
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) {
CacheOperationInvoker operationInvoker =
() -> {

Object result = callback.execute();
// Drop MessageBuilder optimization in favor of Serializable support in cache implementation.
if (result instanceof AbstractIntegrationMessageBuilder<?>) {
Expand All @@ -247,8 +246,7 @@ protected Object doInvoke(ExecutionCallback callback, Object target, Message<?>

};

return this.delegate.execute(operationInvoker, target, HANDLE_REQUEST_METHOD, // NOSONAR
new Object[] { message });
return this.delegate.invoke(operationInvoker, target, message);
}

private static class IntegrationCacheAspect extends CacheAspectSupport {
Expand All @@ -257,9 +255,8 @@ private static class IntegrationCacheAspect extends CacheAspectSupport {
}

@Nullable
@Override
public Object execute(CacheOperationInvoker invoker, Object target, Method method, Object[] args) {
return super.execute(invoker, target, method, args);
Object invoke(CacheOperationInvoker invoker, Object target, Message<?> message) {
return super.execute(invoker, target, HANDLE_REQUEST_METHOD, new Object[] { message }); // NOSONAR
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
Expand Down
61 changes: 55 additions & 6 deletions src/reference/asciidoc/handler-advice.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ In addition to providing the general mechanism to apply AOP advice classes, Spri
* `RequestHandlerCircuitBreakerAdvice` (described in <<circuit-breaker-advice>>)
* `ExpressionEvaluatingRequestHandlerAdvice` (described in <<expression-advice>>)
* `RateLimiterRequestHandlerAdvice` (described in <<rate-limiter-advice>>)
* `CacheRequestHandlerAdvice` (described in <<cache-advice>>)

[[retry-advice]]
===== Retry Advice
Expand Down Expand Up @@ -498,14 +499,64 @@ public String handleRequest(String payload) {
----
====

[[cache-advice]]
===== Caching Advice

Starting with version 5.2, a `CacheRequestHandlerAdvice` has been introduced.
It is fully based on the caching abstraction in https://docs.spring.io/spring/docs/current/spring-framework-reference/integration.html#cache[Spring Framework] and aligned with the concepts and functionality provided by the `@Caching` annotation family.
The logic internally is based on the `CacheAspectSupport` extension, where proxying for caching operations is done around a `AbstractReplyProducingMessageHandler.RequestHandler.handleRequestMessage` method with a request `Message<?>` as an argument.
This advice can be configured with the SpeL expression or `Function` to evaluate a cache key.
The whole request `Message<?>` is available as a root object for evaluation context or as a `Function` input argument.
By default a `payload` of the request message is used for the cache key.
The `CacheRequestHandlerAdvice` must be configured with `cacheNames`, when a default cache operation is going to be a `CacheableOperation`, or with a set of any arbitrary `CacheOperation` s.
Every `CacheOperation` can be configured separately or shared options, like a `CacheManager`, `CacheResolver` and `CacheErrorHandler`, can be reused from the `CacheRequestHandlerAdvice` configuration.
This configuration functionality is similar to Spring Framework's `@CacheConfig` and `@Caching` annotations combination.
If a `CacheManager` is not provided, a single bean is resolved by default from the `BeanFactory` in the `CacheAspectSupport`.

The following example configures two advices with different set of caching operations:
====
[source, java]
----
@Bean
public CacheRequestHandlerAdvice cacheAdvice() {
CacheRequestHandlerAdvice cacheRequestHandlerAdvice = new CacheRequestHandlerAdvice(TEST_CACHE);
cacheRequestHandlerAdvice.setKeyExpressionString("payload");
return cacheRequestHandlerAdvice;
}
@Transformer(inputChannel = "transformerChannel", outputChannel = "nullChannel", adviceChain = "cacheAdvice")
public Object transform(Message<?> message) {
...
}
@Bean
public CacheRequestHandlerAdvice cachePutAndEvictAdvice() {
CacheRequestHandlerAdvice cacheRequestHandlerAdvice = new CacheRequestHandlerAdvice();
cacheRequestHandlerAdvice.setKeyExpressionString("payload");
CachePutOperation.Builder cachePutBuilder = new CachePutOperation.Builder();
cachePutBuilder.setCacheName(TEST_PUT_CACHE);
CacheEvictOperation.Builder cacheEvictBuilder = new CacheEvictOperation.Builder();
cacheEvictBuilder.setCacheName(TEST_CACHE);
cacheRequestHandlerAdvice.setCacheOperations(cachePutBuilder.build(), cacheEvictBuilder.build());
return cacheRequestHandlerAdvice;
}
@ServiceActivator(inputChannel = "serviceChannel", outputChannel = "nullChannel",
adviceChain = "cachePutAndEvictAdvice")
public Message<?> service(Message<?> message) {
...
}
----
====

[[custom-advice]]
==== Custom Advice Classes

In addition to the provided advice classes <<advice-classes,described earlier>>, you can implement your own advice classes.
While you can provide any implementation of `org.aopalliance.aop.Advice` (usually `org.aopalliance.intercept.MethodInterceptor`), we generally recommend that you subclass `o.s.i.handler.advice.AbstractRequestHandlerAdvice`.
This has the benefit of avoiding the writing of low-level aspect-oriented programming code as well as providing a starting point that is specifically tailored for use in this environment.

Subclasses need to implement the `doInvoke()`` method, the definition of which follows:
Subclasses need to implement the `doInvoke()` method, the definition of which follows:

====
[source,java]
Expand Down Expand Up @@ -737,8 +788,7 @@ On the other hand, if you want all the attempts and any recovery operations (in
Sometimes, it is useful to access handler properties from within the advice.
For example, most handlers implement `NamedComponent` to let you access the component name.

The target object can be accessed through the `target` argument (when subclassing `AbstractRequestHandlerAdvice`) or
`invocation.getThis()` (when implementing `org.aopalliance.intercept.MethodInterceptor`).
The target object can be accessed through the `target` argument (when subclassing `AbstractRequestHandlerAdvice`) or `invocation.getThis()` (when implementing `org.aopalliance.intercept.MethodInterceptor`).

When the entire handler is advised (such as when the handler does not produce replies or the advice implements `HandleMessageAdvice`), you can cast the target object to an interface, such as `NamedComponent`, as shown in the following example:

Expand All @@ -758,8 +808,7 @@ String componentName = ((NamedComponent) invocation.getThis()).getComponentName(
----
====

When only the `handleRequestMessage()` method is advised (in a reply-producing handler), you need to access the
full handler, which is an `AbstractReplyProducingMessageHandler`.
When only the `handleRequestMessage()` method is advised (in a reply-producing handler), you need to access the full handler, which is an `AbstractReplyProducingMessageHandler`.
The following example shows how to do so:

====
Expand Down Expand Up @@ -892,9 +941,9 @@ public IntegrationFlow flow() {
...
}
----
====

NOTE: The `IdempotentReceiverInterceptor` is designed only for the `MessageHandler.handleMessage(Message<?>)` method.
Starting with version 4.3.1, it implements `HandleMessageAdvice`, with the `AbstractHandleMessageAdvice` as a base class, for better dissociation.
See <<handle-message-advice>> for more information.

====
7 changes: 7 additions & 0 deletions src/reference/asciidoc/whats-new.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ If you are interested in more details, see the Issue Tracker tickets that were r
The `RateLimiterRequestHandlerAdvice` is now available for limiting requests rate on handlers.
See <<rate-limiter-advice>> for more information.

[[x5.2-cacheAdvice]]
=== Caching Advice Support

The `CacheRequestHandlerAdvice` is now available for caching request results on handlers.
See <<cache-advice>> for more information.


[[x5.2-general]]
=== General Changes

Expand Down

0 comments on commit b5ccfab

Please sign in to comment.