angular源码剖析之Provider系列--CacheFactoryProvider

CacheFactoryProvider 简介

源码里是这么描述的:
Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to
them.
意思就是通过cacheFactory可以构造一个Cache对象来给予访问和执行权限。

这个Cache对象官方文档是这么说的:
A cache object used to store and retrieve data, primarily used by $http and the script directive to cache templates and other data.
用我自己的话来说就是 提供存储和访问缓存对象的服务,angular内部主要被$http,script指令用于
缓存template和其他数据。我们自己可以在Controller内部使用。

CacheFactoryProvider 用法

<!doctype html>
  <html lang="en" ng-app="myapp">
   <head>
    <meta charset="UTF-8">
    <meta name="Generator" content="EditPlus?">
    <meta name="Author" content="">
    <meta name="Keywords" content="">
    <meta name="Description" content="">
    <title>Document</title>
    <script src="js/angular.js"></script>
   </head>
   <body>
    <div ng-controller="MyController">
    </div>
     <script >
       var app=angular.module('myapp',[]);
       app.controller('MyController',function($scope,$cacheFactory){
         var myCache = $cacheFactory("myCache",{capacity: 6});
         //var myCache1 = $cacheFactory("myCache",{capacity: 6}); //会报错
         myCache.put("name","john");
         myCache.put("name1","wonder");
         myCache.put("name","john");
      });

       app.controller('getCacheController',['$scope','$cacheFactory',
          function($scope,$cacheFactory){
                var cache = $cacheFactory.get('myCache');
                var name = cache.get('name');
                console.log(name);  //打印john
            }]);
     </script>
   </body>
  </html>

看了上面这个一个简单的例子,读者可能会产生如下疑惑:

  1. 不是名为CacheFactoryProvider吗,怎么在代码里只看到cacheFactory呢?
  2. cacheFactory是怎么存储对象的?
    下面我们来依次解答这两个问题

$cacheFactory的注入

我们首先来看第一个问题,这个问题要牵涉到angular里面的依赖注入机制,我们前面的分析也讲过,
angular会在启动之前通过调用publishExternalAPI 函数先发布一些扩展API,同时定义ng
模块,在定义ng模块的时候就传入了注入provider的方法

angularModule('ng', ['ngLocale'], ['$provide',
     //通过参数注入$provide
     function ngModule($provide) {
          ///部分代码省略
         $provide.provider({
           $cacheFactory: $CacheFactoryProvider,
         });
    }])

$cacheFactory出现了,它是通过javascript的键值对象作为键传给provider方法。那么它是如何存储
对象的呢?首先我们看它的定义:

CacheFactoryProvider的定义

内部定义了依赖注入核心的$get方法,$get方法返回cacheFactory方法(也就是上面实例代码里的
$cacheFactory参数)。

  function $CacheFactoryProvider() {
    //定义$get方法供依赖调用
    //controller中获取cacheFactory时会调用此方法
    //这个$get方法也是获取provider的关键方法
     this.$get = function() {
       var caches = {};//闭包的一个运用

       function cacheFactory(cacheId, options) {
         //部分代码省略
         //可以用if来判断
         if (cacheId in caches) {//如果caches中已经存在cacheId
             //实例代码里抛出的错误就在此处、
             //统一调用minErr函数
            throw minErr('$cacheFactory')('iid', "CacheId '{0}' is already taken!"
                                          , cacheId);
          }

           var size = 0,
           //把options 和{id:cacheId} 放入{} 中 不是深拷贝
           stats = extend({}, options, {id: cacheId}),
           data = createMap(),//通过Object.create(null) 创建个空对象
           capacity = (options && options.capacity) || Number.MAX_VALUE,
           lruHash = createMap(),
           freshEnd = null,
           staleEnd = null;
           //返回caches中的一个对象
           return caches[cacheId] = {
                //省略部分代码
                //存储里讲解
                put:function(key,value){
                },
                get: function(key) {
                },
                remove: function(key) {
                },
                removeAll: function() {
                },
                destroy: function() {
                },
                info: function() {
                }
           }
           //刷新节点次序
           function refresh(entry) {
           }
           //
           function link(nextEntry, prevEntry) {

           }
         }

         //所有的缓存
        cacheFactory.info = function() {
          var info = {};
          forEach(caches, function(cache, cacheId) {
            info[cacheId] = cache.info();
          });
          return info;
        };

        cacheFactory.get = function(cacheId) {
            return caches[cacheId];
        };

        return cacheFactory;
     }
  }

CacheFactoryProvider的存储

存储分为这几个核心方法:put,refresh,remove,link

put函数

