美文网首页
Spring缓存支持

Spring缓存支持

作者: LAMYMAY | 来源:发表于2018-12-07 13:12 被阅读0次

    ------------------------------------------------------------------------------

    开门见代码:

    [git地址走起](https://github.com/lamymay/ray.git)

    内存的速度远远大于硬盘的速度,当我们需要重复获取相同的数据的时候,一次又一次的请求数据库或远程服务,导致大量时间都消耗在数据库查询或远程方法调用上面,性能下降,这时候就需要使用到缓存技术了。

    本文介绍SpringBoot 如何使用redis做缓存,如何对redis缓存进行定制化配置(如key的有效期)以及初始化redis做缓存。

    使用具体的代码介绍相关注解及其属性的用法。

    @Cacheable,

    @CacheEvict,

    @CachePut,

    @CacheConfig

    子项目 cache 即是 Spring缓存的演示项目,相关sql在cache子项目的根目录,

    Spring定义了 org.springframework.cache.CacheManager 和 org.springframework.cache.Cache 接口来统一不同缓存技术。其中CacheManager是Spring提供的各种缓存技术抽象接口,内部使用Cache接口进行缓存的增删改查操作,我们一般不会直接和Cache打交道。

    针对不同的缓存技术,Spring有不同的CacheManager实现类,定义如下表:

    CacheManager描述

    SimpleCacheManager使用简单的Collection存储缓存数据,用来做测试用

    ConcurrentMapCacheManager使用ConcurrentMap存储缓存数据

    EhCacheCacheManager使用EhCache作为缓存技术

    GuavaCacheManager使用Google Guava的GuavaCache作为缓存技术

    JCacheCacheManager使用JCache(JSR-107)标准的实现作为缓存技术,比如Apache Commons JCS

    RedisCacheManager使用Redis作为缓存技术

    ----------------------------------------------------------------------------

    1. 在我们使用任意一个实现的CacheManager的时候,需要注册实现Bean:

    /**

    * EhCache的配置

    */

    @Bean

    public EhCacheCacheManagercacheManager(CacheManager cacheManager) {

        return new EhCacheCacheManager(cacheManager);

    }

    当然,各种缓存技术都有很多其他配置,但是配置cacheManager是必不可少的。

    声明式缓存注解

    Spring提供4个注解来声明缓存规则,如下表所示:

    注解   说明

    @Cacheable方法执行前先看缓存中是否有数据,如果有直接返回。如果没有就调用方法,并将方法返回值放入缓存

    @CachePut无论怎样都会执行方法,并将方法返回值放入缓存

    @CacheEvict将数据从缓存中删除

    @Caching可通过此注解组合多个注解策略在一个方法上面

    @Cacheable 、@CachePut 、@CacheEvict都有value属性,指定要使用的缓存名称,而key属性指定缓存中存储的键。

    2. 集成Redis缓存

    接下来将讲解如何集成redis来实现缓存。

    2.1 安装redis

    安装和配置redis服务器网上很多教程,这里就不多讲了。在linux服务器上面安装一个redis,启动后端口号为默认的6379。

    2.2 添加maven依赖

        org.springframework.boot

        spring-boot-starter-data-redis

    2.3 配置application.yml

    指定缓存的类型

    配置redis的服务器信息

    spring:

      profiles: dev

    cache:

    type: REDIS

      redis:

        host: 123.207.66.156

        port: 6379

    timeout: 0

    database: 0

        pool:

    max-active: 100

    max-wait: -1

    max-idle: 8

    min-idle: 0

    3. 缓存配置类

    重新配置 RedisCacheManager ,使用新的自定义配置值:

    @Configuration

    @EnableCaching

    public class RedisCacheConfig {

        /**

        * 重新配置RedisCacheManager

        */

        @Autowired

        public voidconfigRedisCacheManger(RedisCacheManager rd) {

            rd.setDefaultExpiration(100L);

        }

    }

    keyGenerator

    一般来讲我们使用key属性就可以满足大部分要求,但是如果你还想更好的自定义key,可以实现keyGenerator。

    这个属性为定义key生成的类,和key属性不能同时存在。

    在 RedisCacheConfig 配置类中添加我自定义的KeyGenerator:

    /**

    * 自定义缓存key的生成类实现

    */

    @Bean(name = "myKeyGenerator")

    public KeyGeneratormyKeyGenerator() {

        return new KeyGenerator() {

            @Override

            public Objectgenerate(Object o, Method method, Object... params) {

                logger.info("自定义缓存,使用第一参数作为缓存key,params = " + Arrays.toString(params));

                // 仅仅用于测试,实际不可能这么写

                return params[0];

            }

        };

    }

    经过以上配置后,redis缓存管理对象已经生成。下面简单介绍如何使用。

    4. 使用

    在service中定义增删改的几个常见方法,通过注解实现缓存:

    @Service

    @CacheConfig(cacheNames="users")

    public class UserService {

        private Logger logger = LoggerFactory.getLogger(this.getClass());

        @Resource

        private UserMapper userMapper;

        /**

        * cacheNames 设置缓存的值

        * key:指定缓存的key,这是指参数id值。 key可以使用spEl表达式

    *@paramid

    *@return

        */

        @Cacheable(cacheNames="user1", key="#id")

        public UsergetById(int id) {

            logger.info("获取用户start...");

            return userMapper.selectById(id);

        }

        /***

        * 如果设置sync=true,

        * 如果缓存中没有数据,多个线程同时访问这个方法,则只有一个方法会执行到方法,其它方法需要等待

        * 如果缓存中已经有数据,则多个线程可以同时从缓存中获取数据

    *@paramid

    *@return

        */

        @Cacheable(cacheNames="user1", key="#id", sync = true)

        public UsergetById2(int id) {

            logger.info("获取用户start...");

            return userMapper.selectById(id);

        }

        /**

        * 以上我们使用默认的keyGenerator,对应spring的SimpleKeyGenerator

        * 如果你的使用很复杂,我们也可以自定义myKeyGenerator的生成key

        * <p>

        * key和keyGenerator是互斥,如果同时制定会出异常

        * The key and keyGenerator parameters are mutually exclusive and an operation specifying both will result in an exception.

        *

    *@paramid

    *@return

        */

        @Cacheable(cacheNames = "user1", keyGenerator = "myKeyGenerator")

        public UserqueryUserById(int id) {

            logger.info("queryUserById,id={}", id);

            return userMapper.selectById(id);

        }

        /**

        * 每次执行都会执行方法,同时使用新的返回值的替换缓存中的值

    *@paramuser

        */

        @CachePut(cacheNames="user1", key="#user.id")

        public voidcreateUser(User user) {

            logger.info("创建用户start...");

            userMapper.insert(user);

        }

        /**

        * 每次执行都会执行方法,同时使用新的返回值的替换缓存中的值

    *@paramuser

        */

        @CachePut(cacheNames="user1", key="#user.id")

        public voidupdateUser(User user) {

            logger.info("更新用户start...");

            userMapper.updateById(user);

        }

        /**

        * 对符合key条件的记录从缓存中user1移除

        */

        @CacheEvict(cacheNames="user1", key="#id")

        public voiddeleteById(int id) {

            logger.info("删除用户start...");

            userMapper.deleteById(id);

        }

        /**

        * allEntries = true: 清空user1里的所有缓存

        */

        @CacheEvict(cacheNames="user1", allEntries=true)

        public voidclearUser1All(){

            logger.info("clearAll");

        }

    }

    注意可以在类上面通过 @CacheConfig 配置全局缓存名称,方法上面如果也配置了就会覆盖。

    然后写个测试类:

    @RunWith(SpringRunner.class)

    @SpringBootTest(classes = Application.class)

    public class UserServiceTest {

        @Autowired

        private UserService userService;

        @Test

        public voidtestCache() {

            int id = new Random().nextInt(100);

            User user = new User(id, "admin", "admin");

            userService.createUser(user);

            User user1 = userService.getById(id); // 第1次访问

            assertEquals(user1.getPassword(), "admin");

            User user2 = userService.getById(id); // 第2次访问

            assertEquals(user2.getPassword(), "admin");

            User user3 = userService.queryUserById(id); // 第3次访问,使用自定义的KeyGenerator

            assertEquals(user3.getPassword(), "admin");

            user.setPassword("123456");

            userService.updateUser(user);

            User user4 = userService.getById(id); // 第4次访问

            assertEquals(user4.getPassword(), "123456");

            userService.deleteById(id);

            assertNull(userService.getById(id));

        }

    }

    下面是测试的打印日志一部分:

    Started UserServiceTest in 12.919 seconds (JVM runni

    创建用户start...

    ==>  Preparing:INSERTINTOt_user(id, username, `

    ==> Parameters: 14(Integer), admin(String), admin(St

    <==    Updates: 1

    获取用户start...

    ==>  Preparing: SELECT id AS id,username,`password`

    ==> Parameters: 14(Integer)

    <==      Total: 1

    自定义缓存,使用第一参数作为缓存key,params = [14]

    更新用户start...

    ==>  Preparing: UPDATE t_user SET username=?, `passw

    ==> Parameters:admin(String), 123456(String), 14(In

    <==    Updates: 1

    获取用户start...

    ==>  Preparing:SELECTidASid,username,`password`

    ==> Parameters: 14(Integer)

    <==      Total: 1

    删除用户start...

    ==>  Preparing:DELETEFROMt_userWHEREid=?

    ==> Parameters: 14(Integer)

    <==    Updates: 1

    获取用户start...

    ==>  Preparing:SELECTidASid,username,`password`

    ==> Parameters: 14(Integer)

    <==      Total: 0

    可以看到,第2次、第3次获取的时候并没有执行方法,说明缓存生效了。后面更新会同时更新缓存,取出来的也是更新后的数据。

    切换缓存技术

    得益于SpringBoot的自动配置机制,切换缓存技术除了替换相关maven依赖包和配置Bean外,使用方式和实例中一样,不需要修改业务代码。如果你要切换到其他缓存技术非常简单。

    EhCache

    当我们需要使用EhCache作为缓存技术的时候,只需要在pom.xml中添加EhCache的依赖:

        net.sf.ehcache

        ehcahe

    EhCache的配置文件ehcache.xml只需要放到类路径下面,SpringBoot会自动扫描,例如:

    <?xml version="1.0" encoding="UTF-8"?>

            xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"

            updateCheck="false" monitoring="autodetect"

            dynamicConfig="true">

                maxElementsInMemory="50000"

                eternal="false"

                timeToIdleSeconds="3600"

                timeToLiveSeconds="3600"

                overflowToDisk="true"

                diskPersistent="false"

                diskExpiryThreadIntervalSeconds="120"

        />

              maxEntriesLocalHeap="2000"

              eternal="false"

              timeToIdleSeconds="3600"

              timeToLiveSeconds="3600"

              overflowToDisk="false"

              statistics="true">

    SpringBoot会为我们自动配置 EhCacheCacheManager 这个Bean,不过你也可以自己定义。

    Guava

    当我们需要Guava作为缓存技术的时候,只需要在pom.xml中增加Guava的依赖即可:

        com.google.guava

        guava

        18.0

    SpringBoot会为我们自动配置 GuavaCacheManager 这个Bean。

    Redis

    最后还提一点,本篇采用Redis作为缓存技术,添加了依赖:

        org.springframework.boot

        spring-boot-starter-data-redis

    SpringBoot会为我们自动配置 RedisCacheManager 这个Bean,同时还会配置 RedisTemplate 这个Bean。后面这个Bean就是下一篇要讲解的操作Redis数据库用,这个就比单纯注解缓存强大和灵活的多了。

    相关文章

      网友评论

          本文标题:Spring缓存支持

          本文链接:https://www.haomeiwen.com/subject/zqrthqtx.html