亚洲国产日韩欧美一区二区三区,精品亚洲国产成人av在线,国产99视频精品免视看7,99国产精品久久久久久久成人热,欧美日韩亚洲国产综合乱

ディレクトリ 検索
閱讀前篇 簡介 Yii 是什么 從 Yii 1.1 升級(jí) 入門 安裝 Yii 運(yùn)行應(yīng)用 第一次問候 使用Forms 數(shù)據(jù)庫應(yīng)用 使用 Gii 生成代碼 進(jìn)階 應(yīng)用結(jié)構(gòu) 概述 入口腳本 應(yīng)用(Applications) 應(yīng)用組件(Application Components) 控制器(Controllers) 模型(Models) 視圖(views) 模塊(Modules) 過濾器(Filters) 小部件(Widgets) 前端資源(Assets) 擴(kuò)展(Extensions) 請求處理 運(yùn)行概述 啟動(dòng)引導(dǎo)(Bootstrapping) 路由和創(chuàng)建URL 請求(Requests) 響應(yīng)(Responses) Sessions 和 Cookies 錯(cuò)誤處理(Handling Errors) 日志(Logging) 關(guān)鍵概念 組件(Component) 屬性(Property) 事件(Events) 行為(Behaviors) 配置(Configurations) 別名(Aliases) 類自動(dòng)加載(Autoloading) 服務(wù)定位器(Service Locator) 依賴注入容器(Dependency Injection Container) 配合數(shù)據(jù)庫工作 數(shù)據(jù)庫訪問 (Data Access Objects) 查詢生成器(Query Builder) 活動(dòng)記錄(Active Record) 數(shù)據(jù)庫遷移(Migrations) Sphinx Redis MongoDB Elasticsearch 接收用戶數(shù)據(jù) 創(chuàng)建表單(Creating Forms) 輸入驗(yàn)證(Validating Input) 文件上傳(Uploading Files) 收集列表輸入(Collecting Tabular Input) 多模型的復(fù)合表單(Getting Data for Multiple Models) 顯示數(shù)據(jù) 格式化輸出數(shù)據(jù)(Data Formatting) 分頁(Pagination) 排序(Sorting) 數(shù)據(jù)提供器(Data Providers) 數(shù)據(jù)小部件(Data Widgets) 客戶端腳本使用(Working with Client Scripts) 主題(Theming) 安全 認(rèn)證(Authentication) 授權(quán)(Authorization) 處理密碼(Working with Passwords) 客戶端認(rèn)證(Auth Clients) 最佳安全實(shí)踐(Best Practices) 緩存 概述 數(shù)據(jù)緩存 片段緩存 頁面緩存 HTTP 緩存 RESTfull Web服務(wù) 快速入門(Quick Start) 資源(Resources) 控制器(Controllers) 路由(Routing) 格式化響應(yīng)(Response Formatting) 授權(quán)認(rèn)證(Authentication) 速率限制(Rate Limiting) 版本(Versioning) 錯(cuò)誤處理(Error Handling) 開發(fā)工具 調(diào)試工具欄和調(diào)試器 使用Gii生成代碼 生成API文檔 測試 概述(Overview) 配置測試環(huán)境(Testing environment setup) 單元測試(Unit Tests) 功能測試(Function Tests) 驗(yàn)收測試(Acceptance Tests) 測試夾具(Fixtures) 高級(jí)專題 高級(jí)應(yīng)用模板 創(chuàng)建自定義應(yīng)用程序結(jié)構(gòu) 控制臺(tái)命令 核心驗(yàn)證器(Core Validators) 國際化 收發(fā)郵件 性能優(yōu)化 共享主機(jī)環(huán)境 模板引擎 集成第三方代碼 小部件 Bootstrap 小部件 Jquery UI 助手類 概述 Array 助手(ArrayHelper) Html 助手(Html) Url 助手(Url)
テキスト

依賴注入容器

依賴注入容器

依賴注入(Dependency Injection,DI)容器就是一個(gè)對象,它知道怎樣初始化并配置對象及其依賴的所有對象。Martin 的文章?已經(jīng)解釋了 DI 容器為什么很有用。這里我們主要講解 Yii 提供的 DI 容器的使用方法。

