Browse Source

Only require an exception CacheResolver if necessary

Previously, a cache infrastructure with only a CacheResolver would have
worked fine until the JSR-107 API is added to the classpath. When this is
the case, the JCache support kicks in and an exception cache resolver is
all of the sudden required.

The CacheResolver _is_ different as the default implementation does look
different attributes so if a custom CacheResolver is set, it is not
possible to "reuse" it as a fallback exception CacheResolver.

Now, an exception CacheResolver is only required if a JSR-107 annotation
with an "exceptionCacheName" attribute is processed (i.e. the exception
CacheResolver is lazily instantiated if necessary).

The use case of having a CachingConfigurerSupport with only a
CacheResolver was still broken though since the JCache support only looks
for a JCacheConfigurer bean (per the generic type set on
AbstractCachingConfiguration). This has been fixed as well.

Issue: SPR-12850
pull/762/head
Stephane Nicoll 10 years ago
parent
commit
314b069fd8
  1. 11
      spring-context-support/src/main/java/org/springframework/cache/jcache/config/AbstractJCacheConfiguration.java
  2. 6
      spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/DefaultJCacheOperationSource.java
  3. 52
      spring-context-support/src/test/java/org/springframework/cache/jcache/config/JCacheJavaConfigTests.java
  4. 8
      spring-context/src/main/java/org/springframework/cache/annotation/AbstractCachingConfiguration.java
  5. 4
      spring-context/src/main/java/org/springframework/cache/annotation/ProxyCachingConfiguration.java

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

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2014 the original author or authors. * Copyright 2002-2015 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.
@ -18,6 +18,7 @@ package org.springframework.cache.jcache.config;
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;
@ -34,14 +35,16 @@ import org.springframework.context.annotation.Role;
* @see JCacheConfigurer * @see JCacheConfigurer
*/ */
@Configuration @Configuration
public class AbstractJCacheConfiguration extends AbstractCachingConfiguration<JCacheConfigurer> { public class AbstractJCacheConfiguration extends AbstractCachingConfiguration {
protected CacheResolver exceptionCacheResolver; protected CacheResolver exceptionCacheResolver;
@Override @Override
protected void useCachingConfigurer(JCacheConfigurer config) { protected void useCachingConfigurer(CachingConfigurer config) {
super.useCachingConfigurer(config); super.useCachingConfigurer(config);
this.exceptionCacheResolver = config.exceptionCacheResolver(); if (config instanceof JCacheConfigurer) {
this.exceptionCacheResolver = ((JCacheConfigurer) config).exceptionCacheResolver();
}
} }
@Bean(name = "jCacheOperationSource") @Bean(name = "jCacheOperationSource")

6
spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/DefaultJCacheOperationSource.java vendored

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2014 the original author or authors. * Copyright 2002-2015 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.
@ -128,9 +128,9 @@ public class DefaultJCacheOperationSource extends AnnotationJCacheOperationSourc
@Override @Override
public void afterSingletonsInstantiated() { public void afterSingletonsInstantiated() {
// Make sure those are initialized on startup... // Make sure that the cache resolver is initialized. An exception cache resolver is only
// required if the exceptionCacheName attribute is set on an operation
Assert.notNull(getDefaultCacheResolver(), "Cache resolver should have been initialized"); Assert.notNull(getDefaultCacheResolver(), "Cache resolver should have been initialized");
Assert.notNull(getDefaultExceptionCacheResolver(), "Exception cache resolver should have been initialized");
} }

52
spring-context-support/src/test/java/org/springframework/cache/jcache/config/JCacheJavaConfigTests.java vendored

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2014 the original author or authors. * Copyright 2002-2015 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.
@ -18,12 +18,15 @@ package org.springframework.cache.jcache.config;
import java.util.Arrays; import java.util.Arrays;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.cache.Cache; import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager; import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCache; import org.springframework.cache.concurrent.ConcurrentMapCache;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.cache.config.SomeKeyGenerator; import org.springframework.cache.config.SomeKeyGenerator;
import org.springframework.cache.interceptor.CacheErrorHandler; import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.cache.interceptor.CacheResolver; import org.springframework.cache.interceptor.CacheResolver;
@ -35,7 +38,6 @@ import org.springframework.cache.interceptor.SimpleKeyGenerator;
import org.springframework.cache.jcache.interceptor.AnnotatedJCacheableService; import org.springframework.cache.jcache.interceptor.AnnotatedJCacheableService;
import org.springframework.cache.jcache.interceptor.DefaultJCacheOperationSource; import org.springframework.cache.jcache.interceptor.DefaultJCacheOperationSource;
import org.springframework.cache.jcache.interceptor.JCacheInterceptor; import org.springframework.cache.jcache.interceptor.JCacheInterceptor;
import org.springframework.cache.jcache.interceptor.SimpleExceptionCacheResolver;
import org.springframework.cache.support.NoOpCacheManager; import org.springframework.cache.support.NoOpCacheManager;
import org.springframework.cache.support.SimpleCacheManager; import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
@ -51,6 +53,9 @@ import static org.junit.Assert.*;
*/ */
public class JCacheJavaConfigTests extends AbstractJCacheAnnotationTests { public class JCacheJavaConfigTests extends AbstractJCacheAnnotationTests {
@Rule
public final ExpectedException thrown = ExpectedException.none();
@Override @Override
protected ApplicationContext getApplicationContext() { protected ApplicationContext getApplicationContext() {
return new AnnotationConfigApplicationContext(EnableCachingConfig.class); return new AnnotationConfigApplicationContext(EnableCachingConfig.class);
@ -80,10 +85,7 @@ public class JCacheJavaConfigTests extends AbstractJCacheAnnotationTests {
assertEquals(SimpleCacheResolver.class, cos.getCacheResolver().getClass()); assertEquals(SimpleCacheResolver.class, cos.getCacheResolver().getClass());
assertSame(context.getBean(CacheManager.class), assertSame(context.getBean(CacheManager.class),
((SimpleCacheResolver) cos.getCacheResolver()).getCacheManager()); ((SimpleCacheResolver) cos.getCacheResolver()).getCacheManager());
assertNotNull(cos.getExceptionCacheResolver()); assertNull(cos.getExceptionCacheResolver());
assertEquals(SimpleExceptionCacheResolver.class, cos.getExceptionCacheResolver().getClass());
assertSame(context.getBean(CacheManager.class),
((SimpleExceptionCacheResolver) cos.getExceptionCacheResolver()).getCacheManager());
context.close(); context.close();
} }
@ -99,6 +101,28 @@ public class JCacheJavaConfigTests extends AbstractJCacheAnnotationTests {
context.close(); context.close();
} }
@Test
public void exceptionCacheResolverFallbacksToMainOne() {
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(
NoExceptionCacheResolverConfig.class);
try {
DefaultJCacheOperationSource cos = context.getBean(DefaultJCacheOperationSource.class);
assertSame(context.getBean("cacheResolver"), cos.getCacheResolver());
assertNull(cos.getExceptionCacheResolver());
JCacheableService<?> service = context.getBean(JCacheableService.class);
service.cache("id");
// This call requires the cache manager to be set
thrown.expect(IllegalStateException.class);
service.cacheWithException("test", false);
}
finally {
context.close();
}
}
@Configuration @Configuration
@EnableCaching @EnableCaching
@ -200,4 +224,20 @@ public class JCacheJavaConfigTests extends AbstractJCacheAnnotationTests {
} }
} }
@Configuration
@EnableCaching
static class NoExceptionCacheResolverConfig extends JCacheConfigurerSupport {
@Override
@Bean
public CacheResolver cacheResolver() {
return new NamedCacheResolver(new ConcurrentMapCacheManager(), "default");
}
@Bean
public JCacheableService<?> cacheableService() {
return new AnnotatedJCacheableService(new ConcurrentMapCache("default"));
}
}
} }

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

