本篇主要讲述yii是如何处理一个web请求的,其中包括了route,filter,controller,action等等。他是如何安排他们各自的顺序,同时又预留了哪些事件函数,以让开发者更好的控制。本文需要一定的编程基础和对yii有一定熟悉,属于进阶型的。另外,由于程序庞大,比较复杂,请千万看准,哪段程序是在哪个类中的。

Ready! Start...

首先要说的肯定是index.php,他作为整个application的入口文件,起到了初始化各种变量的作用,接下来看代码,在这里我们约定下,这里所有的代码都一般情况下的

1 <?php2 3 //change the following paths if necessary4 $yii=dirname(__FILE__).'/../yiiframework/framework/yii.php';5 $config=dirname(__FILE__).'/protected/config/main.php';6 7 //remove the following lines when in production mode8 defined('YII_DEBUG') or define('YII_DEBUG',true);9 //specify how many levels of call stack should be shown in each log message10 defined('YII_TRACE_LEVEL') or define('YII_TRACE_LEVEL',3);11 12 require_once($yii);13 Yii::createWebApplication($config)->run();

在这里唯一要关心的,就是最后一句,因为这句开始了我们的Request Process,让我们接下来看看CreateWebApplication做了什么操作?

1 //创建一个WebApplication2 public static function createWebApplication($config=null)3     {4         return self::createApplication('CWebApplication',$config);5     }6 7 //创建了一恶搞CWebApplication的实例8     public static function createApplication($class,$config=null)9     {10         return new $class($config);11     }12 13 //因为CWebApplication没有特别定义的构造函数14 //其又是继承CApplication的,所以会执行CApplication的构造函数15 //下面是CApplicaiton的构造函数16     public function __construct($config=null)17     {18         Yii::setApplication($this);19 20         //set basePath at early as possible to avoid trouble21         if(is_string($config))22             $config=require($config);23         if(isset($config['basePath']))24         {25             $this->setBasePath($config['basePath']);26             unset($config['basePath']);27         }28         else29             $this->setBasePath('protected');30         Yii::setPathOfAlias('application',$this->getBasePath());31         Yii::setPathOfAlias('webroot',dirname($_SERVER['SCRIPT_FILENAME']));32         Yii::setPathOfAlias('ext',$this->getBasePath().DIRECTORY_SEPARATOR.'extensions');33 34         $this->preinit();35 36         $this->initSystemHandlers();37         $this->registerCoreComponents();38 39         $this->configure($config);40         $this->attachBehaviors($this->behaviors);41         $this->preloadComponents();42 43         $this->init();44     }

仔细阅读代码中的注释,然后,分析代码,我们容易发现,整个构造函数,利用main.php中的configuration对整个app实例进行了变量初始化,分别包括1、设置基地址25行或29行
2、设置了Alias,现在知道为什么你用namespace的格式,文件能找到路径的原因之一了吧。30-32行
3、preinit这个是预留的函数,你可以在自己扩展的Application中override这个,帮助控制。
4、initSystemHandlers这个是用于初始化Yii的ExceptionHandler的,当前前提是你开启了。
5、registerCoreComponents,名字上就很容易理解,注册核心控件,这里面有一些必须加载的component,起主要的工作就是将component存入APP->_components中(这里的APP表示Application实例),注意存的时候还是一个实例化的过程。这里我们截取一段代码,为了更好的理解。现在这里和main.php中的配置是没有关系的,因为这里都是必须加载的,稍后,会有个过程,将一些设置根据main.php的配置进行修改。

1     public function setComponent($id,$component)2     {3         if($component===null)4             unset($this->_components[$id]);5         else6         {7             $this->_components[$id]=$component;8             if(!$component->getIsInitialized())9                 $component->init();10         }11     }

上面的代码是注册核心组件最核心的部分。注意第7-9行。由此可以看出,_components中存入的是各个component的实例。上面的代码在CModule.php文件中,因为CApplication是继承自CModule的。这里还有个变量是需要注意的是_componentConfig,我们看一下代码(来自CModule.php),下面的代码会调用到上面的函数

1     public function setComponents($components,$merge=true)2     {3         foreach($components as $id=>$component)4         {5             if($component instanceof IApplicationComponent)6                 $this->setComponent($id,$component);7             else if(isset($this->_componentConfig[$id]) && $merge)8                 $this->_componentConfig[$id]=CMap::mergeArray($this->_componentConfig[$id],$component);9             else10                 $this->_componentConfig[$id]=$component;11         }12     }

