第3课_自定义缓存注解实现
热度🔥:66 免费课程
授课语音
手写自定义缓存注解,灵活处理缓存问题
缓存是一种常见的优化技术,广泛应用于提升系统性能,减少对数据库的访问压力。在 Java 中,借助注解,我们可以灵活地实现缓存的功能。通过自定义缓存注解,我们不仅能简化缓存操作,还能根据需求灵活地控制缓存策略。本文将介绍如何手写自定义缓存注解,并在 Java 中应用该注解来处理缓存问题。
1. 缓存的基本概念
缓存(Cache)是一种临时存储介质,用于存放经常访问的数据,从而提高数据读取的效率。缓存的常见应用场景包括:
- 数据库查询缓存:缓存数据库查询结果,减少数据库访问频率。
- API 调用缓存:缓存 API 返回的数据,避免重复请求相同的数据。
- 计算结果缓存:缓存计算过程的中间结果,避免重复计算。
缓存的实现方式有很多,常见的有:
- 内存缓存(如 Redis、EhCache 等)
- 本地缓存(如 Guava Cache)
2. 自定义缓存注解的优势
在 Java 开发中,使用注解来管理缓存具有以下优点:
- 简洁性:通过注解,缓存逻辑的实现可以与业务逻辑分离,代码更简洁。
- 灵活性:可以根据不同需求(如缓存失效时间、缓存更新策略等)自定义注解,实现灵活的缓存管理。
- 可维护性:自定义缓存注解可以统一管理缓存逻辑,方便后期的修改与优化。
3. 自定义缓存注解的实现
3.1 创建缓存注解
首先,我们需要定义一个自定义的缓存注解,该注解可以用于方法级别的缓存控制。注解包含以下信息:
value
:缓存的键(可使用表达式生成唯一的缓存键)。expire
:缓存的过期时间(单位:秒)。clear
:是否在每次方法调用后清除缓存。
package com.example.cache;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// 定义一个自定义的缓存注解
@Target(ElementType.METHOD) // 该注解可以作用于方法
@Retention(RetentionPolicy.RUNTIME) // 运行时保留,方便反射读取
public @interface Cacheable {
String value() default ""; // 缓存的键
int expire() default 3600; // 缓存过期时间(默认1小时)
boolean clear() default false; // 是否清除缓存
}
3.2 实现缓存管理器
接下来,我们需要创建一个缓存管理器类,负责根据注解的信息进行缓存操作。为了简单起见,我们这里使用 Map
作为缓存存储。
package com.example.cache;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class CacheManager {
// 使用 ConcurrentHashMap 来存储缓存,保证线程安全
private static final Map<String, CacheItem> cache = new ConcurrentHashMap<>();
// 缓存数据项
static class CacheItem {
Object value;
long timestamp; // 缓存的时间戳
}
// 获取缓存
public static Object getCache(String key) {
CacheItem item = cache.get(key);
if (item != null && System.currentTimeMillis() - item.timestamp < 3600 * 1000) {
return item.value; // 如果缓存未过期,返回缓存数据
}
return null; // 如果缓存已过期或不存在
}
// 设置缓存
public static void setCache(String key, Object value, int expire) {
CacheItem item = new CacheItem();
item.value = value;
item.timestamp = System.currentTimeMillis();
cache.put(key, item); // 将数据放入缓存
}
// 清除缓存
public static void clearCache(String key) {
cache.remove(key); // 删除指定缓存
}
}
3.3 创建缓存切面(Aspect)
为了实现缓存注解的自动处理,我们使用 AOP(面向切面编程)来拦截标注了 @Cacheable
注解的方法,在方法执行前后进行缓存操作。
package com.example.cache;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class CacheAspect {
// 定义切点,匹配所有被 @Cacheable 注解标记的方法
@Pointcut("@annotation(com.example.cache.Cacheable)")
public void cacheableMethods() {}
// 在方法执行之前进行缓存查询
@Around("cacheableMethods() && @annotation(cacheable)")
public Object handleCache(ProceedingJoinPoint joinPoint, Cacheable cacheable) throws Throwable {
String cacheKey = cacheable.value(); // 获取缓存的键
Object cachedValue = CacheManager.getCache(cacheKey); // 尝试从缓存中获取数据
if (cachedValue != null) {
return cachedValue; // 如果缓存命中,直接返回缓存数据
}
// 如果缓存未命中,执行原方法并缓存结果
Object result = joinPoint.proceed();
CacheManager.setCache(cacheKey, result, cacheable.expire()); // 将结果存入缓存
return result;
}
// 在方法执行后进行缓存清除(如果需要)
@After("cacheableMethods() && @annotation(cacheable)")
public void handleCacheClear(Cacheable cacheable) {
if (cacheable.clear()) {
String cacheKey = cacheable.value(); // 获取缓存键
CacheManager.clearCache(cacheKey); // 清除缓存
}
}
}
3.4 配置 Spring AOP
为了让自定义的切面生效,需要在 Spring 配置中启用 AOP 支持。
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@ComponentScan(basePackages = "com.example.cache")
@EnableAspectJAutoProxy // 启用 Spring AOP
public class AppConfig {
}
4. 使用自定义缓存注解
在业务逻辑中,我们可以使用自定义的 @Cacheable
注解来标记需要缓存的方法。
package com.example.service;
import com.example.cache.Cacheable;
import org.springframework.stereotype.Service;
@Service
public class UserService {
// 使用自定义缓存注解
@Cacheable(value = "user_1", expire = 3600, clear = true)
public String getUserInfo(int userId) {
// 模拟从数据库获取用户信息
return "User info for userId: " + userId;
}
}
4.1 业务逻辑
- 上述示例中,
getUserInfo
方法使用了@Cacheable
注解,表示该方法的结果会被缓存,并且缓存的键是user_1
。 - 如果该方法被多次调用,缓存的结果会被重复使用,避免每次都查询数据库。
clear = true
表示方法执行后会清除缓存(可以根据需要灵活配置)。
5. 总结
通过自定义注解和 AOP,Java 开发者可以轻松地实现灵活的缓存管理。使用缓存注解,不仅可以简化代码结构,还能根据不同的业务需求调整缓存策略。自定义缓存注解结合 AOP 的应用,使得缓存的实现变得更加模块化,符合面向切面编程的思想,从而提高了系统的可维护性和扩展性。