/*
 * Copyright 2002-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * 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
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.cache.jcache.interceptor;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.cache.interceptor.AbstractCacheInvoker;
import org.springframework.cache.interceptor.BasicOperation;
import org.springframework.cache.interceptor.CacheOperationInvocationContext;
import org.springframework.cache.interceptor.CacheOperationInvoker;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

Base class for JSR-107 caching aspects, such as the JCacheInterceptor or an AspectJ aspect.

Use the Spring caching abstraction for cache-related operations. No JSR-107 Cache or CacheManager are required to process standard JSR-107 cache annotations.

The JCacheOperationSource is used for determining caching operations

A cache aspect is serializable if its JCacheOperationSource is serializable.

Author:Stephane Nicoll
See Also:
Since:4.1
/** * Base class for JSR-107 caching aspects, such as the {@link JCacheInterceptor} * or an AspectJ aspect. * * <p>Use the Spring caching abstraction for cache-related operations. No JSR-107 * {@link javax.cache.Cache} or {@link javax.cache.CacheManager} are required to * process standard JSR-107 cache annotations. * * <p>The {@link JCacheOperationSource} is used for determining caching operations * * <p>A cache aspect is serializable if its {@code JCacheOperationSource} is serializable. * * @author Stephane Nicoll * @since 4.1 * @see org.springframework.cache.interceptor.CacheAspectSupport * @see KeyGeneratorAdapter * @see CacheResolverAdapter */
public class JCacheAspectSupport extends AbstractCacheInvoker implements InitializingBean { protected final Log logger = LogFactory.getLog(getClass()); @Nullable private JCacheOperationSource cacheOperationSource; @Nullable private CacheResultInterceptor cacheResultInterceptor; @Nullable private CachePutInterceptor cachePutInterceptor; @Nullable private CacheRemoveEntryInterceptor cacheRemoveEntryInterceptor; @Nullable private CacheRemoveAllInterceptor cacheRemoveAllInterceptor; private boolean initialized = false;
Set the CacheOperationSource for this cache aspect.
/** * Set the CacheOperationSource for this cache aspect. */
public void setCacheOperationSource(JCacheOperationSource cacheOperationSource) { Assert.notNull(cacheOperationSource, "JCacheOperationSource must not be null"); this.cacheOperationSource = cacheOperationSource; }
Return the CacheOperationSource for this cache aspect.
/** * Return the CacheOperationSource for this cache aspect. */
public JCacheOperationSource getCacheOperationSource() { Assert.state(this.cacheOperationSource != null, "The 'cacheOperationSource' property is required: " + "If there are no cacheable methods, then don't use a cache aspect."); return this.cacheOperationSource; } public void afterPropertiesSet() { getCacheOperationSource(); this.cacheResultInterceptor = new CacheResultInterceptor(getErrorHandler()); this.cachePutInterceptor = new CachePutInterceptor(getErrorHandler()); this.cacheRemoveEntryInterceptor = new CacheRemoveEntryInterceptor(getErrorHandler()); this.cacheRemoveAllInterceptor = new CacheRemoveAllInterceptor(getErrorHandler()); this.initialized = true; } @Nullable protected Object execute(CacheOperationInvoker invoker, Object target, Method method, Object[] args) { // Check whether aspect is enabled to cope with cases where the AJ is pulled in automatically if (this.initialized) { Class<?> targetClass = AopProxyUtils.ultimateTargetClass(target); JCacheOperation<?> operation = getCacheOperationSource().getCacheOperation(method, targetClass); if (operation != null) { CacheOperationInvocationContext<?> context = createCacheOperationInvocationContext(target, args, operation); return execute(context, invoker); } } return invoker.invoke(); } @SuppressWarnings("unchecked") private CacheOperationInvocationContext<?> createCacheOperationInvocationContext( Object target, Object[] args, JCacheOperation<?> operation) { return new DefaultCacheInvocationContext<>( (JCacheOperation<Annotation>) operation, target, args); } @SuppressWarnings("unchecked") @Nullable private Object execute(CacheOperationInvocationContext<?> context, CacheOperationInvoker invoker) { CacheOperationInvoker adapter = new CacheOperationInvokerAdapter(invoker); BasicOperation operation = context.getOperation(); if (operation instanceof CacheResultOperation) { Assert.state(this.cacheResultInterceptor != null, "No CacheResultInterceptor"); return this.cacheResultInterceptor.invoke( (CacheOperationInvocationContext<CacheResultOperation>) context, adapter); } else if (operation instanceof CachePutOperation) { Assert.state(this.cachePutInterceptor != null, "No CachePutInterceptor"); return this.cachePutInterceptor.invoke( (CacheOperationInvocationContext<CachePutOperation>) context, adapter); } else if (operation instanceof CacheRemoveOperation) { Assert.state(this.cacheRemoveEntryInterceptor != null, "No CacheRemoveEntryInterceptor"); return this.cacheRemoveEntryInterceptor.invoke( (CacheOperationInvocationContext<CacheRemoveOperation>) context, adapter); } else if (operation instanceof CacheRemoveAllOperation) { Assert.state(this.cacheRemoveAllInterceptor != null, "No CacheRemoveAllInterceptor"); return this.cacheRemoveAllInterceptor.invoke( (CacheOperationInvocationContext<CacheRemoveAllOperation>) context, adapter); } else { throw new IllegalArgumentException("Cannot handle " + operation); } }
Execute the underlying operation (typically in case of cache miss) and return the result of the invocation. If an exception occurs it will be wrapped in a ThrowableWrapper: the exception can be handled or modified but it must be wrapped in a ThrowableWrapper as well.
Params:
  • invoker – the invoker handling the operation being cached
See Also:
Returns:the result of the invocation
/** * Execute the underlying operation (typically in case of cache miss) and return * the result of the invocation. If an exception occurs it will be wrapped in * a {@code ThrowableWrapper}: the exception can be handled or modified but it * <em>must</em> be wrapped in a {@code ThrowableWrapper} as well. * @param invoker the invoker handling the operation being cached * @return the result of the invocation * @see CacheOperationInvoker#invoke() */
protected Object invokeOperation(CacheOperationInvoker invoker) { return invoker.invoke(); } private class CacheOperationInvokerAdapter implements CacheOperationInvoker { private final CacheOperationInvoker delegate; public CacheOperationInvokerAdapter(CacheOperationInvoker delegate) { this.delegate = delegate; } @Override public Object invoke() throws ThrowableWrapper { return invokeOperation(this.delegate); } } }