从另一个角度来分析 Laravel 框架的依赖注入功能
22

file

这个 StackOverflow回答 完美的解释了依赖注入这个概念

从根本上说,依赖注入不是让对象创建一个依赖关系,也不是让工厂对象去创建对象,而是将所需的依赖变成一个外部对象,使之成为一个"某些人的问题”

你为"某些人的问题”注入了类的依赖关系。在Laravel中,这个"某人”是 服务容器 。在Laravel中,服务容器负责通过构造函数注入类的依赖关系。

任何时候,你在一个控制器类中请求一个依赖,这个服务容器负责:

  1. 自动地在构造函数中检测依赖关系
  2. 如果需要构建这个依赖关系
  3. 通过构造函数创建对象形成依赖关系

来看一个非常简单的例子。

<?php
namespace App\Http\Controllers;
use App\User;
use App\Repositories\UserRepository;
use App\Http\Controllers\Controller;
class UserController extends Controller
{
    protected $userRepository;
    public function __construct(UserRepository $userRepository)
    {
        $this->userRepository = $userRepository;
    }
    public function show($id)
    {
        $user = $this->userRepository->find($id);
        return view('user.profile', ['user' => $user]);
    }
}

假如,你有一个 UserController 类需要 UserRepository 作为一个构造函数依赖。

  1. 服务容器使用 PHP反射类 来检测,事实 UserRepository 需要被优先解析。
  2. 然后,它构造 UserRepository 实例。
  3. 然后,它构造 UserController 类实例。

依赖关系是如何被解析和注入的,我被很多 Laravel 开发人员不知道这个简单而强大的技术感到迷惑。 这是一个非常强大的技术,它可以被用来解决复杂对象的依赖关系。

如果由于某种原因,您不希望Laravel自动构建一个对象,您还可以通过传递一个可用于创建依赖关系的回调来告诉Laravel Service Container如何构造该对象。

<?php
$container->bind('My\Service', function($container) {
  return new My\Service($container->make('My\AnotherService'));
});

您需要创建一个服务提供商来注册上述服务。

<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class MyServiceProvider extends ServiceProvider
{
    public function register()
    {
        $this->app->singleton(\My\Service::class, function ($app) {
            return new \My\Service($app->make('My\AnotherService'));
        });
    }
}

My\Service 需要被解析的时候,负责返回一个对象的回调函数就会被调用。

<?php
namespace App\Http\Controllers;
use App\User;
use App\Http\Controllers\Controller;
class MyController extends Controller
{
    protected $myService;
    public function __construct(\My\Service $myService)
    {
        $this->myService = $myService;
    }
    // .. 方法
}

真实的例子

假设你的应用需要Facebook的PHP SDK来访问Facebook的API,你的控制器就是这样的:

<?php
namespace App\Http\Controllers;
use App\User;
use App\Http\Controllers\Controller;
use Facebook\Facebook;
class FacebookApiAccessController extends Controller
{
    protected $facebook;
    public function __construct(Facebook\Facebook $facebook)
    {
        $this->facebook = $facebook;
    }
    //.. action methods here
}

现在,您需要告诉Service Container如何构建 Facebook\Facebook的实例.

<?php
$container->singleton('Facebook\Facebook', function() {
  return new \Facebook\Facebook([
    'app_id' => config('services.facebook.app_id'),
    'app_secret' => config('services.facebook.app_secret'),
    'default_graph_version' => 'v2.10',
  ]);
});

注意,我已经调用了方法singleton而不是bind。 唯一的区别是用singleton注册的服务被缓存,随后的解析服务调用返回缓存的服务。

结论

依赖注入是一种强大的技术,你可以在 Laravel 中用来简化对象的创建. 默认情况下, Laravel 的服务容器会自动的用反射去检测和解决依赖关系. 但是, 你可以指定回调来解析服务.


Practice makes perfect.

原文地址:https://medium.com/devcupboard/a-differe...

译文地址:https://laravel-china.org/topics/7496/an...

本帖已被设为精华帖!
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 4

还是看不太明白 bindsingleton 的区别。

9个月前

学习了,有个问题请教一下,如果一个controller依赖很多服务(比如说6个)怎么办,是在构造函数中一次性全部注册进来,还是分action单独注册

9个月前

@tiandaye

A very simple proof is to test out the bevahior. Since the Laravel Application simply extends Illuminate\Container\Container, we'll use just the container (in my case I even only added the container as dependency to my composer.json) to test.

require __DIR__ . '/vendor/autoload.php';

class FirstClass
{
    public $value;
}

class SecondClass
{
    public $value;
}

// Test bind()
$container = new Illuminate\Container\Container();

$container->bind('FirstClass');

$instance = $container->make('FirstClass');
$instance->value = 'test';

$instance2 = $container->make('FirstClass');
$instance2->value = 'test2';

echo "Bind: $instance->value vs. $instance2->value\n";

// Test singleton()
$container->singleton('SecondClass');

$instance = $container->make('SecondClass');
$instance->value = 'test';

$instance2 = $container->make('SecondClass');
$instance2->value = 'test2'; // <--- also changes $instance->value

echo "Singleton: $instance->value vs. $instance2->value\n";

The result is as expected:

Bind: test vs. test2

Singleton: test2 vs. test2

Might be a dirty proof, but indeed it is one.

All the magic lies in the Container::make method. If the binding is registered as shared (which means as singleton), the class instance is returned, otherwise a new instance every time.

Source: https://github.com/laravel/framework/blob/4.2/src/Illuminate/Container/Container.php#L442

BTW, Container::singleton is the same as Container::bind with the third parameter set to true.
摘自:https://stackoverflow.com/questions/25229064/laravel-difference-appbind-and-appsingleton :rabbit:

9个月前

  • 请注意单词拼写,以及中英文排版,参考此页
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`, 更多语法请见这里 Markdown 语法
  • 支持表情,使用方法请见 Emoji 自动补全来咯,可用的 Emoji 请见 :metal: :point_right: Emoji 列表 :star: :sparkles:
  • 上传图片, 支持拖拽和剪切板黏贴上传, 格式限制 - jpg, png, gif
  • 发布框支持本地存储功能,会在内容变更时保存,「提交」按钮点击时清空
  请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!