依賴注入

Yii 通過 yii\di\Container 類提供 DI 容器特性。它支持如下幾種類型的依賴注入:

  • 構(gòu)造方法注入;
  • Setter 和屬性注入;
  • PHP 回調(diào)注入.

構(gòu)造方法注入

在參數(shù)類型提示的幫助下,DI 容器實(shí)現(xiàn)了構(gòu)造方法注入。當(dāng)容器被用于創(chuàng)建一個(gè)新對象時(shí),類型提示會(huì)告訴它要依賴什么類或接口。容器會(huì)嘗試獲取它所依賴的類或接口的實(shí)例,然后通過構(gòu)造器將其注入新的對象。例如:

class Foo{
    public function __construct(Bar $bar)
    {
    }
}

$foo = $container->get('Foo');
// 上面的代碼等價(jià)于:$bar = new Bar;
$foo = new Foo($bar);

Setter 和屬性注入

Setter 和屬性注入是通過配置提供支持的。當(dāng)注冊一個(gè)依賴或創(chuàng)建一個(gè)新對象時(shí),你可以提供一個(gè)配置,該配置會(huì)提供給容器用于通過相應(yīng)的 Setter 或?qū)傩宰⑷胍蕾?。例如?/p>

use yii\base\Object;

class Foo extends Object{
    public $bar;

    private $_qux;

    public function getQux()
    {
        return $this->_qux;
    }

    public function setQux(Qux $qux)
    {
        $this->_qux = $qux;
    }
}

$container->get('Foo', [], [
    'bar' => $container->get('Bar'),
    'qux' => $container->get('Qux'),
]);

PHP 回調(diào)注入

這種情況下,容器將使用一個(gè)注冊過的 PHP 回調(diào)創(chuàng)建一個(gè)類的新實(shí)例?;卣{(diào)負(fù)責(zé)解決依賴并將其恰當(dāng)?shù)刈⑷胄聞?chuàng)建的對象。例如:

$container->set('Foo', function () {
    return new Foo(new Bar);
});

$foo = $container->get('Foo');

注冊依賴關(guān)系

可以用 yii\di\Container::set() 注冊依賴關(guān)系。注冊會(huì)用到一個(gè)依賴關(guān)系名稱和一個(gè)依賴關(guān)系的定義。依賴關(guān)系名稱可以是一個(gè)類名,一個(gè)接口名或一個(gè)別名。依賴關(guān)系的定義可以是一個(gè)類名,一個(gè)配置數(shù)組,或者一個(gè) PHP 回調(diào)。

$container = new \yii\di\Container;

// 注冊一個(gè)同類名一樣的依賴關(guān)系,這個(gè)可以省略。$container->set('yii\db\Connection');

// 注冊一個(gè)接口// 當(dāng)一個(gè)類依賴這個(gè)接口時(shí),相應(yīng)的類會(huì)被初始化作為依賴對象。$container->set('yii\mail\MailInterface', 'yii\swiftmailer\Mailer');

// 注冊一個(gè)別名。// 你可以使用 $container->get('foo') 創(chuàng)建一個(gè) Connection 實(shí)例$container->set('foo', 'yii\db\Connection');

// 通過配置注冊一個(gè)類// 通過 get() 初始化時(shí),配置將會(huì)被使用。$container->set('yii\db\Connection', [
    'dsn' => 'mysql:host=127.0.0.1;dbname=demo',
    'username' => 'root',
    'password' => '',
    'charset' => 'utf8',
]);

// 通過類的配置注冊一個(gè)別名// 這種情況下,需要通過一個(gè) “class” 元素指定這個(gè)類$container->set('db', [
    'class' => 'yii\db\Connection',
    'dsn' => 'mysql:host=127.0.0.1;dbname=demo',
    'username' => 'root',
    'password' => '',
    'charset' => 'utf8',
]);

// 注冊一個(gè) PHP 回調(diào)// 每次調(diào)用 $container->get('db') 時(shí),回調(diào)函數(shù)都會(huì)被執(zhí)行。$container->set('db', function ($container, $params, $config) {
    return new \yii\db\Connection($config);
});

