Browse Source

Stop resolving CachingConfigurer instances eagerly

Closes gh-27751
pull/27832/head
Stephane Nicoll 3 years ago
parent
commit
c7642422c3
  1. 8
      spring-aspects/src/test/java/org/springframework/cache/aspectj/AspectJEnableCachingIsolatedTests.java
  2. 16
      spring-context-support/src/main/java/org/springframework/cache/jcache/config/AbstractJCacheConfiguration.java
  3. 75
      spring-context/src/main/java/org/springframework/cache/annotation/AbstractCachingConfiguration.java
  4. 8
      spring-context/src/test/java/org/springframework/cache/config/EnableCachingTests.java

8
spring-aspects/src/test/java/org/springframework/cache/aspectj/AspectJEnableCachingIsolatedTests.java vendored

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 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.
@ -20,7 +20,6 @@ import org.junit.jupiter.api.AfterEach; @@ -20,7 +20,6 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
@ -107,10 +106,7 @@ public class AspectJEnableCachingIsolatedTests { @@ -107,10 +106,7 @@ public class AspectJEnableCachingIsolatedTests {
try {
load(MultiCacheManagerConfigurer.class, EnableCachingConfig.class);
}
catch (BeanCreationException ex) {
Throwable root = ex.getRootCause();
boolean condition = root instanceof IllegalStateException;
assertThat(condition).isTrue();
catch (IllegalStateException ex) {
assertThat(ex.getMessage().contains("implementations of CachingConfigurer")).isTrue();
}
}

16
spring-context-support/src/main/java/org/springframework/cache/jcache/config/AbstractJCacheConfiguration.java vendored

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 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.
@ -20,7 +20,6 @@ import java.util.function.Supplier; @@ -20,7 +20,6 @@ import java.util.function.Supplier;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.cache.annotation.AbstractCachingConfiguration;
import org.springframework.cache.annotation.CachingConfigurer;
import org.springframework.cache.interceptor.CacheResolver;
import org.springframework.cache.jcache.interceptor.DefaultJCacheOperationSource;
import org.springframework.cache.jcache.interceptor.JCacheOperationSource;
@ -46,11 +45,14 @@ public abstract class AbstractJCacheConfiguration extends AbstractCachingConfigu @@ -46,11 +45,14 @@ public abstract class AbstractJCacheConfiguration extends AbstractCachingConfigu
@Override
protected void useCachingConfigurer(CachingConfigurer config) {
super.useCachingConfigurer(config);
if (config instanceof JCacheConfigurer) {
this.exceptionCacheResolver = ((JCacheConfigurer) config)::exceptionCacheResolver;
}
protected void useCachingConfigurer(CachingConfigurerSupplier cachingConfigurerSupplier) {
super.useCachingConfigurer(cachingConfigurerSupplier);
this.exceptionCacheResolver = cachingConfigurerSupplier.adapt(config -> {
if (config instanceof JCacheConfigurer) {
return ((JCacheConfigurer) config).exceptionCacheResolver();
}
return null;
});
}
@Bean(name = "jCacheOperationSource")

75
spring-context/src/main/java/org/springframework/cache/annotation/AbstractCachingConfiguration.java vendored

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 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,9 +16,12 @@ @@ -16,9 +16,12 @@
package org.springframework.cache.annotation;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.cache.interceptor.CacheErrorHandler;
@ -30,6 +33,7 @@ import org.springframework.core.annotation.AnnotationAttributes; @@ -30,6 +33,7 @@ import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
import org.springframework.util.function.SingletonSupplier;
/**
* Abstract base {@code @Configuration} class providing common structure
@ -70,29 +74,60 @@ public abstract class AbstractCachingConfiguration implements ImportAware { @@ -70,29 +74,60 @@ public abstract class AbstractCachingConfiguration implements ImportAware {
}
}
@Autowired(required = false)
void setConfigurers(Collection<CachingConfigurer> configurers) {
if (CollectionUtils.isEmpty(configurers)) {
return;
}
if (configurers.size() > 1) {
throw new IllegalStateException(configurers.size() + " implementations of " +
"CachingConfigurer were found when only 1 was expected. " +
"Refactor the configuration such that CachingConfigurer is " +
"implemented only once or not at all.");
}
CachingConfigurer configurer = configurers.iterator().next();
useCachingConfigurer(configurer);
@Autowired
void setConfigurers(ObjectProvider<CachingConfigurer> configurers) {
Supplier<CachingConfigurer> cachingConfigurer = () -> {
List<CachingConfigurer> candidates = configurers.stream().collect(Collectors.toList());
if (CollectionUtils.isEmpty(candidates)) {
return null;
}
if (candidates.size() > 1) {
throw new IllegalStateException(candidates.size() + " implementations of " +
"CachingConfigurer were found when only 1 was expected. " +
"Refactor the configuration such that CachingConfigurer is " +
"implemented only once or not at all.");
}
return candidates.get(0);
};
useCachingConfigurer(new CachingConfigurerSupplier(cachingConfigurer));
}
/**
* 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;
protected void useCachingConfigurer(CachingConfigurerSupplier cachingConfigurerSupplier) {
this.cacheManager = cachingConfigurerSupplier.adapt(CachingConfigurer::cacheManager);
this.cacheResolver = cachingConfigurerSupplier.adapt(CachingConfigurer::cacheResolver);
this.keyGenerator = cachingConfigurerSupplier.adapt(CachingConfigurer::keyGenerator);
this.errorHandler = cachingConfigurerSupplier.adapt(CachingConfigurer::errorHandler);
}
protected static class CachingConfigurerSupplier {
private final Supplier<CachingConfigurer> supplier;
public CachingConfigurerSupplier(Supplier<CachingConfigurer> supplier) {
this.supplier = SingletonSupplier.of(supplier);
}
/**
* Adapt the {@link CachingConfigurer} supplier to another supplier
* provided by the specified mapping function. If the underlying
* {@link CachingConfigurer} is {@code null}, {@code null} is returned
* and the mapping function is not invoked.
* @param provider the provider to use to adapt the supplier
* @param <T> the type of the supplier
* @return another supplier mapped by the specified function
*/
@Nullable
public <T> Supplier<T> adapt(Function<CachingConfigurer, T> provider) {
return () -> {
CachingConfigurer cachingConfigurer = this.supplier.get();
return (cachingConfigurer != null) ? provider.apply(cachingConfigurer) : null;
};
}
}
}

8
spring-context/src/test/java/org/springframework/cache/config/EnableCachingTests.java vendored

@ -18,7 +18,6 @@ package org.springframework.cache.config; @@ -18,7 +18,6 @@ package org.springframework.cache.config;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.cache.CacheManager;
@ -107,11 +106,8 @@ public class EnableCachingTests extends AbstractCacheAnnotationTests { @@ -107,11 +106,8 @@ public class EnableCachingTests extends AbstractCacheAnnotationTests {
try {
ctx.refresh();
}
catch (BeanCreationException ex) {
Throwable root = ex.getRootCause();
boolean condition = root instanceof IllegalStateException;
assertThat(condition).isTrue();
assertThat(root.getMessage().contains("implementations of CachingConfigurer")).isTrue();
catch (IllegalStateException ex) {
assertThat(ex.getMessage().contains("implementations of CachingConfigurer")).isTrue();
}
}

Loading…
Cancel
Save