【问题标题】:Bind interface to concrete class with Symfony's service container使用 Symfony 的服务容器将接口绑定到具体类
【发布时间】:2015-11-24 01:31:04
【问题描述】:

使用 C#、ASP.NET MVC 和 Autofac 之类的东西,我可以创建一个接口 IProductService 并将其绑定到一个具体的实现类 ProductService,代码如下(其中 builder 是 Autofac.ContainerBuilder):

builder.RegisterType<ProductService>().As<IProductService>();

然后在我的控制器中,我可以创建一个如下所示的构造函数:

using System.Web.Mvc;

namespace SomeNamespace
{
    public class HomeController : Controller
    {
        private IProductService _productService;

        public HomeController(IProductService productService)
        {
            this._productService = productService;
        }
    }
}

使用反射,Autofac 能够确定我希望将IProductService 实现注入到我的构造函数中,并提供我之前注册的类型作为具体实现类型,并根据需要解决ProductService 类中的依赖关系创建一个实例供控制器使用。


我想使用 Symfony 实现类似的目标。我目前的理解是,我可以通过以下方式使用 Symfony 的服务:

services:
    product_repository:
        class: App\WebBundle\Repository\ProductRepository
    product_service:
        class: App\WebBundle\Service\ProductService
        arguments: ["@product_repository"]
    home_controller:
        class: App\WebBundle\Controller\HomeController
        arguments: ["@product_service"]

然后使用这样的控制器:

namespace App\WebBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;

public class HomeController extends Controller {

    private $productService;

    public function __construct(IProductService $productService) {
        $this->productService = $productService;
    }
}

这可行,但远非理想,因为我现在不仅在构造函数中提供接口类型提示(这已经明确了我要注入的服务),我还必须弄清楚服务名称并提供它们在我想要使用服务的每个类的配置文件中。

我可以看到的另一种选择是利用Symfony\Bundle\FrameworkBundle\Controller\Controller 上的get 函数,如下所示:

namespace App\WebBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;

public class HomeController extends Controller {

    private $productService;

    public function __construct() {
        $this->productService = $this->get("product_service");
    }
}

这种方法有效,除了我现在完全依赖 Symfony 的服务容器(将来很难迁移到另一个框架),我的 IDE 不知道 $this-&gt;productService 是什么类型,所以我也失去了自动完成功能。其他开发者也不清楚该服务来自哪里,我必须反复参考配置文件以找出哪个类注册了“product_service”的id(我无法使用我的IDE的导航工具跳转到接口的声明或查找所有实现该接口的类。

我的理解是 Laravel 的 Illuminate Container 组件提供了我想要实现的功能(使用反射来确定构造函数/方法中暗示的类型,并使用开发人员提供的绑定解决依赖关系)但我不确定是否可以使用 Symfony 轻松设置。有什么方法可以在使用 Symfony 的同时实现这个功能(也许我可以听 kernel.controller 并在那里做一些事情)?

【问题讨论】:

  • JMSDIExtraBundle 可能对你有用.. jmsyst.com/bundles/JMSDiExtraBundle/master/usage
  • @Qoop 感谢您的链接。这个包很有趣,使用注释而不是在配置文件中配置东西可能更明显。遗憾的是,PHP 中的类元数据/注释没有适当的语言支持,因为我不太喜欢当前的方法(使用 doblock cmets)。虽然捆绑似乎有助于使服务注册更加明显,但它似乎不允许接口 => 迄今为止我读过的文档中的类绑定,这是我想做的最重要的事情。

标签: php symfony dependency-injection


【解决方案1】:

Kévin Dunglas 开设了一个 PR 添加服务自动装配,并已从 2.8 版开始集成到 Symfony:https://github.com/symfony/symfony/pull/15613

为实现问题中概述的目标,services.yml 文件应如下所示:

services:
    product_repository:
        class: App\WebBundle\Repository\ProductRepository
    product_service:
        class: App\WebBundle\Service\ProductService
        autowire: true
    home_controller:
        class: App\WebBundle\Controller\HomeController
        autowire: true

product_repository 服务已注册,以便 Symfony 依赖注入组件知道至少一个实现IProductRepository 的具体类。其他服务上的autowire 属性将确保正确注入构造函数中的依赖项。

更多文档可在here 获得。

【讨论】:

  • 我尝试将此详细信息添加到原始答案中,但编辑被拒绝,因此我将其作为另一个答案发布,希望有人会发现它有用。
【解决方案2】:

编辑:PR 现已合并。查看 Symfony 文档:http://symfony.com/doc/current/components/dependency_injection/autowiring.html

Symfony 依赖注入组件当前不支持自动装配。但是我已经打开了一个 PR 添加了该功能,如果它得到足够的积极反馈,它将被集成到 Symfony 2.8 中:https://github.com/symfony/symfony/pull/15613

【讨论】:

    猜你喜欢
    • 2015-10-29
    • 1970-01-01
    • 1970-01-01
    • 2015-11-22
    • 1970-01-01
    • 1970-01-01
    • 2016-03-29
    • 1970-01-01
    • 2018-03-15
    相关资源
    最近更新 更多