SpringBoot 整合 Spring Cache

依赖

<!--Spring Cache-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>

配置

在 启动类 加上注解 @EnableCaching

@SpringBootApplication
@MapperScan("com.ratel.hydra.**.mapper")
@EnableConfigurationProperties
@EnableCaching //开启 Spring Cache
public class HydraApplication {
    public static void main(String[] args) {
        MDC.put("TRACE_ID","-1");
        SpringApplication.run(HydraApplication.class, args);
    }
}

使用

接口
@CacheConfig(cacheNames = "role") //缓存名字
public interface RoleService {

    @CacheEvict(key = "#p0") //表示用参数列表 第一参数作为缓存的 key
    void delById(Long id);

    @Cacheable(key = "#p0.query") //表示用参数列表 第一参数的query对象作为缓存的 key
    IPage<Role> page(PageQuery<Role> query);

    @Cacheable(key = "#p0") //表示用参数列表 第一参数作为缓存的 key
    Role getById(Long id);
    
    @CachePut(key = "#p0") //表示用参数列表 第一参数作为缓存的 key
    void addOrUpdate(Role role);
}
实现类
@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements RoleService {

    @Autowired
    private RoleStruct roleStruct;

    @Override
    public Role getById(Long id) {
        return super.getById(id);
    }

    @Override
    public void delById(Long id) {
        removeById(id);
    }

    @Override
    public IPage<Role> page(PageQuery query) {
        LambdaQueryWrapper<Role> queryWrapper = new LambdaQueryWrapper<Role>().orderByDesc(Role::getModifyTime);
        if (query.getQuery() != null){
            Role role = (Role)query.getQuery();
            if (StringUtils.isNoneBlank(role.getRoleName())) {
                queryWrapper.likeRight(Role::getRoleName,role.getRoleName());
            }
            if (StringUtils.isNoneBlank(role.getRoleCode())){
                queryWrapper.eq(Role::getRoleCode,role.getRoleCode());
            }
            if (StringUtils.isNoneBlank(role.getDescription())){
                queryWrapper.like(Role::getDescription,role.getDescription());
            }
        }
        return page(query.getPage(), queryWrapper);
    }
  
    @Override
    public void addOrUpdate(Role role) {
        saveOrUpdate(role);
    }
}
Controller

非必须,仅用作于 测试

@Slf4j
@RestController
@RequestMapping("role")
@Api(tags = "角色")
public class RoleController extends BaseController {
    @Autowired
    private RoleService roleService;

    @GetMapping("get/{id}")
    @OperatingInfo(operation = "获取角色")
    public WebResult getById(@PathVariable("id") Long id) {
        return WebResultFactory.ok(roleService.getById(id));
    }

    @PostMapping("del/{id}")
    @OperatingInfo(operation = "删除角色")
    public WebResult delById(@PathVariable("id") Long id) {
        roleService.delById(id);
        return WebResultFactory.ok("操作成功");
    }
    
    @GetMapping("page")
    @OperatingInfo(operation = "分页获取角色")
    public WebResult page(PageQuery<Role> query, Role po) {
        return WebResultFactory.ok(roleService.page(query.setQuery(po)));
    }
}