// 注冊一個(gè)組件實(shí)例// $container->get('pageCache') 每次被調(diào)用時(shí)都會(huì)返回同一個(gè)實(shí)例。$container->set('pageCache', new FileCache);

Tip: 如果依賴關(guān)系名稱和依賴關(guān)系的定義相同,則不需要通過 DI 容器注冊該依賴關(guān)系。

通過?set()?注冊的依賴關(guān)系,在每次使用時(shí)都會(huì)產(chǎn)生一個(gè)新實(shí)例??梢允褂?yii\di\Container::setSingleton() 注冊一個(gè)單例的依賴關(guān)系:

$container->setSingleton('yii\db\Connection', [
    'dsn' => 'mysql:host=127.0.0.1;dbname=demo',
    'username' => 'root',
    'password' => '',
    'charset' => 'utf8',
]);

解決依賴關(guān)系

注冊依賴關(guān)系后,就可以使用 DI 容器創(chuàng)建新對象了。容器會(huì)自動(dòng)解決依賴關(guān)系,將依賴實(shí)例化并注入新創(chuàng)建的對象。依賴關(guān)系的解決是遞歸的,如果一個(gè)依賴關(guān)系中還有其他依賴關(guān)系,則這些依賴關(guān)系都會(huì)被自動(dòng)解決。

可以使用 yii\di\Container::get() 創(chuàng)建新的對象。該方法接收一個(gè)依賴關(guān)系名稱,它可以是一個(gè)類名,一個(gè)接口名或一個(gè)別名。依賴關(guān)系名或許是通過?set()?或?setSingleton()?注冊的。你可以隨意地提供一個(gè)類的構(gòu)造器參數(shù)列表和一個(gè)configuration?用于配置新創(chuàng)建的對象。例如:

// "db" 是前面定義過的一個(gè)別名$db = $container->get('db');

// 等價(jià)于: $engine = new \app\components\SearchEngine($apiKey, ['type' => 1]);$engine = $container->get('app\components\SearchEngine', [$apiKey], ['type' => 1]);

代碼背后,DI 容器做了比創(chuàng)建對象多的多的工作。容器首先將檢查類的構(gòu)造方法,找出依賴的類或接口名,然后自動(dòng)遞歸解決這些依賴關(guān)系。

如下代碼展示了一個(gè)更復(fù)雜的示例。UserLister?類依賴一個(gè)實(shí)現(xiàn)了?UserFinderInterface?接口的對象;UserFinder?類實(shí)現(xiàn)了這個(gè)接口,并依賴于一個(gè)?Connection?對象。所有這些依賴關(guān)系都是通過類構(gòu)造器參數(shù)的類型提示定義的。通過屬性依賴關(guān)系的注冊,DI 容器可以自動(dòng)解決這些依賴關(guān)系并能通過一個(gè)簡單的?get('userLister')?調(diào)用創(chuàng)建一個(gè)新的?UserLister?實(shí)例。

namespace app\models;

use yii\base\Object;
use yii\db\Connection;
use yii\di\Container;

interface UserFinderInterface{
    function findUser();
}

class UserFinder extends Object implements UserFinderInterface{
    public $db;

    public function __construct(Connection $db, $config = [])
    {
        $this->db = $db;
        parent::__construct($config);
    }

    public function findUser()
    {
    }
}

class UserLister extends Object{
    public $finder;

    public function __construct(UserFinderInterface $finder, $config = [])
    {
        $this->finder = $finder;
        parent::__construct($config);
    }
}

$container = new Container;
$container->set('yii\db\Connection', [
    'dsn' => '...',
]);
$container->set('app\models\UserFinderInterface', [
    'class' => 'app\models\UserFinder',
]);
$container->set('userLister', 'app\models\UserLister');

$lister = $container->get('userLister');

// 等價(jià)于:

$db = new \yii\db\Connection(['dsn' => '...']);
$finder = new UserFinder($db);
$lister = new UserLister($finder);

實(shí)踐中的運(yùn)用

