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 @@
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -20,7 +20,6 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.cache.CacheManager; import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.annotation.EnableCaching;
@ -107,10 +106,7 @@ public class AspectJEnableCachingIsolatedTests {
try { try {
load(MultiCacheManagerConfigurer.class, EnableCachingConfig.class); load(MultiCacheManagerConfigurer.class, EnableCachingConfig.class);
} }
catch (BeanCreationException ex) { catch (IllegalStateException ex) {
Throwable root = ex.getRootCause();
boolean condition = root instanceof IllegalStateException;
assertThat(condition).isTrue();
assertThat(ex.getMessage().contains("implementations of CachingConfigurer")).isTrue(); 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 @@
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -20,7 +20,6 @@ import java.util.function.Supplier;
import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.cache.annotation.AbstractCachingConfiguration; import org.springframework.cache.annotation.AbstractCachingConfiguration;
import org.springframework.cache.annotation.CachingConfigurer;
import org.springframework.cache.interceptor.CacheResolver; import org.springframework.cache.interceptor.CacheResolver;
import org.springframework.cache.jcache.interceptor.DefaultJCacheOperationSource; import org.springframework.cache.jcache.interceptor.DefaultJCacheOperationSource;
import org.springframework.cache.jcache.interceptor.JCacheOperationSource; import org.springframework.cache.jcache.interceptor.JCacheOperationSource;
@ -46,11 +45,14 @@ public abstract class AbstractJCacheConfiguration extends AbstractCachingConfigu
@Override @Override
protected void useCachingConfigurer(CachingConfigurer config) { protected void useCachingConfigurer(CachingConfigurerSupplier cachingConfigurerSupplier) {
super.useCachingConfigurer(config); super.useCachingConfigurer(cachingConfigurerSupplier);
if (config instanceof JCacheConfigurer) { this.exceptionCacheResolver = cachingConfigurerSupplier.adapt(config -> {
this.exceptionCacheResolver = ((JCacheConfigurer) config)::exceptionCacheResolver; if (config instanceof JCacheConfigurer) {
} return ((JCacheConfigurer) config).exceptionCacheResolver();
}
return null;
});
} }
@Bean(name = "jCacheOperationSource") @Bean(name = "jCacheOperationSource")

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

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,9 +16,12 @@
package org.springframework.cache.annotation; 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.function.Supplier;
import java.util.stream.Collectors;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager; import org.springframework.cache.CacheManager;
import org.springframework.cache.interceptor.CacheErrorHandler; import org.springframework.cache.interceptor.CacheErrorHandler;
@ -30,6 +33,7 @@ import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.AnnotationMetadata;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.springframework.util.function.SingletonSupplier;
/** /**
* Abstract base {@code @Configuration} class providing common structure * Abstract base {@code @Configuration} class providing common structure
@ -70,29 +74,60 @@ public abstract class AbstractCachingConfiguration implements ImportAware {
} }
} }
@Autowired(required = false) @Autowired
void setConfigurers(Collection<CachingConfigurer> configurers) { void setConfigurers(ObjectProvider<CachingConfigurer> configurers) {
if (CollectionUtils.isEmpty(configurers)) { Supplier<CachingConfigurer> cachingConfigurer = () -> {
return; List<CachingConfigurer> candidates = configurers.stream().collect(Collectors.toList());
} if (CollectionUtils.isEmpty(candidates)) {
if (configurers.size() > 1) { return null;
throw new IllegalStateException(configurers.size() + " implementations of " + }
"CachingConfigurer were found when only 1 was expected. " + if (candidates.size() > 1) {
"Refactor the configuration such that CachingConfigurer is " + throw new IllegalStateException(candidates.size() + " implementations of " +
"implemented only once or not at all."); "CachingConfigurer were found when only 1 was expected. " +
} "Refactor the configuration such that CachingConfigurer is " +
CachingConfigurer configurer = configurers.iterator().next(); "implemented only once or not at all.");
useCachingConfigurer(configurer); }
return candidates.get(0);
};
useCachingConfigurer(new CachingConfigurerSupplier(cachingConfigurer));
} }
/** /**
* Extract the configuration from the nominated {@link CachingConfigurer}. * Extract the configuration from the nominated {@link CachingConfigurer}.
*/ */
protected void useCachingConfigurer(CachingConfigurer config) { protected void useCachingConfigurer(CachingConfigurerSupplier cachingConfigurerSupplier) {
this.cacheManager = config::cacheManager; this.cacheManager = cachingConfigurerSupplier.adapt(CachingConfigurer::cacheManager);
this.cacheResolver = config::cacheResolver; this.cacheResolver = cachingConfigurerSupplier.adapt(CachingConfigurer::cacheResolver);
this.keyGenerator = config::keyGenerator; this.keyGenerator = cachingConfigurerSupplier.adapt(CachingConfigurer::keyGenerator);
this.errorHandler = config::errorHandler; 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;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException; import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.cache.CacheManager; import org.springframework.cache.CacheManager;
@ -107,11 +106,8 @@ public class EnableCachingTests extends AbstractCacheAnnotationTests {
try { try {
ctx.refresh(); ctx.refresh();
} }
catch (BeanCreationException ex) { catch (IllegalStateException ex) {
Throwable root = ex.getRootCause(); assertThat(ex.getMessage().contains("implementations of CachingConfigurer")).isTrue();
boolean condition = root instanceof IllegalStateException;
assertThat(condition).isTrue();
assertThat(root.getMessage().contains("implementations of CachingConfigurer")).isTrue();
} }
} }

Loading…
Cancel
Save