SpringBoot学习之基于注解的Spring缓存技术

     Spring 从3.1版开始,Spring Framework提供了对现有Spring应用程序透明地添加缓存的支持。与事务 支持类似,缓存抽象允许一致地使用各种缓存解决方案,而对代码的影响最小。从Spring4.1  开始,JSR107提供了注解开发模式,以及自定义选项的支持,缓存技术得到显著的改善。Spring官网对本章介绍链接地址

     Spring使用的缓存抽象也是基于JSR107规范,我们可以看一下Spring与JSR107的比较;官网有详细的介绍官网地址

Spring vs. JSR-107 缓存注解
Spring JSR-107 Remark

@Cacheable

@CacheResult

@CacheResult无论缓存的内容如何,​​都可以缓存特定的异常并强制执行该方法。

@CachePut

@CachePut

当Spring使用方法调用的结果更新缓存时。

@CacheEvict

@CacheRemove

清除缓存,指定清除某一项

@CacheEvict(allEntries=true)

@CacheRemoveAll

清除所有,Spring还是使用@CacheEvict注解,只需将属性allEntries设置为true即可

@CacheConfig

@CacheDefaults

缓存配置

    Spring从3.1开始定义了org.springframework.cache.Cacheorg.springframework.cache.CacheManager接口实现缓存抽象,

    。Cache接口为缓存的组件规范定义,包含缓存的各种操作集合;
    。Cache接口下Spring提供了各种xxxCache的实现;如RedisCache,EhCacheCache ,ConcurrentMapCache等;

     如果我们想要使用spring的缓存,就需要来实现相应的CacheManager(缓存管理器)来管理相应的Cache组件,缓存接口来实现相应的缓存组件。有了缓存组件,我们才能去通过注解,操作数据将数据缓存到相应的地方。当然我们这里Spring已经默认帮我们实现了一套默认的缓存管理器和缓存组件,我们可以直接使用Spring缓存。下面我们通过一个小的案例来看一下,怎么在SpringBoot中使用Spring的缓存技术(基于注解的形式)。

   需要分成哪些步骤:

      首先创建一个SpringBoot项目(源码已经传到github仓库中地址在最后会贴出,如有需要可以见到查看一下);项目整合了Mybatis通过SpringBoot的自动配置,使用Mybatis的注解版,方便演示。

     1.在启动类中开启缓存注解@EnableCaching

@MapperScan(value = "com.cache.mapper")
@SpringBootApplication
//开启缓存注解
@EnableCaching
public class SpringbootCacheApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootCacheApplication.class, args);
    }
}

      接下来我们domain,service,mapper,controller 下面节选部分代码,

domain

public class User {
    private Integer id;
    private String userName;
    private String userAge;

service 和 serviceImpl

public interface UserService {
    User findUserById(Integer id);

    User updateUser(User user);

    void deleteUser(Integer id);

    User findUserByUser(String userName);

}

    ServiceImpl

package com.cache.service;

import com.cache.domain.User;
import com.cache.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.*;
import org.springframework.stereotype.Service;


@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;


    /**
     * @param id
     * @return
     * @Cacheable 注解属性:主要针对方法配置,能够根据方法的请求参数对其结果进行缓存
     * cacheNames/value 使用这两个属性一样效果一样,指定缓存组件名字,将方法的返回结果放在哪个缓存中,数组的方式可以制定多个缓存
     * key 默认是参数的值,也可以自己指定,可以使用spEL表达式指定key的值。缓存在存放数据时,是根据这个key来标注。
     * keyGenerator  key的生成器,可以在自己来实现key的生成策略;指定key的生成器组件id  key/keyGenerator两者二选一
     * cacheManager  指定缓存管理器,或cacheResolver指定获取解析器;
     * condition  指定条件满足才缓存;也可以使用多个条件,用and连接;
     * unless 除非,当unless指定的条件为true时候,不缓存
     * sync 是否异步 (默认是false,在使用异步情况下,unless就不支持了)
     */
    @Cacheable(cacheNames = "user")
    @Override
    public User findUserById(Integer id) {
        User user = userMapper.getUserById(id);
        return user;
    }


    /**
     * @param user
     * @return
     * @CachePut 方法既调用,结果也会保存,修改了某条数据,缓存也会跟着更新(需要KEY保持一致)
     * value 既保存数据时指定的cacheName/value
     * key 既查询时,在进行数据保存时的key,这里取值有两种方式
     * 1. 可以通过传入的对象的属性方式拿到更改对象的key  #user.id
     * 2. 也可以通过返回值的值来进行绑定ker的值 #result.id
     */
    @CachePut(value = "user", key = "#user.id")
    @Override
    public User updateUser(User user) {
        userMapper.updateUser(user);
        return user;
    }


