ehcache-3.x版本实战

什么是缓存呢?我们先来看一个例子

实现一个斐波那契数列--递归

 public static int fbnq(int n){
        if(n==1 || n ==2){
            return 1;
        }else{
            return fbnq(n-2)+fbnq(n-1);
        }
    }

如果我们使用debug调试进入就会发现,递归的方式需要反复计算一些值,比如fbnq(3)重复执行。每次都要重新计算,这样效率很低,于是我们想到何不把静态结果保存起来,下次用到直接取就好了,改进代码如下:

static Map<Integer, Integer> map = new HashMap<>();//定义map存放每次计算的值 键是n 值是fbnQ(n)
public static int fbnq(int n) {
        if(n==1 || n==2) {
            return 1;
        } 
        Integer value = map.get(n);
        if(value == null) {
            value = fbnq(n - 1) + fbnq(n - 2);
            map.put(n, value);
        }
        return value;
    }

在使用debug调试,就会发现每值只需要计算一次,这样效率明显提高了,这段代码中map集合充当的就是一个缓存的角色。它将每次计算的值存下来,下一次如果需要就用重新计算,只需要从 map缓存中根据键 n 取值就好了。

什么是LRU,如何简单实现缓存?

LRU:最近最少使用,即最少使用的会被从缓存中移除。

举个例子:比如依次往缓存中存入键为1 2 3 4 5,那么如果此时内存不够,优先移除的元素就是1,因为他距离使用最远。

但如果在插入第6个元素之前执行过类似于查找 ---> get(1) 这类操作,也就是说1这个键最近被使用了,那么如果内存不够,需要移除元素,此时队列为 2 3 4 5 1,很显然2应该被移除。这个类似于队列,从左到右,先进先出。

上面的HashMap作为缓存并不是最好的实现,缓存是存在内存中的,所以说不能无限使用,必须定义一个限制,当达到什么条件,必须清除一些缓存。这个时候采用LinkedHashMap比较合适。

LinkedHashMap中有一个构造方法:

public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder)

第一个参数是初始化容量,第二个参数是负载因子,最后一个参数是控制是否开启元数放入集合的顺序,默认是false,也就是

accessOrder=false //表示按顺序排列
accessOrder=true //即如果遇到get(key)方法则,调整元素在集合中的顺序, 最新访问的排在最后

 简单实现代码如下:

LinkedHashMap<String, String> map = new LinkedHashMap(16, 0.75f, true){

    @Override
    protected boolean removeEldestEntry(Map.Entry eldest) {
        // 希望集合中放5个元素, 超过5个要移除最远使用的元素
        if(this.size() <= 5) {
            return false;
        } else {
            return true;
        }
    }
};

LinkedHashMap的 removeEldestEntry() 方法可以保证满足一定条件的时候返回true,触发remove集合元素

ehcache缓存3.x

ehcache是本地缓存(类似于单机模式),如果是分布式缓存一般采用redis

添加依赖

<!-- cache 的实现 -->
<dependency>
    <groupId>org.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>3.6.3</version>
</dependency>
<!-- cache 的接口 -->
<dependency>
    <groupId>javax.cache</groupId>
    <artifactId>cache-api</artifactId>
</dependency>

配置缓存文件

<?xml version="1.0" encoding="UTF-8"?>
<config
        xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
        xmlns='http://www.ehcache.org/v3'
        xmlns:jsr107='http://www.ehcache.org/v3/jsr107'
        xsi:schemaLocation="
        http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.3.xsd
        http://www.ehcache.org/v3/jsr107 http://www.ehcache.org/schema/ehcache-107-ext-3.3.xsd">

    <cache alias="cache1">//文件中可以定义多个缓存,所以需要用alias来区分
        <!-- key-type: key的类型  value-type: 值的类型 -->
        <key-type>java.lang.String</key-type>
        <value-type>java.lang.String</value-type>

       <!-- 用来控制元素的过期时间 -->
        <expiry>
            <tti unit="seconds">60</tti>
            <ttl unit="seconds">60</ttl>
        </expiry>
        <!-- 元素放于什么位置 -->
        <resources>
            <heap unit="entries">200</heap> 
            <offheap></offheap> 
            <disk></disk>  
        </resources>
    </cache>
</config>

注意<expiry>必须在<resource>标签之前定义。

// 1. 类对象.getResource() 获取类路径上的一个资源文件(url对象)
URL url = GunsApplication.class.getResource("/ehcache.xml");

// 2. 准备配置对象
XmlConfiguration config = new XmlConfiguration(url);

// 3. 创建一个新的缓存管理器类
CacheManager cacheManager = CacheManagerBuilder.newCacheManager(config);

// 4. 初始化缓存管理器
cacheManager.init();//一定要初始化,否则会报错。

// 5. 获取缓存对象
Cache<String, String> cache = cacheManager.getCache("cache1", String.class, String.class);

// 6. 存储键值
cache.put("xi,an", "西安");

// 7. 根据键获取值
System.out.println(cache.get("xi,an"));

// 8. 删除键
cache.remove("xi,an");
System.out.println(cache.get("xi,an"));

// 9. 清空缓存
cache.clear();

SpringBoot整合ehcache

application.yml配置:

spring:
  cache:
    jcache:
      config: classpath:ehcache.xml

记得加入classpath:表示ehcache.xml在根路径下

@EnableCaching  

将@EnableCaching定义在SpringBoot启动类上,相当于启用缓存功能,内部就会创建 CacheManager(注意是spring框架的), 并交给spring容器管理。

在需要开启缓存的方法上方加上:@Cacheable(cacheNames="缓存名(在配置文件中定义的)",key = 与配置文件中的value相对(类型) )
注意key=的写法一般写字符串类型 如: 

key = "'dept_' + #deptId" (springEL表达式)

除此之外:@CacheEvict ,写在需要删除缓存的方法上,用来删除某个key value,或者清空整个缓存, 用在执行了数据的增删改时,这三种情况下,都应该让缓存失效。

缓存执行流程:

 1) 先访问的是设置缓存类的代理对象 (由于cglib 生成该类的子类对象作为代理对象)。
 2) 代理对象重写了目标方法, 在该方法内, 通过缓存管理器(cache)找对应缓存名称的缓存。
 3) Cache.get(key)去获取value, 第一次访问value为空, 执行的是原本的方法。
 4) 该类的方法返回结果作为value放入缓存中。
 5) 第二次访问的时候,先访问的是该类的代理对象。
 6) 代理对象重写了目标方法,方法内部,通过缓存管理器(cacheManager)找到对应缓存。
 7) Cache.get(key)去获取对应value, 返回value不为空,直接返回缓存中的value, 没有执行原本的方法,而是执行代理对象中的目标方法。

缓存一般使用在读多写少的程序中。 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值