@ -39,7 +39,7 @@ import org.springframework.util.CollectionUtils;
* @see EnableCaching * @see EnableCaching
*/ */
@Configuration @Configuration
public abstract class AbstractCachingConfiguration<C extends CachingConfigurer> implements ImportAware { public abstract class AbstractCachingConfiguration implements ImportAware {
protected AnnotationAttributes enableCaching; protected AnnotationAttributes enableCaching;
@ -63,7 +63,7 @@ public abstract class AbstractCachingConfiguration<C extends CachingConfigurer>
} }
@Autowired(required = false) @Autowired(required = false)
void setConfigurers(Collection<C> configurers) { void setConfigurers(Collection<CachingConfigurer> configurers) {
if (CollectionUtils.isEmpty(configurers)) { if (CollectionUtils.isEmpty(configurers)) {
return; return;
} }
@ -73,14 +73,14 @@ public abstract class AbstractCachingConfiguration<C extends CachingConfigurer>
"Refactor the configuration such that CachingConfigurer is " + "Refactor the configuration such that CachingConfigurer is " +
"implemented only once or not at all."); "implemented only once or not at all.");
} }
C configurer = configurers.iterator().next(); CachingConfigurer configurer = configurers.iterator().next();
useCachingConfigurer(configurer); useCachingConfigurer(configurer);
} }
/** /**
* Extract the configuration from the nominated {@link CachingConfigurer}. * Extract the configuration from the nominated {@link CachingConfigurer}.
*/ */
protected void useCachingConfigurer(C config) { protected void useCachingConfigurer(CachingConfigurer config) {
this.cacheManager = config.cacheManager(); this.cacheManager = config.cacheManager();
this.cacheResolver = config.cacheResolver(); this.cacheResolver = config.cacheResolver();
this.keyGenerator = config.keyGenerator(); this.keyGenerator = config.keyGenerator();

4
spring-context/src/main/java/org/springframework/cache/annotation/ProxyCachingConfiguration.java vendored

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2014 the original author or authors. * Copyright 2002-2015 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.
@ -35,7 +35,7 @@ import org.springframework.context.annotation.Role;
* @see CachingConfigurationSelector * @see CachingConfigurationSelector
*/ */
@Configuration @Configuration
public class ProxyCachingConfiguration extends AbstractCachingConfiguration<CachingConfigurer> { public class ProxyCachingConfiguration extends AbstractCachingConfiguration {
@Bean(name = CacheManagementConfigUtils.CACHE_ADVISOR_BEAN_NAME) @Bean(name = CacheManagementConfigUtils.CACHE_ADVISOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE) @Role(BeanDefinition.ROLE_INFRASTRUCTURE)

Loading…
Cancel
Save