前言

Laravel 支持多种缓存系统, 并提供了统一的api接口.

(Laravel 5.5)默认支持的存储驱动包括如下:

  • file (默认使用)
  • apc
  • array (数组, 测试用)
  • database (关系型数据库)
  • memcached
  • redis

默认的缓存配置文件在 config/cache.php

参考链接:

使用

直接使用Laravel为我们提供的Facade

use Illuminate\Support\Facades\Cache;
$cache = Cache::get('key');

支持的大部分方法:

Cache::put('key', 'value', $minutes);
Cache::add('key', 'value', $minutes);
Cache::forever('key', 'value');
Cache::remember('key', $minutes, function(){ return 'value' });
Cache::rememberForever('key', function(){ return 'value' });
Cache::forget('key');
Cache::has('key');
Cache::get('key');
Cache::get('key', 'default');
Cache::get('key', function(){ return 'default'; });
Cache::tags('my-tag')->put('key','value', $minutes);
Cache::tags('my-tag')->has('key');
Cache::tags('my-tag')->get('key');
Cache::tags('my-tag')->forget('key');
Cache::tags('my-tag')->flush();
Cache::increment('key');
Cache::increment('key', $amount);
Cache::decrement('key');
Cache::decrement('key', $amount);
Cache::tags('group')->put('key', $value);
Cache::tags('group')->get('key');
Cache::tags('group')->flush();

其他使用方法请参照官方翻译(中文)文档: https://learnku.com/docs/laravel/5.5/cache/1316

源码

Laravel 中常用 Cache Facade 来操作缓存, 对应的实际类是 Illuminate\Cache\CacheManager 缓存管理类(工厂).

Cache::xxx()

我们通过 CacheManager 类获取持有不同存储驱动的 Illuminate\Cache\Repository

CacheManager::store($name = null)

Repository 仓库类代理了实现存储驱动接口 Illuminate\Contracts\Cache\Store 的类实例.

Cache Facade

首先从 Cache Facade 开始分析, 先看一下其源码:

<?php

namespace Illuminate\Support\Facades;

/**
* @see \Illuminate\Cache\CacheManager
* @see \Illuminate\Cache\Repository
*/
class Cache extends Facade
{
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor()
{
return 'cache';
}
}

在配置文件 config\app.php 中定义了 Cache 服务提供者

//...
'providers' => [
// ......
Illuminate\Cache\CacheServiceProvider::class,
// ......
],
//...

Illuminate\Cache\CacheServiceProvider 源文件:

<?php

namespace Illuminate\Cache;

use Illuminate\Support\ServiceProvider;

class CacheServiceProvider extends ServiceProvider
{
// ...... public function register()
{
$this->app->singleton('cache', function ($app) {
return new CacheManager($app);
}); $this->app->singleton('cache.store', function ($app) {
return $app['cache']->driver();
}); $this->app->singleton('memcached.connector', function () {
return new MemcachedConnector;
});
} // ......
}

通过上面源码可知, Cache Facade 关联的项是 Illuminate\Cache\CacheManager, 也就是我们通过 Cache Facade 实际调用的是 CacheManager实例的方法.

CacheManager

<?php

namespace Illuminate\Contracts\Cache;

interface Factory
{
/**
* Get a cache store instance by name.
*
* @param string|null $name
* @return \Illuminate\Contracts\Cache\Repository
*/
public function store($name = null);
}

CacheManager 实现了 Illuminate\Contracts\Cache\Factory 接口(↑), 即实现了一个简单工厂, 传入存储驱动名, 返回对应的驱动实例.

CacheManager实现的简单工厂接口方法:

<?php

namespace Illuminate\Cache;