value会放入data对象中,key会放入lruHash链表

      put: function(key, value) {
         if (isUndefined(value)) return;
           //如果设定的capcity小于maxvalue
         if (capacity < Number.MAX_VALUE) {
           //lruHash 存了当前的key 还有可能是 p 和n  (previous和next)
           var lruEntry = lruHash[key] || (lruHash[key] = {key: key});
           //刷新各节点的次序
           refresh(lruEntry);//把当前entry放入链表末尾
         }
         //如果key 在data里不存在 那么增加size
         if (!(key in data)) size++;
         data[key] = value;
         //当大于capacity时 会清除最早加入的那个
         if (size > capacity) {
           this.remove(staleEnd.key);//移除淘汰节点stableEnd
         }
         return value;
     }

get函数

Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object
获取存储在cache对象中的指定数据

        get: function(key) {
          if (capacity < Number.MAX_VALUE) {
            var lruEntry = lruHash[key];
            if (!lruEntry) return;
            // 获取first的时候 因为staleEnd为first 所以会让staleEnd指向 second
            // 内部会执行link 使得 second.p = null
            // first.p =  third  third.n = first
            //stableEnd为 second freshEnd为first
            refresh(lruEntry);
          }
          return data[key];
        }

remove函数

Removes an entry from the {@link $cacheFactory.Cache Cache} object.
从cache对象删除一个entry

remove: function(key) {
          //如果capacity小于maxvalue
          if (capacity < Number.MAX_VALUE) {
            //先取出当前key的entry
            var lruEntry = lruHash[key];
            if (!lruEntry) return;
            //第一次超过时 freshEnd 为third  lryEntry为first
            if (lruEntry == freshEnd) freshEnd = lruEntry.p;
             //第一次超过时 staleEnd 为first  lryEntry为first
             //所以 会让 stalEnd 指向second 以便于下次移除时
            if (lruEntry == staleEnd) staleEnd = lruEntry.n;
            //把淘汰节点的一个节点选中
            //第一次超过时 lryEntry.n为 second  lryEntry.p 为null
            //执行结果为 second.p = null
            link(lruEntry.n,lruEntry.p);
            //把当前key从lruHash中删除
            delete lruHash[key];
          }
          if (!(key in data)) return;
          delete data[key];
          size--;
        }

refresh函数

makes the entry the freshEnd of the LRU linked list。
把entry 放入链表的末尾

    function refresh(entry) {
      if (entry != freshEnd) {
        if (!staleEnd) { //staleEnd为空那么就让他指向当前entry
          staleEnd = entry;
        } else if (staleEnd == entry) {
          //如果淘汰节点等于当前节点
          staleEnd = entry.n; //用于把 当前的下一个节点 用作淘汰节点
        }
        //放入第一个元素时 entry.n,entry.p都为undefined
        link(entry.n, entry.p); //当前的上一个节点 和当前的下一个节点
        link(entry, freshEnd); // 当前的节点 和 最新的末尾节点
        freshEnd = entry;
        freshEnd.n = null;
        //第一次执行完 结果为: freshEnd = first  staleEnd为first
        //first.p=null first.n=null
        //第二次执行完 结果为:freshEnd = second staleEnd为first
        // first.p=null first.n= second
        // scecond.p = first scecond.n = null
        //第三次执行完 freshEnd = third staleEnd为first first.p=null
        //first.n= second
        // second.p = first second.n = null
        // third.p = second third.n = null
      }
    }

link函数

bidirectionally(双向链表) links two entries of the LRU linked list
双向链接链表里的两个元素。

function link(nextEntry, prevEntry) {
      //undefined 不等于undefined
      if (nextEntry != prevEntry) {
        //
        if (nextEntry) nextEntry.p = prevEntry;
        //p stands for previous, 'prev' didn't minify
        if (prevEntry) prevEntry.n = nextEntry;
         //n stands for next, 'next' didn't minify
      }
    }

欢迎关注我的公众号,获取最新源码解析文章!

参考资料:
  1. 【算法】—— LRU算法
  2. 缓存淘汰算法--LRU算法
  3. 漫画:什么是LRU算法?

原文地址:https://www.cnblogs.com/johnwonder/p/10908325.html

时间: 2024-08-02 19:11:49

angular源码剖析之Provider系列--CacheFactoryProvider的相关文章

WorldWind源码剖析系列:星球类World

