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

Symfony2中文手冊 / 服務(wù)容器

服務(wù)容器

現(xiàn)代php程序全部是對象。一個對象可以負責電子郵件的發(fā)送,另一個對象能讓你把信息持久化到數(shù)據(jù)庫中。在程序中你可以建立一個對象,用來管理產(chǎn)品庫存,或者用另一個對象處理第三方API中的數(shù)據(jù)。結(jié)論是,現(xiàn)代程序可以做許多事,而程序是由許多組織在一起的處理各自任務(wù)的對象構(gòu)成。

本章講的是Symfony中一個特殊的PHP對象,它幫助你實例化、組織和取出程序中的這許多對象。這個對象,被稱為“服務(wù)容器”,它允許你在程序中把對象的組織方式變得標準化與集中化。容器令事情變得簡單,它非???,而且強調(diào)從架構(gòu)上提升代碼的復(fù)用性同時降低藕合性。由于Symfony所有的類都要使用容器,你將學(xué)習(xí)到如何擴展、配置與使用Symfony中的對象。從大的方面講,服務(wù)容器是Symfony的速度與擴展性的最大功臣。

最后,配置與使用服務(wù)容器很簡單。學(xué)完本章,你能通過服務(wù)容器輕松創(chuàng)建自己的對象,也能自定義第三方bundle中的任何對象。你將能夠開始書寫可復(fù)用、可測試、松藕合的代碼,其原因就在于服務(wù)容器令編寫良好代碼變得容易。

如果你在讀完本章還想了解更多,請參考 依賴注入組件。

什么是服務(wù) ?

簡單地說,服務(wù)(Service)可以是任何執(zhí)行“全局”任務(wù)的對象。它在計算機科學(xué)中是一個專有名詞,用來形容一個“為完成某種使命(比如發(fā)送郵件)”而被創(chuàng)建的對象。在程序中,當你需要某個服務(wù)所提供的特定功能時,可以隨時取用該服務(wù)。創(chuàng)建服務(wù)時你不需要做任何特殊的事:只要寫一個PHP類,令其完成某個任務(wù)即可。恭喜,你已經(jīng)創(chuàng)建了一個服務(wù)!

作為原則,一個PHP對象若要成為服務(wù),必須能在程序的全局范圍使用。一個獨立的 Mailer 服務(wù)被“全局”用于發(fā)送郵件信息,然而它所傳輸?shù)倪@些 Message 信息對象并是服務(wù)。類似的,一個 Product 對象,并不是服務(wù),但是一個能夠把產(chǎn)品持久化到數(shù)據(jù)庫中的對象,就是服務(wù)。