注解描述

  1. @CacheConfig
    主要用于配置当前类中会用到的一些共用的缓存配置。@CacheConfig(cacheNames = “role”):表示当前类中使用 @CachePut 修饰的方法的返回值,会存储至名字叫 role 的缓存对象中。当然也可以不使用该注解,直接通过@Cacheable`自己配置缓存集的名字来定义;
  2. @Cacheable
    配置了该注解的page方法的返回值将被加入缓存。同时在查询时,会先从缓存中获取,若不存在才再发起对数据库的访问。该注解主要有下面几个参数:
  • valuecacheNames:两个等同的参数(cacheNames为Spring 4新增,作为value的别名),用于指定缓存存储的集合名。由于Spring 4中新增了@CacheConfig,因此在Spring 3中原本必须有的value属性,也成为非必需项了;
  • key:缓存对象存储在Map集合中的key值,非必需,缺省按照函数的所有参数组合作为key值,若自己配置需使用SpEL表达式,比如:@Cacheable(key = "#p0"):使用函数第一个参数作为缓存的key值,更多关于SpEL表达式的详细内容可参考https://docs.spring.io/spring/docs/current/spring-framework-reference/integration.html#cache;
  • condition:缓存对象的条件,非必需,也需使用SpEL表达式,只有满足表达式条件的内容才会被缓存,比如:@Cacheable(key = "#p0", condition = "#p0.length() < 3"),表示只有当第一个参数的长度小于3的时候才会被缓存;
  • unless:另外一个缓存条件参数,非必需,需使用SpEL表达式。它不同于condition参数的地方在于它的判断时机,该条件是在函数被调用之后才做判断的,所以它可以通过对result进行判断;
  • keyGenerator:用于指定key生成器,非必需。若需要指定一个自定义的key生成器,我们需要去实现org.springframework.cache.interceptor.KeyGenerator接口,并使用该参数来指定;
  • cacheManager:用于指定使用哪个缓存管理器,非必需。只有当有多个时才需要使用;
  • cacheResolver:用于指定使用那个缓存解析器,非必需。需通过org.springframework.cache.interceptor.CacheResolver接口来实现自己的缓存解析器,并用该参数指定;
  1. @CachePut
    作用在方法上,能够根据参数定义条件来进行缓存,其缓存的是方法的返回值,它与@Cacheable不同的是,它每次都会真实调用真实方法,所以主要用于数据新增和修改操作上。它的参数与@Cacheable类似,具体功能可参考上面对@Cacheable参数的解析;
  2. @CacheEvict
    作用在方法上,通常用在删除方法上,用来从缓存中移除相应数据。除了同@Cacheable一样的参数之外,它还有下面两个参数:
  • allEntries:非必需,默认为false。当为true时,会移除所有数据;
  • beforeInvocation:非必需,默认为false,会在调用方法之后移除数据。当为true时,会在调用方法之前移除数据。

使用 Redis 作为缓存

依赖

<!-- spring-boot redis -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

配置

application.yml
spring:
  ################# MySQL 配置 #################
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/ratel_singleton?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2b8
    username: root
    password: 'Root@0804'
    dbcp2:
      min-idle: 5 #数据库连接池最小维持连接数
      initial-size: 5 #初始化连接数
      max-total: 5 #最大连接数
      max-wait-millis: 200 #等待获取连接的最大超时时间



  ################# Redis 配置 #################
  redis:
    # Redis数据库索引(默认为0)
    database: 0
    # Redis服务器地址
    host: 127.0.0.1
    # Redis服务器连接端口
    port: 6379
    password: redis@0804
    pool:
      # 连接池最大连接数(使用负值表示没有限制)
      max-active: 8
      # 连接池最大阻塞等待时间(使用负值表示没有限制)
      max-wait: -1
      # 连接池中的最大空闲连接
      max-idle: 8
      # 连接池中的最小空闲连接
      min-idle: 0
    # 连接超时时间(毫秒)
    timeout: 0
RedisConfiguration.java
/**
 * Redis 配置类
 * @author ratel
 * @date 2020/7/14
 */
@Configuration
public class RedisConfiguration extends CachingConfigurerSupport {

    /**
     * @Description 自定义缓存 key 生成策略
     *              这里仅用作测试,实际情况不建议使用超长 Key
     * @Author      ratel
     * @Date        2020/7/14
     * @return      org.springframework.cache.interceptor.KeyGenerator
     **/
    @Bean
    public KeyGenerator initKeyGenerator(){
        return new KeyGenerator() {
            @Override
            public Object generate(Object o, Method method, Object... objects) {
                StringBuilder sb = new StringBuilder();
                sb.append(o.getClass().getSimpleName());
                sb.append(method.getName());
                for (Object object : objects) {
                    sb.append(object.toString());
                }
                return sb.toString();
            }
        };
    }

    /**
     * @Description 缓存管理器
     * @Author      ratel
     * @Date        2020/7/14
     * @param       redisConnectionFactory
     * @return      org.springframework.cache.CacheManager
     **/
    @Bean
    public CacheManager initRedisCacheManager(RedisConnectionFactory redisConnectionFactory){
        RedisCacheManager cacheManager = RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(redisConnectionFactory).build();
        return cacheManager;
    }

    /**
     * @Description Redis 模板类 功能类似 JDBC template 可以直接操作Redis
     * @Author      ratel
     * @Date        2020/7/14
     * @param       factory
     * @return      org.springframework.data.redis.core.RedisTemplate<java.lang.String,java.lang.String>
     **/
    @Bean
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
        StringRedisTemplate template = new StringRedisTemplate(factory);
        setSerializer(template);// 设置序列化工具
        template.afterPropertiesSet();
        return template;
    }
    
    /**
     * @Description 配置序列化策略
     * @Author      ratel
     * @Date        2020/7/14
     * @param       template
     * @return      void
     **/
    private void setSerializer(StringRedisTemplate template) {
        @SuppressWarnings({ "rawtypes", "unchecked" })
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        template.setValueSerializer(jackson2JsonRedisSerializer);
    }
}

注意:

如果 缓存注解 作用在 service上会失效,失效原因是因为spring的事务切面会覆盖 cache 切面,(事务切面 order = 2147483647, cache 切面 order = 2147483647,两者order 一致时,会按 类名排序,所以会先添加 cache切面,再添加 事务切面),解决方案 修改 事务切面的优先级 @EnableTransactionManagement(order = 100)
ps:order 小于 cache就行

使用 Ehcache 作为缓存

todo

依赖

todo

配置

todo