    /**
     * @cacheEvict 清除缓存
     * allEntity 清除所有员工,默认的是false,如果设置为true,会将缓存中的所有数据全部清空
     * beforeInvocation 缓存是否在方法执行之前,默认的是false;
     * false,方法执行之后之后执行;
     * true,方法执行之前执行;(缓存必定被清除,无论方法时候有错误)
     */
    @CacheEvict(value = "user", key = "#id")
    public void deleteUser(Integer id) {
        System.out.println("清空ID为" + id + "的缓存");
    }


    /**
     * @param userName
     * @return Caching 组合包含了Cacheable、CachePut、CacheEvict 可以以数组的形式传入多个值
     *
     * 注解的value值可以在类上,统一使用cacheConfig(cacheName="user")形式替换,
     * 就无需再一个一个的写在具体的注解上
     */
    @Caching(cacheable = {
            @Cacheable(value = "user", key = "#userName")
    }, put = {@CachePut(value = "user", key = "#result.id")}
    )
    public User findUserByUser(String userName) {
        return userMapper.getUserByName(userName);
    }

}

mapper

public interface UserMapper {

    @Select("select * from USER where id=#{id}")
    User getUserById(Integer id);

    @Options(useGeneratedKeys = true, keyProperty = "id")
    @Insert("insert into USER (USER_NAME,USER_AGE) VALUES (#{userName},#{userAge})")
    int insertUser(User user);

    @Update("update USER SET USER_NAME = #{userName}, USER_AGE = #{userAge} where ID = #{id}")
    int updateUser(User user);

    @Delete("delete from user where ID = #{id}")
    int deleteUser(Integer id);

    @Select("select * from USER where USER_NAME=#{userName}")
    User getUserByName(String userName);
}

controller

@RestController
public class UserController {
    @Autowired
    UserMapper userMapper;
    @Autowired
    UserService userService;

    @GetMapping("/findUserById/{id}")
    public User userfinById(@PathVariable("id") Integer id) {
        User user = userService.findUserById(id);
        return user;
    }

    @GetMapping("/insertUser")
    public User save(User user) {
        int total = userMapper.insertUser(user);
        return user;
    }

    @GetMapping("/updateUser")
    public User update(User user) {
        User user1 = userService.updateUser(user);
        return user1;
    }

    @GetMapping("/deleteUser")
    public String deleteUser(Integer id) {
        userService.deleteUser(id);
        return "删除成功";
    }

    @GetMapping("/findUserByName")
    public User findUserByName(String userName) {
        User user = userService.findUserByUser(userName);
        return user;
    }

}

  注解@Cacheable、@CachePut、@CacheEvict、@Caching、@CacheConfig都在serviceImpl方法上标注,每个注解的作用,以及注解各个属性的设置;

这里补充一个keyGenerator,生成key的内容,首先key,最简单的使用就是无需指定默认使我们传入参数的值,也可以通过SpEL语法来制定相应的key。可以取方法名来用作key的值,也可以是方法的name;下面还有其他属性详细的使用以表格的形式展示给大家;

Cache SpEL available metadata

名字

位置 描述 示例

methodName

root object 当前被调用的方法名 #root.methodName

method

root object 当前被调用的方法 #root.method.name

target

root object 当前被调用的目标对象 #root.target

targetClass

root object 当前被调用的目标对象类 #root.targetClass

args

root object 当前被调用的方法的参数列表 #root.args[0]

caches

root object 当前方法调用使用的缓存列表(如@Cacheable(value={"cache1", #root.caches[0].name "cache2"})),则有两个cache

argument name

evaluation context 方法参数的名字. 可以直接 #参数名 ,也可以使用 #p0或#a0 的 #iban 、 #a0 、 #p0 形式,0代表参数的索引;

result

evaluation context 方法执行后的返回值(仅当方法执行之后的判断有效,如 #result‘unless’,’cache put’的表达式 ’cache evict’的表达式

beforeInvocation=false)

我们还可以自己指定keyGenerator属性,属性值是我们自定义的key的生成策略;通过向容器中添加自定义配置就可以使用相应的keyGenerator;这里简单写了一个;

@Configuration
public class MyCacheConfig {

    /**
     * 默认组件id就是方法名,可以自己自定义
     * @return
     */
    @Bean("myCacheKeyGenerator")
    public KeyGenerator keyGenerator(){
        return new KeyGenerator() {
            @Override
            public Object generate(Object o, Method method, Object... objects) {
                //返回方法名+参数
                return method.getName() + Arrays.asList(objects).toString();
            }
        };
    }
}

底层是如何实现的呢?我们可以通过后面执行过程来解析为什么我们配置了自己的KeyGenerator的生成策略,就会起作用;

自动配置是通过CacheAutoConfiguration自动配置类来实现;可以通过查看源码的方式来看默认的是如何获取的。

后面的整合Redis会在后面介绍;本次的代码 githu仓库地址

 

你可能感兴趣的:(springBoot)