use Closure;
use InvalidArgumentException;
use Illuminate\Contracts\Cache\Store;
use Illuminate\Contracts\Cache\Factory as FactoryContract;
use Illuminate\Contracts\Events\Dispatcher as DispatcherContract; /**
* @mixin \Illuminate\Contracts\Cache\Repository
*/
class CacheManager implements FactoryContract
{
/**
* Get a cache store instance by name.
*
* @param string|null $name
* @return \Illuminate\Contracts\Cache\Repository
*/
public function store($name = null)
{
$name = $name ?: $this->getDefaultDriver(); return $this->stores[$name] = $this->get($name);
} /**
* Get the default cache driver name.
*
* @return string
*/
public function getDefaultDriver()
{
return $this->app['config']['cache.default'];
} /**
* Attempt to get the store from the local cache.
*
* @param string $name
* @return \Illuminate\Contracts\Cache\Repository
*/
protected function get($name)
{
return $this->stores[$name] ?? $this->resolve($name);
} /**
* Resolve the given store.
*
* @param string $name
* @return \Illuminate\Contracts\Cache\Repository
*
* @throws \InvalidArgumentException
*/
protected function resolve($name)
{
$config = $this->getConfig($name); if (is_null($config)) {
throw new InvalidArgumentException("Cache store [{$name}] is not defined.");
} if (isset($this->customCreators[$config['driver']])) {
return $this->callCustomCreator($config);
} else {
$driverMethod = 'create'.ucfirst($config['driver']).'Driver'; if (method_exists($this, $driverMethod)) {
return $this->{$driverMethod}($config);
} else {
throw new InvalidArgumentException("Driver [{$config['driver']}] is not supported.");
}
}
} /**
* Dynamically call the default driver instance.
*
* @param string $method
* @param array $parameters
* @return mixed
*/
public function __call($method, $parameters)
{
return $this->store()->$method(...$parameters);
}
}

可以看到 CacheManager 提供了会话级别的实例缓存, 当解析驱动名时, 它会按如下顺序解析:

  1. 自定义驱动: 查看是否有通过 CacheManager::extend(...)自定义的驱动
  2. Laravel提供的驱动: 查看是否存在 CacheManager::createXxxDriver(...)方法

这些方法返回的实例必须是实现了 Illuminate\Contracts\Cache\Repository 接口

本质上, CacheManager 就是一个提供了会话级别缓存Repository 实例工厂, 同时它提供了一个 __call 魔术方法, 以便快速调用默认缓存驱动.

$value = Cache::store('file')->get('foo');

// 通过 _call, 调用默认缓存驱动的 get 方法
$value = Cache::get('key');

Repository

Illuminate\Contracts\Cache\Repository 接口

<?php

namespace Illuminate\Contracts\Cache;

use Closure;
use Psr\SimpleCache\CacheInterface; interface Repository extends CacheInterface
{
public function has($key);
public function get($key, $default = null);
public function pull($key, $default = null);
public function put($key, $value, $minutes);
public function add($key, $value, $minutes);
public function increment($key, $value = 1);
public function decrement($key, $value = 1);
public function forever($key, $value);
public function remember($key, $minutes, Closure $callback);
public function sear($key, Closure $callback);
public function rememberForever($key, Closure $callback);
public function forget($key);
public function getStore();
}

Repository 是一个符合 PSR-16: Common Interface for Caching Libraries 规范的缓存仓库类, 其在Laravel相应的实现类: Illuminate\Cache\Repository

Illuminate\Cache\Repository 部分代码如下:

<?php

namespace Illuminate\Cache;

