Spring AOP 实现 Redis 缓存切面

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
添加 @RedisCache

UserServiceImpl 的方法 selectByPrimaryKey() 添加 @RedisCache,就会被缓存切面环绕。然后生成 Key 先去 Redis 中查询,查询不到再去查询数据库
添加 @RedisCache
查询数据库成功后,会把查询结果保存到Redis中
在这里插入图片描述
之后只要是在失效时间内查询,就直接从Redis中查询结果
直接从Redis中查询

3. 总结

  1. 当方法使用事务,即添加了注解@Transactional时。再添加注解 @RedisCache,缓存切面不会生效。

参考:
SpringBoot集成Redis实现缓存处理(Spring AOP实现)
mall整合Redis实现缓存功能

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值