先讲服务定位器,
有些摘录于 http://www.digpage.com/convention.html
Service Locator目的也在于解耦他的模式非常贴合Web这种基于服务和组件的应用的运作特点
优点:

* Service Locator充当了一个运行时的链接器的角色,可以在运行时动态地修改一个类所要选用的服务, 而不必对类作任何的修改。
* 一个类可以在运行时,有针对性地增减、替换所要用到的服务,从而得到一定程度的优化。
* 实现服务提供方、服务使用方完全的解耦,便于独立测试和代码跨框架复用

Service Locator的基本功能

在Yii中Service Locator由 yii\di\ServiceLocator 来实现。 从代码组织上,Yii将Service Locator放到与DI同一层次来对待,都组织在 yii\di 命名空间下。 下面是Service Locator的源代码:
class ServiceLocator extends Component{

// 用于缓存服务、组件等的实例
private $_components = [];// 用于保存服务和组件的定义,通常为配置数组,可以用来创建具体的实例
private $_definitions = [];
// 重载了 getter 方法,使得访问服务和组件就跟访问类的属性一样。
// 同时,也保留了原来Component的 getter所具有的功能。
// 请留意,ServiceLocator 并未重载 __set(),
// 仍然使用 yii\base\Component::__set()
public function __get($name)
{... ...
}// 对比Component,增加了对是否具有某个服务和组件的判断。
public function __isset($name)
{... ...
}// 当 $checkInstance === false 时,用于判断是否已经定义了某个服务或组件
// 当 $checkInstance === true 时,用于判断是否已经有了某人服务或组件的实例
public function has($id, $checkInstance = false)
{return $checkInstance ? isset($this->_components[$id]) :isset($this->_definitions[$id]);
}// 根据 $id 获取对应的服务或组件的实例
public function get($id, $throwException = true)
{... ...
}// 用于注册一个组件或服务,其中 $id 用于标识服务或组件。
// $definition 可以是一个类名,一个配置数组,一个PHP callable,或者一个对象
public function set($id, $definition)
{... ...
}// 删除一个服务或组件
public function clear($id)
{unset($this->_definitions[$id], $this->_components[$id]);
}// 用于返回Service Locator的 $_components 数组或 $_definitions 数组,
// 同时也是 components 属性的getter函数
public function getComponents($returnDefinitions = true)
{... ...
}// 批量方式注册服务或组件,同时也是 components 属性的setter函数
public function setComponents($components)
{... ...
}}

从代码可以看出,Service Locator继承自 yii\base\Component ,这是Yii中的一个基础类, 提供了属性、事件、行为等基本功能,关于Component的有关知识,可以看看 属性(Property) 、 事件(Event) 和 行为(Behavior)。
Service Locator 通过 get() isset() has() 等方法, 扩展了 yii\base\Component 的最基本功能,提供了对于服务和组件的属性化支持。
从功能来看,Service Locator提供了注册服务和组件的 set() setComponents() 等方法, 用于删除的 clear() 。用于读取的 get() 和 getComponents() 等方法。
细心的读者可能一看到 setComponents() 和 getComponents() 就猜到了, Service Locator还具有一个可读写的 components 属性。
Service Locator的数据结构

从上面的代码中,可以看到Service Locator维护了两个数组, $_components 和 $_definitions 。这两个数组均是以服务或组件的ID为键的数组。
其中, $_components 用于缓存存Service Locator中的组件或服务的实例。 Service Locator 为其提供了getter和setter。使其成为一个可读写的属性。 $_definitions 用于保存这些组件或服务的定义。这个定义可以是:

* 配置数组。在向Service Locator索要服务或组件时,这个数组会被用于创建服务或组件的实例。 与DI容器的要求类似,当定义是配置数组时,要求配置数组必须要有 class 元素,表示要创建的是什么类。不然你让Yii调用哪个构造函数?
* PHP callable。每当向Service Locator索要实例时,这个PHP callable都会被调用,其返回值,就是所要的对象。 对于这个PHP callable有一定的形式要求,一是它要返回一个服务或组件的实例。 二是它不接受任何的参数。 至于具体原因,后面会讲到。
* 对象。这个更直接,每当你索要某个特定实例时,直接把这个对象给你就是了。
* 类名。即,使得 is_callable($definition, true) 为真的定义。

