鍍金池/ 問答/PHP/ laravel中Container依賴注入,多次生成類實例

laravel中Container依賴注入,多次生成類實例

laravelComtroller發(fā)現(xiàn),類依賴注入時,當(dāng)我通過make創(chuàng)建對象時,發(fā)現(xiàn)依賴被創(chuàng)建了多份實例。

代碼如下:

<?php

namespace Tests\Unit;

use Illuminate\Container\Container;
use Tests\TestCase;

interface SessionStorage
{
    public function get($key);

    public function set($key, $value);
}

class FileSessionStorage implements SessionStorage
{
    public function __construct()
    {
        echo "file init \n";
    }

    public function get($key)
    {
        // TODO: Implement get() method.
    }

    public function set($key, $value)
    {
        // TODO: Implement set() method.
    }
}

class MySqlSessionStorage implements SessionStorage
{
    public function __construct()
    {
        echo "mysql init \n";
    }

    public function get($key)
    {
        // TODO: Implement get() method.
    }

    public function set($key, $value)
    {
        // TODO: Implement set() method.

    }
}

class SimpleAuth
{
    protected $session;

    public function __construct(SessionStorage $session)
    {
        $this->session = $session;
    }

    public function get()
    {
        $this->session->get(null);
    }

}

class ExampleTest extends TestCase
{
    /**
     * A basic test example.
     *
     * @return void
     */
    public function testBasicTest()
    {
        $container = Container::getInstance();

        $container->bind( SessionStorage::class, MysqlSessionStorage::class );
        $container->make(SimpleAuth::class);
        echo "-\n";

        $container->bind( SessionStorage::class, FileSessionStorage::class );
        $container->make(SimpleAuth::class);
        echo "-\n";

        $container->bind( SessionStorage::class, MysqlSessionStorage::class );
        $container->make(SimpleAuth::class);
    }
}

代碼的輸出結(jié)果如下:

跟預(yù)期不同的地方:

  1. 第一次make操作的時候,通過構(gòu)造函數(shù)的輸出,可以看出,依賴關(guān)系僅僅被實例化了一次
  2. 但后面再次調(diào)用bind,make發(fā)現(xiàn)構(gòu)造函數(shù)被調(diào)用了兩次
mysql init 
-
file init 
file init 
-
mysql init 
mysql init 

嘗試debug:在make函數(shù)中記錄調(diào)用的次數(shù)。:

public function make($abstract, array $parameters = [])
{
    if (stripos($abstract, 'SessionStorage') !== false) {
        echo "make plus \n";
    }
    return $this->resolve($abstract, $parameters);
}

=================== 輸出結(jié)果 =================
make plus 
make plus 
mysql init 
-
make plus 
make plus 
file init 
make plus 
make plus 
file init 
-
make plus 
make plus 
mysql init 
make plus 
make plus 
mysql init 

怎么破!??!

回答
編輯回答
憶當(dāng)年

綁定方式就注定會有多個實例啊, 將bind改用singleton綁定

2018年5月25日 20:15
編輯回答
陪我終

跟一下源碼,能找到問題:
1、bind()調(diào)用時,會把bind的abstract放在bindings數(shù)組中;
2、make()調(diào)用時,其實調(diào)用resolve,如果是單例(singleton)就會在instances中,查到返回就是單例,直接bind的不是單例,所以每次調(diào)用會有切僅有一次構(gòu)造函數(shù)調(diào)用;
3、但是為啥調(diào)用了多次,問題在于第二次bind()的時候,因為前面已經(jīng)make()過(也就是resolve過),所以在bind()函數(shù)的最后,會調(diào)用rebound()函數(shù),在rebound()函數(shù)中會實例化一個abstract用于調(diào)用rebound的回調(diào);
4、所以,并不是第二次make()的時候調(diào)用了兩次實例化,而是第二次bind()一次,第二次make()一次;因為第二次bind的時候,會觸發(fā)rebound;

(PS:不知道為啥我上傳圖片一直失敗。。。將就看文字吧)

2018年1月25日 23:35