001/* 002 * Copyright 2012-2016 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.boot.autoconfigure.cache; 018 019import java.util.List; 020 021import javax.annotation.PostConstruct; 022 023import org.springframework.beans.BeansException; 024import org.springframework.beans.factory.ObjectProvider; 025import org.springframework.beans.factory.annotation.Autowired; 026import org.springframework.beans.factory.config.BeanDefinition; 027import org.springframework.beans.factory.config.BeanFactoryPostProcessor; 028import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; 029import org.springframework.boot.autoconfigure.AutoConfigureAfter; 030import org.springframework.boot.autoconfigure.AutoConfigureBefore; 031import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 032import org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration.CacheConfigurationImportSelector; 033import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 034import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 035import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 036import org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration; 037import org.springframework.boot.autoconfigure.data.jpa.EntityManagerFactoryDependsOnPostProcessor; 038import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; 039import org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration; 040import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; 041import org.springframework.boot.context.properties.EnableConfigurationProperties; 042import org.springframework.cache.CacheManager; 043import org.springframework.cache.annotation.EnableCaching; 044import org.springframework.cache.interceptor.CacheAspectSupport; 045import org.springframework.context.annotation.Bean; 046import org.springframework.context.annotation.Configuration; 047import org.springframework.context.annotation.Import; 048import org.springframework.context.annotation.ImportSelector; 049import org.springframework.context.annotation.Role; 050import org.springframework.core.type.AnnotationMetadata; 051import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean; 052import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; 053import org.springframework.util.Assert; 054 055/** 056 * {@link EnableAutoConfiguration Auto-configuration} for the cache abstraction. Creates a 057 * {@link CacheManager} if necessary when caching is enabled via {@link EnableCaching}. 058 * <p> 059 * Cache store can be auto-detected or specified explicitly via configuration. 060 * 061 * @author Stephane Nicoll 062 * @since 1.3.0 063 * @see EnableCaching 064 */ 065@Configuration 066@ConditionalOnClass(CacheManager.class) 067@ConditionalOnBean(CacheAspectSupport.class) 068@ConditionalOnMissingBean(value = CacheManager.class, name = "cacheResolver") 069@EnableConfigurationProperties(CacheProperties.class) 070@AutoConfigureBefore(HibernateJpaAutoConfiguration.class) 071@AutoConfigureAfter({ CouchbaseAutoConfiguration.class, HazelcastAutoConfiguration.class, 072 RedisAutoConfiguration.class }) 073@Import(CacheConfigurationImportSelector.class) 074public class CacheAutoConfiguration { 075 076 static final String VALIDATOR_BEAN_NAME = "cacheAutoConfigurationValidator"; 077 078 @Bean 079 @ConditionalOnMissingBean 080 public CacheManagerCustomizers cacheManagerCustomizers( 081 ObjectProvider<List<CacheManagerCustomizer<?>>> customizers) { 082 return new CacheManagerCustomizers(customizers.getIfAvailable()); 083 } 084 085 @Bean 086 @Role(BeanDefinition.ROLE_INFRASTRUCTURE) 087 public static CacheManagerValidatorPostProcessor cacheAutoConfigurationValidatorPostProcessor() { 088 return new CacheManagerValidatorPostProcessor(); 089 } 090 091 @Bean(name = VALIDATOR_BEAN_NAME) 092 public CacheManagerValidator cacheAutoConfigurationValidator() { 093 return new CacheManagerValidator(); 094 } 095 096 @Configuration 097 @ConditionalOnClass(LocalContainerEntityManagerFactoryBean.class) 098 @ConditionalOnBean(AbstractEntityManagerFactoryBean.class) 099 protected static class CacheManagerJpaDependencyConfiguration 100 extends EntityManagerFactoryDependsOnPostProcessor { 101 102 public CacheManagerJpaDependencyConfiguration() { 103 super("cacheManager"); 104 } 105 106 } 107 108 /** 109 * {@link BeanFactoryPostProcessor} to ensure that the {@link CacheManagerValidator} 110 * is triggered before {@link CacheAspectSupport} but without causing early 111 * instantiation. 112 */ 113 static class CacheManagerValidatorPostProcessor implements BeanFactoryPostProcessor { 114 115 @Override 116 public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) 117 throws BeansException { 118 for (String name : beanFactory.getBeanNamesForType(CacheAspectSupport.class, 119 false, false)) { 120 BeanDefinition definition = beanFactory.getBeanDefinition(name); 121 definition.setDependsOn( 122 append(definition.getDependsOn(), VALIDATOR_BEAN_NAME)); 123 } 124 } 125 126 private String[] append(String[] array, String value) { 127 String[] result = new String[array == null ? 1 : array.length + 1]; 128 if (array != null) { 129 System.arraycopy(array, 0, result, 0, array.length); 130 } 131 result[result.length - 1] = value; 132 return result; 133 } 134 135 } 136 137 /** 138 * Bean used to validate that a CacheManager exists and provide a more meaningful 139 * exception. 140 */ 141 static class CacheManagerValidator { 142 143 @Autowired 144 private CacheProperties cacheProperties; 145 146 @Autowired(required = false) 147 private CacheManager cacheManager; 148 149 @PostConstruct 150 public void checkHasCacheManager() { 151 Assert.notNull(this.cacheManager, 152 "No cache manager could " 153 + "be auto-configured, check your configuration (caching " 154 + "type is '" + this.cacheProperties.getType() + "')"); 155 } 156 157 } 158 159 /** 160 * {@link ImportSelector} to add {@link CacheType} configuration classes. 161 */ 162 static class CacheConfigurationImportSelector implements ImportSelector { 163 164 @Override 165 public String[] selectImports(AnnotationMetadata importingClassMetadata) { 166 CacheType[] types = CacheType.values(); 167 String[] imports = new String[types.length]; 168 for (int i = 0; i < types.length; i++) { 169 imports[i] = CacheConfigurations.getConfigurationClass(types[i]); 170 } 171 return imports; 172 } 173 174 } 175 176}