星球类World代表通用的星球类,因为可能需要绘制除地球之外的其它星球,如月球.火星等.该类的类图如下. 需要说明的是,在WorldWind中星球球体的渲染和经纬网格的渲染时分别绘制的.经纬网格的渲染过程请参见文章<WorldWind源码剖析系列:星球经纬度格网的绘制>,是通过Form.OnPaint()函数激活.刷新和绘制的.星球球体的渲染过程请参见文章<WorldWind源码剖析系列:星球球体的加载与渲染>.而星球类World是绘制过程中从XML配置文件中读取参数构造的用来代表

WorldWind源码剖析系列:星球球体的加载与渲染

WorldWind源码剖析系列:星球球体的加载与渲染 WorldWind中主函数Main()的分析 在文件WorldWind.cs中主函数Main()依次作以下几个事情: 1.  使用System.Version在内部,读取软件版本信息,并格式化输出.我们在外面配置软件版本,“关于”部分中版本自动更改. 获取格式化版本号 // Establish the version number string used for user display, // such as the Splash and 

【java集合框架源码剖析系列】java源码剖析之TreeMap

注:博主java集合框架源码剖析系列的源码全部基于JDK1.8.0版本.本博客将从源码角度带领大家学习关于ArrayList的知识. 一TreeMap的定义: public class TreeMap<K,V> extends AbstractMap<K,V> implements NavigableMap<K,V>, Cloneable, java.io.Serializable 可以看到TreeMap是继承自AbstractMap同时实现了NavigableMap,

Phaser实现源码剖析

在这里首先说明一下,由于Phaser在4.3代码里是存在,但并没有被开放出来供使用,但已经被本人大致研究了,因此也一并进行剖析. Phaser是一个可以重复利用的同步栅栏,功能上与CyclicBarrier和CountDownLatch相似,不过提供更加灵活的用法.也就是说,Phaser的同步模型与它们差不多.一般运用的场景是一组线程希望同时到达某个执行点后(先到达的会被阻塞),执行一个指定任务,然后这些线程才被唤醒继续执行其它任务. Phaser一般是定义一个parties数(parties一

GDAL源码剖析(一)(转载)

GDAL源码剖析(一) GDAL 前言:一直在使用和研究GDAL的相关东西,发现网上对GDAL的内容倒是不少,但是很少有系统的介绍说明,以及内部的一些结构说明,基于这些原因,将本人的一些粗浅的理解放在此处,形成一个系列,暂时名为<GDAL源码剖析>(名称有点大言不惭,欢迎大家口水吐之,板砖拍之),供大家交流参考,有什么错误之处,望大家不吝指正,本系列对于GDAL的使用均是在Windows平台下,对于Linux平台下的不在此系列讨论范围之内.此外,转载本博客内容,请注明出处,强烈鄙视转载后不注明

豆瓣Redis解决方案Codis源码剖析:Proxy代理

豆瓣Redis解决方案Codis源码剖析:Proxy代理 1.预备知识 1.1 Codis Codis就不详细说了,摘抄一下GitHub上的一些项目描述: Codis is a proxy based high performance Redis cluster solution written in Go/C, an alternative to Twemproxy. It supports multiple stateless proxy with multiple redis instan

SpringMVC源码剖析(四)- DispatcherServlet请求转发的实现

SpringMVC完成初始化流程之后,就进入Servlet标准生命周期的第二个阶段,即“service”阶段.在“service”阶段中,每一次Http请求到来,容器都会启动一个请求线程,通过service()方法,委派到doGet()或者doPost()这些方法,完成Http请求的处理. 在初始化流程中,SpringMVC巧妙的运用依赖注入读取参数,并最终建立一个与容器上下文相关联的spring子上下文.这个子上下文,就像Struts2中xwork容器一样,为接下来的Http处理流程中各种编程

转:【Java集合源码剖析】LinkedHashmap源码剖析

转载请注明出处:http://blog.csdn.net/ns_code/article/details/37867985   前言:有网友建议分析下LinkedHashMap的源码,于是花了一晚上时间研究了下,分享出此文(这个系列的最后一篇博文了),希望大家相互学习.LinkedHashMap的源码理解起来也不难(当然,要建立在对HashMap源码有较好理解的基础上). LinkedHashMap简介 LinkedHashMap是HashMap的子类,与HashMap有着同样的存储结构,但它加

angular源码阅读的起点,setupModuleLoader方法

angular源码其实结构非常清晰,划分的有条有理的,大概就是这样子: (function(window,document,jquery,undefined){ //一些工具函数 //EXPR 编译器 自执行 //setupModuleLoader方法,公司内部的框架是vxsetup方法,(只是定义,没有调用) //moduler方法() //angular初始化方法,公司内部的框架是vxinit方法 //bootstrap //createInjector //一系列指令,服务,过滤器等指令