這說明了什么?以“服務(wù)”角度思考問題的好處在于,你已經(jīng)開始想要把程序中的每一個功能給分離出來,形成一系列服務(wù)。由于每個服務(wù)只做一件事,你可以在任何需要的時候輕松訪問到這個服務(wù)。對每個服務(wù)的測試和配置變得更加容易,因為它們已經(jīng)從你程序中的其他功能性中分離出來。這個理念被稱為 面向服務(wù)架構(gòu),并非Symfony或PHP專有。把你的程序通過一組獨立存在的服務(wù)類進行“結(jié)構(gòu)化”,是久經(jīng)考驗且廣為人知的面向?qū)ο缶幊讨罴褜嵺`。這個技巧可謂是成為任何一門語言的優(yōu)秀開發(fā)者的關(guān)鍵。

什么是服務(wù)容器 ?

服務(wù)容器(Service Container/dependency injection container)就是一個PHP對象,它管理服務(wù)(即對象)的實例化。

舉例來說,假設(shè)你有一個簡單的類,用于發(fā)送郵件信息。如果沒有服務(wù)容器,你必須在需要的時候手動創(chuàng)建對象。

use Acme\HelloBundle\Mailer;
 $mailer = new Mailer('sendmail');
$mailer->send('ryan@example.com', ...);

這很簡單。一個虛構(gòu)的 Mailer 郵件服務(wù)類,允許你配置郵件的發(fā)送方法(比如 sendmail ,或 smtp ,等等)。但如果你想在其他地方使用郵件服務(wù)怎么辦?你當然不希望每次都重新配置 Mailer 對象。如果你想改變郵件的傳輸方式,把整個程序中所有的 sendmail 改成 smtp 怎么辦?你不得不找到所有創(chuàng)建了 Mailer 的地方,手動去更新。

在容器中創(chuàng)建和配置服務(wù) ?

上面問題的最佳答案,是讓服務(wù)容器來為你創(chuàng)建Mailer對象。為了讓容器正常工作,你先要教會它如何創(chuàng)建Mailer服務(wù)。這是通過配置來實現(xiàn)的,配置方式有YAML,XML或PHP:

PHP:// app/config/services.phpuse Symfony\Component\DependencyInjection\Definition; $container->setDefinition('app.mailer', new Definition(
    'AppBundle\Mailer',
    array('sendmail')));
XML:<!-- app/config/services.xml --><?xml version="1.0" encoding="UTF-8" ?><container xmlns="http://symfony.com/schema/dic/services"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="http://symfony.com/schema/dic/services        http://symfony.com/schema/dic/services/services-1.0.xsd">     <services>
        <service id="app.mailer" class="AppBundle\Mailer">
            <argument>sendmail</argument>
        </service>
    </services></container>
YAML:# app/config/services.ymlservices:
    app.mailer:
        class:        AppBundle\Mailer
        arguments:    [sendmail]

當Symfony初始化時,它要根據(jù)配置信息來建立服務(wù)容器(默認配置文件是 app/config/config.yml )。被加載的正確配置文件是由 AppKernel::registerContainerConfiguration() 方法指示,該方法加載了一個“特定環(huán)境”的配置文件(如config_dev.yml是dev開發(fā)環(huán)境的,而 config_prod.yml 則是生產(chǎn)環(huán)境的)。

一個 Acme\HelloBundle\Mailer 對象的實例已經(jīng)可以通過服務(wù)容器來使用了。容器在任何一個標準的Symfony控制器中可以通過get()快捷方法直接獲得。

class HelloController extends Controller{
    // ...     public function sendEmailAction()
    {
        // ...
        $mailer = $this->get('app.mailer');
        $mailer->send('ryan@foobar.net', ...);
    }}

當你從容器中請求 app.mailer 服務(wù)時,容器構(gòu)造了該對象并返回(實例化之后的)它。這是使用服務(wù)容器的又一個好處。即,一個服務(wù)不會被構(gòu)造(constructed),除非在需要時。如果你定義了一個服務(wù),但在請求(request)過程中從未用到,該服務(wù)不會被創(chuàng)建。這可節(jié)省內(nèi)存并提高程序運行速度。這也意味著在定義大量服務(wù)時,很少會對性能有沖擊。從不使用的服務(wù)絕對不會被構(gòu)造。

附帶一點,Mailer服務(wù)只被創(chuàng)建一次,每次你請求它時返回的是同一實例。這可滿足你的大多數(shù)需求(該行為靈活而強大),但是后面你要了解怎樣才能配置一個擁有多個實例的服務(wù),參考 如何定義非共享服務(wù) 一文。

本例中,控制器繼承了Symfony的Controller基類,給了你一個使用服務(wù)容器的機會,通過get()方法就可從容器中找到并取出 app.mailer 服務(wù)。

服務(wù)參數(shù) ?

通過容器建立新服務(wù)的過程十分簡單明了。參數(shù)(Parameter)可以令服務(wù)的定義更加靈活、有序:

PHP:// app/config/services.phpuse Symfony\Component\DependencyInjection\Definition; $container->setParameter('app.mailer.transport', 'sendmail'); $container->setDefinition('app.mailer', new Definition(
    'AppBundle\Mailer',
    array('%app.mailer.transport%')));
XML:<!-- app/config/services.xml --><?xml version="1.0" encoding="UTF-8" ?><container xmlns="http://symfony.com/schema/dic/services"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="http://symfony.com/schema/dic/services        http://symfony.com/schema/dic/services/services-1.0.xsd">     <parameters>
        <parameter key="app.mailer.transport">sendmail</parameter>
    </parameters>     <services>
        <service id="app.mailer" class="AppBundle\Mailer">
            <argument>%app.mailer.transport%</argument>
        </service>
    </services></container>
YAML:# app/config/services.ymlparameters:
    app.mailer.transport:  sendmailservices:
    app.mailer:
        class:        AppBundle\Mailer
        arguments:    ['%app.mailer.transport%']


結(jié)果就和之前一樣。區(qū)別在于,你是如何定義的服務(wù)。通過把 app.mailer.transport% 百分號給括起來,容器就知道應(yīng)該去找對應(yīng)這個名字的參數(shù)。容器自身生成時,會把每個參數(shù)的值,還原到服務(wù)定義中。

如果你要把一個由 @ 開頭的字符串,在YAML文件中用做參數(shù)值的話(例如一個非常安全的郵件密碼),你需要添加另一個 @ 符號進行轉(zhuǎn)義(這種情況只在YAML格式的配置文件中適用)

# app/config/parameters.ymlparameters:    # This will be parsed as string '@securepass'
    mailer_password: '@@securepass'

配置參數(shù)(parameter)或方法參數(shù)(argument)中的百分號,也必須用另一個%進行轉(zhuǎn)義:

1
<argument type="string">http://symfony.com/?foo=%%s&bar=%%d</argument>

參數(shù)的目的,是要把信息傳給服務(wù)。當然,不用參數(shù)的話,也不會有什么問題。但使用參數(shù)有以下幾個好處:

  • 分離并組織所有服務(wù)“選項”到一個統(tǒng)一的“參數(shù)”鍵下

  • 參數(shù)值可以被用到多個服務(wù)定義中

  • 在bundle中創(chuàng)建服務(wù)時,使用參數(shù)可以令服務(wù)在全局程序中的定制變得容易

是否使用參數(shù),選擇權(quán)在你。高質(zhì)量第三方bundles,始終使用參數(shù),因為他們要讓存于容器中的服務(wù)具備更強的“可配置性”。當然,你可能并不需要參數(shù)帶來的靈活性。

數(shù)組參數(shù) ?

參數(shù)可以包含數(shù)組,參見 數(shù)組參數(shù)(Array Parameters)。

引用(注入)服務(wù) ?

至此,原來的 app.mailer 服務(wù)是簡單的:它在構(gòu)造器中只有一個參數(shù),因此很容易配置。你可以預(yù)見到,在你創(chuàng)建一個需要依賴一個或多個容器中的其他服務(wù)時,容器的真正威力開始體現(xiàn)出來。

例如,你有一個新的服務(wù), NewsletterManager ,它幫助你管理和發(fā)送郵件信息到地址集。 app.mailer 服務(wù)已經(jīng)可以發(fā)郵件了,因此你可以把它用在 NewsletterManager 中來負責信息傳送的部分。這個類看上去像下面這樣:

// src/Acme/HelloBundle/Newsletter/NewsletterManager.phpnamespace Acme\HelloBundle\Newsletter; use Acme\HelloBundle\Mailer; class NewsletterManager{
    protected $mailer;     public function __construct(Mailer $mailer)
    {
        $this->mailer = $mailer;
    }     // ...}

如果不使用服務(wù)容器,你可以在controller中很容易地創(chuàng)建一個NewsletterManager:

use Acme\HelloBundle\Newsletter\NewsletterManager; // ... public function sendNewsletterAction(){
    $mailer = $this->get('app.mailer');
    $newsletter = new NewsletterManager($mailer);
    // ...}

這樣去實現(xiàn)是可以的,但當你以后要對 NewsletterManager 類增加第二或第三個構(gòu)造器參數(shù)時怎么辦?如果你決定重構(gòu)代碼并且重命名這個類時怎么辦?這兩種情況,你都需要找到每一個 NewsletterManager 類被實例化的地方,然后手動個性它。毫無疑問,服務(wù)容器提供了一個更加吸引人的處理方式:

PHP:// app/config/services.phpuse Symfony\Component\DependencyInjection\Definition;use Symfony\Component\DependencyInjection\Reference; $container->setDefinition('app.mailer', ...); $container->setDefinition('app.newsletter_manager', new Definition(
    'AppBundle\Newsletter\NewsletterManager',
    array(new Reference('app.mailer'))));
XML:<!-- app/config/services.xml --><?xml version="1.0" encoding="UTF-8" ?><container xmlns="http://symfony.com/schema/dic/services"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="http://symfony.com/schema/dic/services        http://symfony.com/schema/dic/services/services-1.0.xsd">     <services>
        <service id="app.mailer">
        <!-- ... -->
        </service>         <service id="app.newsletter_manager" class="AppBundle\Newsletter\NewsletterManager">
            <argument type="service" id="app.mailer"/>
        </service>
    </services></container>
YAML:# app/config/services.ymlservices:
    app.mailer:        # ...
    app.newsletter_manager:
        class:     AppBundle\Newsletter\NewsletterManager
        arguments: ['@app.mailer']

在YAML中,特殊的 @app.mailer 語法,告訴容器去尋找一個名為 app.mailer 的服務(wù),然后把這個對象傳給 NewsletterManager 的構(gòu)造器參數(shù)。本例中,指定的 app.mailer 服務(wù)是確實存在的。如果它不存在,則異常會拋出。不過你可以標記依賴可選 – 這個話題將在下一小節(jié)中討論。

(對服務(wù)的)引用是個強大的工具,它允許你創(chuàng)建獨立的服務(wù),卻擁有準確定義的依賴關(guān)系。在這個例子中,  app.newsletter_manager  服務(wù)為了實現(xiàn)功能,需要依賴  app.mailer  服務(wù)。當你在服務(wù)容器中定義了這個依賴時,容器托管了對這個類進行實例化的全部工作。

可選的依賴:Setter注入 ?

將依賴對象注入到構(gòu)造器中是一個辦法,這可確保依賴可以利用(否則構(gòu)造函數(shù)無法執(zhí)行)。但是對一個類來說,如果它有一個可選的依賴,那么“setter注入”是一個更好的方案。這意味著用一個類方法來注入依賴,而不是構(gòu)造器。這個類看上去可能是這樣的:

namespace AppBundle\Newsletter; use AppBundle\Mailer; class NewsletterManager{
    protected $mailer;     public function setMailer(Mailer $mailer)
    {
        $this->mailer = $mailer;
    }     // ...}

服務(wù)定義需要對setter注入做出相應(yīng)調(diào)整:

PHP:// app/config/services.phpuse Symfony\Component\DependencyInjection\Definition;use Symfony\Component\DependencyInjection\Reference; $container->setDefinition('app.mailer', ...); $container->setDefinition('app.newsletter_manager', new Definition(
    'AppBundle\Newsletter\NewsletterManager'))->addMethodCall('setMailer', array(
    new Reference('app.mailer'),));
XML:<!-- app/config/services.xml --><?xml version="1.0" encoding="UTF-8" ?><container xmlns="http://symfony.com/schema/dic/services"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="http://symfony.com/schema/dic/services        http://symfony.com/schema/dic/services/services-1.0.xsd">     <services>
        <service id="app.mailer">
        <!-- ... -->
        </service>         <service id="app.newsletter_manager" class="AppBundle\Newsletter\NewsletterManager">
            <call method="setMailer">
                <argument type="service" id="app.mailer" />
            </call>
        </service>
    </services></container>

YAML:# app/config/services.ymlservices:
    app.mailer:        # ...
    app.newsletter_manager:
        class:     AppBundle\Newsletter\NewsletterManager
        calls:            - [setMailer, ['@app.mailer']]

本節(jié)所實現(xiàn)的過程被稱為“構(gòu)造器注入”和“setter注入”。此外Symfony的容器體系還支持屬性注入(property injection)。