use Closure;
use ArrayAccess;
use DateTimeInterface;
use BadMethodCallException;
use Illuminate\Support\Carbon;
use Illuminate\Cache\Events\CacheHit;
use Illuminate\Contracts\Cache\Store;
use Illuminate\Cache\Events\KeyWritten;
use Illuminate\Cache\Events\CacheMissed;
use Illuminate\Support\Traits\Macroable;
use Illuminate\Cache\Events\KeyForgotten;
use Illuminate\Support\InteractsWithTime;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Contracts\Cache\Repository as CacheContract; /**
* @mixin \Illuminate\Contracts\Cache\Store
*/
class Repository implements CacheContract, ArrayAccess
{
use InteractsWithTime;
use Macroable {
__call as macroCall;
} /**
* The cache store implementation.
*
* @var \Illuminate\Contracts\Cache\Store
*/
protected $store; /**
* The event dispatcher implementation.
*
* @var \Illuminate\Contracts\Events\Dispatcher
*/
protected $events; protected $default = 60; /**
* Create a new cache repository instance.
*
* @param \Illuminate\Contracts\Cache\Store $store
* @return void
*/
public function __construct(Store $store)
{
$this->store = $store;
} public function has($key)
{
return ! is_null($this->get($key));
} public function get($key, $default = null)
{
if (is_array($key)) {
return $this->many($key);
} $value = $this->store->get($this->itemKey($key)); // If we could not find the cache value, we will fire the missed event and get
// the default value for this cache value. This default could be a callback
// so we will execute the value function which will resolve it if needed.
if (is_null($value)) {
$this->event(new CacheMissed($key)); $value = value($default);
} else {
$this->event(new CacheHit($key, $value));
} return $value;
} public function pull($key, $default = null)
{
return tap($this->get($key, $default), function ($value) use ($key) {
$this->forget($key);
});
} protected function event($event)
{
if (isset($this->events)) {
$this->events->dispatch($event);
}
} /**
* Set the event dispatcher instance.
*
* @param \Illuminate\Contracts\Events\Dispatcher $events
* @return void
*/
public function setEventDispatcher(Dispatcher $events)
{
$this->events = $events;
} public function __call($method, $parameters)
{
if (static::hasMacro($method)) {
return $this->macroCall($method, $parameters);
} return $this->store->$method(...$parameters);
} public function __clone()
{
$this->store = clone $this->store;
}
}

从源码可以看出, Illuminate\Cache\Repository 实现了代理模式, 具体的实现是交由 Illuminate\Contracts\Cache\Store 来处理, Repository 主要作用是

  1. 提供一些便捷操作(可以理解为语法糖)
  2. Event 事件触发, 包括缓存命中/未命中、写入/删除键值

Store

Illuminate\Contracts\Cache 缓存驱动是实际处理缓存如何写入/读取/删除的类, 接口内容如下:

<?php

namespace Illuminate\Contracts\Cache;

interface Store
{
public function get($key);
public function many(array $keys);
public function put($key, $value, $minutes);
public function putMany(array $values, $minutes);
public function increment($key, $value = 1);
public function decrement($key, $value = 1);
public function forever($key, $value);
public function forget($key);
public function flush();
public function getPrefix();
}

具体的实现类有:

  • ApcStore
  • ArrayStore
  • NullStore
  • DatabaseStore
  • FileStore
  • MemcachedStore
  • RedisStore

