1. 缓存切面
添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--spring2.0集成redis所需common-pool2-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.8.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.72</version>
</dependency>
修改application.yml
spring:
redis:
# Redis默认情况下有16个分片,这里配置具体使用的分片,默认是0
database: 0
host: localhost
port: 6379
# 连接密码(默认为空)
password:
# 连接超时时间(毫秒)
timeout: 10000ms
lettuce:
pool:
# 连接池最大连接数(使用负值表示没有限制) 默认 8
max-active: 8
# 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1
max-wait: -1
# 连接池中的最大空闲连接 默认 8
max-idle: 8
# 连接池中的最小空闲连接 默认 0
min-idle: 0
logging:
level:
com.shpun: debug
自定义RedisTemplate,使用fastjson进行序列化
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericFastJsonRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(new GenericFastJsonRedisSerializer());
return template;
}
}
定义注解,只有方法使用了该注解才进行缓存切面,默认失效时间为5分钟。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RedisCache {
/**
* 默认失效时间为5分钟
*/
long expire() default 300;
}
定义 Redis 缓存切面
@Aspect
@Component
public class RedisCacheAspect {
private static final Logger logger = LoggerFactory.getLogger(RedisCacheAspect.class);
@Autowired
private RedisService redisService;
@Pointcut("@annotation(com.shpun.aop.RedisCache)")
public void redisCachePointcut() {
}
@Around("redisCachePointcut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
StringBuilder redisKeySb = new StringBuilder("AOP").append("::");
// 类
String className = joinPoint.getTarget().toString().split("@")[0];
redisKeySb.append(className).append("::");
// 方法
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
String methodName = signature.getName();
redisKeySb.append(methodName);
// 参数
Object[] args = joinPoint.getArgs();
if (args != null && args.length > 0) {
for (int i = 0; i < args.length; i++) {
redisKeySb.append(":").append(args[i]);
}
}
String redisKey = redisKeySb.toString();
logger.info("Redis Key:{}", redisKey);
Object result = redisService.get(redisKey);
if (result != null) {
logger.info("从Redis中获取数据:{}", result);
return result;
} else {
try {
result = joinPoint.proceed();
logger.info("从MySQL中获取数据:{}", result);
} catch (Throwable e) {
throw new RuntimeException(e.getMessage(), e);
}
// 获取失效时间
RedisCache redisCache = signature.getMethod().getAnnotation(RedisCache.class);
long expire = redisCache.expire();
redisService.set(redisKey, result, expire);
}
return result;
}
}
Redis 操作类
public interface RedisService {
void set(String key, Object value);
void set(String key, Object value, long expire);
Object get(String key);
void expire(String key, long expire);
void delete(String key);
}
实现 RedisService
@Service("redisService")
public class RedisServiceImpl implements RedisService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Override
public void set(String key, Object value) {
redisTemplate.opsForValue().set(key, value);
}
@Override
public void set(String key, Object value, long expire) {
redisTemplate.opsForValue().set(key, value, expire, TimeUnit.SECONDS);
}
@Override
public Object get(String key) {
return redisTemplate.opsForValue().get(key);
}
@Override
public void expire(String key, long expire) {
redisTemplate.expire(key, expire, TimeUnit.SECONDS);
}
@Override
public void delete(String key) {
redisTemplate.delete(key);
}
}
2. 测试
添加依赖
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
<scope>runtime</scope>
</dependency>
修改application.yml
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:4306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false
username: root
password: root
mybatis:
typeAliasesPackage: com.shpun.model
mapper-locations: classpath:mapper/**.xml
Model,Mapper,Service等省略。
UserServiceImpl 实现 UserService
@Service("userService")
public class UserServiceImpl implements UserService {
private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);
@Autowired
private UserMapper userMapper;
@Override
public User selectByPrimaryKey(Integer userId) {
logger.info("查询数据库...");
User user = userMapper.selectByPrimaryKey(userId);
logger.info("查询结果:{}", user);
return user;
}
}
测试
@SpringBootTest
public class UserServiceTest {
@Autowired
private UserService userService;
@Test
public void get() {
User user = userService.selectByPrimaryKey(1);
System.out.println(user.getName());
}
}
未添加 @RedisCache
UserServiceImpl 的方法 selectByPrimaryKey() 未添加 @RedisCache,就不会被切面环绕
添加 @RedisCache
UserServiceImpl 的方法 selectByPrimaryKey() 添加 @RedisCache,就会被缓存切面环绕。然后生成 Key 先去 Redis 中查询,查询不到再去查询数据库
查询数据库成功后,会把查询结果保存到Redis中
之后只要是在失效时间内查询,就直接从Redis中查询结果
3. 总结
- 当方法使用事务,即添加了注解
@Transactional
时。再添加注解@RedisCache
,缓存切面不会生效。