从 yii\di\ServiceLocator::set() 的代码:

public function set($id, $definition){

// 当定义为 null 时,表示要从Service Locator中删除一个服务或组件
if ($definition === null) {unset($this->_components[$id], $this->_definitions[$id]);return;
}// 确保服务或组件ID的唯一性
unset($this->_components[$id]);// 定义如果是个对象或PHP callable,或类名,直接作为定义保存
// 留意这里 is_callable的第二个参数为true,所以,类名也可以。
if (is_object($definition) || is_callable($definition, true)) {// 定义的过程,只是写入了 $_definitions 数组$this->_definitions[$id] = $definition;// 定义如果是个数组,要确保数组中具有 class 元素
} elseif (is_array($definition)) {if (isset($definition['class'])) {// 定义的过程,只是写入了 $_definitions 数组$this->_definitions[$id] = $definition;} else {throw new InvalidConfigException("The configuration for the \"$id\" component must contain a \"class\" element.");}// 这也不是,那也不是,那么就抛出异常吧
} else {throw new InvalidConfigException("Unexpected configuration type for the \"$id\" component: "            . gettype($definition));    }}

服务或组件的ID在Service Locator中是唯一的,用于区别彼此。在任何情况下,Service Locator中同一ID只有一个实例、一个定义。也就是说,Service Locator中,所有的服务和组件,只保存一个单例。 这也是正常的逻辑,既然称为服务定位器,你只要给定一个ID,它必然返回一个确定的实例。这一点跟DI容器是一样的。
Service Locator 中ID仅起标识作用,可以是任意字符串,但通常用服务或组件名称来表示。 如,以 db 来表示数据库连接,以 cache 来表示缓存组件等。
至于批量注册的 yii\di\ServiceLocator::setCompoents() 只不过是简单地遍历数组,循环调用 set() 而已。 就算我不把代码贴出来,像你这么聪明的,一下子就可以自己写出来了。
向Service Locator注册服务或组件,其实就是向 $_definitions 数组写入信息而已。
访问Service Locator中的服务Service Locator重载了 get() 使得可以像访问类的属性一样访问已经实例化好的服务和组件。 下面是重载的get() 方法:

public function __get($name){

// has() 方法就是判断 $_definitions 数组中是否已经保存了服务或组件的定义
// 请留意,这个时候服务或组件仅是完成定义,不一定已经实例化
if ($this->has($name)) {// get() 方法用于返回服务或组件的实例return $this->get($name);// 未定义的服务或组件,那么视为正常的属性、行为,
// 调用 yii\base\Component::__get()
} else {return parent::__get($name);
}}

在注册好了服务或组件定义之后,就可以像访问属性一样访问这些服务(组件)。 前提是已经完成注册,不要求已经实例化。 访问这些服务或属性,被转换成了调用 yii\di\ServiceLocator::get() 来获取实例。 下面是使用这种形式访问服务或组件的例子:

// 创建一个Service Locator$serviceLocator = new yii\di\ServiceLocator;

// 注册一个 cache 服务$serviceLocator->set('cache', [

'class' => 'yii\cache\MemCache',
'servers' => [... ...
],]);

// 使用访问属性的方法访问这个 cache 服务$serviceLocator->cache->flushValues();

// 上面的方法等效于下面这个$serviceLocator->get('cache')->flushValues();
在Service Locator中,并未重载 __set() 。所以,Service Locator中的服务和组件看起来就好像只读属性一样。 要向Service Locator中“写”入服务和组件,没有 setter 可以使用,需要调用 yii\di\ServiceLocator::set() 对服务和组件进行注册。
通过Service Locator获取实例与注册服务和组件的简单之极相反,Service Locator在创建获取服务或组件实例的过程要稍微复杂一点。 这一点和DI容器也是很像的。 Service Locator通过 yii\di\ServiceLocator::get() 来创建、获取服务或组件的实例:

