Expose caching-specific infrastructure
Expose the underlying cache infrastructure bean if Boot auto-configures it. This is the case for ehCache, hazelcast and JCache. This change has two side effects: 1. It is now possible to customize the underlying cache infrastructure and let Boot only wrap it in the Spring's CacheManager abstraction. No customizations are applied if the caching-specific service is customized 2. Such infrastructure is disposed when the application terminates as it is now defined as `@Bean` and both `close()` and `shutdown()` methods are invoked if present on the target type. While the latter can be troublesome, we feel that a particular cache instance is not meant to be shared and must be disposed when the application terminates. Closes gh-2848
This commit is contained in:
parent
b20d11fe9c
commit
1b3efd41f5
|
@ -17,11 +17,11 @@
|
|||
package org.springframework.boot.autoconfigure.cache;
|
||||
|
||||
import net.sf.ehcache.Cache;
|
||||
import net.sf.ehcache.CacheManager;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.ehcache.EhCacheCacheManager;
|
||||
import org.springframework.cache.ehcache.EhCacheManagerUtils;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
@ -34,11 +34,12 @@ import org.springframework.core.io.Resource;
|
|||
* a default configuration file exists.
|
||||
*
|
||||
* @author Eddú Meléndez
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass({ Cache.class, EhCacheCacheManager.class })
|
||||
@ConditionalOnMissingBean(CacheManager.class)
|
||||
@ConditionalOnMissingBean(org.springframework.cache.CacheManager.class)
|
||||
@Conditional({ CacheCondition.class,
|
||||
EhCacheCacheConfiguration.ConfigAvailableCondition.class })
|
||||
class EhCacheCacheConfiguration {
|
||||
|
@ -47,13 +48,18 @@ class EhCacheCacheConfiguration {
|
|||
private CacheProperties properties;
|
||||
|
||||
@Bean
|
||||
public EhCacheCacheManager cacheManager() {
|
||||
public EhCacheCacheManager cacheManager(CacheManager ehCacheCacheManager) {
|
||||
return new EhCacheCacheManager(ehCacheCacheManager);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public CacheManager ehCacheCacheManager() {
|
||||
Resource location = this.properties.resolveConfigLocation();
|
||||
if (location != null) {
|
||||
return new EhCacheCacheManager(
|
||||
EhCacheManagerUtils.buildCacheManager(location));
|
||||
return EhCacheManagerUtils.buildCacheManager(location);
|
||||
}
|
||||
return new EhCacheCacheManager(EhCacheManagerUtils.buildCacheManager());
|
||||
return EhCacheManagerUtils.buildCacheManager();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -57,11 +57,13 @@ class HazelcastCacheConfiguration {
|
|||
private CacheProperties cacheProperties;
|
||||
|
||||
@Bean
|
||||
public HazelcastCacheManager cacheManager() throws IOException {
|
||||
return new HazelcastCacheManager(createHazelcastInstance());
|
||||
public HazelcastCacheManager cacheManager(HazelcastInstance hazelcastInstance) {
|
||||
return new HazelcastCacheManager(hazelcastInstance);
|
||||
}
|
||||
|
||||
private HazelcastInstance createHazelcastInstance() throws IOException {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public HazelcastInstance hazelcastInstance() throws IOException {
|
||||
Resource location = this.cacheProperties.resolveConfigLocation();
|
||||
if (location != null) {
|
||||
Config cfg = new XmlConfigBuilder(location.getURL()).build();
|
||||
|
|
|
@ -27,9 +27,11 @@ import javax.cache.configuration.MutableConfiguration;
|
|||
import javax.cache.spi.CachingProvider;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
|
||||
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
|
||||
import org.springframework.boot.bind.RelaxedPropertyResolver;
|
||||
import org.springframework.cache.jcache.JCacheCacheManager;
|
||||
|
@ -68,19 +70,13 @@ class JCacheCacheConfiguration {
|
|||
private List<JCacheManagerCustomizer> cacheManagerCustomizers;
|
||||
|
||||
@Bean
|
||||
public JCacheCacheManager cacheManager() throws IOException {
|
||||
CacheManager cacheManager = createCacheManager();
|
||||
List<String> cacheNames = this.cacheProperties.getCacheNames();
|
||||
if (!CollectionUtils.isEmpty(cacheNames)) {
|
||||
for (String cacheName : cacheNames) {
|
||||
cacheManager.createCache(cacheName, getDefaultCacheConfiguration());
|
||||
}
|
||||
}
|
||||
customize(cacheManager);
|
||||
return new JCacheCacheManager(cacheManager);
|
||||
public JCacheCacheManager cacheManager(CacheManager jCacheCacheManager) {
|
||||
return new JCacheCacheManager(jCacheCacheManager);
|
||||
}
|
||||
|
||||
private CacheManager createCacheManager() throws IOException {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public CacheManager jCacheCacheManager() throws IOException {
|
||||
CachingProvider cachingProvider = getCachingProvider(this.cacheProperties
|
||||
.getJcache().getProvider());
|
||||
Resource configLocation = this.cacheProperties.resolveConfigLocation();
|
||||
|
@ -89,7 +85,15 @@ class JCacheCacheConfiguration {
|
|||
cachingProvider.getDefaultClassLoader(),
|
||||
createCacheManagerProperties(configLocation));
|
||||
}
|
||||
return cachingProvider.getCacheManager();
|
||||
CacheManager jCacheCacheManager = cachingProvider.getCacheManager();
|
||||
List<String> cacheNames = this.cacheProperties.getCacheNames();
|
||||
if (!CollectionUtils.isEmpty(cacheNames)) {
|
||||
for (String cacheName : cacheNames) {
|
||||
jCacheCacheManager.createCache(cacheName, getDefaultCacheConfiguration());
|
||||
}
|
||||
}
|
||||
customize(jCacheCacheManager);
|
||||
return jCacheCacheManager;
|
||||
}
|
||||
|
||||
private CachingProvider getCachingProvider(String cachingProviderFqn) {
|
||||
|
@ -125,12 +129,32 @@ class JCacheCacheConfiguration {
|
|||
}
|
||||
|
||||
/**
|
||||
* Determines if JCache is available. This either kick in if a default
|
||||
* Determine if JCache is available. This either kick in if a provider is available
|
||||
* as defined per {@link JCacheProviderAvailableCondition} or if a {@link CacheManager}
|
||||
* has already been defined.
|
||||
*/
|
||||
@Order(Ordered.LOWEST_PRECEDENCE)
|
||||
static class JCacheAvailableCondition extends AnyNestedCondition {
|
||||
|
||||
public JCacheAvailableCondition() {
|
||||
super(ConfigurationPhase.REGISTER_BEAN);
|
||||
}
|
||||
|
||||
@Conditional(JCacheProviderAvailableCondition.class)
|
||||
static class JCacheProvider {}
|
||||
|
||||
@ConditionalOnSingleCandidate(CacheManager.class)
|
||||
static class CustomJCacheCacheManager {}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a JCache provider is available. This either kick in if a default
|
||||
* {@link CachingProvider} has been found or if the property referring to the provider
|
||||
* to use has been set.
|
||||
*/
|
||||
@Order(Ordered.LOWEST_PRECEDENCE)
|
||||
static class JCacheAvailableCondition extends SpringBootCondition {
|
||||
static class JCacheProviderAvailableCondition extends SpringBootCondition {
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||
|
|
|
@ -19,16 +19,22 @@ package org.springframework.boot.autoconfigure.cache;
|
|||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import javax.cache.configuration.CompleteConfiguration;
|
||||
import javax.cache.configuration.MutableConfiguration;
|
||||
import javax.cache.expiry.CreatedExpiryPolicy;
|
||||
import javax.cache.expiry.Duration;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.hazelcast.cache.HazelcastCachingProvider;
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
import com.hazelcast.spring.cache.HazelcastCacheManager;
|
||||
import net.sf.ehcache.Status;
|
||||
import org.junit.After;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.boot.autoconfigure.cache.support.MockCachingProvider;
|
||||
|
@ -56,10 +62,6 @@ import org.springframework.core.io.Resource;
|
|||
import org.springframework.data.redis.cache.RedisCacheManager;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.hazelcast.cache.HazelcastCachingProvider;
|
||||
import com.hazelcast.spring.cache.HazelcastCacheManager;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
|
@ -70,6 +72,7 @@ import static org.hamcrest.core.Is.is;
|
|||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* Tests for {@link CacheAutoConfiguration}.
|
||||
|
@ -213,6 +216,8 @@ public class CacheAutoConfigurationTests {
|
|||
"spring.cache.jcache.provider=" + cachingProviderFqn);
|
||||
JCacheCacheManager cacheManager = validateCacheManager(JCacheCacheManager.class);
|
||||
assertThat(cacheManager.getCacheNames(), is(empty()));
|
||||
assertThat(this.context.getBean(javax.cache.CacheManager.class),
|
||||
is(cacheManager.getCacheManager()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -244,6 +249,13 @@ public class CacheAutoConfigurationTests {
|
|||
defaultCacheConfiguration);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void jCacheCacheWithExistingJCacheManager() {
|
||||
load(JCacheCustomCacheManager.class, "spring.cache.type=jcache");
|
||||
JCacheCacheManager cacheManager = validateCacheManager(JCacheCacheManager.class);
|
||||
assertThat(cacheManager.getCacheManager(), is(this.context.getBean("customJCacheCacheManager")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void jCacheCacheWithUnknownProvider() {
|
||||
String wrongCachingProviderFqn = "org.acme.FooBar";
|
||||
|
@ -280,36 +292,29 @@ public class CacheAutoConfigurationTests {
|
|||
@Test
|
||||
public void ehCacheCacheWithCaches() {
|
||||
load(DefaultCacheConfiguration.class, "spring.cache.type=ehcache");
|
||||
EhCacheCacheManager cacheManager = null;
|
||||
try {
|
||||
cacheManager = validateCacheManager(EhCacheCacheManager.class);
|
||||
assertThat(cacheManager.getCacheNames(),
|
||||
containsInAnyOrder("cacheTest1", "cacheTest2"));
|
||||
assertThat(cacheManager.getCacheNames(), hasSize(2));
|
||||
}
|
||||
finally {
|
||||
if (cacheManager != null) {
|
||||
cacheManager.getCacheManager().shutdown();
|
||||
}
|
||||
}
|
||||
EhCacheCacheManager cacheManager = validateCacheManager(EhCacheCacheManager.class);
|
||||
assertThat(cacheManager.getCacheNames(),
|
||||
containsInAnyOrder("cacheTest1", "cacheTest2"));
|
||||
assertThat(cacheManager.getCacheNames(), hasSize(2));
|
||||
assertThat(this.context.getBean(net.sf.ehcache.CacheManager.class),
|
||||
is(cacheManager.getCacheManager()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ehCacheCacheWithConfig() {
|
||||
load(DefaultCacheConfiguration.class, "spring.cache.type=ehcache",
|
||||
"spring.cache.config=cache/ehcache-override.xml");
|
||||
EhCacheCacheManager cacheManager = null;
|
||||
try {
|
||||
cacheManager = validateCacheManager(EhCacheCacheManager.class);
|
||||
assertThat(cacheManager.getCacheNames(),
|
||||
containsInAnyOrder("cacheOverrideTest1", "cacheOverrideTest2"));
|
||||
assertThat(cacheManager.getCacheNames(), hasSize(2));
|
||||
}
|
||||
finally {
|
||||
if (cacheManager != null) {
|
||||
cacheManager.getCacheManager().shutdown();
|
||||
}
|
||||
}
|
||||
EhCacheCacheManager cacheManager = validateCacheManager(EhCacheCacheManager.class);
|
||||
assertThat(cacheManager.getCacheNames(),
|
||||
containsInAnyOrder("cacheOverrideTest1", "cacheOverrideTest2"));
|
||||
assertThat(cacheManager.getCacheNames(), hasSize(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ehCacheCacheWithExistingCacheManager() {
|
||||
load(EhCacheCustomCacheManager.class, "spring.cache.type=ehcache");
|
||||
EhCacheCacheManager cacheManager = validateCacheManager(EhCacheCacheManager.class);
|
||||
assertThat(cacheManager.getCacheManager(), is(this.context.getBean("customEhCacheCacheManager")));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -320,6 +325,8 @@ public class CacheAutoConfigurationTests {
|
|||
cacheManager.getCache("defaultCache");
|
||||
assertThat(cacheManager.getCacheNames(), containsInAnyOrder("defaultCache"));
|
||||
assertThat(cacheManager.getCacheNames(), hasSize(1));
|
||||
assertThat(this.context.getBean(HazelcastInstance.class),
|
||||
is(new DirectFieldAccessor(cacheManager).getPropertyValue("hazelcastInstance")));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -340,65 +347,49 @@ public class CacheAutoConfigurationTests {
|
|||
"spring.cache.config=foo/bar/unknown.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hazelcastCacheWithExistingHazelcastInstance() {
|
||||
load(HazelcastCustomHazelcastInstance.class, "spring.cache.type=hazelcast");
|
||||
HazelcastCacheManager cacheManager = validateCacheManager(HazelcastCacheManager.class);
|
||||
assertThat(new DirectFieldAccessor(cacheManager).getPropertyValue("hazelcastInstance"),
|
||||
is(this.context.getBean("customHazelcastInstance")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hazelcastAsJCacheWithCaches() {
|
||||
String cachingProviderFqn = HazelcastCachingProvider.class.getName();
|
||||
JCacheCacheManager cacheManager = null;
|
||||
try {
|
||||
load(DefaultCacheConfiguration.class, "spring.cache.type=jcache",
|
||||
"spring.cache.jcache.provider=" + cachingProviderFqn,
|
||||
"spring.cache.cacheNames[0]=foo", "spring.cache.cacheNames[1]=bar");
|
||||
cacheManager = validateCacheManager(JCacheCacheManager.class);
|
||||
assertThat(cacheManager.getCacheNames(), containsInAnyOrder("foo", "bar"));
|
||||
assertThat(cacheManager.getCacheNames(), hasSize(2));
|
||||
}
|
||||
finally {
|
||||
if (cacheManager != null) {
|
||||
cacheManager.getCacheManager().close();
|
||||
}
|
||||
}
|
||||
load(DefaultCacheConfiguration.class, "spring.cache.type=jcache",
|
||||
"spring.cache.jcache.provider=" + cachingProviderFqn,
|
||||
"spring.cache.cacheNames[0]=foo", "spring.cache.cacheNames[1]=bar");
|
||||
JCacheCacheManager cacheManager = validateCacheManager(JCacheCacheManager.class);
|
||||
assertThat(cacheManager.getCacheNames(), containsInAnyOrder("foo", "bar"));
|
||||
assertThat(cacheManager.getCacheNames(), hasSize(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hazelcastAsJCacheWithConfig() throws IOException {
|
||||
String cachingProviderFqn = HazelcastCachingProvider.class.getName();
|
||||
String configLocation = "org/springframework/boot/autoconfigure/cache/hazelcast-specific.xml";
|
||||
JCacheCacheManager cacheManager = null;
|
||||
try {
|
||||
load(DefaultCacheConfiguration.class, "spring.cache.type=jcache",
|
||||
"spring.cache.jcache.provider=" + cachingProviderFqn,
|
||||
"spring.cache.config=" + configLocation);
|
||||
cacheManager = validateCacheManager(JCacheCacheManager.class);
|
||||
load(DefaultCacheConfiguration.class, "spring.cache.type=jcache",
|
||||
"spring.cache.jcache.provider=" + cachingProviderFqn,
|
||||
"spring.cache.config=" + configLocation);
|
||||
JCacheCacheManager cacheManager = validateCacheManager(JCacheCacheManager.class);
|
||||
|
||||
Resource configResource = new ClassPathResource(configLocation);
|
||||
assertThat(cacheManager.getCacheManager().getURI(),
|
||||
is(configResource.getURI()));
|
||||
}
|
||||
finally {
|
||||
if (cacheManager != null) {
|
||||
cacheManager.getCacheManager().close();
|
||||
}
|
||||
}
|
||||
Resource configResource = new ClassPathResource(configLocation);
|
||||
assertThat(cacheManager.getCacheManager().getURI(),
|
||||
is(configResource.getURI()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void jCacheCacheWithCachesAndCustomizer() {
|
||||
String cachingProviderFqn = HazelcastCachingProvider.class.getName();
|
||||
JCacheCacheManager cacheManager = null;
|
||||
try {
|
||||
load(JCacheWithCustomizerConfiguration.class, "spring.cache.type=jcache",
|
||||
"spring.cache.jcache.provider=" + cachingProviderFqn,
|
||||
"spring.cache.cacheNames[0]=foo", "spring.cache.cacheNames[1]=bar");
|
||||
cacheManager = validateCacheManager(JCacheCacheManager.class);
|
||||
assertThat(cacheManager.getCacheNames(), containsInAnyOrder("foo", "custom1")); // see
|
||||
// customizer
|
||||
assertThat(cacheManager.getCacheNames(), hasSize(2));
|
||||
}
|
||||
finally {
|
||||
if (cacheManager != null) {
|
||||
cacheManager.getCacheManager().close();
|
||||
}
|
||||
}
|
||||
load(JCacheWithCustomizerConfiguration.class, "spring.cache.type=jcache",
|
||||
"spring.cache.jcache.provider=" + cachingProviderFqn,
|
||||
"spring.cache.cacheNames[0]=foo", "spring.cache.cacheNames[1]=bar");
|
||||
JCacheCacheManager cacheManager = validateCacheManager(JCacheCacheManager.class);
|
||||
assertThat(cacheManager.getCacheNames(), containsInAnyOrder("foo", "custom1")); // see
|
||||
// customizer
|
||||
assertThat(cacheManager.getCacheNames(), hasSize(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -505,6 +496,19 @@ public class CacheAutoConfigurationTests {
|
|||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableCaching
|
||||
static class JCacheCustomCacheManager {
|
||||
|
||||
@Bean
|
||||
public javax.cache.CacheManager customJCacheCacheManager() {
|
||||
javax.cache.CacheManager cacheManager = mock(javax.cache.CacheManager.class);
|
||||
when(cacheManager.getCacheNames()).thenReturn(Collections.<String>emptyList());
|
||||
return cacheManager;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableCaching
|
||||
static class JCacheWithCustomizerConfiguration {
|
||||
|
@ -526,6 +530,31 @@ public class CacheAutoConfigurationTests {
|
|||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableCaching
|
||||
static class EhCacheCustomCacheManager {
|
||||
|
||||
@Bean
|
||||
public net.sf.ehcache.CacheManager customEhCacheCacheManager() {
|
||||
net.sf.ehcache.CacheManager cacheManager = mock(net.sf.ehcache.CacheManager.class);
|
||||
when(cacheManager.getStatus()).thenReturn(Status.STATUS_ALIVE);
|
||||
when(cacheManager.getCacheNames()).thenReturn(new String[0]);
|
||||
return cacheManager;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableCaching
|
||||
static class HazelcastCustomHazelcastInstance {
|
||||
|
||||
@Bean
|
||||
public HazelcastInstance customHazelcastInstance() {
|
||||
return mock(HazelcastInstance.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import({ GenericCacheConfiguration.class, RedisCacheConfiguration.class })
|
||||
static class CustomCacheManagerConfiguration {
|
||||
|
|
Loading…
Reference in New Issue