第6行使用到了,我们讲解的方法,但是在这里不是我们的重点,我们着重说的是第7-10行,根据前面的逻辑我们知道只有非IApplicationComponent的实例才可以存入_componentConfig中,那么什么是IApplicationComponent呢?“After the application completes configuration, it will invoke the {@link init()} method of every loaded application component.”上面是相关的文档注释,他是这么解释IApplicationComponent的,其实说白了,就是有init方法,因为在setComponent方法中就用到了,见第9行。典型的这类Component有哪些呢?还真不少,举几个比如CCache,CAssetManager,CClientScript等,个人感觉就就是默认是preload的那些。另外,一些,比如CLogger什么的就不是了,他会被存放在_componentConfig中。6、configure,这个是将main.php中的关联数组,转成此application的属性。以后再使用的时候,就是直接使用属性了,不在用这个数组。但是需要注意的是他这里的属性是指component,import,module这些第一纬的数组键,并不是其中的,类似request,urlManager这类的。
这里是有必要详细说下的,因为这里使用了__set,__get来简化操作,同时如果不搞清这个机制的话,在阅读其他yii的代码会遇到一定障碍的。接下来看代码,configure方法在CModule.php中

1     public function configure($config)2     {3         if(is_array($config))4         {5             foreach($config as $key=>$value)6                 $this->$key=$value;7         }8     }9 10     //In the CModule11     public function __get($name)12     {13         if($this->hasComponent($name))14             return $this->getComponent($name);15         else16             return parent::__get($name);17     }18 19 20     //In the CComponent,__set存在于此中,21     //CWebApplication,CApplication,CModule中是没有的22     public function __set($name,$value)23     {24         $setter='set'.$name;25         if(method_exists($this,$setter))26             return $this->$setter($value);27         else if(strncasecmp($name,'on',2)===0 && method_exists($this,$name))28         {29             //duplicating getEventHandlers() here for performance30             $name=strtolower($name);31             if(!isset($this->_e[$name]))32                 $this->_e[$name]=new CList;33             return $this->_e[$name]->add($value);34         }35         else if(is_array($this->_m))36         {37             foreach($this->_m as $object)38             {39                 if($object->getEnabled() && (property_exists($object,$name) || $object->canSetProperty($name)))40                     return $object->$name=$value;41             }42         }43         if(method_exists($this,'get'.$name))44             throw new CException(Yii::t('yii','Property "{class}.{property}" is read only.',45                 array('{class}'=>get_class($this), '{property}'=>$name)));46         else47             throw new CException(Yii::t('yii','Property "{class}.{property}" is not defined.',48                 array('{class}'=>get_class($this), '{property}'=>$name)));49     }

其实,很多configure的属性是不存在的,也就是无法访问的。比如$this->request之类的,这个时候因为不存在所以就会在读取的时候调用__get,存入的时候调用__set。接下来,我们就看看,读取和存入这些不存在的属性的时候是如何处理的吧。先看读取,就是__get方法,看行13,我们看得到了他判断是否存在_component和_componentConfig两个数组中是否有,有的话就直接从其中读取。这里就说,他会有限判断看看是不是在这两个数组中存在。那么在__set中又是如何处理的呢?前面我们提到过,这里的$key是main.php的数组中第一维的key。这里我们以$key="componnets"作为例子,进行说明,在执行第6行时,会调用__set方法,24行将其拼贴成了一个setter函数,名为setComponents方法,因为该方法是存在,所以就执行该方法。我们看下该方法的代码,

1     public function setComponents($components,$merge=true)2     {3         foreach($components as $id=>$component)4         {5             if($component instanceof IApplicationComponent)6                 $this->setComponent($id,$component);7             else if(isset($this->_componentConfig[$id]) && $merge)8                 $this->_componentConfig[$id]=CMap::mergeArray($this->_componentConfig[$id],$component);9             else10                 $this->_componentConfig[$id]=$component;11         }12     }

这里就很明显了,就是把main.php中components里面的内容存入了_component或者_componentConfig中。至此完成了,整个configure的过程。现在我们以$this->request为例,简要说明下是程序是如何获取的。正常是不含有这个属性的,所以此时如果是read就会访问__get,就会从_component或_componentConfig中取,如果是write,那么就会走__set因为其中没有符合的项(也可以认为没有对应的setter),所以是不可写的。

7、attachBehaviors就是注册Application的Behavior其实就是一个事件集合。

8、preloadComponents,这个是将前面configure中从main.php中导入的preload设置的component,他和前面的corecomponent一样,会存入_components中,并且完成初始化。
9、最后的是init,这个过程执行的是CWebApplication中的init()

1     protected function init()2     {3         parent::init();4         //preload 'request' so that it has chance to respond to onBeginRequest event.5         $this->getRequest();6     }

该getRequest方法就是返回request Component,在CApplication.php中的第957行,默认使用的是CHttpRequest.注意这个request是从_component中出来的,因为其实一个IApplicationComponent。
这里要顺带一提,在yii大量使用了重写__set,__get函数,所以你虽然会看到$this->$key=$value,但是有可能是经过重写的,并不一定就是一个属性,而可能是个setter。结合步骤6,好好理解yii中的getter和setter应用,不过需要注意的是,只有当不存在对应属性时才会调用该方法。
言归正传,这里相对比较绕,我把相关的代码贴出来,结合代码看会简单很多。

1     //In the CWebApplication2         protected function init()3     {4         parent::init();5         //preload 'request' so that it has chance to respond to onBeginRequest event.6         $this->getRequest();7     }8 9         //In the CApplicaiton10     public function getRequest()11     {12         return $this->getComponent('request');13     }14 15 16         //In the CModule17     public function getComponent($id,$createIfNull=true)18     {19         if(isset($this->_components[$id]))20             return $this->_components[$id];21         else if(isset($this->_componentConfig[$id]) && $createIfNull)22         {23             $config=$this->_componentConfig[$id];24             if(!isset($config['enabled']) || $config['enabled'])25             {26                 Yii::trace("Loading \"$id\" application component",'system.CModule');27                 unset($config['enabled']);28                 $component=Yii::createComponent($config);29                 $component->init();30                 return $this->_components[$id]=$component;31             }32         }33     }   

注释说明了此段代码所属的类,下面的方法被上面的方法调用,让我们看getComponent方法,注意20行,此处获得了request component对应的实例。这里要说下第28行,在之前虽然这个的步骤中preloadComponents方法中,已经将main.php中的configure用来实例化对应的component了,那么剩下的都是非compnoent的,这里的28行,可以理解为是中延迟加载(初始化),根据$config创建个component实例,存入_componentConfig中。

好了终于讲解完application的construct过程了。

我们可以接下来继续分析了。走了一大圈,我们其实还在下面代码处

Yii::createWebApplication($config)->run();

前面返回了一个Application的实例,对于web其实就是CWebApplication的实例,然后执行run方法,该run方法在CApplicaiton中,代码如下

    public function run()    {if($this->hasEventHandler('onBeginRequest'))$this->onBeginRequest(new CEvent($this));$this->processRequest();if($this->hasEventHandler('onEndRequest'))$this->onEndRequest(new CEvent($this));    }

这段代码逻辑很清晰,主要是两个事件,一个是onBeginRequest另一个是onEndRequest,这里是预留给我们扩展的。这里的最主要的是$this->processRequest();语句

    public function processRequest()    {if(is_array($this->catchAllRequest) && isset($this->catchAllRequest[0]))        {$route=$this->catchAllRequest[0];foreach(array_splice($this->catchAllRequest,1) as $name=>$value)$_GET[$name]=$value;        }else$route=$this->getUrlManager()->parseUrl($this->getRequest());$this->runController($route);    }

查看该方法,这里就是根据相应的情况生成route,有了route后自然就可以运行Controller了。此时因为route只是生成,app还不知道需要访问哪个Controller。所以这个runController其实还有初始化对应Controller的作用。看一下代码

    public function runController($route)    {if(($ca=$this->createController($route))!==null)        {list($controller,$actionID)=$ca;$oldController=$this->_controller;$this->_controller=$controller;$controller->init();$controller->run($actionID);$this->_controller=$oldController;        }elsethrow new CHttpException(404,Yii::t('yii','Unable to resolve the request "{route}".',array('{route}'=>$route===''?$this->defaultController:$route)));    }

当route正确时候,可以创建出相应的controller。然后根据最主要是init方法和run($actionID)方法。init方法在前面已经介绍过了,在默认的yii程序中,实际上是没有任何作用的。

这里我们要看下createAction方法,对于action如何生成,对于后面的action调用的方法理解很重要,见代码

 1     public function createAction($actionID) 2     { 3         if($actionID==='') 4             $actionID=$this->defaultAction; 5         if(method_exists($this,'action'.$actionID) && strcasecmp($actionID,'s')) // we have actions method 6             return new CInlineAction($this,$actionID); 7         else 8         { 9             $action=$this->createActionFromMap($this->actions(),$actionID,$actionID);10             if($action!==null && !method_exists($action,'run'))11                 throw new CException(Yii::t('yii', 'Action class {class} must implement the "run" method.', array('{class}'=>get_class($action))));12             return $action;13         }14     }

有了上面的基础,我们这里主要看run($actionID),继续看代码。该run方法是在CController中的

1     public function run($actionID)2     {3         if(($action=$this->createAction($actionID))!==null)4         {5             if(($parent=$this->getModule())===null)6                 $parent=Yii::app();7             if($parent->beforeControllerAction($this,$action))8             {9                 $this->runActionWithFilters($action,$this->filters());10                 $parent->afterControllerAction($this,$action);11             }12         }13         else14             $this->missingAction($actionID);15     }

此段代码表示,在请求的action存在时,执行相应的action。第5行表示,首先看看当前controller中有没有action,有的话就返回CInlineAction,其中可是有run方法的哦。后面会用到的。否则,则是一个Action类,就需要做map了。本来嘛Yii中就是这两种Action的设置方法,后者可以带来很好的复用。

接下来,我们需要注意这么几个方法,CWebApplication.beforeControllerAction方法,CController.runActionWithFilters方法,最后是CWebApplication.afterControllerAction方法。对于第一和第三个方法,都是用来override的,这样的话可以在执行Action做一些操作,并且这些操作是在Filter执行前或在action执行完后的。这里我们着重要看的是runActionWithFilter方法,看代码。

1     //In the CController2     public function runActionWithFilters($action,$filters)3     {4         if(empty($filters))5             $this->runAction($action);6         else7         {8             $priorAction=$this->_action;9             $this->_action=$action;10             CFilterChain::create($this,$action,$filters)->run();11             $this->_action=$priorAction;12         }13     }

此段代码很简单,就是如果没有filter的话,直接执行Action,如果有filter的话,则要先执行所有的filter。核心代码是第10行。让我们来看看代码

1 public static function create($controller,$action,$filters)2     {3         $chain=new CFilterChain($controller,$action);4 5         $actionID=$action->getId();6         foreach($filters as $filter)7         {8             if(is_string($filter))  //filterName [+|- action1 action2]9             {10                 if(($pos=strpos($filter,'+'))!==false || ($pos=strpos($filter,'-'))!==false)11                 {12                     $matched=preg_match("/\b{$actionID}\b/i",substr($filter,$pos+1))>0;13                     if(($filter[$pos]==='+')===$matched)14                         $filter=CInlineFilter::create($controller,trim(substr($filter,0,$pos)));15                 }16                 else17                     $filter=CInlineFilter::create($controller,$filter);18             }19             else if(is_array($filter))  //array('path.to.class [+|- action1, action2]','param1'=>'value1',...)20             {21                 if(!isset($filter[0]))22                     throw new CException(Yii::t('yii','The first element in a filter configuration must be the filter class.'));23                 $filterClass=$filter[0];24                 unset($filter[0]);25                 if(($pos=strpos($filterClass,'+'))!==false || ($pos=strpos($filterClass,'-'))!==false)26                 {27                     $matched=preg_match("/\b{$actionID}\b/i",substr($filterClass,$pos+1))>0;28                     if(($filterClass[$pos]==='+')===$matched)29                         $filterClass=trim(substr($filterClass,0,$pos));30                     else31                         continue;32                 }33                 $filter['class']=$filterClass;34                 $filter=Yii::createComponent($filter);35             }36 37             if(is_object($filter))38             {39                 $filter->init();40                 $chain->add($filter);41             }42         }43         return $chain;44     }

这段程序,其实就是根据我们在controller中的filter()中配置的filter,生成一个filterchain,filterchain其实是一个filter的集合。此段代码还根据配置挨个生成了filter
获得了该controller的filterchain后,随后执行了run()方法,我们继续看看run方法的代码。

1     public function run()2     {3         if($this->offsetExists($this->filterIndex))4         {5             $filter=$this->itemAt($this->filterIndex++);6             Yii::trace('Running filter '.($filter instanceof CInlineFilter ? get_class($this->controller).'.filter'.$filter->name.'()':get_class($filter).'.filter()'),'system.web.filters.CFilterChain');7             $filter->filter($this);8         }9         else10             $this->controller->runAction($this->action);11     }

这段程序当有未执行的filter时就运行filter,否则就运行相应Action。这里其实用到了一个迭代$filter->filter($this);,此处我们需要看CFilter中的filter方法的代码,

1     /**2 * Performs the filtering.3 * The default implementation is to invoke {@link preFilter}4 * and {@link postFilter} which are meant to be overridden5 * child classes. If a child class needs to override this method,6 * make sure it calls <code>$filterChain->run()</code>7 * if the action should be executed.8 * @param CFilterChain $filterChain the filter chain that the filter is on.9 */10     public function filter($filterChain)11     {12         if($this->preFilter($filterChain))13         {14             $filterChain->run();15             $this->postFilter($filterChain);16         }17     }

这里我把注释也搬过来了。请注意第6行,“请保证会调用$filterChain->run()”,回到FilterChain的run方法中,我们不难发现,其实他是将自身传给了单个filter,然后filter会再让其运行,在FilterChain中通过$filterIndex来作为游标,标定处理到哪个了。知道全部处理过后,运行runAction。接下来让我们看runAction方法

1     public function runAction($action)2     {3         $priorAction=$this->_action;4         $this->_action=$action;5         if($this->beforeAction($action))6         {7             if($action->runWithParams($this->getActionParams())===false)8                 $this->invalidActionParams($action);9             else10                 $this->afterAction($action);11         }12         $this->_action=$priorAction;13     }

我们又在此看到了,预留的函数beforeAction和afterAction,这里就不多做解释了。关键看第7,8行。

先看第8行,第8行就是当Action执行失败时,运行的,yii默认在里面只是做了个抛出异常的操作。接下来看第7行。

这里的runWithParams方法是可以override的,所以需要注意多态性。我们将定传进来的是CInlineAction吧。

它继承了CAction,这个代表我们在controller中直接以action作为前缀的Action所对应的类。

1     //CAction2     public function __construct($controller,$id)3     {4         $this->_controller=$controller;5         $this->_id=$id;6     }7 8 9     //CInlineAction10     public function run()11     {12         $method='action'.$this->getId();13         $this->getController()->$method();14     }15 16     //CInlineAction17     public function runWithParams($params)18     {19         $methodName='action'.$this->getId();20         $controller=$this->getController();21         $method=new ReflectionMethod($controller, $methodName);22         if($method->getNumberOfParameters()>0)23             return $this->runWithParamsInternal($controller, $method, $params);24         else25             return $controller->$methodName();26     }

首先看下构造函数,这样你就很容易明白,这个CInlineAction是如何与Controller关联的,就是就是将当前的controller内聚到此Action中,然后调用该controller的对应action方法。接下来看runWithParams方法。runWithParams就是用来判定action的函数签名是否有参数,有的话,就把传入$param运行,否则就直接运行action。run方法更简单,直接就是执行相应的action,但是你要注意这里的run和runWithParams实际上是有功能相似性的(一个可以带参,一个不带),根据实际情况选用即可。支持action运行完了。我们就可以一层一层的跳出去了。随后运行CController.runAtion方法中的afterAction语句,现在就等于执行完了,语句CFilterChain::create($this,$action,$filters)->run();(位于CController.runActionWithFilters方法中),之后又回到了CController.run方法中,执行afterControllerAction方法。之后再回到CWebApplication.runController方法中,之后再回到CWebApplication.processRequest方法中,之后再回到CApplication.run方法中,如果存在onEndRequest handler则运行之。支持整个Yii的Request Process Flow运行完毕了。

内容很多,但是很有必要好好研究,这对于整个request控制大有益处,尤其是对几个预留的处理函数,另外,behavior和component的加载~

转载于:https://www.cnblogs.com/JosephLiu/archive/2011/12/19/2292852.html

Yii Framework的process flow分析相关推荐

  1. php行为和事件是什么,Yii Framework框架中事件和行为的区别及应用实例分析

    本文实例讲述了Yii Framework框架中事件和行为的区别及应用.分享给大家供大家参考,具体如下: 个人觉得,在 Yii 里面,最难以明白的就是事件(Event)和行为(behavior)了.这不 ...

  2. php excel 组件,Yii Framework框架使用PHPExcel组件的方法示例

    本文实例讲述了Yii Framework框架使用PHPExcel组件的方法.分享给大家供大家参考,具体如下: PHPExcel下载地址http://www.yiiframework.com/exten ...

  3. [Yii Framework] Yii如何实现前后台的session分离

    Yii Framework实现前后台frontend,backend分离的方法有几种,总结如下: 1. 分开入口文件 目录结构 index.php admin.php assets/ ...其它目录 ...

  4. [Yii Framework] spl_autoload_register 导致加载顺序冲突

    php版本 davidhhuan@davidhhuan-ThinkPad-T410:~$ php -version PHP 5.3.10-1ubuntu3.4 with Suhosin-Patch ( ...

  5. [转]Yii Framework: 从 model 中生成 select option

    转载自: http://www.cnblogs.com/analyzer/articles/1673016.html 在 Yii framework 的论坛有人问,如何用 yii 的方式来生成一个下拉 ...

  6. php detailview,PHP开发框架Yii Framework教程(31) Zii组件-DetailView示例

    CDetailView为某个Model显示详细内容.这个要显示的Model可以为CModel或是关联数组. CDetailView通过配置 attributes来决定Model的那些属性需要显示已经以 ...

  7. Yii Framework 开发教程(32) Zii组件-GridView示例

     CGridView 以表格的形式显示数据,CGridView 也支持分页和排序,CGridView最基本的用法和ListView类型,也是通过设置 data provider,通常是CActiv ...

  8. Yii Framework 开发教程(30) Zii组件-ListView 示例

    CListView可以用来显示列表,CListView支持使用自定义的View模板显示列表的的记录,因此可以非常灵活的显示数据的表,这点有点像Android的ListView:-). CListVie ...

  9. Yii Framework 开发教程(31) Zii组件-DetailView 示例

     CDetailView为某个Model显示详细内容.这个要显示的Model可以为CModel或是关联数组. CDetailView通过配置 attributes来决定Model的那些属性需要显示 ...

  10. Yii Framework 开发教程Zii组件-Tabs示例

    有关Yii Tab类: http://www.yiichina.com/api/CTabView http://www.yiichina.com/api/CJuiTabs http://blog.cs ...

最新文章

  1. x位全排列(next_permutation)
  2. vue keep-alive保存路由状态2 (高级用法,接上篇)
  3. 视频需求超平常数 10 倍,却节省了 60% 的 IT 成本投入是一种什么样的体验?
  4. 创建二级索引_Mysql创建索引
  5. java判断题_【Java判断题】请大神们进来看下、这些判断题你都知道多少~
  6. 信息学奥赛一本通 1172:求10000以内n的阶乘 | OpenJudge NOI 1.6 14:求10000以内n的阶乘
  7. 国内一些SCM相关论坛站点
  8. 2.6.24内核中对S3C2440的引导启动分析
  9. C#中使用Log4Net记录日志
  10. SQL Server2008安装详细教程
  11. KVM#TyporaKVM虚拟机笔记
  12. 项目管理知识体系指南 (一)
  13. handler机制--Handler使用
  14. LeetCode 9. 回文数 Palindrome Number
  15. 前端使用谷歌打开钉钉的H5页面开发地址
  16. 解决锐捷(武大)校园网 登陆界面乱码 无法登陆
  17. 学考计算机使用说明,《大学计算机》上机考试系统操作指南
  18. 【微信小程序】自定义navigationBar标题栏
  19. linux管道使用_如何在Linux上使用管道
  20. 关于CKCsec安全研究院

热门文章

  1. charles抓包工具的使用:手机抓包设置和安装证书
  2. 再见!LayUI !
  3. 全网最全的IDEA热部署方案,看完弄懂,再也不用加班了~
  4. 面试官:有没有比读写锁更快的锁?
  5. 如何打造高可伸缩的移动电商架构?
  6. linux 格式化硬盘_linux系统装进移动硬盘
  7. 最易理解的傅里叶分析讲解
  8. Centos7.x 装机优化
  9. 【C#】C#获取本地的内网(局域网)和外网(公网)IP地址的方法
  10. 浅谈Java、Python、C++、PHP、JavaScript5大编程语言该如何选择