當(dāng)在應(yīng)用程序的入口腳本中引入?Yii.php?文件時(shí),Yii 就創(chuàng)建了一個(gè) DI 容器。這個(gè) DI 容器可以通過 Yii::$container 訪問。當(dāng)調(diào)用 Yii::createObject() 時(shí),此方法實(shí)際上會(huì)調(diào)用這個(gè)容器的 yii\di\Container::get() 方法創(chuàng)建新對象。如上所述,DI 容器會(huì)自動(dòng)解決依賴關(guān)系(如果有)并將其注入新創(chuàng)建的對象中。因?yàn)?Yii 在其多數(shù)核心代碼中都使用了 Yii::createObject() 創(chuàng)建新對象,所以你可以通過 Yii::$container 全局性地自定義這些對象。

例如,你可以全局性自定義 yii\widgets\LinkPager 中分頁按鈕的默認(rèn)數(shù)量:

\Yii::$container->set('yii\widgets\LinkPager', ['maxButtonCount' => 5]);

這樣如果你通過如下代碼在一個(gè)視圖里使用這個(gè)掛件,它的?maxButtonCount?屬性就會(huì)被初始化為 5 而不是類中定義的默認(rèn)值 10。

echo \yii\widgets\LinkPager::widget();

然而你依然可以覆蓋通過 DI 容器設(shè)置的值:

echo \yii\widgets\LinkPager::widget(['maxButtonCount' => 20]);

另一個(gè)例子是借用 DI 容器中自動(dòng)構(gòu)造方法注入帶來的好處。假設(shè)你的控制器類依賴一些其他對象,例如一個(gè)旅館預(yù)訂服務(wù)。你可以通過一個(gè)構(gòu)造器參數(shù)聲明依賴關(guān)系,然后讓 DI 容器幫你自動(dòng)解決這個(gè)依賴關(guān)系。

namespace app\controllers;

use yii\web\Controller;
use app\components\BookingInterface;

class HotelController extends Controller{
    protected $bookingService;

    public function __construct($id, $module, BookingInterface $bookingService, $config = [])
    {
        $this->bookingService = $bookingService;
        parent::__construct($id, $module, $config);
    }
}

如果你從瀏覽器中訪問這個(gè)控制器,你將看到一個(gè)報(bào)錯(cuò)信息,提醒你?BookingInterface?無法被實(shí)例化。這是因?yàn)槟阈枰嬖V DI 容器怎樣處理這個(gè)依賴關(guān)系。

\Yii::$container->set('app\components\BookingInterface', 'app\components\BookingService');

現(xiàn)在如果你再次訪問這個(gè)控制器,一個(gè)?app\components\BookingService?的實(shí)例就會(huì)被創(chuàng)建并被作為第三個(gè)參數(shù)注入到控制器的構(gòu)造器中。

什么時(shí)候注冊依賴關(guān)系

由于依賴關(guān)系在創(chuàng)建新對象時(shí)需要解決,因此它們的注冊應(yīng)該盡早完成。如下是推薦的實(shí)踐:

  • 如果你是一個(gè)應(yīng)用程序的開發(fā)者,你可以在應(yīng)用程序的入口腳本或者被入口腳本引入的腳本中注冊依賴關(guān)系。
  • 如果你是一個(gè)可再分發(fā)擴(kuò)展的開發(fā)者,你可以將依賴關(guān)系注冊到擴(kuò)展的引導(dǎo)類中。

總結(jié)

依賴注入和服務(wù)定位器都是流行的設(shè)計(jì)模式,它們使你可以用充分解耦且更利于測試的風(fēng)格構(gòu)建軟件。強(qiáng)烈推薦你閱讀?Martin 的文章?,對依賴注入和服務(wù)定位器有個(gè)更深入的理解。

Yii 在依賴住入(DI)容器之上實(shí)現(xiàn)了它的服務(wù)定位器。當(dāng)一個(gè)服務(wù)定位器嘗試創(chuàng)建一個(gè)新的對象實(shí)例時(shí),它會(huì)把調(diào)用轉(zhuǎn)發(fā)到 DI 容器。后者將會(huì)像前文所述那樣自動(dòng)解決依賴關(guān)系。
前の記事: 次の記事: