21. 如何在Spring Boot中配置和使用缓存?Spring Boot中的缓存抽象是如何实现的?
在Spring Boot中,缓存(Caching)是通过Spring的缓存抽象(Spring Cache Abstraction)来实现的。这个抽象层为各种缓存实现(如EhCache、Redis、Guava等)提供了统一的API,从而使得开发者可以轻松地在应用中添加缓存功能,并且可以在不同的缓存实现之间切换,而无需改变代码。
1. Spring Boot 中的缓存配置
Spring Boot 提供了对缓存的自动配置支持,只需要简单的配置,就可以快速在项目中启用缓存。
1.1 引入依赖
首先,根据你选择的缓存实现,添加相应的依赖。
EhCache(本地缓存):
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> </dependency>
Redis(分布式缓存):
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
1.2 启用缓存功能
在Spring Boot应用的主类或配置类上添加@EnableCaching
注解,以启用缓存功能。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
@SpringBootApplication
@EnableCaching
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
1.3 配置缓存
Spring Boot会根据你引入的依赖自动配置缓存。如果使用EhCache,可以在src/main/resources/
目录下创建ehcache.xml
文件来配置EhCache。如果使用Redis,则需要在application.properties
或application.yml
中配置Redis连接信息。
EhCache配置(
ehcache.xml
):<ehcache> <cache name="myCache"> <maxEntriesLocalHeap>1000</maxEntriesLocalHeap> <timeToLiveSeconds>3600</timeToLiveSeconds> </cache> </ehcache>
Redis配置(
application.yml
):spring: cache: type: redis redis: host: localhost port: 6379
2. 使用缓存
Spring的缓存抽象通过几个注解来实现,主要包括@Cacheable
、@CachePut
、@CacheEvict
和@Caching
。
2.1 @Cacheable
注解
@Cacheable
注解用于将方法的返回结果缓存起来,当下一次使用相同的参数调用该方法时,将直接从缓存中获取结果,而不会执行方法体。
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Cacheable(value = "users", key = "#id")
public User getUserById(Long id) {
System.out.println("Fetching user from database...");
return findUserById(id); // 假设这是一个耗时的数据库查询
}
private User findUserById(Long id) {
// 模拟数据库查询
return new User(id, "John Doe");
}
}
value
:指定缓存的名称。key
:指定缓存的键,支持SpEL表达式,默认是方法参数。
2.2 @CachePut
注解
@CachePut
注解用于更新缓存。每次调用方法时,方法都会执行,结果也会更新到缓存中。
import org.springframework.cache.annotation.CachePut;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@CachePut(value = "users", key = "#user.id")
public User updateUser(User user) {
System.out.println("Updating user in database...");
return saveUser(user); // 假设这是一个更新数据库的方法
}
private User saveUser(User user) {
// 模拟保存用户
return user;
}
}
- **
@CachePut
**不会影响方法的执行,它保证方法体每次都会被执行,并且返回值会被放入缓存。
2.3 @CacheEvict
注解
@CacheEvict
注解用于从缓存中移除一个或多个条目。
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@CacheEvict(value = "users", key = "#id")
public void deleteUserById(Long id) {
System.out.println("Deleting user from database...");
removeUser(id); // 假设这是一个删除数据库记录的方法
}
private void removeUser(Long id) {
// 模拟删除用户
}
}
allEntries = true
:如果设置为true
,将清空指定缓存中的所有条目。
2.4 @Caching
注解
@Caching
注解允许组合多个缓存操作,比如同时使用@Cacheable
和@CachePut
。
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Caching(
cacheable = @Cacheable(value = "users", key = "#id"),
put = @CachePut(value = "users", key = "#result.id")
)
public User getUser(Long id) {
System.out.println("Fetching and caching user...");
return findUserById(id);
}
private User findUserById(Long id) {
// 模拟数据库查询
return new User(id, "John Doe");
}
}
3. Spring Cache Abstraction 的工作原理
Spring的缓存抽象提供了对缓存行为的统一API,具体的缓存实现可以是EhCache、Redis、Guava、Simple Map等。Spring Cache的工作原理可以概括如下:
- 注解驱动:Spring通过
@Cacheable
、@CachePut
、@CacheEvict
等注解驱动缓存的行为。Spring AOP拦截标注了这些注解的方法调用,并在合适的时机执行缓存逻辑。 - 缓存管理器:Spring Cache通过
CacheManager
接口抽象了缓存管理器,不同的缓存实现对应不同的CacheManager
。例如,EhCacheManager
用于管理EhCache实例,RedisCacheManager
用于管理Redis缓存。 - 缓存对象:
Cache
接口表示一个具体的缓存对象,提供了对缓存数据的基本操作,如获取、添加和删除缓存条目。 - 缓存键的生成:
KeyGenerator
接口负责生成缓存键,默认使用参数组合生成键。也可以通过实现KeyGenerator
接口定制缓存键生成策略。
4. 定制缓存行为
4.1 自定义KeyGenerator
如果默认的键生成策略不满足需求,可以实现KeyGenerator
接口并在配置类中注册。
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.lang.reflect.Method;
@Configuration
public class CacheConfig {
@Bean("customKeyGenerator")
public KeyGenerator keyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... params) {
return method.getName() + "_" + params[0].toString();
}
};
}
}
使用自定义的KeyGenerator
:
@Cacheable(value = "users", keyGenerator = "customKeyGenerator")
public User getUserById(Long id) {
// ...
}
4.2 配置不同的CacheManager
如果需要在同一个应用中使用多种缓存实现,可以在配置类中定义多个CacheManager
,并在@Cacheable
等注解中指定使用哪个缓存管理器。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
@Configuration
public class CacheConfig {
@Bean
public RedisCacheManager redisCacheManager(RedisConnectionFactory connectionFactory) {
return RedisCacheManager.create(connectionFactory);
}
@Bean
public ConcurrentMapCacheManager concurrentMapCacheManager() {
return new ConcurrentMapCacheManager("defaultCache");
}
}
使用不同的CacheManager
:
@Cacheable(value = "users", cacheManager = "redisCacheManager")
public User getUserById(Long id) {
// ...
}
5. 总结
在Spring Boot中,缓存抽象通过简化的注解驱动机制,使得开发者可以轻松