Lazily retrieve delegate beans in AsyncConfigurer and CachingConfigurer
Introduces a configure method pattern for Supplier-style configuration and a common SingletonSupplier decorator for method reference suppliers. Also declares jcache.config and jcache.interceptor for non-null conventions. Issue: SPR-17021
This commit is contained in:
parent
680afa75d8
commit
f6fdffd663
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -24,6 +24,7 @@ import java.util.concurrent.CompletionException;
|
|||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
|
@ -41,6 +42,7 @@ import org.springframework.lang.Nullable;
|
|||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
import org.springframework.util.function.SingletonSupplier;
|
||||
|
||||
/**
|
||||
* Base class for asynchronous method execution aspects, such as
|
||||
|
|
@ -72,10 +74,9 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
|
|||
|
||||
private final Map<Method, AsyncTaskExecutor> executors = new ConcurrentHashMap<>(16);
|
||||
|
||||
@Nullable
|
||||
private volatile Executor defaultExecutor;
|
||||
private SingletonSupplier<Executor> defaultExecutor;
|
||||
|
||||
private AsyncUncaughtExceptionHandler exceptionHandler;
|
||||
private SingletonSupplier<AsyncUncaughtExceptionHandler> exceptionHandler;
|
||||
|
||||
@Nullable
|
||||
private BeanFactory beanFactory;
|
||||
|
|
@ -89,7 +90,8 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
|
|||
* executor will be looked up at invocation time against the enclosing bean factory
|
||||
*/
|
||||
public AsyncExecutionAspectSupport(@Nullable Executor defaultExecutor) {
|
||||
this(defaultExecutor, new SimpleAsyncUncaughtExceptionHandler());
|
||||
this.defaultExecutor = new SingletonSupplier<>(defaultExecutor, () -> getDefaultExecutor(this.beanFactory));
|
||||
this.exceptionHandler = SingletonSupplier.of(SimpleAsyncUncaughtExceptionHandler::new);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -101,11 +103,23 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
|
|||
* @param exceptionHandler the {@link AsyncUncaughtExceptionHandler} to use
|
||||
*/
|
||||
public AsyncExecutionAspectSupport(@Nullable Executor defaultExecutor, AsyncUncaughtExceptionHandler exceptionHandler) {
|
||||
this.defaultExecutor = defaultExecutor;
|
||||
this.exceptionHandler = exceptionHandler;
|
||||
this.defaultExecutor = new SingletonSupplier<>(defaultExecutor, () -> getDefaultExecutor(this.beanFactory));
|
||||
this.exceptionHandler = SingletonSupplier.of(exceptionHandler);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Configure this aspect with the given executor and exception handler suppliers,
|
||||
* applying the corresponding default if a supplier is not resolvable.
|
||||
* @since 5.1
|
||||
*/
|
||||
public void configure(@Nullable Supplier<Executor> defaultExecutor,
|
||||
@Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {
|
||||
|
||||
this.defaultExecutor = new SingletonSupplier<>(defaultExecutor, () -> getDefaultExecutor(this.beanFactory));
|
||||
this.exceptionHandler = new SingletonSupplier<>(exceptionHandler, SimpleAsyncUncaughtExceptionHandler::new);
|
||||
}
|
||||
|
||||
/**
|
||||
* Supply the executor to be used when executing async methods.
|
||||
* @param defaultExecutor the {@code Executor} (typically a Spring {@code AsyncTaskExecutor}
|
||||
|
|
@ -117,7 +131,7 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
|
|||
* @see #getDefaultExecutor(BeanFactory)
|
||||
*/
|
||||
public void setExecutor(Executor defaultExecutor) {
|
||||
this.defaultExecutor = defaultExecutor;
|
||||
this.defaultExecutor = SingletonSupplier.of(defaultExecutor);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -125,7 +139,7 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
|
|||
* thrown by invoking asynchronous methods with a {@code void} return type.
|
||||
*/
|
||||
public void setExceptionHandler(AsyncUncaughtExceptionHandler exceptionHandler) {
|
||||
this.exceptionHandler = exceptionHandler;
|
||||
this.exceptionHandler = SingletonSupplier.of(exceptionHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -155,15 +169,7 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
|
|||
targetExecutor = findQualifiedExecutor(this.beanFactory, qualifier);
|
||||
}
|
||||
else {
|
||||
targetExecutor = this.defaultExecutor;
|
||||
if (targetExecutor == null) {
|
||||
synchronized (this.executors) {
|
||||
if (this.defaultExecutor == null) {
|
||||
this.defaultExecutor = getDefaultExecutor(this.beanFactory);
|
||||
}
|
||||
targetExecutor = this.defaultExecutor;
|
||||
}
|
||||
}
|
||||
targetExecutor = this.defaultExecutor.get();
|
||||
}
|
||||
if (targetExecutor == null) {
|
||||
return null;
|
||||
|
|
@ -305,7 +311,7 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
|
|||
else {
|
||||
// Could not transmit the exception to the caller with default executor
|
||||
try {
|
||||
this.exceptionHandler.handleUncaughtException(ex, method, params);
|
||||
this.exceptionHandler.obtain().handleUncaughtException(ex, method, params);
|
||||
}
|
||||
catch (Throwable ex2) {
|
||||
logger.error("Exception handler for async method '" + method.toGenericString() +
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* 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.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -29,13 +29,13 @@ import org.apache.commons.logging.LogFactory;
|
|||
*/
|
||||
public class SimpleAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler {
|
||||
|
||||
private final Log logger = LogFactory.getLog(SimpleAsyncUncaughtExceptionHandler.class);
|
||||
private static final Log logger = LogFactory.getLog(SimpleAsyncUncaughtExceptionHandler.class);
|
||||
|
||||
|
||||
@Override
|
||||
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
|
||||
if (logger.isErrorEnabled()) {
|
||||
logger.error(String.format("Unexpected error occurred invoking async " +
|
||||
"method '%s'.", method), ex);
|
||||
logger.error("Unexpected error occurred invoking async method: " + method, ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import org.springframework.context.annotation.Role;
|
|||
*
|
||||
* @author Chris Beams
|
||||
* @author Stephane Nicoll
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
* @see org.springframework.cache.annotation.EnableCaching
|
||||
* @see org.springframework.cache.annotation.CachingConfigurationSelector
|
||||
|
|
@ -41,18 +42,7 @@ public class AspectJCachingConfiguration extends AbstractCachingConfiguration {
|
|||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
public AnnotationCacheAspect cacheAspect() {
|
||||
AnnotationCacheAspect cacheAspect = AnnotationCacheAspect.aspectOf();
|
||||
if (this.cacheResolver != null) {
|
||||
cacheAspect.setCacheResolver(this.cacheResolver);
|
||||
}
|
||||
else if (this.cacheManager != null) {
|
||||
cacheAspect.setCacheManager(this.cacheManager);
|
||||
}
|
||||
if (this.keyGenerator != null) {
|
||||
cacheAspect.setKeyGenerator(this.keyGenerator);
|
||||
}
|
||||
if (this.errorHandler != null) {
|
||||
cacheAspect.setErrorHandler(this.errorHandler);
|
||||
}
|
||||
cacheAspect.configure(this.errorHandler, this.keyGenerator, this.cacheResolver, this.cacheManager);
|
||||
return cacheAspect;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -30,6 +30,7 @@ import org.springframework.scheduling.config.TaskManagementConfigUtils;
|
|||
*
|
||||
* @author Chris Beams
|
||||
* @author Stephane Nicoll
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
* @see EnableAsync
|
||||
* @see org.springframework.scheduling.annotation.AsyncConfigurationSelector
|
||||
|
|
@ -42,12 +43,7 @@ public class AspectJAsyncConfiguration extends AbstractAsyncConfiguration {
|
|||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
public AnnotationAsyncExecutionAspect asyncAdvisor() {
|
||||
AnnotationAsyncExecutionAspect asyncAspect = AnnotationAsyncExecutionAspect.aspectOf();
|
||||
if (this.executor != null) {
|
||||
asyncAspect.setExecutor(this.executor);
|
||||
}
|
||||
if (this.exceptionHandler != null) {
|
||||
asyncAspect.setExceptionHandler(this.exceptionHandler);
|
||||
}
|
||||
asyncAspect.configure(this.executor, this.exceptionHandler);
|
||||
return asyncAspect;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -120,7 +120,7 @@ public class AspectJEnableCachingIsolatedTests {
|
|||
load(EmptyConfig.class);
|
||||
}
|
||||
catch (IllegalStateException ex) {
|
||||
assertTrue(ex.getMessage().contains("No bean of type CacheManager"));
|
||||
assertTrue(ex.getMessage().contains("no bean of type CacheManager"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package org.springframework.scheduling.aspectj;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
|
@ -56,14 +58,14 @@ public class AnnotationDrivenBeanDefinitionParserTests {
|
|||
public void asyncPostProcessorExecutorReference() {
|
||||
Object executor = context.getBean("testExecutor");
|
||||
Object aspect = context.getBean(TaskManagementConfigUtils.ASYNC_EXECUTION_ASPECT_BEAN_NAME);
|
||||
assertSame(executor, new DirectFieldAccessor(aspect).getPropertyValue("defaultExecutor"));
|
||||
assertSame(executor, ((Supplier) new DirectFieldAccessor(aspect).getPropertyValue("defaultExecutor")).get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void asyncPostProcessorExceptionHandlerReference() {
|
||||
Object exceptionHandler = context.getBean("testExceptionHandler");
|
||||
Object aspect = context.getBean(TaskManagementConfigUtils.ASYNC_EXECUTION_ASPECT_BEAN_NAME);
|
||||
assertSame(exceptionHandler, new DirectFieldAccessor(aspect).getPropertyValue("exceptionHandler"));
|
||||
assertSame(exceptionHandler, ((Supplier) new DirectFieldAccessor(aspect).getPropertyValue("exceptionHandler")).get());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package org.springframework.cache.jcache.config;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.cache.annotation.AbstractCachingConfiguration;
|
||||
import org.springframework.cache.annotation.CachingConfigurer;
|
||||
|
|
@ -25,45 +27,37 @@ import org.springframework.cache.jcache.interceptor.JCacheOperationSource;
|
|||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Role;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Abstract JSR-107 specific {@code @Configuration} class providing common
|
||||
* structure for enabling JSR-107 annotation-driven cache management capability.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Juergen Hoeller
|
||||
* @since 4.1
|
||||
* @see JCacheConfigurer
|
||||
*/
|
||||
@Configuration
|
||||
public class AbstractJCacheConfiguration extends AbstractCachingConfiguration {
|
||||
|
||||
protected CacheResolver exceptionCacheResolver;
|
||||
@Nullable
|
||||
protected Supplier<CacheResolver> exceptionCacheResolver;
|
||||
|
||||
|
||||
@Override
|
||||
protected void useCachingConfigurer(CachingConfigurer config) {
|
||||
super.useCachingConfigurer(config);
|
||||
if (config instanceof JCacheConfigurer) {
|
||||
this.exceptionCacheResolver = ((JCacheConfigurer) config).exceptionCacheResolver();
|
||||
this.exceptionCacheResolver = ((JCacheConfigurer) config)::exceptionCacheResolver;
|
||||
}
|
||||
}
|
||||
|
||||
@Bean(name = "jCacheOperationSource")
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
public JCacheOperationSource cacheOperationSource() {
|
||||
DefaultJCacheOperationSource source = new DefaultJCacheOperationSource();
|
||||
if (this.cacheManager != null) {
|
||||
source.setCacheManager(this.cacheManager);
|
||||
}
|
||||
if (this.keyGenerator != null) {
|
||||
source.setKeyGenerator(this.keyGenerator);
|
||||
}
|
||||
if (this.cacheResolver != null) {
|
||||
source.setCacheResolver(this.cacheResolver);
|
||||
}
|
||||
if (this.exceptionCacheResolver != null) {
|
||||
source.setExceptionCacheResolver(this.exceptionCacheResolver);
|
||||
}
|
||||
return source;
|
||||
return new DefaultJCacheOperationSource(
|
||||
this.cacheManager, this.cacheResolver, this.exceptionCacheResolver, this.keyGenerator);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -18,6 +18,7 @@ package org.springframework.cache.jcache.config;
|
|||
|
||||
import org.springframework.cache.annotation.CachingConfigurer;
|
||||
import org.springframework.cache.interceptor.CacheResolver;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Extension of {@link CachingConfigurer} for the JSR-107 implementation.
|
||||
|
|
@ -58,6 +59,7 @@ public interface JCacheConfigurer extends CachingConfigurer {
|
|||
* </pre>
|
||||
* See {@link org.springframework.cache.annotation.EnableCaching} for more complete examples.
|
||||
*/
|
||||
@Nullable
|
||||
CacheResolver exceptionCacheResolver();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -18,6 +18,7 @@ package org.springframework.cache.jcache.config;
|
|||
|
||||
import org.springframework.cache.annotation.CachingConfigurerSupport;
|
||||
import org.springframework.cache.interceptor.CacheResolver;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* An extension of {@link CachingConfigurerSupport} that also implements
|
||||
|
|
@ -34,6 +35,7 @@ import org.springframework.cache.interceptor.CacheResolver;
|
|||
public class JCacheConfigurerSupport extends CachingConfigurerSupport implements JCacheConfigurer {
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public CacheResolver exceptionCacheResolver() {
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import org.springframework.context.annotation.Role;
|
|||
* <p>Can safely be used alongside Spring's caching support.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Juergen Hoeller
|
||||
* @since 4.1
|
||||
* @see org.springframework.cache.annotation.EnableCaching
|
||||
* @see org.springframework.cache.annotation.CachingConfigurationSelector
|
||||
|
|
@ -55,11 +56,8 @@ public class ProxyJCacheConfiguration extends AbstractJCacheConfiguration {
|
|||
@Bean(name = "jCacheInterceptor")
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
public JCacheInterceptor cacheInterceptor() {
|
||||
JCacheInterceptor interceptor = new JCacheInterceptor();
|
||||
JCacheInterceptor interceptor = new JCacheInterceptor(this.errorHandler);
|
||||
interceptor.setCacheOperationSource(cacheOperationSource());
|
||||
if (this.errorHandler != null) {
|
||||
interceptor.setErrorHandler(this.errorHandler);
|
||||
}
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,4 +6,9 @@
|
|||
* <p>Provide an extension of the {@code CachingConfigurer} that exposes
|
||||
* the exception cache resolver to use, see {@code JCacheConfigurer}.
|
||||
*/
|
||||
@NonNullApi
|
||||
@NonNullFields
|
||||
package org.springframework.cache.jcache.config;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
import org.springframework.lang.NonNullFields;
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ abstract class AbstractCacheInterceptor<O extends AbstractJCacheOperation<A>, A
|
|||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
protected abstract Object invoke(CacheOperationInvocationContext<O> context, CacheOperationInvoker invoker)
|
||||
throws Throwable;
|
||||
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ public abstract class AbstractFallbackJCacheOperationSource implements JCacheOpe
|
|||
|
||||
|
||||
@Override
|
||||
public JCacheOperation<?> getCacheOperation(Method method, Class<?> targetClass) {
|
||||
public JCacheOperation<?> getCacheOperation(Method method, @Nullable Class<?> targetClass) {
|
||||
MethodClassKey cacheKey = new MethodClassKey(method, targetClass);
|
||||
Object cached = this.cache.get(cacheKey);
|
||||
|
||||
|
|
@ -78,7 +78,7 @@ public abstract class AbstractFallbackJCacheOperationSource implements JCacheOpe
|
|||
}
|
||||
|
||||
@Nullable
|
||||
private JCacheOperation<?> computeCacheOperation(Method method, Class<?> targetClass) {
|
||||
private JCacheOperation<?> computeCacheOperation(Method method, @Nullable Class<?> targetClass) {
|
||||
// Don't allow no-public methods as required.
|
||||
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
|
||||
return null;
|
||||
|
|
@ -113,7 +113,7 @@ public abstract class AbstractFallbackJCacheOperationSource implements JCacheOpe
|
|||
* (or {@code null} if none)
|
||||
*/
|
||||
@Nullable
|
||||
protected abstract JCacheOperation<?> findCacheOperation(Method method, Class<?> targetType);
|
||||
protected abstract JCacheOperation<?> findCacheOperation(Method method, @Nullable Class<?> targetType);
|
||||
|
||||
/**
|
||||
* Should only public methods be allowed to have caching semantics?
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ abstract class AbstractJCacheOperation<A extends Annotation> implements JCacheOp
|
|||
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
* Construct a new {@code AbstractJCacheOperation}.
|
||||
* @param methodDetails the {@link CacheMethodDetails} related to the cached method
|
||||
* @param cacheResolver the cache resolver to resolve regular caches
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ abstract class AbstractKeyCacheInterceptor<O extends AbstractJCacheKeyOperation<
|
|||
super(errorHandler);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate a key for the specified invocation.
|
||||
* @param context the context of the invocation
|
||||
|
|
@ -58,8 +59,7 @@ abstract class AbstractKeyCacheInterceptor<O extends AbstractJCacheKeyOperation<
|
|||
* @param context the context of the invocation.
|
||||
* @return the related {@code CacheKeyInvocationContext}
|
||||
*/
|
||||
protected CacheKeyInvocationContext<A> createCacheKeyInvocationContext(
|
||||
CacheOperationInvocationContext<O> context) {
|
||||
protected CacheKeyInvocationContext<A> createCacheKeyInvocationContext(CacheOperationInvocationContext<O> context) {
|
||||
return new DefaultCacheKeyInvocationContext<>(context.getOperation(), context.getTarget(), context.getArgs());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -45,7 +45,7 @@ import org.springframework.util.StringUtils;
|
|||
public abstract class AnnotationJCacheOperationSource extends AbstractFallbackJCacheOperationSource {
|
||||
|
||||
@Override
|
||||
protected JCacheOperation<?> findCacheOperation(Method method, Class<?> targetType) {
|
||||
protected JCacheOperation<?> findCacheOperation(Method method, @Nullable Class<?> targetType) {
|
||||
CacheResult cacheResult = method.getAnnotation(CacheResult.class);
|
||||
CachePut cachePut = method.getAnnotation(CachePut.class);
|
||||
CacheRemove cacheRemove = method.getAnnotation(CacheRemove.class);
|
||||
|
|
@ -74,15 +74,16 @@ public abstract class AnnotationJCacheOperationSource extends AbstractFallbackJC
|
|||
}
|
||||
}
|
||||
|
||||
protected CacheDefaults getCacheDefaults(Method method, Class<?> targetType) {
|
||||
@Nullable
|
||||
protected CacheDefaults getCacheDefaults(Method method, @Nullable Class<?> targetType) {
|
||||
CacheDefaults annotation = method.getDeclaringClass().getAnnotation(CacheDefaults.class);
|
||||
if (annotation != null) {
|
||||
return annotation;
|
||||
}
|
||||
return targetType.getAnnotation(CacheDefaults.class);
|
||||
return (targetType != null ? targetType.getAnnotation(CacheDefaults.class) : null);
|
||||
}
|
||||
|
||||
protected CacheResultOperation createCacheResultOperation(Method method, CacheDefaults defaults, CacheResult ann) {
|
||||
protected CacheResultOperation createCacheResultOperation(Method method, @Nullable CacheDefaults defaults, CacheResult ann) {
|
||||
String cacheName = determineCacheName(method, defaults, ann.cacheName());
|
||||
CacheResolverFactory cacheResolverFactory =
|
||||
determineCacheResolverFactory(defaults, ann.cacheResolverFactory());
|
||||
|
|
@ -100,7 +101,7 @@ public abstract class AnnotationJCacheOperationSource extends AbstractFallbackJC
|
|||
return new CacheResultOperation(methodDetails, cacheResolver, keyGenerator, exceptionCacheResolver);
|
||||
}
|
||||
|
||||
protected CachePutOperation createCachePutOperation(Method method, CacheDefaults defaults, CachePut ann) {
|
||||
protected CachePutOperation createCachePutOperation(Method method, @Nullable CacheDefaults defaults, CachePut ann) {
|
||||
String cacheName = determineCacheName(method, defaults, ann.cacheName());
|
||||
CacheResolverFactory cacheResolverFactory =
|
||||
determineCacheResolverFactory(defaults, ann.cacheResolverFactory());
|
||||
|
|
@ -111,7 +112,7 @@ public abstract class AnnotationJCacheOperationSource extends AbstractFallbackJC
|
|||
return new CachePutOperation(methodDetails, cacheResolver, keyGenerator);
|
||||
}
|
||||
|
||||
protected CacheRemoveOperation createCacheRemoveOperation(Method method, CacheDefaults defaults, CacheRemove ann) {
|
||||
protected CacheRemoveOperation createCacheRemoveOperation(Method method, @Nullable CacheDefaults defaults, CacheRemove ann) {
|
||||
String cacheName = determineCacheName(method, defaults, ann.cacheName());
|
||||
CacheResolverFactory cacheResolverFactory =
|
||||
determineCacheResolverFactory(defaults, ann.cacheResolverFactory());
|
||||
|
|
@ -122,7 +123,7 @@ public abstract class AnnotationJCacheOperationSource extends AbstractFallbackJC
|
|||
return new CacheRemoveOperation(methodDetails, cacheResolver, keyGenerator);
|
||||
}
|
||||
|
||||
protected CacheRemoveAllOperation createCacheRemoveAllOperation(Method method, CacheDefaults defaults, CacheRemoveAll ann) {
|
||||
protected CacheRemoveAllOperation createCacheRemoveAllOperation(Method method, @Nullable CacheDefaults defaults, CacheRemoveAll ann) {
|
||||
String cacheName = determineCacheName(method, defaults, ann.cacheName());
|
||||
CacheResolverFactory cacheResolverFactory =
|
||||
determineCacheResolverFactory(defaults, ann.cacheResolverFactory());
|
||||
|
|
@ -136,7 +137,9 @@ public abstract class AnnotationJCacheOperationSource extends AbstractFallbackJC
|
|||
return new DefaultCacheMethodDetails<>(method, annotation, cacheName);
|
||||
}
|
||||
|
||||
protected CacheResolver getCacheResolver(CacheResolverFactory factory, CacheMethodDetails<?> details) {
|
||||
protected CacheResolver getCacheResolver(
|
||||
@Nullable CacheResolverFactory factory, CacheMethodDetails<?> details) {
|
||||
|
||||
if (factory != null) {
|
||||
javax.cache.annotation.CacheResolver cacheResolver = factory.getCacheResolver(details);
|
||||
return new CacheResolverAdapter(cacheResolver);
|
||||
|
|
@ -146,8 +149,8 @@ public abstract class AnnotationJCacheOperationSource extends AbstractFallbackJC
|
|||
}
|
||||
}
|
||||
|
||||
protected CacheResolver getExceptionCacheResolver(CacheResolverFactory factory,
|
||||
CacheMethodDetails<CacheResult> details) {
|
||||
protected CacheResolver getExceptionCacheResolver(
|
||||
@Nullable CacheResolverFactory factory, CacheMethodDetails<CacheResult> details) {
|
||||
|
||||
if (factory != null) {
|
||||
javax.cache.annotation.CacheResolver cacheResolver = factory.getExceptionCacheResolver(details);
|
||||
|
|
@ -159,13 +162,13 @@ public abstract class AnnotationJCacheOperationSource extends AbstractFallbackJC
|
|||
}
|
||||
|
||||
@Nullable
|
||||
protected CacheResolverFactory determineCacheResolverFactory(CacheDefaults defaults,
|
||||
Class<? extends CacheResolverFactory> candidate) {
|
||||
protected CacheResolverFactory determineCacheResolverFactory(
|
||||
@Nullable CacheDefaults defaults, Class<? extends CacheResolverFactory> candidate) {
|
||||
|
||||
if (CacheResolverFactory.class != candidate) {
|
||||
if (candidate != CacheResolverFactory.class) {
|
||||
return getBean(candidate);
|
||||
}
|
||||
else if (defaults != null && CacheResolverFactory.class != defaults.cacheResolverFactory()) {
|
||||
else if (defaults != null && defaults.cacheResolverFactory() != CacheResolverFactory.class) {
|
||||
return getBean(defaults.cacheResolverFactory());
|
||||
}
|
||||
else {
|
||||
|
|
@ -173,8 +176,10 @@ public abstract class AnnotationJCacheOperationSource extends AbstractFallbackJC
|
|||
}
|
||||
}
|
||||
|
||||
protected KeyGenerator determineKeyGenerator(CacheDefaults defaults, Class<? extends CacheKeyGenerator> candidate) {
|
||||
if (CacheKeyGenerator.class != candidate) {
|
||||
protected KeyGenerator determineKeyGenerator(
|
||||
@Nullable CacheDefaults defaults, Class<? extends CacheKeyGenerator> candidate) {
|
||||
|
||||
if (candidate != CacheKeyGenerator.class) {
|
||||
return new KeyGeneratorAdapter(this, getBean(candidate));
|
||||
}
|
||||
else if (defaults != null && CacheKeyGenerator.class != defaults.cacheKeyGenerator()) {
|
||||
|
|
@ -185,7 +190,7 @@ public abstract class AnnotationJCacheOperationSource extends AbstractFallbackJC
|
|||
}
|
||||
}
|
||||
|
||||
protected String determineCacheName(Method method, CacheDefaults defaults, String candidate) {
|
||||
protected String determineCacheName(Method method, @Nullable CacheDefaults defaults, String candidate) {
|
||||
if (StringUtils.hasText(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package org.springframework.cache.jcache.interceptor;
|
|||
import org.springframework.aop.ClassFilter;
|
||||
import org.springframework.aop.Pointcut;
|
||||
import org.springframework.aop.support.AbstractBeanFactoryPointcutAdvisor;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Advisor driven by a {@link JCacheOperationSource}, used to include a
|
||||
|
|
@ -30,6 +31,7 @@ import org.springframework.aop.support.AbstractBeanFactoryPointcutAdvisor;
|
|||
@SuppressWarnings("serial")
|
||||
public class BeanFactoryJCacheOperationSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {
|
||||
|
||||
@Nullable
|
||||
private JCacheOperationSource cacheOperationSource;
|
||||
|
||||
private final JCacheOperationSourcePointcut pointcut = new JCacheOperationSourcePointcut() {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -37,16 +37,16 @@ class CachePutInterceptor extends AbstractKeyCacheInterceptor<CachePutOperation,
|
|||
super(errorHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object invoke(CacheOperationInvocationContext<CachePutOperation> context,
|
||||
CacheOperationInvoker invoker) {
|
||||
|
||||
CacheKeyInvocationContext<CachePut> invocationContext = createCacheKeyInvocationContext(context);
|
||||
@Override
|
||||
protected Object invoke(
|
||||
CacheOperationInvocationContext<CachePutOperation> context, CacheOperationInvoker invoker) {
|
||||
|
||||
CachePutOperation operation = context.getOperation();
|
||||
CacheKeyInvocationContext<CachePut> invocationContext = createCacheKeyInvocationContext(context);
|
||||
|
||||
boolean earlyPut = operation.isEarlyPut();
|
||||
Object value = invocationContext.getValueParameter().getValue();
|
||||
|
||||
if (earlyPut) {
|
||||
cacheValue(context, value);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -24,6 +24,7 @@ import javax.cache.annotation.CachePut;
|
|||
|
||||
import org.springframework.cache.interceptor.CacheResolver;
|
||||
import org.springframework.cache.interceptor.KeyGenerator;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ExceptionTypeFilter;
|
||||
|
||||
/**
|
||||
|
|
@ -44,13 +45,17 @@ class CachePutOperation extends AbstractJCacheKeyOperation<CachePut> {
|
|||
CacheMethodDetails<CachePut> methodDetails, CacheResolver cacheResolver, KeyGenerator keyGenerator) {
|
||||
|
||||
super(methodDetails, cacheResolver, keyGenerator);
|
||||
|
||||
CachePut ann = methodDetails.getCacheAnnotation();
|
||||
this.exceptionTypeFilter = createExceptionTypeFilter(ann.cacheFor(), ann.noCacheFor());
|
||||
this.valueParameterDetail = initializeValueParameterDetail(methodDetails.getMethod(), this.allParameterDetails);
|
||||
if (this.valueParameterDetail == null) {
|
||||
|
||||
CacheParameterDetail valueParameterDetail =
|
||||
initializeValueParameterDetail(methodDetails.getMethod(), this.allParameterDetails);
|
||||
if (valueParameterDetail == null) {
|
||||
throw new IllegalArgumentException("No parameter annotated with @CacheValue was found for " +
|
||||
"" + methodDetails.getMethod());
|
||||
methodDetails.getMethod());
|
||||
}
|
||||
this.valueParameterDetail = valueParameterDetail;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -85,6 +90,7 @@ class CachePutOperation extends AbstractJCacheKeyOperation<CachePut> {
|
|||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
private static CacheParameterDetail initializeValueParameterDetail(
|
||||
Method method, List<CacheParameterDetail> allParameters) {
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -30,21 +30,20 @@ import org.springframework.cache.interceptor.CacheOperationInvoker;
|
|||
* @since 4.1
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
class CacheRemoveAllInterceptor
|
||||
extends AbstractCacheInterceptor<CacheRemoveAllOperation, CacheRemoveAll> {
|
||||
class CacheRemoveAllInterceptor extends AbstractCacheInterceptor<CacheRemoveAllOperation, CacheRemoveAll> {
|
||||
|
||||
protected CacheRemoveAllInterceptor(CacheErrorHandler errorHandler) {
|
||||
super(errorHandler);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Object invoke(CacheOperationInvocationContext<CacheRemoveAllOperation> context,
|
||||
CacheOperationInvoker invoker) {
|
||||
protected Object invoke(
|
||||
CacheOperationInvocationContext<CacheRemoveAllOperation> context, CacheOperationInvoker invoker) {
|
||||
|
||||
CacheRemoveAllOperation operation = context.getOperation();
|
||||
|
||||
boolean earlyRemove = operation.isEarlyRemove();
|
||||
|
||||
if (earlyRemove) {
|
||||
removeAll(context);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,13 +36,14 @@ class CacheRemoveEntryInterceptor extends AbstractKeyCacheInterceptor<CacheRemov
|
|||
super(errorHandler);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Object invoke(CacheOperationInvocationContext<CacheRemoveOperation> context,
|
||||
CacheOperationInvoker invoker) {
|
||||
protected Object invoke(
|
||||
CacheOperationInvocationContext<CacheRemoveOperation> context, CacheOperationInvoker invoker) {
|
||||
|
||||
CacheRemoveOperation operation = context.getOperation();
|
||||
|
||||
final boolean earlyRemove = operation.isEarlyRemove();
|
||||
|
||||
boolean earlyRemove = operation.isEarlyRemove();
|
||||
if (earlyRemove) {
|
||||
removeValue(context);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -24,6 +24,7 @@ import org.springframework.cache.interceptor.CacheOperationInvocationContext;
|
|||
import org.springframework.cache.interceptor.CacheOperationInvoker;
|
||||
import org.springframework.cache.interceptor.CacheResolver;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ExceptionTypeFilter;
|
||||
import org.springframework.util.SerializationUtils;
|
||||
|
||||
|
|
@ -42,8 +43,9 @@ class CacheResultInterceptor extends AbstractKeyCacheInterceptor<CacheResultOper
|
|||
|
||||
|
||||
@Override
|
||||
protected Object invoke(CacheOperationInvocationContext<CacheResultOperation> context,
|
||||
CacheOperationInvoker invoker) {
|
||||
@Nullable
|
||||
protected Object invoke(
|
||||
CacheOperationInvocationContext<CacheResultOperation> context, CacheOperationInvoker invoker) {
|
||||
|
||||
CacheResultOperation operation = context.getOperation();
|
||||
Object cacheKey = generateKey(context);
|
||||
|
|
@ -74,17 +76,19 @@ class CacheResultInterceptor extends AbstractKeyCacheInterceptor<CacheResultOper
|
|||
/**
|
||||
* Check for a cached exception. If the exception is found, throw it directly.
|
||||
*/
|
||||
protected void checkForCachedException(Cache exceptionCache, Object cacheKey) {
|
||||
protected void checkForCachedException(@Nullable Cache exceptionCache, Object cacheKey) {
|
||||
if (exceptionCache == null) {
|
||||
return;
|
||||
}
|
||||
Cache.ValueWrapper result = doGet(exceptionCache, cacheKey);
|
||||
if (result != null) {
|
||||
throw rewriteCallStack((Throwable) result.get(), getClass().getName(), "invoke");
|
||||
Throwable ex = (Throwable) result.get();
|
||||
Assert.state(ex != null, "No exception in cache");
|
||||
throw rewriteCallStack(ex, getClass().getName(), "invoke");
|
||||
}
|
||||
}
|
||||
|
||||
protected void cacheException(Cache exceptionCache, ExceptionTypeFilter filter, Object cacheKey, Throwable ex) {
|
||||
protected void cacheException(@Nullable Cache exceptionCache, ExceptionTypeFilter filter, Object cacheKey, Throwable ex) {
|
||||
if (exceptionCache == null) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -143,6 +147,7 @@ class CacheResultInterceptor extends AbstractKeyCacheInterceptor<CacheResultOper
|
|||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
private static <T extends Throwable> T cloneException(T exception) {
|
||||
try {
|
||||
return (T) SerializationUtils.deserialize(SerializationUtils.serialize(exception));
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -36,15 +36,18 @@ class CacheResultOperation extends AbstractJCacheKeyOperation<CacheResult> {
|
|||
|
||||
private final ExceptionTypeFilter exceptionTypeFilter;
|
||||
|
||||
@Nullable
|
||||
private final CacheResolver exceptionCacheResolver;
|
||||
|
||||
@Nullable
|
||||
private final String exceptionCacheName;
|
||||
|
||||
|
||||
public CacheResultOperation(CacheMethodDetails<CacheResult> methodDetails, CacheResolver cacheResolver,
|
||||
KeyGenerator keyGenerator, CacheResolver exceptionCacheResolver) {
|
||||
KeyGenerator keyGenerator, @Nullable CacheResolver exceptionCacheResolver) {
|
||||
|
||||
super(methodDetails, cacheResolver, keyGenerator);
|
||||
|
||||
CacheResult ann = methodDetails.getCacheAnnotation();
|
||||
this.exceptionTypeFilter = createExceptionTypeFilter(ann.cachedExceptions(), ann.nonCachedExceptions());
|
||||
this.exceptionCacheResolver = exceptionCacheResolver;
|
||||
|
|
@ -70,6 +73,7 @@ class CacheResultOperation extends AbstractJCacheKeyOperation<CacheResult> {
|
|||
* Return the {@link CacheResolver} instance to use to resolve the cache to
|
||||
* use for matching exceptions thrown by this operation.
|
||||
*/
|
||||
@Nullable
|
||||
public CacheResolver getExceptionCacheResolver() {
|
||||
return this.exceptionCacheResolver;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ import java.lang.annotation.Annotation;
|
|||
import javax.cache.annotation.CacheInvocationParameter;
|
||||
import javax.cache.annotation.CacheKeyInvocationContext;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* The default {@link CacheKeyInvocationContext} implementation.
|
||||
*
|
||||
|
|
@ -32,10 +34,11 @@ class DefaultCacheKeyInvocationContext<A extends Annotation>
|
|||
|
||||
private final CacheInvocationParameter[] keyParameters;
|
||||
|
||||
@Nullable
|
||||
private final CacheInvocationParameter valueParameter;
|
||||
|
||||
public DefaultCacheKeyInvocationContext(AbstractJCacheKeyOperation<A> operation,
|
||||
Object target, Object[] args) {
|
||||
|
||||
public DefaultCacheKeyInvocationContext(AbstractJCacheKeyOperation<A> operation, Object target, Object[] args) {
|
||||
super(operation, target, args);
|
||||
this.keyParameters = operation.getKeyParameters(args);
|
||||
if (operation instanceof CachePutOperation) {
|
||||
|
|
@ -46,12 +49,14 @@ class DefaultCacheKeyInvocationContext<A extends Annotation>
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public CacheInvocationParameter[] getKeyParameters() {
|
||||
return this.keyParameters.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public CacheInvocationParameter getValueParameter() {
|
||||
return this.valueParameter;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -17,11 +17,11 @@
|
|||
package org.springframework.cache.jcache.interceptor;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
|
||||
import org.springframework.beans.factory.SmartInitializingSingleton;
|
||||
|
|
@ -34,6 +34,8 @@ import org.springframework.cache.interceptor.SimpleCacheResolver;
|
|||
import org.springframework.cache.interceptor.SimpleKeyGenerator;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.function.SingletonSupplier;
|
||||
import org.springframework.util.function.SupplierUtils;
|
||||
|
||||
/**
|
||||
* The default {@link JCacheOperationSource} implementation delegating
|
||||
|
|
@ -41,30 +43,61 @@ import org.springframework.util.Assert;
|
|||
* when not present.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Juergen Hoeller
|
||||
* @since 4.1
|
||||
*/
|
||||
public class DefaultJCacheOperationSource extends AnnotationJCacheOperationSource
|
||||
implements BeanFactoryAware, InitializingBean, SmartInitializingSingleton {
|
||||
implements BeanFactoryAware, SmartInitializingSingleton {
|
||||
|
||||
private CacheManager cacheManager;
|
||||
@Nullable
|
||||
private SingletonSupplier<CacheManager> cacheManager;
|
||||
|
||||
private CacheResolver cacheResolver;
|
||||
@Nullable
|
||||
private SingletonSupplier<CacheResolver> cacheResolver;
|
||||
|
||||
private CacheResolver exceptionCacheResolver;
|
||||
@Nullable
|
||||
private SingletonSupplier<CacheResolver> exceptionCacheResolver;
|
||||
|
||||
private KeyGenerator keyGenerator = new SimpleKeyGenerator();
|
||||
private SingletonSupplier<KeyGenerator> keyGenerator;
|
||||
|
||||
private KeyGenerator adaptedKeyGenerator;
|
||||
private final SingletonSupplier<KeyGenerator> adaptedKeyGenerator =
|
||||
SingletonSupplier.of(() -> new KeyGeneratorAdapter(this, getKeyGenerator()));
|
||||
|
||||
@Nullable
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Set the default {@link CacheManager} to use to lookup cache by name. Only mandatory
|
||||
* if the {@linkplain CacheResolver cache resolvers} have not been set.
|
||||
* Construct a new {@code DefaultJCacheOperationSource} with the default key generator.
|
||||
* @see SimpleKeyGenerator
|
||||
*/
|
||||
public DefaultJCacheOperationSource() {
|
||||
this.keyGenerator = SingletonSupplier.of(SimpleKeyGenerator::new);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new {@code DefaultJCacheOperationSource} with the given cache manager,
|
||||
* cache resolver and key generator suppliers, applying the corresponding default
|
||||
* if a supplier is not resolvable.
|
||||
* @since 5.1
|
||||
*/
|
||||
public DefaultJCacheOperationSource(
|
||||
@Nullable Supplier<CacheManager> cacheManager, @Nullable Supplier<CacheResolver> cacheResolver,
|
||||
@Nullable Supplier<CacheResolver> exceptionCacheResolver, @Nullable Supplier<KeyGenerator> keyGenerator) {
|
||||
|
||||
this.cacheManager = SingletonSupplier.ofNullable(cacheManager);
|
||||
this.cacheResolver = SingletonSupplier.ofNullable(cacheResolver);
|
||||
this.exceptionCacheResolver = SingletonSupplier.ofNullable(exceptionCacheResolver);
|
||||
this.keyGenerator = new SingletonSupplier<>(keyGenerator, SimpleKeyGenerator::new);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the default {@link CacheManager} to use to lookup cache by name.
|
||||
* Only mandatory if the {@linkplain CacheResolver cache resolver} has not been set.
|
||||
*/
|
||||
public void setCacheManager(@Nullable CacheManager cacheManager) {
|
||||
this.cacheManager = cacheManager;
|
||||
this.cacheManager = SingletonSupplier.ofNullable(cacheManager);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -72,7 +105,7 @@ public class DefaultJCacheOperationSource extends AnnotationJCacheOperationSourc
|
|||
*/
|
||||
@Nullable
|
||||
public CacheManager getCacheManager() {
|
||||
return this.cacheManager;
|
||||
return SupplierUtils.resolve(this.cacheManager);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -80,7 +113,7 @@ public class DefaultJCacheOperationSource extends AnnotationJCacheOperationSourc
|
|||
* implementation using the specified cache manager will be used.
|
||||
*/
|
||||
public void setCacheResolver(@Nullable CacheResolver cacheResolver) {
|
||||
this.cacheResolver = cacheResolver;
|
||||
this.cacheResolver = SingletonSupplier.ofNullable(cacheResolver);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -88,7 +121,7 @@ public class DefaultJCacheOperationSource extends AnnotationJCacheOperationSourc
|
|||
*/
|
||||
@Nullable
|
||||
public CacheResolver getCacheResolver() {
|
||||
return this.cacheResolver;
|
||||
return SupplierUtils.resolve(this.cacheResolver);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -96,7 +129,7 @@ public class DefaultJCacheOperationSource extends AnnotationJCacheOperationSourc
|
|||
* implementation using the specified cache manager will be used.
|
||||
*/
|
||||
public void setExceptionCacheResolver(@Nullable CacheResolver exceptionCacheResolver) {
|
||||
this.exceptionCacheResolver = exceptionCacheResolver;
|
||||
this.exceptionCacheResolver = SingletonSupplier.ofNullable(exceptionCacheResolver);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -104,7 +137,7 @@ public class DefaultJCacheOperationSource extends AnnotationJCacheOperationSourc
|
|||
*/
|
||||
@Nullable
|
||||
public CacheResolver getExceptionCacheResolver() {
|
||||
return this.exceptionCacheResolver;
|
||||
return SupplierUtils.resolve(this.exceptionCacheResolver);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -112,16 +145,15 @@ public class DefaultJCacheOperationSource extends AnnotationJCacheOperationSourc
|
|||
* honoring the JSR-107 {@link javax.cache.annotation.CacheKey} and
|
||||
* {@link javax.cache.annotation.CacheValue} will be used.
|
||||
*/
|
||||
public void setKeyGenerator(@Nullable KeyGenerator keyGenerator) {
|
||||
this.keyGenerator = keyGenerator;
|
||||
public void setKeyGenerator(KeyGenerator keyGenerator) {
|
||||
this.keyGenerator = SingletonSupplier.of(keyGenerator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the specified key generator to use, if any.
|
||||
* Return the specified key generator to use.
|
||||
*/
|
||||
@Nullable
|
||||
public KeyGenerator getKeyGenerator() {
|
||||
return this.keyGenerator;
|
||||
return this.keyGenerator.obtain();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -130,21 +162,17 @@ public class DefaultJCacheOperationSource extends AnnotationJCacheOperationSourc
|
|||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
this.adaptedKeyGenerator = new KeyGeneratorAdapter(this, this.keyGenerator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterSingletonsInstantiated() {
|
||||
// Make sure that the cache resolver is initialized. An exception cache resolver is only
|
||||
// required if the exceptionCacheName attribute is set on an operation
|
||||
// required if the exceptionCacheName attribute is set on an operation.
|
||||
Assert.notNull(getDefaultCacheResolver(), "Cache resolver should have been initialized");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected <T> T getBean(Class<T> type) {
|
||||
Assert.state(this.beanFactory != null, "BeanFactory required for resolution of [" + type + "]");
|
||||
try {
|
||||
return this.beanFactory.getBean(type);
|
||||
}
|
||||
|
|
@ -161,9 +189,10 @@ public class DefaultJCacheOperationSource extends AnnotationJCacheOperationSourc
|
|||
}
|
||||
|
||||
protected CacheManager getDefaultCacheManager() {
|
||||
if (this.cacheManager == null) {
|
||||
if (getCacheManager() == null) {
|
||||
Assert.state(this.beanFactory != null, "BeanFactory required for default CacheManager resolution");
|
||||
try {
|
||||
this.cacheManager = this.beanFactory.getBean(CacheManager.class);
|
||||
this.cacheManager = SingletonSupplier.of(this.beanFactory.getBean(CacheManager.class));
|
||||
}
|
||||
catch (NoUniqueBeanDefinitionException ex) {
|
||||
throw new IllegalStateException("No unique bean of type CacheManager found. "+
|
||||
|
|
@ -174,50 +203,50 @@ public class DefaultJCacheOperationSource extends AnnotationJCacheOperationSourc
|
|||
"bean or remove the @EnableCaching annotation from your configuration.");
|
||||
}
|
||||
}
|
||||
return this.cacheManager;
|
||||
return getCacheManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CacheResolver getDefaultCacheResolver() {
|
||||
if (this.cacheResolver == null) {
|
||||
this.cacheResolver = new SimpleCacheResolver(getDefaultCacheManager());
|
||||
if (getCacheResolver() == null) {
|
||||
this.cacheResolver = SingletonSupplier.of(new SimpleCacheResolver(getDefaultCacheManager()));
|
||||
}
|
||||
return this.cacheResolver;
|
||||
return getCacheResolver();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CacheResolver getDefaultExceptionCacheResolver() {
|
||||
if (this.exceptionCacheResolver == null) {
|
||||
this.exceptionCacheResolver = new LazyCacheResolver();
|
||||
if (getExceptionCacheResolver() == null) {
|
||||
this.exceptionCacheResolver = SingletonSupplier.of(new LazyCacheResolver());
|
||||
}
|
||||
return this.exceptionCacheResolver;
|
||||
return getExceptionCacheResolver();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected KeyGenerator getDefaultKeyGenerator() {
|
||||
return this.adaptedKeyGenerator;
|
||||
return this.adaptedKeyGenerator.obtain();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Only resolve the default exception cache resolver when an exception needs to be handled.
|
||||
* <p>A non-JSR-107 setup requires either a {@link CacheManager} or a {@link CacheResolver}. If only
|
||||
* the latter is specified, it is not possible to extract a default exception {@code CacheResolver}
|
||||
* from a custom {@code CacheResolver} implementation so we have to fallback on the {@code CacheManager}.
|
||||
* <p>This gives this weird situation of a perfectly valid configuration that breaks all the sudden
|
||||
* because the JCache support is enabled. To avoid this we resolve the default exception {@code CacheResolver}
|
||||
* as late as possible to avoid such hard requirement in other cases.
|
||||
* <p>A non-JSR-107 setup requires either a {@link CacheManager} or a {@link CacheResolver}.
|
||||
* If only the latter is specified, it is not possible to extract a default exception
|
||||
* {@code CacheResolver} from a custom {@code CacheResolver} implementation so we have to
|
||||
* fall back on the {@code CacheManager}.
|
||||
* <p>This gives this weird situation of a perfectly valid configuration that breaks all
|
||||
* the sudden because the JCache support is enabled. To avoid this we resolve the default
|
||||
* exception {@code CacheResolver} as late as possible to avoid such hard requirement
|
||||
* in other cases.
|
||||
*/
|
||||
class LazyCacheResolver implements CacheResolver {
|
||||
|
||||
private CacheResolver cacheResolver;
|
||||
private final SingletonSupplier<CacheResolver> cacheResolver =
|
||||
SingletonSupplier.of(() -> new SimpleExceptionCacheResolver(getDefaultCacheManager()));
|
||||
|
||||
@Override
|
||||
public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context) {
|
||||
if (this.cacheResolver == null) {
|
||||
this.cacheResolver = new SimpleExceptionCacheResolver(getDefaultCacheManager());
|
||||
}
|
||||
return this.cacheResolver.resolveCaches(context);
|
||||
return this.cacheResolver.obtain().resolveCaches(context);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -28,6 +28,7 @@ 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;
|
||||
|
||||
/**
|
||||
|
|
@ -52,19 +53,27 @@ public class JCacheAspectSupport extends AbstractCacheInvoker implements Initial
|
|||
|
||||
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;
|
||||
|
||||
private CacheResultInterceptor cacheResultInterceptor;
|
||||
|
||||
private CachePutInterceptor cachePutInterceptor;
|
||||
|
||||
private CacheRemoveEntryInterceptor cacheRemoveEntryInterceptor;
|
||||
|
||||
private CacheRemoveAllInterceptor cacheRemoveAllInterceptor;
|
||||
|
||||
|
||||
/**
|
||||
* Set the CacheOperationSource for this cache aspect.
|
||||
*/
|
||||
public void setCacheOperationSource(JCacheOperationSource cacheOperationSource) {
|
||||
Assert.notNull(cacheOperationSource, "JCacheOperationSource must not be null");
|
||||
this.cacheOperationSource = cacheOperationSource;
|
||||
|
|
@ -74,12 +83,13 @@ public class JCacheAspectSupport extends AbstractCacheInvoker implements Initial
|
|||
* 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() {
|
||||
Assert.state(getCacheOperationSource() != null, "The 'cacheOperationSource' property is required: " +
|
||||
"If there are no cacheable methods, then don't use a cache aspect.");
|
||||
getCacheOperationSource();
|
||||
|
||||
this.cacheResultInterceptor = new CacheResultInterceptor(getErrorHandler());
|
||||
this.cachePutInterceptor = new CachePutInterceptor(getErrorHandler());
|
||||
|
|
@ -90,6 +100,7 @@ public class JCacheAspectSupport extends AbstractCacheInvoker implements Initial
|
|||
}
|
||||
|
||||
|
||||
@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) {
|
||||
|
|
@ -114,23 +125,28 @@ public class JCacheAspectSupport extends AbstractCacheInvoker implements Initial
|
|||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -18,11 +18,16 @@ package org.springframework.cache.jcache.interceptor;
|
|||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import org.springframework.cache.interceptor.CacheErrorHandler;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvoker;
|
||||
import org.springframework.cache.interceptor.SimpleCacheErrorHandler;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.function.SingletonSupplier;
|
||||
|
||||
/**
|
||||
* AOP Alliance MethodInterceptor for declarative cache
|
||||
|
|
@ -35,13 +40,32 @@ import org.springframework.cache.interceptor.CacheOperationInvoker;
|
|||
* <p>JCacheInterceptors are thread-safe.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Juergen Hoeller
|
||||
* @since 4.1
|
||||
* @see org.springframework.cache.interceptor.CacheInterceptor
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class JCacheInterceptor extends JCacheAspectSupport implements MethodInterceptor, Serializable {
|
||||
|
||||
/**
|
||||
* Construct a new {@code JCacheInterceptor} with the default error handler.
|
||||
*/
|
||||
public JCacheInterceptor() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new {@code JCacheInterceptor} with the given error handler.
|
||||
* @param errorHandler a supplier for the error handler to use,
|
||||
* applying the default error handler if the supplier is not resolvable
|
||||
* @since 5.1
|
||||
*/
|
||||
public JCacheInterceptor(@Nullable Supplier<CacheErrorHandler> errorHandler) {
|
||||
this.errorHandler = new SingletonSupplier<>(errorHandler, SimpleCacheErrorHandler::new);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Object invoke(final MethodInvocation invocation) throws Throwable {
|
||||
Method method = invocation.getMethod();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -33,7 +33,6 @@ public interface JCacheOperationSource {
|
|||
/**
|
||||
* Return the cache operations for this method, or {@code null}
|
||||
* if the method contains no <em>JSR-107</em> related metadata.
|
||||
*
|
||||
* @param method the method to introspect
|
||||
* @param targetClass the target class (may be {@code null}, in which case
|
||||
* the declaring class of the method must be used)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -31,11 +31,10 @@ import org.springframework.util.ObjectUtils;
|
|||
* @since 4.1
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public abstract class JCacheOperationSourcePointcut
|
||||
extends StaticMethodMatcherPointcut implements Serializable {
|
||||
public abstract class JCacheOperationSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
|
||||
|
||||
@Override
|
||||
public boolean matches(Method method, Class<?> targetClass) {
|
||||
public boolean matches(Method method, @Nullable Class<?> targetClass) {
|
||||
JCacheOperationSource cas = getCacheOperationSource();
|
||||
return (cas != null && cas.getCacheOperation(method, targetClass) != null);
|
||||
}
|
||||
|
|
@ -47,6 +46,7 @@ public abstract class JCacheOperationSourcePointcut
|
|||
@Nullable
|
||||
protected abstract JCacheOperationSource getCacheOperationSource();
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import javax.cache.annotation.CacheKeyGenerator;
|
|||
import javax.cache.annotation.CacheKeyInvocationContext;
|
||||
|
||||
import org.springframework.cache.interceptor.KeyGenerator;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
|
|
@ -34,14 +35,17 @@ import org.springframework.util.CollectionUtils;
|
|||
* so that only relevant parameters are handled.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Juergen Hoeller
|
||||
* @since 4.1
|
||||
*/
|
||||
class KeyGeneratorAdapter implements KeyGenerator {
|
||||
|
||||
private final JCacheOperationSource cacheOperationSource;
|
||||
|
||||
@Nullable
|
||||
private KeyGenerator keyGenerator;
|
||||
|
||||
@Nullable
|
||||
private CacheKeyGenerator cacheKeyGenerator;
|
||||
|
||||
|
||||
|
|
@ -72,7 +76,11 @@ class KeyGeneratorAdapter implements KeyGenerator {
|
|||
* or a {@link CacheKeyGenerator}.
|
||||
*/
|
||||
public Object getTarget() {
|
||||
return (this.keyGenerator != null ? this.keyGenerator : this.cacheKeyGenerator);
|
||||
if (this.cacheKeyGenerator != null) {
|
||||
return this.cacheKeyGenerator;
|
||||
}
|
||||
Assert.state(this.keyGenerator != null, "No key generator");
|
||||
return this.keyGenerator;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -87,6 +95,7 @@ class KeyGeneratorAdapter implements KeyGenerator {
|
|||
return this.cacheKeyGenerator.generateCacheKey(invocationContext);
|
||||
}
|
||||
else {
|
||||
Assert.state(this.keyGenerator != null, "No key generator");
|
||||
return doGenerate(this.keyGenerator, invocationContext);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,4 +7,9 @@
|
|||
* <p>Builds on the AOP infrastructure in org.springframework.aop.framework.
|
||||
* Any POJO can be cache-advised with Spring.
|
||||
*/
|
||||
@NonNullApi
|
||||
@NonNullFields
|
||||
package org.springframework.cache.jcache.interceptor;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
import org.springframework.lang.NonNullFields;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -47,16 +47,15 @@ public class AnnotationCacheOperationSourceTests extends AbstractJCacheTests {
|
|||
|
||||
private final DefaultJCacheOperationSource source = new DefaultJCacheOperationSource();
|
||||
|
||||
private final DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
private final DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
public void setup() {
|
||||
source.setCacheResolver(defaultCacheResolver);
|
||||
source.setExceptionCacheResolver(defaultExceptionCacheResolver);
|
||||
source.setKeyGenerator(defaultKeyGenerator);
|
||||
source.setBeanFactory(beanFactory);
|
||||
source.afterPropertiesSet();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -113,7 +113,6 @@ public class JCacheInterceptorTests extends AbstractJCacheTests {
|
|||
source.setExceptionCacheResolver(exceptionCacheResolver);
|
||||
source.setKeyGenerator(keyGenerator);
|
||||
source.setBeanFactory(new StaticListableBeanFactory());
|
||||
source.afterPropertiesSet();
|
||||
source.afterSingletonsInstantiated();
|
||||
return source;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -17,6 +17,7 @@
|
|||
package org.springframework.cache.annotation;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cache.CacheManager;
|
||||
|
|
@ -36,6 +37,7 @@ import org.springframework.util.CollectionUtils;
|
|||
*
|
||||
* @author Chris Beams
|
||||
* @author Stephane Nicoll
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
* @see EnableCaching
|
||||
*/
|
||||
|
|
@ -46,16 +48,16 @@ public abstract class AbstractCachingConfiguration implements ImportAware {
|
|||
protected AnnotationAttributes enableCaching;
|
||||
|
||||
@Nullable
|
||||
protected CacheManager cacheManager;
|
||||
protected Supplier<CacheManager> cacheManager;
|
||||
|
||||
@Nullable
|
||||
protected CacheResolver cacheResolver;
|
||||
protected Supplier<CacheResolver> cacheResolver;
|
||||
|
||||
@Nullable
|
||||
protected KeyGenerator keyGenerator;
|
||||
protected Supplier<KeyGenerator> keyGenerator;
|
||||
|
||||
@Nullable
|
||||
protected CacheErrorHandler errorHandler;
|
||||
protected Supplier<CacheErrorHandler> errorHandler;
|
||||
|
||||
|
||||
@Override
|
||||
|
|
@ -87,10 +89,10 @@ public abstract class AbstractCachingConfiguration implements ImportAware {
|
|||
* Extract the configuration from the nominated {@link CachingConfigurer}.
|
||||
*/
|
||||
protected void useCachingConfigurer(CachingConfigurer config) {
|
||||
this.cacheManager = config.cacheManager();
|
||||
this.cacheResolver = config.cacheResolver();
|
||||
this.keyGenerator = config.keyGenerator();
|
||||
this.errorHandler = config.errorHandler();
|
||||
this.cacheManager = config::cacheManager;
|
||||
this.cacheResolver = config::cacheResolver;
|
||||
this.keyGenerator = config::keyGenerator;
|
||||
this.errorHandler = config::errorHandler;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -40,13 +40,13 @@ public class CachingConfigurerSupport implements CachingConfigurer {
|
|||
|
||||
@Override
|
||||
@Nullable
|
||||
public KeyGenerator keyGenerator() {
|
||||
public CacheResolver cacheResolver() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public CacheResolver cacheResolver() {
|
||||
public KeyGenerator keyGenerator() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import org.springframework.context.annotation.Role;
|
|||
* to enable proxy-based annotation-driven cache management.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
* @see EnableCaching
|
||||
* @see CachingConfigurationSelector
|
||||
|
|
@ -61,19 +62,8 @@ public class ProxyCachingConfiguration extends AbstractCachingConfiguration {
|
|||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
public CacheInterceptor cacheInterceptor() {
|
||||
CacheInterceptor interceptor = new CacheInterceptor();
|
||||
interceptor.setCacheOperationSources(cacheOperationSource());
|
||||
if (this.cacheResolver != null) {
|
||||
interceptor.setCacheResolver(this.cacheResolver);
|
||||
}
|
||||
else if (this.cacheManager != null) {
|
||||
interceptor.setCacheManager(this.cacheManager);
|
||||
}
|
||||
if (this.keyGenerator != null) {
|
||||
interceptor.setKeyGenerator(this.keyGenerator);
|
||||
}
|
||||
if (this.errorHandler != null) {
|
||||
interceptor.setErrorHandler(this.errorHandler);
|
||||
}
|
||||
interceptor.configure(this.errorHandler, this.keyGenerator, this.cacheResolver, this.cacheManager);
|
||||
interceptor.setCacheOperationSource(cacheOperationSource());
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -112,21 +112,21 @@ class AnnotationDrivenCacheBeanDefinitionParser implements BeanDefinitionParser
|
|||
*/
|
||||
private static void parseCacheResolution(Element element, BeanDefinition def, boolean setBoth) {
|
||||
String name = element.getAttribute("cache-resolver");
|
||||
if (StringUtils.hasText(name)) {
|
||||
boolean hasText = StringUtils.hasText(name);
|
||||
if (hasText) {
|
||||
def.getPropertyValues().add("cacheResolver", new RuntimeBeanReference(name.trim()));
|
||||
}
|
||||
if (!StringUtils.hasText(name) || setBoth) {
|
||||
if (!hasText || setBoth) {
|
||||
def.getPropertyValues().add("cacheManager",
|
||||
new RuntimeBeanReference(CacheNamespaceHandler.extractCacheManager(element)));
|
||||
}
|
||||
}
|
||||
|
||||
private static BeanDefinition parseErrorHandler(Element element, BeanDefinition def) {
|
||||
private static void parseErrorHandler(Element element, BeanDefinition def) {
|
||||
String name = element.getAttribute("error-handler");
|
||||
if (StringUtils.hasText(name)) {
|
||||
def.getPropertyValues().add("errorHandler", new RuntimeBeanReference(name.trim()));
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -18,27 +18,28 @@ package org.springframework.cache.interceptor;
|
|||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.function.SingletonSupplier;
|
||||
|
||||
/**
|
||||
* A base component for invoking {@link Cache} operations and using a
|
||||
* configurable {@link CacheErrorHandler} when an exception occurs.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Juergen Hoeller
|
||||
* @since 4.1
|
||||
* @see org.springframework.cache.interceptor.CacheErrorHandler
|
||||
*/
|
||||
public abstract class AbstractCacheInvoker {
|
||||
|
||||
private CacheErrorHandler errorHandler;
|
||||
protected SingletonSupplier<CacheErrorHandler> errorHandler;
|
||||
|
||||
|
||||
protected AbstractCacheInvoker() {
|
||||
this.errorHandler = new SimpleCacheErrorHandler();
|
||||
this.errorHandler = SingletonSupplier.of(SimpleCacheErrorHandler::new);
|
||||
}
|
||||
|
||||
protected AbstractCacheInvoker(CacheErrorHandler errorHandler) {
|
||||
this.errorHandler = errorHandler;
|
||||
this.errorHandler = SingletonSupplier.of(errorHandler);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -48,15 +49,14 @@ public abstract class AbstractCacheInvoker {
|
|||
* is used who throws any exception as is.
|
||||
*/
|
||||
public void setErrorHandler(CacheErrorHandler errorHandler) {
|
||||
Assert.notNull(errorHandler, "CacheErrorHandler must not be null");
|
||||
this.errorHandler = errorHandler;
|
||||
this.errorHandler = SingletonSupplier.of(errorHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link CacheErrorHandler} to use.
|
||||
*/
|
||||
public CacheErrorHandler getErrorHandler() {
|
||||
return this.errorHandler;
|
||||
return this.errorHandler.obtain();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -40,9 +40,17 @@ public abstract class AbstractCacheResolver implements CacheResolver, Initializi
|
|||
private CacheManager cacheManager;
|
||||
|
||||
|
||||
/**
|
||||
* Construct a new {@code AbstractCacheResolver}.
|
||||
* @see #setCacheManager
|
||||
*/
|
||||
protected AbstractCacheResolver() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new {@code AbstractCacheResolver} for the given {@link CacheManager}.
|
||||
* @param cacheManager the CacheManager to use
|
||||
*/
|
||||
protected AbstractCacheResolver(CacheManager cacheManager) {
|
||||
this.cacheManager = cacheManager;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
|
@ -52,6 +53,8 @@ import org.springframework.util.LinkedMultiValueMap;
|
|||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.function.SingletonSupplier;
|
||||
import org.springframework.util.function.SupplierUtils;
|
||||
|
||||
/**
|
||||
* Base class for caching aspects, such as the {@link CacheInterceptor} or an
|
||||
|
|
@ -89,10 +92,10 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
|
|||
@Nullable
|
||||
private CacheOperationSource cacheOperationSource;
|
||||
|
||||
private KeyGenerator keyGenerator = new SimpleKeyGenerator();
|
||||
private SingletonSupplier<KeyGenerator> keyGenerator = SingletonSupplier.of(SimpleKeyGenerator::new);
|
||||
|
||||
@Nullable
|
||||
private CacheResolver cacheResolver;
|
||||
private SingletonSupplier<CacheResolver> cacheResolver;
|
||||
|
||||
@Nullable
|
||||
private BeanFactory beanFactory;
|
||||
|
|
@ -100,10 +103,27 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
|
|||
private boolean initialized = false;
|
||||
|
||||
|
||||
/**
|
||||
* Configure this aspect with the given error handler, key generator and cache resolver/manager
|
||||
* suppliers, applying the corresponding default if a supplier is not resolvable.
|
||||
* @since 5.1
|
||||
*/
|
||||
public void configure(
|
||||
@Nullable Supplier<CacheErrorHandler> errorHandler, @Nullable Supplier<KeyGenerator> keyGenerator,
|
||||
@Nullable Supplier<CacheResolver> cacheResolver, @Nullable Supplier<CacheManager> cacheManager) {
|
||||
|
||||
this.errorHandler = new SingletonSupplier<>(errorHandler, SimpleCacheErrorHandler::new);
|
||||
this.keyGenerator = new SingletonSupplier<>(keyGenerator, SimpleKeyGenerator::new);
|
||||
this.cacheResolver = new SingletonSupplier<>(cacheResolver,
|
||||
() -> SimpleCacheResolver.of(SupplierUtils.resolve(cacheManager)));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set one or more cache operation sources which are used to find the cache
|
||||
* attributes. If more than one source is provided, they will be aggregated
|
||||
* using a {@link CompositeCacheOperationSource}.
|
||||
* @see #setCacheOperationSource
|
||||
*/
|
||||
public void setCacheOperationSources(CacheOperationSource... cacheOperationSources) {
|
||||
Assert.notEmpty(cacheOperationSources, "At least 1 CacheOperationSource needs to be specified");
|
||||
|
|
@ -111,6 +131,15 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
|
|||
new CompositeCacheOperationSource(cacheOperationSources) : cacheOperationSources[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the CacheOperationSource for this cache aspect.
|
||||
* @since 5.1
|
||||
* @see #setCacheOperationSources
|
||||
*/
|
||||
public void setCacheOperationSource(@Nullable CacheOperationSource cacheOperationSource) {
|
||||
this.cacheOperationSource = cacheOperationSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the CacheOperationSource for this cache aspect.
|
||||
*/
|
||||
|
|
@ -125,14 +154,14 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
|
|||
* <p>The default is a {@link SimpleKeyGenerator}.
|
||||
*/
|
||||
public void setKeyGenerator(KeyGenerator keyGenerator) {
|
||||
this.keyGenerator = keyGenerator;
|
||||
this.keyGenerator = SingletonSupplier.of(keyGenerator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default {@link KeyGenerator} that this cache aspect delegates to.
|
||||
*/
|
||||
public KeyGenerator getKeyGenerator() {
|
||||
return this.keyGenerator;
|
||||
return this.keyGenerator.obtain();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -144,7 +173,7 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
|
|||
* @see SimpleCacheResolver
|
||||
*/
|
||||
public void setCacheResolver(@Nullable CacheResolver cacheResolver) {
|
||||
this.cacheResolver = cacheResolver;
|
||||
this.cacheResolver = SingletonSupplier.ofNullable(cacheResolver);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -152,7 +181,7 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
|
|||
*/
|
||||
@Nullable
|
||||
public CacheResolver getCacheResolver() {
|
||||
return this.cacheResolver;
|
||||
return SupplierUtils.resolve(this.cacheResolver);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -162,7 +191,7 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
|
|||
* @see SimpleCacheResolver
|
||||
*/
|
||||
public void setCacheManager(CacheManager cacheManager) {
|
||||
this.cacheResolver = new SimpleCacheResolver(cacheManager);
|
||||
this.cacheResolver = SingletonSupplier.of(new SimpleCacheResolver(cacheManager));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -192,8 +221,7 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
|
|||
}
|
||||
catch (NoUniqueBeanDefinitionException ex) {
|
||||
throw new IllegalStateException("No CacheResolver specified, and no unique bean of type " +
|
||||
"CacheManager found. Mark one as primary (or give it the name 'cacheManager') or " +
|
||||
"declare a specific CacheManager to use, that serves as the default one.");
|
||||
"CacheManager found. Mark one as primary or declare a specific CacheManager to use.");
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
throw new IllegalStateException("No CacheResolver specified, and no bean of type CacheManager found. " +
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import java.util.Collection;
|
|||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* A simple {@link CacheResolver} that resolves the {@link Cache} instance(s)
|
||||
|
|
@ -27,14 +28,23 @@ import org.springframework.cache.CacheManager;
|
|||
* cache(s) as provided by {@link BasicOperation#getCacheNames() getCacheNames()}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Juergen Hoeller
|
||||
* @since 4.1
|
||||
* @see BasicOperation#getCacheNames()
|
||||
*/
|
||||
public class SimpleCacheResolver extends AbstractCacheResolver {
|
||||
|
||||
/**
|
||||
* Construct a new {@code SimpleCacheResolver}.
|
||||
* @see #setCacheManager
|
||||
*/
|
||||
public SimpleCacheResolver() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new {@code SimpleCacheResolver} for the given {@link CacheManager}.
|
||||
* @param cacheManager the CacheManager to use
|
||||
*/
|
||||
public SimpleCacheResolver(CacheManager cacheManager) {
|
||||
super(cacheManager);
|
||||
}
|
||||
|
|
@ -45,4 +55,16 @@ public class SimpleCacheResolver extends AbstractCacheResolver {
|
|||
return context.getOperation().getCacheNames();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a {@code SimpleCacheResolver} for the given {@link CacheManager}.
|
||||
* @param cacheManager the CacheManager (potentially {@code null})
|
||||
* @return the SimpleCacheResolver ({@code null} if the CacheManager was {@code null})
|
||||
* @since 5.1
|
||||
*/
|
||||
@Nullable
|
||||
static SimpleCacheResolver of(@Nullable CacheManager cacheManager) {
|
||||
return (cacheManager != null ? new SimpleCacheResolver(cacheManager) : null);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -18,6 +18,7 @@ package org.springframework.scheduling.annotation;
|
|||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
|
@ -33,6 +34,7 @@ import org.springframework.util.CollectionUtils;
|
|||
* Spring's asynchronous method execution capability.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @author Juergen Hoeller
|
||||
* @author Stephane Nicoll
|
||||
* @since 3.1
|
||||
* @see EnableAsync
|
||||
|
|
@ -44,10 +46,10 @@ public abstract class AbstractAsyncConfiguration implements ImportAware {
|
|||
protected AnnotationAttributes enableAsync;
|
||||
|
||||
@Nullable
|
||||
protected Executor executor;
|
||||
protected Supplier<Executor> executor;
|
||||
|
||||
@Nullable
|
||||
protected AsyncUncaughtExceptionHandler exceptionHandler;
|
||||
protected Supplier<AsyncUncaughtExceptionHandler> exceptionHandler;
|
||||
|
||||
|
||||
@Override
|
||||
|
|
@ -72,8 +74,8 @@ public abstract class AbstractAsyncConfiguration implements ImportAware {
|
|||
throw new IllegalStateException("Only one AsyncConfigurer may exist");
|
||||
}
|
||||
AsyncConfigurer configurer = configurers.iterator().next();
|
||||
this.executor = configurer.getAsyncExecutor();
|
||||
this.exceptionHandler = configurer.getAsyncUncaughtExceptionHandler();
|
||||
this.executor = configurer::getAsyncExecutor;
|
||||
this.exceptionHandler = configurer::getAsyncUncaughtExceptionHandler;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -21,12 +21,12 @@ import java.util.HashSet;
|
|||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.aopalliance.aop.Advice;
|
||||
|
||||
import org.springframework.aop.Pointcut;
|
||||
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
|
||||
import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler;
|
||||
import org.springframework.aop.support.AbstractPointcutAdvisor;
|
||||
import org.springframework.aop.support.ComposablePointcut;
|
||||
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
|
||||
|
|
@ -35,6 +35,7 @@ import org.springframework.beans.factory.BeanFactoryAware;
|
|||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.function.SingletonSupplier;
|
||||
|
||||
/**
|
||||
* Advisor that activates asynchronous method execution through the {@link Async}
|
||||
|
|
@ -54,8 +55,6 @@ import org.springframework.util.ClassUtils;
|
|||
@SuppressWarnings("serial")
|
||||
public class AsyncAnnotationAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {
|
||||
|
||||
private AsyncUncaughtExceptionHandler exceptionHandler;
|
||||
|
||||
private Advice advice;
|
||||
|
||||
private Pointcut pointcut;
|
||||
|
|
@ -65,7 +64,7 @@ public class AsyncAnnotationAdvisor extends AbstractPointcutAdvisor implements B
|
|||
* Create a new {@code AsyncAnnotationAdvisor} for bean-style configuration.
|
||||
*/
|
||||
public AsyncAnnotationAdvisor() {
|
||||
this(null, null);
|
||||
this((Supplier<Executor>) null, (Supplier<AsyncUncaughtExceptionHandler>) null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -77,7 +76,25 @@ public class AsyncAnnotationAdvisor extends AbstractPointcutAdvisor implements B
|
|||
* @see AnnotationAsyncExecutionInterceptor#getDefaultExecutor(BeanFactory)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public AsyncAnnotationAdvisor(@Nullable Executor executor, @Nullable AsyncUncaughtExceptionHandler exceptionHandler) {
|
||||
public AsyncAnnotationAdvisor(
|
||||
@Nullable Executor executor, @Nullable AsyncUncaughtExceptionHandler exceptionHandler) {
|
||||
|
||||
this(SingletonSupplier.ofNullable(executor), SingletonSupplier.ofNullable(exceptionHandler));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code AsyncAnnotationAdvisor} for the given task executor.
|
||||
* @param executor the task executor to use for asynchronous methods
|
||||
* (can be {@code null} to trigger default executor resolution)
|
||||
* @param exceptionHandler the {@link AsyncUncaughtExceptionHandler} to use to
|
||||
* handle unexpected exception thrown by asynchronous method executions
|
||||
* @since 5.1
|
||||
* @see AnnotationAsyncExecutionInterceptor#getDefaultExecutor(BeanFactory)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public AsyncAnnotationAdvisor(
|
||||
@Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {
|
||||
|
||||
Set<Class<? extends Annotation>> asyncAnnotationTypes = new LinkedHashSet<>(2);
|
||||
asyncAnnotationTypes.add(Async.class);
|
||||
try {
|
||||
|
|
@ -87,24 +104,11 @@ public class AsyncAnnotationAdvisor extends AbstractPointcutAdvisor implements B
|
|||
catch (ClassNotFoundException ex) {
|
||||
// If EJB 3.1 API not present, simply ignore.
|
||||
}
|
||||
if (exceptionHandler != null) {
|
||||
this.exceptionHandler = exceptionHandler;
|
||||
}
|
||||
else {
|
||||
this.exceptionHandler = new SimpleAsyncUncaughtExceptionHandler();
|
||||
}
|
||||
this.advice = buildAdvice(executor, this.exceptionHandler);
|
||||
this.advice = buildAdvice(executor, exceptionHandler);
|
||||
this.pointcut = buildPointcut(asyncAnnotationTypes);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Specify the default task executor to use for asynchronous methods.
|
||||
*/
|
||||
public void setTaskExecutor(Executor executor) {
|
||||
this.advice = buildAdvice(executor, this.exceptionHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the 'async' annotation type.
|
||||
* <p>The default async annotation type is the {@link Async} annotation, as well
|
||||
|
|
@ -143,8 +147,12 @@ public class AsyncAnnotationAdvisor extends AbstractPointcutAdvisor implements B
|
|||
}
|
||||
|
||||
|
||||
protected Advice buildAdvice(@Nullable Executor executor, AsyncUncaughtExceptionHandler exceptionHandler) {
|
||||
return new AnnotationAsyncExecutionInterceptor(executor, exceptionHandler);
|
||||
protected Advice buildAdvice(
|
||||
@Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {
|
||||
|
||||
AnnotationAsyncExecutionInterceptor interceptor = new AnnotationAsyncExecutionInterceptor(null);
|
||||
interceptor.configure(executor, exceptionHandler);
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -18,6 +18,7 @@ package org.springframework.scheduling.annotation;
|
|||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
|
@ -28,6 +29,7 @@ import org.springframework.beans.factory.BeanFactory;
|
|||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.function.SingletonSupplier;
|
||||
|
||||
/**
|
||||
* Bean post-processor that automatically applies asynchronous invocation
|
||||
|
|
@ -75,14 +77,15 @@ public class AsyncAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAd
|
|||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
@Nullable
|
||||
private Supplier<Executor> executor;
|
||||
|
||||
@Nullable
|
||||
private Supplier<AsyncUncaughtExceptionHandler> exceptionHandler;
|
||||
|
||||
@Nullable
|
||||
private Class<? extends Annotation> asyncAnnotationType;
|
||||
|
||||
@Nullable
|
||||
private Executor executor;
|
||||
|
||||
@Nullable
|
||||
private AsyncUncaughtExceptionHandler exceptionHandler;
|
||||
|
||||
|
||||
public AsyncAnnotationBeanPostProcessor() {
|
||||
|
|
@ -90,6 +93,40 @@ public class AsyncAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAd
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Configure this post-processor with the given executor and exception handler suppliers,
|
||||
* applying the corresponding default if a supplier is not resolvable.
|
||||
* @since 5.1
|
||||
*/
|
||||
public void configure(
|
||||
@Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {
|
||||
|
||||
this.executor = executor;
|
||||
this.exceptionHandler = exceptionHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link Executor} to use when invoking methods asynchronously.
|
||||
* <p>If not specified, default executor resolution will apply: searching for a
|
||||
* unique {@link TaskExecutor} bean in the context, or for an {@link Executor}
|
||||
* bean named "taskExecutor" otherwise. If neither of the two is resolvable,
|
||||
* a local default executor will be created within the interceptor.
|
||||
* @see AnnotationAsyncExecutionInterceptor#getDefaultExecutor(BeanFactory)
|
||||
* @see #DEFAULT_TASK_EXECUTOR_BEAN_NAME
|
||||
*/
|
||||
public void setExecutor(Executor executor) {
|
||||
this.executor = SingletonSupplier.of(executor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link AsyncUncaughtExceptionHandler} to use to handle uncaught
|
||||
* exceptions thrown by asynchronous method executions.
|
||||
* @since 4.1
|
||||
*/
|
||||
public void setExceptionHandler(AsyncUncaughtExceptionHandler exceptionHandler) {
|
||||
this.exceptionHandler = SingletonSupplier.of(exceptionHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the 'async' annotation type to be detected at either class or method
|
||||
* level. By default, both the {@link Async} annotation and the EJB 3.1
|
||||
|
|
@ -104,29 +141,6 @@ public class AsyncAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAd
|
|||
this.asyncAnnotationType = asyncAnnotationType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link Executor} to use when invoking methods asynchronously.
|
||||
* <p>If not specified, default executor resolution will apply: searching for a
|
||||
* unique {@link TaskExecutor} bean in the context, or for an {@link Executor}
|
||||
* bean named "taskExecutor" otherwise. If neither of the two is resolvable,
|
||||
* a local default executor will be created within the interceptor.
|
||||
* @see AsyncAnnotationAdvisor#AsyncAnnotationAdvisor(Executor, AsyncUncaughtExceptionHandler)
|
||||
* @see AnnotationAsyncExecutionInterceptor#getDefaultExecutor(BeanFactory)
|
||||
* @see #DEFAULT_TASK_EXECUTOR_BEAN_NAME
|
||||
*/
|
||||
public void setExecutor(Executor executor) {
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link AsyncUncaughtExceptionHandler} to use to handle uncaught
|
||||
* exceptions thrown by asynchronous method executions.
|
||||
* @since 4.1
|
||||
*/
|
||||
public void setExceptionHandler(AsyncUncaughtExceptionHandler exceptionHandler) {
|
||||
this.exceptionHandler = exceptionHandler;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -32,6 +32,7 @@ import org.springframework.util.Assert;
|
|||
*
|
||||
* @author Chris Beams
|
||||
* @author Stephane Nicoll
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
* @see EnableAsync
|
||||
* @see AsyncConfigurationSelector
|
||||
|
|
@ -45,16 +46,11 @@ public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration {
|
|||
public AsyncAnnotationBeanPostProcessor asyncAdvisor() {
|
||||
Assert.notNull(this.enableAsync, "@EnableAsync annotation metadata was not injected");
|
||||
AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor();
|
||||
bpp.configure(this.executor, this.exceptionHandler);
|
||||
Class<? extends Annotation> customAsyncAnnotation = this.enableAsync.getClass("annotation");
|
||||
if (customAsyncAnnotation != AnnotationUtils.getDefaultValue(EnableAsync.class, "annotation")) {
|
||||
bpp.setAsyncAnnotationType(customAsyncAnnotation);
|
||||
}
|
||||
if (this.executor != null) {
|
||||
bpp.setExecutor(this.executor);
|
||||
}
|
||||
if (this.exceptionHandler != null) {
|
||||
bpp.setExceptionHandler(this.exceptionHandler);
|
||||
}
|
||||
bpp.setProxyTargetClass(this.enableAsync.getBoolean("proxyTargetClass"));
|
||||
bpp.setOrder(this.enableAsync.<Integer>getNumber("order"));
|
||||
return bpp;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -66,23 +66,21 @@ public class EnableCachingTests extends AbstractCacheAnnotationTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void singleCacheManagerBean() throws Throwable {
|
||||
public void singleCacheManagerBean() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(SingleCacheManagerConfig.class);
|
||||
ctx.refresh();
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void multipleCacheManagerBeans() throws Throwable {
|
||||
@Test
|
||||
public void multipleCacheManagerBeans() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(MultiCacheManagerConfig.class);
|
||||
try {
|
||||
ctx.refresh();
|
||||
}
|
||||
catch (BeanCreationException ex) {
|
||||
Throwable root = ex.getRootCause();
|
||||
assertTrue(root.getMessage().contains("beans of type CacheManager"));
|
||||
throw root;
|
||||
catch (IllegalStateException ex) {
|
||||
assertTrue(ex.getMessage().contains("no unique bean of type CacheManager"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -93,8 +91,8 @@ public class EnableCachingTests extends AbstractCacheAnnotationTests {
|
|||
ctx.refresh(); // does not throw an exception
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void multipleCachingConfigurers() throws Throwable {
|
||||
@Test
|
||||
public void multipleCachingConfigurers() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(MultiCacheManagerConfigurer.class, EnableCachingConfig.class);
|
||||
try {
|
||||
|
|
@ -102,22 +100,20 @@ public class EnableCachingTests extends AbstractCacheAnnotationTests {
|
|||
}
|
||||
catch (BeanCreationException ex) {
|
||||
Throwable root = ex.getRootCause();
|
||||
assertTrue(root instanceof IllegalStateException);
|
||||
assertTrue(root.getMessage().contains("implementations of CachingConfigurer"));
|
||||
throw root;
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void noCacheManagerBeans() throws Throwable {
|
||||
@Test
|
||||
public void noCacheManagerBeans() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(EmptyConfig.class);
|
||||
try {
|
||||
ctx.refresh();
|
||||
}
|
||||
catch (BeanCreationException ex) {
|
||||
Throwable root = ex.getRootCause();
|
||||
assertTrue(root.getMessage().contains("No bean of type CacheManager"));
|
||||
throw root;
|
||||
catch (IllegalStateException ex) {
|
||||
assertTrue(ex.getMessage().contains("no bean of type CacheManager"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -33,16 +33,19 @@ import org.springframework.aop.Advisor;
|
|||
import org.springframework.aop.framework.Advised;
|
||||
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
import org.springframework.beans.factory.BeanNotOfRequiredTypeException;
|
||||
import org.springframework.beans.factory.UnsatisfiedDependencyException;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.context.annotation.AdviceMode;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
|
@ -218,6 +221,29 @@ public class EnableAsyncTests {
|
|||
ctx.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customExecutorBeanConfig() throws InterruptedException {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(CustomExecutorBeanConfig.class, ExecutorPostProcessor.class);
|
||||
ctx.refresh();
|
||||
|
||||
AsyncBean asyncBean = ctx.getBean(AsyncBean.class);
|
||||
asyncBean.work();
|
||||
Thread.sleep(500);
|
||||
assertThat(asyncBean.getThreadOfExecution().getName(), startsWith("Post-"));
|
||||
|
||||
TestableAsyncUncaughtExceptionHandler exceptionHandler = (TestableAsyncUncaughtExceptionHandler)
|
||||
ctx.getBean("exceptionHandler");
|
||||
assertFalse("handler should not have been called yet", exceptionHandler.isCalled());
|
||||
|
||||
asyncBean.fail();
|
||||
Thread.sleep(500);
|
||||
Method method = ReflectionUtils.findMethod(AsyncBean.class, "fail");
|
||||
exceptionHandler.assertCalledWith(method, UnsupportedOperationException.class);
|
||||
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void spr14949FindsOnInterfaceWithInterfaceProxy() throws InterruptedException {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Spr14949ConfigA.class);
|
||||
|
|
@ -440,6 +466,53 @@ public class EnableAsyncTests {
|
|||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@EnableAsync
|
||||
static class CustomExecutorBeanConfig implements AsyncConfigurer {
|
||||
|
||||
@Bean
|
||||
public AsyncBean asyncBean() {
|
||||
return new AsyncBean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Executor getAsyncExecutor() {
|
||||
return executor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ThreadPoolTaskExecutor executor() {
|
||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||
executor.setThreadNamePrefix("Custom-");
|
||||
executor.initialize();
|
||||
return executor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
|
||||
return exceptionHandler();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AsyncUncaughtExceptionHandler exceptionHandler() {
|
||||
return new TestableAsyncUncaughtExceptionHandler();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class ExecutorPostProcessor implements BeanPostProcessor {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (bean instanceof ThreadPoolTaskExecutor) {
|
||||
((ThreadPoolTaskExecutor) bean).setThreadNamePrefix("Post-");
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public interface AsyncInterface {
|
||||
|
||||
@Async
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package org.springframework.scheduling.config;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
|
|
@ -55,7 +57,7 @@ public class AnnotationDrivenBeanDefinitionParserTests {
|
|||
public void asyncPostProcessorExecutorReference() {
|
||||
Object executor = context.getBean("testExecutor");
|
||||
Object postProcessor = context.getBean(TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME);
|
||||
assertSame(executor, new DirectFieldAccessor(postProcessor).getPropertyValue("executor"));
|
||||
assertSame(executor, ((Supplier) new DirectFieldAccessor(postProcessor).getPropertyValue("executor")).get());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -69,7 +71,7 @@ public class AnnotationDrivenBeanDefinitionParserTests {
|
|||
public void asyncPostProcessorExceptionHandlerReference() {
|
||||
Object exceptionHandler = context.getBean("testExceptionHandler");
|
||||
Object postProcessor = context.getBean(TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME);
|
||||
assertSame(exceptionHandler, new DirectFieldAccessor(postProcessor).getPropertyValue("exceptionHandler"));
|
||||
assertSame(exceptionHandler, ((Supplier) new DirectFieldAccessor(postProcessor).getPropertyValue("exceptionHandler")).get());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* Useful generic {@code java.util.concurrent.Future} extension.
|
||||
* Useful generic {@code java.util.concurrent.Future} extensions.
|
||||
*/
|
||||
@NonNullApi
|
||||
@NonNullFields
|
||||
|
|
|
|||
|
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* 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.util.function;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link java.util.function Supplier} decorator that caches a singleton result and
|
||||
* makes it available from {@link #get()} (nullable) and {@link #obtain()} (null-safe).
|
||||
*
|
||||
* <p>A {@code SingletonSupplier} can be constructed via {@code of} factory methods
|
||||
* or via constructors that provide a default supplier as a fallback. This is
|
||||
* particularly useful for method reference suppliers, falling back to a default
|
||||
* supplier for a method that returned {@code null} and caching the result.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 5.1
|
||||
* @param <T> the type of results supplied by this supplier
|
||||
*/
|
||||
public class SingletonSupplier<T> implements Supplier<T> {
|
||||
|
||||
@Nullable
|
||||
private final Supplier<? extends T> instanceSupplier;
|
||||
|
||||
@Nullable
|
||||
private final Supplier<? extends T> defaultSupplier;
|
||||
|
||||
@Nullable
|
||||
private volatile T singletonInstance;
|
||||
|
||||
|
||||
/**
|
||||
* Build a {@code SingletonSupplier} with the given singleton instance
|
||||
* and a default supplier for the case when the instance is {@code null}.
|
||||
* @param instance the singleton instance (potentially {@code null})
|
||||
* @param defaultSupplier the default supplier as a fallback
|
||||
*/
|
||||
public SingletonSupplier(@Nullable T instance, Supplier<? extends T> defaultSupplier) {
|
||||
this.instanceSupplier = null;
|
||||
this.defaultSupplier = defaultSupplier;
|
||||
this.singletonInstance = instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a {@code SingletonSupplier} with the given instance supplier
|
||||
* and a default supplier for the case when the instance is {@code null}.
|
||||
* @param instanceSupplier the immediate instance supplier
|
||||
* @param defaultSupplier the default supplier as a fallback
|
||||
*/
|
||||
public SingletonSupplier(@Nullable Supplier<? extends T> instanceSupplier, Supplier<? extends T> defaultSupplier) {
|
||||
this.instanceSupplier = instanceSupplier;
|
||||
this.defaultSupplier = defaultSupplier;
|
||||
}
|
||||
|
||||
private SingletonSupplier(Supplier<? extends T> supplier) {
|
||||
this.instanceSupplier = supplier;
|
||||
this.defaultSupplier = null;
|
||||
}
|
||||
|
||||
private SingletonSupplier(T singletonInstance) {
|
||||
this.instanceSupplier = null;
|
||||
this.defaultSupplier = null;
|
||||
this.singletonInstance = singletonInstance;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the shared singleton instance for this supplier.
|
||||
* @return the singleton instance (or {@code null} if none)
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public T get() {
|
||||
T instance = this.singletonInstance;
|
||||
if (instance == null) {
|
||||
synchronized (this) {
|
||||
instance = this.singletonInstance;
|
||||
if (instance == null) {
|
||||
if (this.instanceSupplier != null) {
|
||||
instance = this.instanceSupplier.get();
|
||||
}
|
||||
if (instance == null && this.defaultSupplier != null) {
|
||||
instance = this.defaultSupplier.get();
|
||||
}
|
||||
this.singletonInstance = instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the shared singleton instance for this supplier.
|
||||
* @return the singleton instance (never {@code null})
|
||||
* @throws IllegalStateException in case of no instance
|
||||
*/
|
||||
public T obtain() {
|
||||
T instance = get();
|
||||
Assert.state(instance != null, "No instance from Supplier");
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Build a {@code SingletonSupplier} with the given singleton instance.
|
||||
* @param instance the singleton instance (never {@code null})
|
||||
* @return the singleton supplier (never {@code null})
|
||||
*/
|
||||
public static <T> SingletonSupplier<T> of(T instance) {
|
||||
return new SingletonSupplier<>(instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a {@code SingletonSupplier} with the given singleton instance.
|
||||
* @param instance the singleton instance (potentially {@code null})
|
||||
* @return the singleton supplier, or {@code null} if the instance was {@code null}
|
||||
*/
|
||||
@Nullable
|
||||
public static <T> SingletonSupplier<T> ofNullable(@Nullable T instance) {
|
||||
return (instance != null ? new SingletonSupplier<>(instance) : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a {@code SingletonSupplier} with the given supplier.
|
||||
* @param supplier the instance supplier (never {@code null})
|
||||
* @return the singleton supplier (never {@code null})
|
||||
*/
|
||||
public static <T> SingletonSupplier<T> of(Supplier<T> supplier) {
|
||||
return new SingletonSupplier<>(supplier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a {@code SingletonSupplier} with the given supplier.
|
||||
* @param supplier the instance supplier (potentially {@code null})
|
||||
* @return the singleton supplier, or {@code null} if the instance supplier was {@code null}
|
||||
*/
|
||||
@Nullable
|
||||
public static <T> SingletonSupplier<T> ofNullable(@Nullable Supplier<T> supplier) {
|
||||
return (supplier != null ? new SingletonSupplier<>(supplier) : null);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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.util.function;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Convenience utilities for {@link java.util.function.Supplier} handling.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 5.1
|
||||
* @see SingletonSupplier
|
||||
*/
|
||||
public abstract class SupplierUtils {
|
||||
|
||||
/**
|
||||
* Resolve the given {@code Supplier}, getting its result or immediately
|
||||
* returning {@code null} if the supplier itself was {@code null}.
|
||||
* @param supplier the supplier to resolve
|
||||
* @return the supplier's result, or {@code null} if none
|
||||
*/
|
||||
@Nullable
|
||||
public static <T> T resolve(@Nullable Supplier<T> supplier) {
|
||||
return (supplier != null ? supplier.get() : null);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
/**
|
||||
* Useful generic {@code java.util.function} helper classes.
|
||||
*/
|
||||
@NonNullApi
|
||||
@NonNullFields
|
||||
package org.springframework.util.function;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
import org.springframework.lang.NonNullFields;
|
||||
Loading…
Reference in New Issue