public function get($id, $throwException = true){

// 如果已经有实例化好的组件或服务,直接使用缓存中的就OK了
if (isset($this->_components[$id])) {return $this->_components[$id];
}// 如果还没有实例化好,那么再看看是不是已经定义好
if (isset($this->_definitions[$id])) {$definition = $this->_definitions[$id];// 如果定义是个对象,且不是Closure对象,那么直接将这个对象返回if (is_object($definition) && !$definition instanceof Closure) {// 实例化后,保存进 $_components 数组中,以后就可以直接引用了return $this->_components[$id] = $definition;// 是个数组或者PHP callable,调用 Yii::createObject()来创建一个实例} else {// 实例化后,保存进 $_components 数组中,以后就可以直接引用了return $this->_components[$id] = Yii::createObject($definition);}
} elseif ($throwException) {throw new InvalidConfigException("Unknown component ID: $id");// 即没实例化,也没定义,万能的Yii也没办法通过一个任意的ID,
// 就给你找到想要的组件或服务呀,给你个 null 吧。
// 表示Service Locator中没有这个ID的服务或组件。
} else {return null;
}}

Service Locator创建获取服务或组件实例的过程是:

* 看看缓存数组 $_components 中有没有已经创建好的实例。有的话,皆大欢喜,直接用缓存中的就可以了。
* 缓存中没有的话,那就要从定义开始创建了。
* 如果服务或组件的定义是个对象,那么直接把这个对象作为服务或组件的实例返回就可以了。 但有一点要注意,当使用一个PHP callable定义一个服务或组件时,这个定义是一个Closure类的对象。 这种定义虽然也对象,但是可不能把这种对象直接当成服务或组件的实例返回。
* 如果定义是一个数组或者一个PHP callable,那么把这个定义作为参数,调用 Yii::createObject() 来创建实例。

