Allow auto-configured caches to be overridden

This commit updates the JSR 107 cache auto-configuration to only create
a cache if it does not already exist. It also puts a specific order for
the auto-configured customizer so that another instance can run prior to
it reliably.

Closes gh-1157
This commit is contained in:
Stephane Nicoll
2020-12-08 15:32:37 +01:00
parent 7cb9f5bcaf
commit 8d61cdd6aa
4 changed files with 92 additions and 10 deletions

View File

@@ -1143,6 +1143,9 @@ checking for the latest Spring Boot versions too often, you should enable cachin
service. Spring Initializr has some auto-configuration to apply the proper caches if you
are willing to use a JCache (JSR-107) implementation.
NOTE: The caches are created with an auto-configured `JCacheManagerCustomizer` with order `0` and only if they don't exist already.
You can contribute a bean of the same type with a lower `@Order` to override some of the configuration to suit your specific needs.
Add the `javax.cache:cache-api` and your favorite JCache implementation and simply enable
caching by adding `@EnableCaching` to your `@SpringBootApplication`. For instance, you
could use `ehcache` by adding the following:

View File

@@ -98,6 +98,11 @@
<artifactId>spring-restdocs-mockmvc</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.vaadin.external.google</groupId>
<artifactId>android-json</artifactId>

View File

@@ -17,6 +17,8 @@
package io.spring.initializr.web.autoconfigure;
import java.nio.file.Files;
import java.util.function.Supplier;
import java.util.stream.StreamSupport;
import javax.cache.configuration.MutableConfiguration;
import javax.cache.expiry.CreatedExpiryPolicy;
@@ -62,6 +64,7 @@ import org.springframework.cache.support.NoOpCache;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
/**
@@ -182,13 +185,30 @@ public class InitializrAutoConfiguration {
@Bean
JCacheManagerCustomizer initializrCacheManagerCustomizer() {
return (cacheManager) -> {
cacheManager.createCache("initializr.metadata",
config().setExpiryPolicyFactory(CreatedExpiryPolicy.factoryOf(Duration.TEN_MINUTES)));
cacheManager.createCache("initializr.dependency-metadata", config());
cacheManager.createCache("initializr.project-resources", config());
cacheManager.createCache("initializr.templates", config());
};
return new InitializrJCacheManagerCustomizer();
}
}
@Order(0)
private static class InitializrJCacheManagerCustomizer implements JCacheManagerCustomizer {
@Override
public void customize(javax.cache.CacheManager cacheManager) {
createMissingCache(cacheManager, "initializr.metadata",
() -> config().setExpiryPolicyFactory(CreatedExpiryPolicy.factoryOf(Duration.TEN_MINUTES)));
createMissingCache(cacheManager, "initializr.dependency-metadata", this::config);
createMissingCache(cacheManager, "initializr.project-resources", this::config);
createMissingCache(cacheManager, "initializr.templates", this::config);
}
private void createMissingCache(javax.cache.CacheManager cacheManager, String cacheName,
Supplier<MutableConfiguration<Object, Object>> config) {
boolean cacheExist = StreamSupport.stream(cacheManager.getCacheNames().spliterator(), true)
.anyMatch((name) -> name.equals(cacheName));
if (!cacheExist) {
cacheManager.createCache(cacheName, config.get());
}
}
private MutableConfiguration<Object, Object> config() {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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,6 +16,11 @@
package io.spring.initializr.web.autoconfigure;
import javax.cache.Cache;
import javax.cache.CacheManager;
import javax.cache.configuration.CompleteConfiguration;
import javax.cache.configuration.MutableConfiguration;
import io.spring.initializr.generator.io.template.TemplateRenderer;
import io.spring.initializr.metadata.DependencyMetadataProvider;
import io.spring.initializr.metadata.InitializrMetadataProvider;
@@ -28,14 +33,18 @@ import io.spring.initializr.web.support.InitializrMetadataUpdateStrategy;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration;
import org.springframework.boot.autoconfigure.cache.JCacheManagerCustomizer;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.jcache.JCacheCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
@@ -140,8 +149,30 @@ class InitializrAutoConfigurationTests {
}
@Test
void cacheConfiguration() {
this.contextRunner.run((context) -> assertThat(context).hasSingleBean(JCacheManagerCustomizer.class));
void cacheConfigurationCreatesInitializrCachesIfNecessary() {
this.contextRunner.withConfiguration(AutoConfigurations.of(CacheAutoConfiguration.class))
.withUserConfiguration(CacheTestConfiguration.class).run((context) -> {
assertThat(context).hasSingleBean(JCacheManagerCustomizer.class)
.hasSingleBean(JCacheCacheManager.class);
JCacheCacheManager cacheManager = context.getBean(JCacheCacheManager.class);
assertThat(cacheManager.getCacheNames()).containsOnly("initializr.metadata",
"initializr.dependency-metadata", "initializr.project-resources", "initializr.templates");
assertThat(getConfiguration(cacheManager, "initializr.metadata").isStatisticsEnabled()).isTrue();
});
}
@Test
void cacheConfigurationDoesNotOverrideExistingCaches() {
this.contextRunner.withConfiguration(AutoConfigurations.of(CacheAutoConfiguration.class))
.withUserConfiguration(CacheTestConfiguration.class, CustomJCacheManagerCustomizer.class)
.run((context) -> {
assertThat(context).getBeans(JCacheManagerCustomizer.class).hasSize(2);
JCacheCacheManager cacheManager = context.getBean(JCacheCacheManager.class);
assertThat(cacheManager.getCacheNames()).containsOnly("initializr.metadata",
"initializr.dependency-metadata", "initializr.project-resources", "initializr.templates",
"custom.cache");
assertThat(getConfiguration(cacheManager, "initializr.metadata").isStatisticsEnabled()).isFalse();
});
}
@Test
@@ -150,6 +181,12 @@ class InitializrAutoConfigurationTests {
.run((context) -> assertThat(context).doesNotHaveBean(JCacheManagerCustomizer.class));
}
@SuppressWarnings("unchecked")
private CompleteConfiguration<?, ?> getConfiguration(JCacheCacheManager cacheManager, String cacheName) {
Cache<?, ?> cache = (Cache<?, ?>) cacheManager.getCache("initializr.metadata").getNativeCache();
return (CompleteConfiguration<?, ?>) cache.getConfiguration(CompleteConfiguration.class);
}
@Configuration
static class CustomTemplateRendererConfiguration {
@@ -200,4 +237,21 @@ class InitializrAutoConfigurationTests {
}
@Configuration
@EnableCaching
static class CacheTestConfiguration {
}
@Order(-1)
private static class CustomJCacheManagerCustomizer implements JCacheManagerCustomizer {
@Override
public void customize(CacheManager cacheManager) {
cacheManager.createCache("initializr.metadata", new MutableConfiguration<>().setStatisticsEnabled(false));
cacheManager.createCache("custom.cache", new MutableConfiguration<>());
}
}
}