Spring学习笔记[8]之Cache[缓存]

缓存概述

缓存可以定义为一种存储机制,它将数据保存在某个地方,并以一种更快的方式提供服务。

比如:在从数据库查询用户的时候,首次被查询的用户将信息保存在缓存里,在用户再次被查询的时候可以直接从缓存获取信息,更加快速。

缓存基本概念

缓存命中率

即从缓存中读取的次数与总读取次数的比率

命中率=从缓存中读取的次数/(总读取次数[从缓存读取的次数+从慢速设备读取的次数])

过期策略

  • FIFO:先进先出策略,即先放入缓存的数据最先被移除
  • LRU:未久使用策略,即使用时间距离现在最久的那个数据被移除
  • LFU:最近最少使用策略,即一定时间段内使用次数最少的那个数据被移除

Spring Cache

优点

  • 支持开箱即用,并提供基本的Cache抽象,方便切换各种底层Cache
  • 类似于Spring提供的事务管理,通过Cache注解即可实现缓存逻辑透明化
  • 当事务回滚,缓存也会自动回滚
  • 支持比较复杂的缓存逻辑

缺点

  • Spring Cache 并不针对多线程的应用环境进行专门处理
  • Spring Cache抽象的操作没有锁的概念,当多线程并发操作同一个缓存项时候,将可能得到过期的数据

自定义缓存实现

User实体类

public class User implements Serializable {
    private String userId;
    private String userName;
    private int age;
    ...get&set...
}

缓存管理器

public class CacheManager<T> {
    private Map<String,T> cache=new ConcurrentHashMap<String,T>();
    public T getValue(Object key){
        return cache.get(key);
    }
    public void addOrUpdateCache(String key,T value){
        cache.put(key,value);
    }
    //根据key来删除缓存中的一条记录
    public void evictCache(String key){
        if(cache.containsKey(key))
            cache.remove(key);
    }
    //清空缓存中的记录
    public void evictCache(){
        cache.clear();
    }
}

用户查询服务类

public class UserService {
    private CacheManager<User> cacheManager;
    public UserService(){
        //构造一个缓存管理器
        cacheManager=new CacheManager<User>();
    }
    public User getUserById(String userId){
        //首先查询缓存
        User result=cacheManager.getValue(userId);
        if(result!=null){
            System.out.println("get from cache...."+userId);
            //如果在缓存中,直接返回缓存的结果
            return result;
        }
        //否则到数据库中查询
        result=getFromDB(userId);
        if(result!=null){
            //将查询结果更新到缓存中
            cacheManager.addOrUpdateCache(userId,result);
        }
        return result;
    }
    public void reload(){
        cacheManager.evictCache();
    }
    private User getFromDB(String userId){
        System.out.println("real querying db..."+userId);
        return new User(userId);
    }
}

测试

public class UserMain {
    public static void main(String[] args) {
        UserService userService=new UserService();
        //开始查询账号
        //第一次从数据库查询
        userService.getUserById("001001");
        //第二次直接从缓存返回
        userService.getUserById("001001");
        //缓存重置
        userService.reload();
        System.out.println("after reload...");
        //再次查询
        userService.getUserById("001001");
        userService.getUserById("001001");
    }
}

Spring Cache 改进

Spring已经提供了默认的缓存管理器,所以只需要改造 UserServiceUserMain

用户查询服务类

@Service(value = "userServiceBean")
public class UserService {
    //使用一个名为 users 的缓存
    @Cacheable(cacheNames = "users")
    public User getUserById(String userId){
        //方法内部实现不考虑缓存逻辑,直接实现业务
        System.out.println("real query user."+userId);
        return getFromDB(userId);
    }
    private User getFromDB(String userId){
        System.out.println("real querying db..."+userId);
        return new User(userId);
    }
}

XML配置文件

    <cache:annotation-driven/>
    <bean id="userServiceBean" class="main.Demo02.Service.UserService"/>
    <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
        <property name="caches">
            <set>
                <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="default"/>
                <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="users"/>
            </set>
        </property>
    </bean>

测试

public class UserMain {
    public static void main(String[] args) {
        ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService=(UserService) context.getBean("userServiceBean");
        //第一次查询
        System.out.println("first query...");
        userService.getUserById("somebody");
        //第二次查询
        System.out.println("second query...");
        userService.getUserById("somebody");
    }
}

缓存注解

@CaCheable

它指定了被注解方法的返回值是可被缓存的,其工作原理是Spring首先在缓存中查找数据,如果没有则执行方法并缓存结果,然后返回数据。

@Cacheable(cacheNames = "users")

还可以以列表的形式提供多个缓存

@Cacheable(cacheNames={"cache1","cache2"})
  • 键生成器

    缓存的本质就是键/值对集合。Spring 4.0 默认提供了SimpleKeyGenerator生成器,其生成规则:

    • 如果没有方法入参,则使用SimpleKey.EMPTY作为key
    • 如果只有一个入参,则使用该入参作为Key
    • 如果多个入参,则返回包含所有入参的一个SimpleKay

    源码:

    public static Object generatorKey(Object... params){
        if(params.length==0)
            return SimpleKay.EMPTY;
        if(params.length==1){
            Object param=params[0];
            if(param!=null && param.getClass().isArray())
                return param;
        }
        return new SimpleKey(params);
    }
    
    
  • 带条件的缓存

    condition属性使用了SpEL表达式动态评估方法入参是否满足缓存条件

    @Cacheable(cacheNames="users",condition="#user.age<35")
    

其他

  • @CachePut 同 @Cacheable
  • @CacheEvict 是@Cacheable注解的反向操作
  • @Caching 组注解,可以为一个方法定义提供基于@Cacheable、@CacheEvict或者@CachePu注解的数组

基于XML的Cache声明

<!-- 定义需要使用缓存的类 -->
<bean id="userService" class="main.Demo02.Service.UserService"/>
<!-- 缓存定义 -->
<cache:advice id="cacheAdvice" cache-manager="cacheManager">
    <cache:caching cache"users">
        <cache:cacheable method="findUser" key="#userId"/>
        <cache:cache-evict method="loadUsers" all-entries="true"/>
    </cache:caching>
</cache:advice>
<aop:config>
    <aop:advisor addvice-ref="cacheAdvice" 
                 pointcut="execution(*main.Demo02.Service.UserService.*(..))"/>
</aop:config>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值