`

底层分析开始

$application = new yii\web\Application($config);//先从这入手
$application->run();//先不急,后面会提到

从上面注释的位置入口

$config为配置文件,这里我们来看看是如何加载配置文件内容的。
顺着application我们能找到:yii\web\Application.php

class Application extends \yii\base\Application
yii\web\Application.php中没有构造函数,所以我们顺理成章的找找
它的父类也就是\yii\base\Application,看看父类里面是否有构造函数
\yii\base\Application没有让我们失望,
构造方法如下:

abstract class Application extends Module{.....public function __construct($config = [])
{Yii::$app = $this;$this->setInstance($this);//将\yii\base\Application中的所有的属性和方法交给Yii::$app->loadedModules数组中$this->state = self::STATE_BEGIN;$this->preInit($config);//加载配置文件的框架信息 如:设置别名,设置框架路径等等 最为重要的是给加载默认组件$this->registerErrorHandler($config);//加载配置文件中的异常组件Component::__construct($config);//将配置文件中的所有信息赋值给Object,也就是Yii::$app->配置文件参数可以直接调用配置文件的内容 如:Yii::$app->vendorPath//输出框架路径  Yii::$app->components['redis']//输出redis配置信息
}

......

下面我们来分析下面的代码

首先是:Yii::$app = $this;

这一句指的是,将\yii\base\Application里所有的公共方法都交给了,Yii::$app,其实Yii大部分信息都在Yii::$app变量中
当然也包括它的父类如:\yii\base\Module \yii\di\ServiceLocator \yii\base\Component \yii\base\Object

$this->setInstance($this);//module里会用到,为getInstance提供
这一句是指向\yii\base\Module

public static function setInstance($instance)//module模块里会用到,为getInstance提供
{if ($instance === null) {unset(Yii::$app->loadedModules[get_called_class()]);} else {Yii::$app->loadedModules[get_class($instance)] = $instance;}
}

这句意思是:将当前类名和存储类的对象变量加入Yii::$app->loadedModules['yii\web\Application']数组中
这样直接通过Yii::$app->loadedModules['yii\web\Application']就可以直接调用这个类
重要的用处在于后面的使用如:
在Module里,也就是module使用的时候,可以通过self::getInstance()获取App对象,类似于Yii::$app。这个研究的比较浅,以后再深入,有疑问的童鞋可以深入

Yii::$app = $this;
$this->setInstance($this);

这两句做的操作是一样的,其实是有所不同的。

Yii::$app = $this;
指的是通过Yii::$app可以调用yii\web\Application及其父类所有的方法

Yii::$app->loadedModules['yii\web\Application']//也能同样做到
loadedModules是一个数组,存放成员类的。它除了能调用当前,还能调用其它许许多多的类....

$this->preInit($config);
这一句是将配置文件中的一些变量设置别名,主要是针对路径、URL之类的

$this->registerErrorHandler($config);
加载异常处理,这块比较深就先不研究了,觉得比较浅的童鞋可以接着补充哈

Component::__construct($config);
这一句指向Object

public function __construct($config = [])
{if (!empty($config)) {Yii::configure($this, $config);//将配置文件里面的所有配置信息赋值给Object,由于Object是大部分类的基类,实际上也就是交给了yii\web\Application 您可以Yii::$app->配置参数来访问配置文件中的内容}$this->init();//下面会细分析
}
foreach ($this->coreComponents() as $id => $component) {//加载默认组件componentsif (!isset($config['components'][$id])) {$config['components'][$id] = $component;} elseif (is_array($config['components'][$id]) && !isset($config['components'][$id]['class'])) {$config['components'][$id]['class'] = $component['class'];}
}

这个就是把当前的配置文件config变量中内容交给Object 再就是讲components默认需要加载的组件类,赋到config配置文件变量中。
Object是基础类,所以绝大部分类都能直接调用配置文件中配置内容
如:
var_dump(Yii::$app->name);
实际上config文件的数组中有name属性

return ['id' => 'app-frontend','name' => '环球在线',

......

再回到Object

public function __construct($config = [])
{if (!empty($config)) {Yii::configure($this, $config);}// 这以上已经执行完了$this->init();//接下来分析这一句
}
$this->init();

这句实际上执行的是yii\base\Module.php

/*取出控制器的命名空间,您也可以理解为路径(* 注:第一次加载它的时候。)表面看起来没有太多的意义,实则不然,yii2的大部分组件都是以Object为基类的,所以init函数很重要,控制器、模型、模块module,自定义组件等都可以去实现init方法。比如说默认的控制器SiteController吧。在里面写一个init方法,当你访问site控制器下任意的$route路径,都会先执行init方法。作用大不?其它组件同样如此。
*/
public function init()
{if ($this->controllerNamespace === null) {$class = get_class($this);if (($pos = strrpos($class, '\\')) !== false) {$this->controllerNamespace = substr($class, 0, $pos) . '\\controllers';}}
}

至此第一部分执行完了,再看第二部分吧

$application = new yii\web\Application($config);//分析完成
$application->run();//加载主要组件,运行默认控制器

接下拆分 $application->run
用ide指向直接到了\yii\base\Application.php

public function run()
{try {$this->state = self::STATE_BEFORE_REQUEST;$this->trigger(self::EVENT_BEFORE_REQUEST);//加载事件函数函数的。这一句以后再分析吧$this->state = self::STATE_HANDLING_REQUEST;$response = $this->handleRequest($this->getRequest());//这里才是加载控制器的地方,我也迷惑了半天$this->state = self::STATE_AFTER_REQUEST;$this->trigger(self::EVENT_AFTER_REQUEST);//加载事件函数$this->state = self::STATE_SENDING_RESPONSE;$response->send();//将页面内容输入缓冲,然后输出$this->state = self::STATE_END;return $response->exitStatus;} catch (ExitException $e) {$this->end($e->statusCode, isset($response) ? $response : null);return $e->statusCode;}
}
$response = $this->handleRequest($this->getRequest());

摘出来的这句:

$this->getRequest()//获取Request对象
这个没啥可说的,获取Request对象

$this->handleRequest($this->getRequest());
通过指向\yii\web\Application.php

public function handleRequest($request)
{if (empty($this->catchAll)) {list ($route, $params) = $request->resolve();//取出路由及参数} else {$route = $this->catchAll[0];$params = $this->catchAll;unset($params[0]);}try {Yii::trace("Route requested: '$route'", __METHOD__);$this->requestedRoute = $route;$result = $this->runAction($route, $params);//运行控制器中的Acition,下面有详细介绍if ($result instanceof Response) {return $result;} else {$response = $this->getResponse();/*这个是加载yii\base\Response类,在外部可以Yii::$app->get('response')、Yii::$app->getResponse()、Yii::$app->response 等等方式来加载response类,主要用来加载http状态,及头信息,如301,302,404,ajax头等等的获取*/if ($result !== null) {$response->data = $result;}return $response;}} catch (InvalidRouteException $e) {throw new NotFoundHttpException(Yii::t('yii', 'Page not found.'), $e->getCode(), $e);}
}$result = $this->runAction($route, $params);//运行控制器

我们再看看这句指向:\yii\base\Module.php

public function runAction($route, $params = [])
{$parts = $this->createController($route);//根据路由创建控制器if (is_array($parts)) {/* @var $controller Controller */list($controller, $actionID) = $parts;//获得$actionId和$controller$oldController = Yii::$app->controller;Yii::$app->controller = $controller;$result = $controller->runAction($actionID, $params);//运行使用控制器加载 action方法Yii::$app->controller = $oldController;//将对象交给Yii::$app->controller 这里面起的作用应该是运行控制器,最后释放控制器的对象变量return $result;} else {$id = $this->getUniqueId();throw new InvalidRouteException('Unable to resolve the request "' . ($id === '' ? $route : $id . '/' . $route) . '".');}
}

Module里有一段:

$controller = Yii::createObject($this->controllerMap[$id], [$id, $this]);
其实在

$this->createController($route)
这个时候创建了控制器对象

下面看看如何加载action的。

$result = $controller->runAction($actionID, $params);//运行使用控制器加载 action方法
上面这句指向:

public function runAction($id, $params = [])
{$action = $this->createAction($id);//创建actionif ($action === null) {throw new InvalidRouteException('Unable to resolve the request: ' . $this->getUniqueId() . '/' . $id);}Yii::trace("Route to run: " . $action->getUniqueId(), __METHOD__);if (Yii::$app->requestedAction === null) {Yii::$app->requestedAction = $action;}$oldAction = $this->action;$this->action = $action;$modules = [];$runAction = true;// 加载默认模块如:Application log等。再调用模块内的beforeAction方法foreach ($this->getModules() as $module) {if ($module->beforeAction($action)) {array_unshift($modules, $module);} else {$runAction = false;break;}}$result = null;if ($runAction && $this->beforeAction($action)) {//执行beforeAction// run the action$result = $action->runWithParams($params);//执行控制器里的action$result = $this->afterAction($action, $result);//执行beforeAction// call afterAction on modulesforeach ($modules as $module) {/* @var $module Module */$result = $module->afterAction($action, $result);}}$this->action = $oldAction;return $result;
}$result = $action->runWithParams($params);//执行控制器里的action

这里才是真正执行action的地方

首先弄清楚$action是什么类?这里可以var_dump一下就清楚了,刚开始我也被编辑器迷糊了找半天
$action类是yii\base\InlineAction

public function runWithParams($params)
{$args = $this->controller->bindActionParams($this, $params);//对action的参数进行分析,并且赋值给控制器Yii::trace('Running action: ' . get_class($this->controller) . '::' . $this->actionMethod . '()', __METHOD__);if (Yii::$app->requestedParams === null) {Yii::$app->requestedParams = $args;}return call_user_func_array([$this->controller, $this->actionMethod], $args);//用控制器类去执行action方法,并且带上参数。
}

Yii2.0  ar  joinwith  sql  yii

分享到: QQ空间 新浪微博 腾讯微博 人人网 微信 QQ好友

共 4 条评论

  • 默认排序
  • 最后评论
  • fecommerce 评论于 2016-05-20 17:01 举报 

    总体写的还不错,不过有一些地方有疏漏

     回复 0 0

  • fecommerce 评论于 2016-05-20 17:11 举报 

    比较重要的漏点就是:初始化的引导过程bootstrap
    另外一个就是controllerMap过程和actionMap过程

    这里我想说的是bootstrap过程。我顺着你的代码说:
    1.

    public function __construct($config = []){Yii::$app = $this;$this->setInstance($this);$this->state = self::STATE_BEGIN;$this->preInit($config);$this->registerErrorHandler($config);Component::__construct($config);}
    

    上面的代码:Component::construct($config); 最终会执行Object的construct($config)。
    Object代码你贴上去了:

    public function __construct($config = [])
    {if (!empty($config)) {Yii::configure($this, $config);//将配置文件里面的所有配置信息赋值给Object,由于Object是大部分类的基类,实际上也就是交给了yii\web\Application 您可以Yii::$app->配置参数来访问配置文件中的内容}$this->init();//下面会细分析
    }
    

    需要注意的是代码: $this->init();
    现在回到yii\base\Application

    public function init(){$this->state = self::STATE_INIT;$this->bootstrap();}
    

    在这里可以看到执行了bootstrap()方法,这是系统的引导过程,这个 是非常重要的,被你疏漏了。

    protected function bootstrap(){if ($this->extensions === null) {$file = Yii::getAlias('@vendor/yiisoft/extensions.php');$this->extensions = is_file($file) ? include($file) : [];}foreach ($this->extensions as $extension) {if (!empty($extension['alias'])) {foreach ($extension['alias'] as $name => $path) {Yii::setAlias($name, $path);}}if (isset($extension['bootstrap'])) {$component = Yii::createObject($extension['bootstrap']);if ($component instanceof BootstrapInterface) {Yii::trace('Bootstrap with ' . get_class($component) . '::bootstrap()', __METHOD__);$component->bootstrap($this);} else {Yii::trace('Bootstrap with ' . get_class($component), __METHOD__);}}}foreach ($this->bootstrap as $class) {$component = null;if (is_string($class)) {if ($this->has($class)) {$component = $this->get($class);} elseif ($this->hasModule($class)) {$component = $this->getModule($class);} elseif (strpos($class, '\\') === false) {throw new InvalidConfigException("Unknown bootstrapping component ID: $class");}}if (!isset($component)) {$component = Yii::createObject($class);}if ($component instanceof BootstrapInterface) {Yii::trace('Bootstrap with ' . get_class($component) . '::bootstrap()', __METHOD__);$component->bootstrap($this);} else {Yii::trace('Bootstrap with ' . get_class($component), __METHOD__);}}}

    这里包括 yii插件,组件,模块 这三个方面的bootstrap初始化过程。
    详细参看文章:http://www.fancyecommerce.com/2016/05/18/yii2-初始化的bootstrap过程-引导/
    这里不细说了。
    另外,index.php里面的autoload部分,也得细致说一下各个方面的加载
    在另外controllerMap actionMap部分也细致说一下,基本就齐全了。

     回复 0 0

  • 鲁鲁槟 评论于 2016-07-31 14:35 举报 

    这个后面的内容不就是这篇文章的吗?http://www.yiichina.com/code/546

     回复 0 0

  • 598516810 评论于 2016-11-18 18:56 举报 

    $_definitions是在哪被赋值的呢?

Service Locator服务定位器相关推荐

  1. php service locator,服务定位器模式(Service Locator)

    服务定位器模式(Service Locator) 服务定位器模式被一些人认为是一种反面模式.它违反了依赖倒置原则.该模式隐藏类的依赖,而不是暴露依赖(如果暴露可通过依赖注入的方式注入依赖).当某项服务 ...

  2. 设计模式 - 服务定位模式 Service Locator Pattern

    译者序:看 spring framework 时候了解到的 Service Locator 模式,顺便搜到了这篇文章,感觉很棒,顺手翻译下,好安利给其他小伙伴. 原文链接:http://gamepro ...

  3. php service locator,Yii源码解读-服务定位器(ServiceLocator)

    SL的目的也是解耦,并且非常适合基于服务和组件的应用. Service Locator充当了一个运行时的链接器的角色,可以在运行时动态地修改一个类所要选用的服务, 而不必对类作任何的修改. 一个类可以 ...

  4. 三十六、服务定位器模式 (Service Locator Pattern)

    服务定位器模式(Service Locator Pattern)用于想使用 JNDI 查询定位各种服务的时候 考虑到为某个服务查找 JNDI 的代价很高,服务定位器模式充分利用了缓存技术 在首次请求某 ...

  5. php 服务定位,服务定位器(Service Locator)

    服务定位器 服务定位器是一个了解如何提供各种应用所需的服务(或组件)的对象.在服务定位器中,每个组件都只有一个单独的实例,并通过ID 唯一地标识.用这个 ID 就能从服务定位器中得到这个组件. 在 Y ...

  6. Java服务定位器模式

    服务定位器模式(Service Locator Pattern)用在我们想使用 JNDI 查询定位各种服务的时候.考虑到为某个服务查找 JNDI 的代价很高,服务定位器模式充分利用了缓存技术.在首次请 ...

  7. 避免在ASP.NET Core中使用服务定位器模式

    (此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 题记:服务定位器(Service Locator)作为一种反模式,一般情况下应该避免使用,在 ...

  8. 服务定位器 - Caliburn.Micro 文档系列

    文章目录 服务定位器 (Service Locator) 入门 为 IoC 注入功能 在你的应用中使用 IOC 获取单一服务 获得一系列服务 注入实例 服务定位器 (Service Locator) ...

  9. Service Locator 模式

    什么是Service Locator 模式? 服务定位模式(Service Locator Pattern)是一种软件开发中的设计模式,通过应用强大的抽象层,可对涉及尝试获取一个服务的过程进行封装.该 ...

最新文章

  1. 开源 免费 java CMS - FreeCMS1.2-功能说明-网上调查
  2. GAN完整理论推导与实现,Perfect!
  3. node.js require()缓存-可能无效?
  4. Qt文档阅读笔记-两视图共享模型实现冻结列效果(frozencolumn解析与实例)
  5. CSS, JavaScript, jQuery实现标签页切换
  6. vector添加元素java_在Java中的Vector中间添加元素
  7. 剑指offer面试题31. 栈的压入、弹出序列(链表)
  8. JDK1.8优雅的集合排序(集合的排序)
  9. 2018麦考林杂志计算机科学,加拿大大学2020年最新的麦考林杂志排名
  10. excel合并两列内容_Excel 两列合并成一列,又一种快捷方法!
  11. Android开发————简易App设计(三)
  12. Codeforces Round #734 (Div. 3)_B2. Wonderful Coloring - 2(贪心)
  13. QStyleOption类型
  14. HIF转16位TIF或者PNG
  15. android 视频解决方案,短视频SDK升级为短视频解决方案
  16. 《乐跑宝典》读书笔记
  17. html给字添加音频,如何给视频加字幕并与语音同步?方法用得好就是这么简单!...
  18. android-控件之Textview属性
  19. VV overall
  20. jitsi-meet安卓端进入房间就断开连接

热门文章

  1. 计算机维护工程师好找工作吗,一个IT运维工程师的内心独白
  2. linux ubuntu22.04各种问题的解决
  3. Python爬虫经典案例:爬取天猫评论 完整源代码
  4. PHP七牛云上传大视频文件异步存为m3u8格式(包括回调过程)
  5. python(基本介绍)
  6. 古月居ROS 入门21讲--PA5-PA8笔记
  7. 计算机网络的检验批怎么分,智能建筑计算机网络系统检验批质量验收记录(2页)-原创力文档...
  8. MySQL的in条件走不走索引
  9. 计算机图形的组合和旋转教案,《图形的旋转与扭曲》教学设计
  10. IDEA快捷键(帮你省了一个鼠标钱)