Skip to content

Commit c764242

Browse files
committed
Stop resolving CachingConfigurer instances eagerly
Closes gh-27751
1 parent 4c2e0ee commit c764242

File tree

4 files changed

+68
-39
lines changed

4 files changed

+68
-39
lines changed

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

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -20,7 +20,6 @@
2020
import org.junit.jupiter.api.Disabled;
2121
import org.junit.jupiter.api.Test;
2222

23-
import org.springframework.beans.factory.BeanCreationException;
2423
import org.springframework.cache.CacheManager;
2524
import org.springframework.cache.annotation.CachingConfigurerSupport;
2625
import org.springframework.cache.annotation.EnableCaching;
@@ -107,10 +106,7 @@ public void multipleCachingConfigurers() {
107106
try {
108107
load(MultiCacheManagerConfigurer.class, EnableCachingConfig.class);
109108
}
110-
catch (BeanCreationException ex) {
111-
Throwable root = ex.getRootCause();
112-
boolean condition = root instanceof IllegalStateException;
113-
assertThat(condition).isTrue();
109+
catch (IllegalStateException ex) {
114110
assertThat(ex.getMessage().contains("implementations of CachingConfigurer")).isTrue();
115111
}
116112
}

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

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -20,7 +20,6 @@
2020

2121
import org.springframework.beans.factory.config.BeanDefinition;
2222
import org.springframework.cache.annotation.AbstractCachingConfiguration;
23-
import org.springframework.cache.annotation.CachingConfigurer;
2423
import org.springframework.cache.interceptor.CacheResolver;
2524
import org.springframework.cache.jcache.interceptor.DefaultJCacheOperationSource;
2625
import org.springframework.cache.jcache.interceptor.JCacheOperationSource;
@@ -46,11 +45,14 @@ public abstract class AbstractJCacheConfiguration extends AbstractCachingConfigu
4645

4746

4847
@Override
49-
protected void useCachingConfigurer(CachingConfigurer config) {
50-
super.useCachingConfigurer(config);
51-
if (config instanceof JCacheConfigurer) {
52-
this.exceptionCacheResolver = ((JCacheConfigurer) config)::exceptionCacheResolver;
53-
}
48+
protected void useCachingConfigurer(CachingConfigurerSupplier cachingConfigurerSupplier) {
49+
super.useCachingConfigurer(cachingConfigurerSupplier);
50+
this.exceptionCacheResolver = cachingConfigurerSupplier.adapt(config -> {
51+
if (config instanceof JCacheConfigurer) {
52+
return ((JCacheConfigurer) config).exceptionCacheResolver();
53+
}
54+
return null;
55+
});
5456
}
5557

5658
@Bean(name = "jCacheOperationSource")

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

Lines changed: 55 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,9 +16,12 @@
1616

1717
package org.springframework.cache.annotation;
1818

19-
import java.util.Collection;
19+
import java.util.List;
20+
import java.util.function.Function;
2021
import java.util.function.Supplier;
22+
import java.util.stream.Collectors;
2123

24+
import org.springframework.beans.factory.ObjectProvider;
2225
import org.springframework.beans.factory.annotation.Autowired;
2326
import org.springframework.cache.CacheManager;
2427
import org.springframework.cache.interceptor.CacheErrorHandler;
@@ -30,6 +33,7 @@
3033
import org.springframework.core.type.AnnotationMetadata;
3134
import org.springframework.lang.Nullable;
3235
import org.springframework.util.CollectionUtils;
36+
import org.springframework.util.function.SingletonSupplier;
3337

3438
/**
3539
* Abstract base {@code @Configuration} class providing common structure
@@ -70,29 +74,60 @@ public void setImportMetadata(AnnotationMetadata importMetadata) {
7074
}
7175
}
7276

73-
@Autowired(required = false)
74-
void setConfigurers(Collection<CachingConfigurer> configurers) {
75-
if (CollectionUtils.isEmpty(configurers)) {
76-
return;
77-
}
78-
if (configurers.size() > 1) {
79-
throw new IllegalStateException(configurers.size() + " implementations of " +
80-
"CachingConfigurer were found when only 1 was expected. " +
81-
"Refactor the configuration such that CachingConfigurer is " +
82-
"implemented only once or not at all.");
83-
}
84-
CachingConfigurer configurer = configurers.iterator().next();
85-
useCachingConfigurer(configurer);
77+
@Autowired
78+
void setConfigurers(ObjectProvider<CachingConfigurer> configurers) {
79+
Supplier<CachingConfigurer> cachingConfigurer = () -> {
80+
List<CachingConfigurer> candidates = configurers.stream().collect(Collectors.toList());
81+
if (CollectionUtils.isEmpty(candidates)) {
82+
return null;
83+
}
84+
if (candidates.size() > 1) {
85+
throw new IllegalStateException(candidates.size() + " implementations of " +
86+
"CachingConfigurer were found when only 1 was expected. " +
87+
"Refactor the configuration such that CachingConfigurer is " +
88+
"implemented only once or not at all.");
89+
}
90+
return candidates.get(0);
91+
};
92+
useCachingConfigurer(new CachingConfigurerSupplier(cachingConfigurer));
8693
}
8794

8895
/**
8996
* Extract the configuration from the nominated {@link CachingConfigurer}.
9097
*/
91-
protected void useCachingConfigurer(CachingConfigurer config) {
92-
this.cacheManager = config::cacheManager;
93-
this.cacheResolver = config::cacheResolver;
94-
this.keyGenerator = config::keyGenerator;
95-
this.errorHandler = config::errorHandler;
98+
protected void useCachingConfigurer(CachingConfigurerSupplier cachingConfigurerSupplier) {
99+
this.cacheManager = cachingConfigurerSupplier.adapt(CachingConfigurer::cacheManager);
100+
this.cacheResolver = cachingConfigurerSupplier.adapt(CachingConfigurer::cacheResolver);
101+
this.keyGenerator = cachingConfigurerSupplier.adapt(CachingConfigurer::keyGenerator);
102+
this.errorHandler = cachingConfigurerSupplier.adapt(CachingConfigurer::errorHandler);
103+
}
104+
105+
106+
protected static class CachingConfigurerSupplier {
107+
108+
private final Supplier<CachingConfigurer> supplier;
109+
110+
public CachingConfigurerSupplier(Supplier<CachingConfigurer> supplier) {
111+
this.supplier = SingletonSupplier.of(supplier);
112+
}
113+
114+
/**
115+
* Adapt the {@link CachingConfigurer} supplier to another supplier
116+
* provided by the specified mapping function. If the underlying
117+
* {@link CachingConfigurer} is {@code null}, {@code null} is returned
118+
* and the mapping function is not invoked.
119+
* @param provider the provider to use to adapt the supplier
120+
* @param <T> the type of the supplier
121+
* @return another supplier mapped by the specified function
122+
*/
123+
@Nullable
124+
public <T> Supplier<T> adapt(Function<CachingConfigurer, T> provider) {
125+
return () -> {
126+
CachingConfigurer cachingConfigurer = this.supplier.get();
127+
return (cachingConfigurer != null) ? provider.apply(cachingConfigurer) : null;
128+
};
129+
}
130+
96131
}
97132

98133
}

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

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818

1919
import org.junit.jupiter.api.Test;
2020

21-
import org.springframework.beans.factory.BeanCreationException;
2221
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
2322
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
2423
import org.springframework.cache.CacheManager;
@@ -107,11 +106,8 @@ public void multipleCachingConfigurers() {
107106
try {
108107
ctx.refresh();
109108
}
110-
catch (BeanCreationException ex) {
111-
Throwable root = ex.getRootCause();
112-
boolean condition = root instanceof IllegalStateException;
113-
assertThat(condition).isTrue();
114-
assertThat(root.getMessage().contains("implementations of CachingConfigurer")).isTrue();
109+
catch (IllegalStateException ex) {
110+
assertThat(ex.getMessage().contains("implementations of CachingConfigurer")).isTrue();
115111
}
116112
}
117113

0 commit comments

Comments
 (0)