[原创]Laravel 的缓存源码解析的更多相关文章

  1. laravel 读写分离源码解析

    前言:上一篇我们说了<laravel 配置MySQL读写分离>,这次我们说下,laravel的底层代码是怎样实现读写分离的.   一.实现原理 说明: 1.根据 database.php ...

  2. [原创]android开源项目源码解析(一)----CircleImageView的源码解析

    CircleImageView的代码很简洁,因此先将此工程作为源码解析系列的第一篇文章. 解析说明都在代码里了. /* * Copyright 2014 - 2015 Henning Dodenhof ...

  3. 【原创】ui.router源码解析

    Angular系列文章之angular路由 路由(route),几乎所有的MVC(VM)框架都应该具有的特性,因为它是前端构建单页面应用(SPA)必不可少的组成部分. 那么,对于angular而言,它 ...

  4. Mybaits 源码解析 (九)----- 全网最详细,没有之一:一级缓存和二级缓存源码分析

    像Mybatis.Hibernate这样的ORM框架,封装了JDBC的大部分操作,极大的简化了我们对数据库的操作. 在实际项目中,我们发现在一个事务中查询同样的语句两次的时候,第二次没有进行数据库查询 ...

  5. Laravel源码解析--看看Lumen到底比Laravel轻在哪里

    在前面一篇<Laravel源码解析--Laravel生命周期详解>中我们利用xdebug详细了解了下Laravel一次请求中到底做了哪些处理.今天我们跟 Lumen 对比下,看看 Lume ...

  6. jQuery2.x源码解析(缓存篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 缓存是jQuery中的又一核心设计,jQuery ...

  7. 【MyBatis源码解析】MyBatis一二级缓存

    MyBatis缓存 我们知道,频繁的数据库操作是非常耗费性能的(主要是因为对于DB而言,数据是持久化在磁盘中的,因此查询操作需要通过IO,IO操作速度相比内存操作速度慢了好几个量级),尤其是对于一些相 ...

  8. .Net Core缓存组件(Redis)源码解析

    上一篇文章已经介绍了MemoryCache,MemoryCache存储的数据类型是Object,也说了Redis支持五中数据类型的存储,但是微软的Redis缓存组件只实现了Hash类型的存储.在分析源 ...

  9. .Net Core缓存组件(MemoryCache)源码解析

    一.介绍 由于CPU从内存中读取数据的速度比从磁盘读取快几个数量级,并且存在内存中,减小了数据库访问的压力,所以缓存几乎每个项目都会用到.一般常用的有MemoryCache.Redis.MemoryC ...

随机推荐

  1. 第2章—Java内存区域与内存溢出异常

    2.1 概述 总结:本章将从概念上介绍 Java 虚拟机内存的各个区域,讲解这些区域的作用.服务对象以及其中可能产生的问题. 2.2 运行时数据区域 Java 虚拟机在执行 Java 程序的过程中会把 ...

  2. JavaScript Math.floor() 方法

    定义和用法: floor() 方法可对一个数进行下舍入. 语法: Math.floor(x); x:必须参数,可以是任意数值或表达式: 返回值: 小于等于 x,且与 x 最接近的整数. 说明: flo ...

  3. yarn 完美替代 npm

    众所周知,npm是nodejs默认的包管理工具,我们通过npm可以下载安装或者发布包,但是npm其实存在着很多小问题,比如安装速度慢.每次都要在线重新安装等,而yarn也正是为了解决npm当前存在的问 ...

  4. 第十届国际用户体验创新大赛携Mockplus走进校园

    今日立夏,万木并秀,生生不息,第十届国际用户体验创新大赛即将拉开序幕.5月5日下午,一场用户体验设计经验分享活动携带众多嘉宾降临成都理工大学,为西南赛区首站赛事宣讲. 本次宣讲活动邀请了华为技术有限公 ...

  5. Spring boot——logback.xml 配置详解(二)

    阅读目录 1 根节点包含的属性 2 根节点的子节点 文章转载自:http://aub.iteye.com/blog/1101260,在此对作者的辛苦表示感谢! 回到顶部 1 根节点<config ...

  6. numpy中tile()函数

    函数形式: tile(A,rep) 功能:重复A的各个维度 参数类型: - A: Array类的都可以,即A是一个ndarry数组- rep:A沿着各个维度重复的次数,表示变成的矩阵的形状,例如rep ...

  7. Linux网络(一)

    [root@localhost ~]# ifconfig eth0 Link encap:Ethernet HWaddr :0C:::F6: inet addr:172.17.4.128 Bcast: ...

  8. Java异常封装

    转载: Java异常封装 Java里面的异常在真正工作中使用还是十分普遍的.什么时候该抛出什么异常,这个是必须知道的. 当然真正工作里面主动抛出的异常都是经过分装过的,自己可以定义错误码和异常描述. ...

  9. github注册与使用

    个人信息: 姓名:赵建 学号:1413042015 班级:网络工程141班 兴趣爱好:码代码,看电影,折腾linux github注册: 首先在地址栏输入https://www.github.com, ...

  10. Java泛型学习笔记

    泛型是Java5引进的新特征,是类和接口的一种拓展机制,主要实现参数化类型机制.Java的泛型,跟C++的类模板有很多相似的地方,或者说,就是C++类模板的升级版. 泛型类 在开发过程中,我们或许要设 ...