spring-boot-redis
├── pom.xml
├── src/main/java/com/kfit
│ ├── App.java # 啟動類
│ ├── config/RedisCacheConfig.java # Redis 快取配置
│ ├── bean/DemoInfo.java # 實體類
│ ├── repository/DemoInfoRepository.java # JPA Repository
│ ├── service/
│ │ ├── DemoInfoService.java # 服務介面
│ │ └── impl/DemoInfoServiceImpl.java # 服務實作
│ └── controller/DemoInfoController.java # 控制器
└── src/main/resources/application.properties
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.3.RELEASE</version>
</parent>
<dependencies>
<!-- Web + AOP -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 快取支援 (EhCache, Redis 等) -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<!-- Redis Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
</dependency>
<!-- JPA -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- MySQL 驅動 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- 測試 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
# ===== MySQL 資料源 =====
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.max-active=20
spring.datasource.max-idle=8
spring.datasource.min-idle=8
spring.datasource.initial-size=10
# ===== Redis 配置 =====
spring.redis.database=0
spring.redis.host=127.0.0.1
spring.redis.port=6379
# spring.redis.password= # 若有密碼取消註解
spring.redis.pool.max-idle=8
spring.redis.pool.min-idle=0
spring.redis.pool.max-active=8
spring.redis.pool.max-wait=-1
# ===== JPA 配置 =====
spring.jpa.database=MYSQL
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.hibernate.naming-strategy=org.hibernate.cfg.ImprovedNamingStrategy
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
@Configuration
@EnableCaching
public class RedisCacheConfig extends CachingConfigurerSupport {
@Bean
public CacheManager cacheManager(RedisTemplate, ?> redisTemplate) {
return new RedisCacheManager(redisTemplate);
}
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
RedisTemplate template = new RedisTemplate<>();
template.setConnectionFactory(factory);
// 預設使用 JdkSerializationRedisSerializer,Key 會顯示為亂碼
return template;
}
}
@Configuration
@EnableCaching
public class RedisCacheConfig extends CachingConfigurerSupport {
@Bean
public CacheManager cacheManager(RedisTemplate, ?> redisTemplate) {
return new RedisCacheManager(redisTemplate);
}
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
RedisTemplate template = new RedisTemplate<>();
template.setConnectionFactory(factory);
// Key 使用 String 序列化,人類可讀
RedisSerializer stringSerializer = new StringRedisSerializer();
template.setKeySerializer(stringSerializer);
template.setHashKeySerializer(stringSerializer);
// Value 仍用 JDK 序列化支援任意物件
return template;
}
@Override
public KeyGenerator keyGenerator() {
return (target, method, params) -> {
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getName());
sb.append(method.getName());
for (Object param : params) {
sb.append(param.toString());
}
String key = sb.toString();
System.out.println("KeyGenerator: " + key);
return key;
};
}
}
關鍵點:繼承 CachingConfigurerSupport 並覆寫 keyGenerator(),全域生效無需在 @Cacheable 指定。
@Entity
public class DemoInfo implements Serializable {
@Id @GeneratedValue
private Long id;
private String name;
private String pwd;
// getter/setter/toString
}
public interface DemoInfoRepository extends CrudRepository { }
@Service
public class DemoInfoServiceImpl implements DemoInfoService {
@Autowired DemoInfoRepository repo;
@Autowired RedisTemplate redisTemplate;
@Override
@Cacheable(value = "demoInfo") // 使用自訂 KeyGenerator
public DemoInfo findById(Long id) {
System.err.println("DB 查詢: id=" + id);
return repo.findOne(id);
}
@Override
@CacheEvict(value = "demoInfo")
public void deleteFromCache(Long id) {
System.out.println("快取清除: id=" + id);
}
public void test() {
ValueOperations ops = redisTemplate.opsForValue();
ops.set("mykey4", "random1=" + Math.random());
System.out.println(ops.get("mykey4"));
}
}
@RestController
public class DemoInfoController {
@Autowired DemoInfoService service;
@GetMapping("/test")
public String test() {
DemoInfo d1 = service.findById(1L); // 第1次:DB
DemoInfo d2 = service.findById(1L); // 第2次:快取
DemoInfo d3 = service.findById(2L); // DB
return "ok";
}
@GetMapping("/delete")
public String delete(@RequestParam Long id) {
service.deleteFromCache(id);
return "ok";
}
@GetMapping("/test1")
public String test1() {
service.test(); // 直接操作 RedisTemplate
return "ok";
}
}
GET /testDB 查詢: id=1
loaded=DemoInfo[id=1, name=張三, pwd=123456]
cached=DemoInfo[id=1, name=張三, pwd=123456] # 無 DB 日誌 = 命中快取
DB 查詢: id=2
loaded2=DemoInfo[id=2, name=張三, pwd=123456]
GET /test(全快取命中)loaded=DemoInfo[id=1, name=張三, pwd=123456]
cached=DemoInfo[id=1, name=張三, pwd=123456]
loaded2=DemoInfo[id=2, name=張三, pwd=123456]
GET /delete?id=1 再呼叫 /test快取清除: id=1
DB 查詢: id=1
loaded=DemoInfo[id=1, name=張三, pwd=123456]
cached=DemoInfo[id=1, name=張三, pwd=123456]
loaded2=DemoInfo[id=2, name=張三, pwd=123456]
127.0.0.1:6379> KEYS *
1) "com.kfit.service.impl.DemoInfoServiceImplfindById1"
2) "com.kfit.service.impl.DemoInfoServiceImplfindById2"
| 主題 | 說明 |
|---|---|
| @Cacheable 進階屬性 | key 指定 SpEL 表達式、condition 條件快取、unless 結果過濾、sync 同步載入防穿透 |
| @CacheEvict 進階 | allEntries=true 清空整個 Cache、beforeInvocation=true 方法前清除 |
| @CachePut | 方法總是執行並更新快取,不阻擋方法執行 |
| @Caching | 組合多個快取操作 |
| 快取失效策略 | TTL 過期、主動清除、CacheManager 配置預設過期時間 |
| 序列化選擇 | StringRedisSerializer(Key)、GenericJackson2JsonRedisSerializer(Value,可讀+型別安全) |
| Redis Cluster 支援 | Spring Boot 2.x+ 自動支援,配置 spring.redis.cluster.nodes |
Spring Boot + Spring Cache + Redis 實現聲明式快取極其簡潔。核心步驟:引入 Starter → 配置連線 → @EnableCaching → 定義 CacheManager → 業務方法加 @Cacheable/@CacheEvict。掌握 KeyGenerator 客製化、序列化策略、失效模式,即可應對生產環境多數快取場景。