Symfony2 学习笔记之服务容器

时间:2021-07-25 06:23:19

现在的PHP应用程序都是面向对象开发,所以主要是由对象构成。有的对象可以方便的分发邮件信息而有的可能帮你把信息写入到数据库中。在你的应用程序中,你可能创建一个对象用于管理你的产品库存,或者另外一个对象处理来自第三方API的数据。重要的是现在应用程序要做的这些事情都是被组织到许许多多的对象中来处理它的每一项任务的。

我们将套路一下Symfony2中一个特殊的PHP对象,它帮助我们实例化,组织和获取你应用程序汇总的许多对象。这个对象叫做服务容器,它可以帮助你使用标准统一的方式来创建你程序中的对象。它能简化你的繁杂的初始对象工作,并且拥有超快的执行速度,强调该框架提高了可重用性和降低了代码耦合。
因为所有的Symfony2的类都是用了该容器,所以你需要了解怎样去扩展,配置和使用对象。
从大的方面说,服务容器对Symfony2的速度和可扩展性是一个最大的贡献者。

最后,配置和使用一个服务容器是非常简单的。在本章的最后,你将能通过容器和来自第三方Bundle的自定义对象很舒适的创建你自己的对象。你将能够写出更加具有重用性,可测试性以及松散耦合的代码。

什么是服务?
简而言之,一个服务就是任何一个执行一些全局任务的PHP对象。
在计算机科学中它是一个通用名字用来描述一个对象被创建用来满足特定目的。
每个服务都会应用于你整个应用程序,无论何时你需要他们的时候,他们都能提供。

你不需要做任何特别的是来制造服务,仅仅写一个PHP类,在类中定义一些完成某种功能的代码即可。

有一个规则,当一个PHP对象被在整个应用程序中全局使用的时候,该PHP对象就是一个服务了。
一个单独的Mailer服务用于整个应用程序的邮件信息发送,而许多Message对象被分发这不是服务。
同样的,Product对象不是一个服务,但是一个对象能够持久化Product到数据库,那么这个对象就是服务了。

那么接下来还有什么重要的事呢?
考虑服务的一个优点就是说明你开始考虑从你的应用程序中分离某一功能到一些列的服务。因为每个服务只做一件工作。
当你需要的时候你可以很容易的访问每个服务和使用它们的功能。
每个服务也必须能很容易的测试和配置,因为它们是从你应用程序的其他的功能中分离了出来的。
这种主意叫做面向服务架构。 围绕着一些列相对独立的服务构造你的应用程序是一个非常著名和可信任的面向对象实践。
这也是能成为一个优秀开发者的必备。

什么是服务容器?
一个服务容器,也叫做依赖注入容器,仅仅是一个PHP对象,用于管理服务的实例化。
比如,假设我们有一个简单的PHP类分发Email信息。 没有服务容器,我们必须在使用它的时候手动的创建这个对象:

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

这看上去很容易,假设Mailer类允许我们配置用于分发邮件信息的方法(sendmail,smtp等)。但是如果我们想在别的地方使用mailer服务? 我们又不想重复的在每一个使用Mailer的地方配置它。如果我们需要改变邮件发送的地址怎么办?我们需要找到每一个Mailer配置的然后修改代码。

在容器中 创建/配置服务

一个更好的做法是让服务容器来为我们创建Mailer对象。为了能够实现这一做法,我们必须教会容器怎样去创建Mailer服务。这些工作是通过配置文件来实现的,配置文件你可以设置成YAML,XML或者PHP格式均可。

YAML格式:

#app/config/config.yml
services:
my_mailer:
class: Acme\HelloBundle\Mailer
arguments: [sendmail]

XML格式:

<!-- app/config/config.xml-->
<services>
<service id="my_mailer" class="Acme\HelloBundle\Mailer">
<argument>sendmail</argument>
</service>
</services>

PHP代码格式:

//app/config/config.php
use Symfony\Component\DependencyInjection\Definition;
$container->setDefinition('my_mailer',new Definition(
'Acme\HelloBundle\Mailer',
array('sendmail')
));

当Symfony2 初始化时,它会默认根据应用程序配置(app/config/config.yml)创建服务容器。真正的服务容器配置文件是AppKernel::registerContainerConfiguration()方法加载的一个环境特定配置文件,config_dev.yml 用于开发阶段,config_prod.yml用于运营阶段。
Symfony2配置启动后,Acme\HelloBundle\Mailer的对象实例就能够通过服务容器使用了。服务容器可以在任何传统的Symfony2的controller中使用,通过简写方法 get()调用。

Symfony2 学习笔记之服务容器
class HelloController extends Controller
{
//...
public function sendEmailAction()
{
//...
$mailer = $this->get('my_mailer');
$mailer->send('ryan@foobar.net',...);
}
}
Symfony2 学习笔记之服务容器

当我们从服务容器中获取Mailer实例时,使用my_mailer. 容器会创建这个对象并返回它。有另外一个好处,服务直到用到它时才会创建。这样能够节省内存提高程序效率。

服务参数:

通过服务容器创建一个新服务很简单,参数的设置会使定义更加有组织性和有适应性。

YAML格式:

Symfony2 学习笔记之服务容器
#app/config/config.yml
parameters:
my_mailer.class: Acme\HelloBundle\Mailer
my_mailer.transport: sendmail services:
my_mailer:
class: %my_mailer.class%
arguments: [%my_mailer.transport%]
Symfony2 学习笔记之服务容器

XML格式:

Symfony2 学习笔记之服务容器
<!-- app/config/config.xml -->
<parameters>
<parameter key="my_mailer.class">Acme\HelloBundle\Mailer</parameter>
<parameter key="my_mailer.transport">sendmail</parameter>
</parameters> <services>
<service id="my_mailer" class="%my_mailer.class%">
<argument>%my_mailer.transport%</argument>
</service>
</services>
Symfony2 学习笔记之服务容器

PHP代码格式:

Symfony2 学习笔记之服务容器
// app/config/config.php
use Symfony\Component\DependencyInjection\Definition; $container->setParameter('my_mailer.class', 'Acme\HelloBundle\Mailer');
$container->setParameter('my_mailer.transport', 'sendmail'); $container->setDefinition('my_mailer', new Definition(
'%my_mailer.class%',
array('%my_mailer.transport%')
));
Symfony2 学习笔记之服务容器

上面配置文件中,%% 是参数定义方式,当容器创建完成后就会查找参数定义,如果%参数或者变量作为字符串一部分时,需要添加另一个%进行转换:

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

参数的目的就是把信息传入服务,当然,不定义任何参数也是没有问题的。

定义参数会具有某些优势:
在一个参数键parameters中分离和组织服务的所有可选项。
参数值可以用于多个服务的定义。
在一个bundle中创建一个服务,使用参数可以很容易的在你的应用程序中进行自定义化服务。

当然,用不用参数完全取决于你的决定。高质量的第三方bundles总是使用参数,当他们把服务放入容器中使其更具可配置性。当然,在你的应用程序中你可能不需要这样的配置性。

数组参数:

参数不一定都是普通字符串,也有可能是数组。如果是写在XML格式的配置文件中,那么你需要为数组参数定义type="collection" 属性。

YAML格式:

# app/config/config.yml
parameters:
    my_mailer.gateways:
        - mail1
        - mail2
        - mail3
    my_multilang.language_fallback:
        en:
            - en
            - fr
        fr:
            - fr
            - en

XML格式:

Symfony2 学习笔记之服务容器
<!-- app/config/config.xml -->
<parameters>
<parameter key="my_mailer.gateways" type="collection">
<parameter>mail1</parameter>
<parameter>mail2</parameter>
<parameter>mail3</parameter>
</parameter>
<parameter key="my_multilang.language_fallback" type="collection">
<parameter key="en" type="collection">
<parameter>en</parameter>
<parameter>fr</parameter>
</parameter>
<parameter key="fr" type="collection">
<parameter>fr</parameter>
<parameter>en</parameter>
</parameter>
</parameter>
</parameters>
Symfony2 学习笔记之服务容器

PHP代码格式:

Symfony2 学习笔记之服务容器
// app/config/config.php
use Symfony\Component\DependencyInjection\Definition; $container->setParameter('my_mailer.gateways', array('mail1', 'mail2', 'mail3'));
$container->setParameter('my_multilang.language_fallback',
array('en' => array('en', 'fr'),
'fr' => array('fr', 'en'),
));
Symfony2 学习笔记之服务容器

导入其它容器配置资源:

在这里我们把服务配置文件看成资源。这里就是想说明一个事实,几乎所有的配置资源都是文件,如YAML,XML,PHP等。
Symfony2 非常灵活,它的配置可以放到任何地方,比如数据库更或者外部的一个webservice。

服务容器默认情况下用一个单一的配置资源创建(app/config/config.yml)。其它所有的服务配置必须从这个文件中一次或者多次导入。这包括Symfony2核心配置和第三方bundle配置。这给你的应用程序在服务上有了相对的灵活性。

扩展的服务配置可以通过两种方式导入:第一,我们最常用的是 imports 命令。接下来我们会介绍第二种方法,它是更灵活并且是导入第三方bundles中的服务配置的首选。

1. 通过imports导入配置

到目前为止,我们已经把my_mailer服务的配置直接定义到了应用程序配置文件(app/config/config.yml)中了。当然,因为Mailer本身就在AcmeHelloBundle中,其实把my_mailer的容器定义放到bundle中也可以。

首先,我们把my_mailer的容器定义移到一个新的容器资源文件中把它放到AcmeHelloBundle之外。如果Resources或者Resources/config 目录不存在,我们创建它。

YAML格式:

Symfony2 学习笔记之服务容器
# src/Acme/HelloBundle/Resources/config/services.yml
parameters:
my_mailer.class: Acme\HelloBundle\Mailer
my_mailer.transport: sendmail services:
my_mailer:
class: %my_mailer.class%
arguments: [%my_mailer.transport%]
Symfony2 学习笔记之服务容器

XML格式:

Symfony2 学习笔记之服务容器
<!-- src/Acme/HelloBundle/Resources/config/services.xml -->
<parameters>
<parameter key="my_mailer.class">Acme\HelloBundle\Mailer</parameter>
<parameter key="my_mailer.transport">sendmail</parameter>
</parameters> <services>
<service id="my_mailer" class="%my_mailer.class%">
<argument>%my_mailer.transport%</argument>
</service>
</services>
Symfony2 学习笔记之服务容器

PHP代码格式:

Symfony2 学习笔记之服务容器
// src/Acme/HelloBundle/Resources/config/services.php
use Symfony\Component\DependencyInjection\Definition; $container->setParameter('my_mailer.class', 'Acme\HelloBundle\Mailer');
$container->setParameter('my_mailer.transport', 'sendmail'); $container->setDefinition('my_mailer', new Definition(
'%my_mailer.class%',
array('%my_mailer.transport%')
));
Symfony2 学习笔记之服务容器

配置定义的本身没有发生变化,只是挪了个位置。当然,服务容器不知道我们的资源文件新位置。幸运的是我们可以利用imports 键在应用程序的配置文件中很容易的导入它们。

YAML格式:

# app/config/config.yml
imports:
- { resource: @AcmeHelloBundle/Resources/config/services.yml }

XML格式:

<!-- app/config/config.xml -->
<imports>
<import resource="@AcmeHelloBundle/Resources/config/services.xml"/>
</imports>

PHP代码格式:

// app/config/config.php
$this->import('@AcmeHelloBundle/Resources/config/services.php');

imports命令允许你的应用程序从其他地方获取服务容器的配置资源(一般是从一些bundle中)。resource的位置,对于文件资源,是绝对路径。@AcmeHello语法决定了AcmeHelloBundle 的路径。这使得你指定资源路径的时候不用担心以后移动AcmeHelloBundle到别的目录的问题。

2. 使用容器扩展导入配置

在使用Symfony2 开发过程中,你将经常用到imports命令来从你创建的bundle中导入容器配置。而第三方的bundle容器配置,包括Symfony2的核心服务在内,通常使用另外一种方法,它更灵活更易于配置。

它是如何工作的呢?
实际上,每个bundle在定义自己的服务配置是都是跟到目前为止我看到的是一样的。换句话说,一个bundle使用一个或者多个配置资源文件(通常是XML)来指定bundle所需要的参数和服务。然而,我们不直接在配置文件中使用imports命令导入它们,而是仅仅在bundle中调用一个服务容器扩展来为我们做同样的工作。

一个服务容器扩展bundle的作者创建的是一个PHP类,它主要完成两件事情:
为该bundle配置服务导入需要的所有的服务容器资源。
提供语法上简介配置,让bundle能够直接被配置,而不需要再与bundle的服务容器配置参数交互。

换句话说,一个服务容器扩展会帮你配置好它的bundle所需的服务。

让我们看看FrameworkBundle是如何做的。
FrameworkBundle是Symfony2框架bundle,下面的代码显示了在你的应用程序配置中调用FrameworkBundle中的服务容器扩展。

YAML格式:

Symfony2 学习笔记之服务容器
# app/config/config.yml
framework:
secret: xxxxxxxxxx
charset: UTF-8
form: true
csrf_protection: true
router: { resource: "%kernel.root_dir%/config/routing.yml" }
# ...
Symfony2 学习笔记之服务容器

XML格式:

Symfony2 学习笔记之服务容器
<!-- app/config/config.xml -->
<framework:config charset="UTF-8" secret="xxxxxxxxxx">
<framework:form />
<framework:csrf-protection />
<framework:router resource="%kernel.root_dir%/config/routing.xml" />
<!-- ... -->
</framework>
Symfony2 学习笔记之服务容器

PHP代码格式:

Symfony2 学习笔记之服务容器
// app/config/config.php
$container->loadFromExtension('framework', array(
'secret' => 'xxxxxxxxxx',
'charset' => 'UTF-8',
'form' => array(),
'csrf-protection' => array(),
'router' => array('resource' => '%kernel.root_dir%/config/routing.php'),
// ...
));
Symfony2 学习笔记之服务容器

当这个配置被解析时,容器会查找一个可以处理framework配置命令的扩展。这个扩展在FrameworkBundle中,它会被调用来为FrameworkBundle加载服务配置。如果你从你的配置文件中完全去掉framework键,Symfony 核心服务将不会被加载。这完全是由你控制的。

当然,你可以做更多,而不只是激活FrameworkBundle的服务容器扩展。每个扩展都允许你很容易的个性化bundle,而不用关系其内部服务是怎么定义的。

比如你可以个性化charset,error_handler,csrf_protection,router等配置。实际上,FrameworkBundle使用的这里指定的项目来配置服务于它自身的服务配置。bundle负责为服务容器创建所有需要的parameters和services,同时依然允许大量的配置可以被个性化。作为一个额外的好处,大部分服务容器扩展能够执行校验,通知你那些选项丢失或者数据类型不正确。当安装或者配置一个bundle时应该看看bundle的说明,了解一下如何安装和配置它需要的服务。

注意:服务容器天生能够识别parameters,services 和imports命令,其它的命令则需要服务容器扩展来处理。

引用(注入)服务:
到目前为止,我们创建的my_mailer服务非常简单:它仅仅在它的构造函数中接受一个参数,非常容易配置。只有当我们需要创建一个服务而它又依赖于一个或者多个其它服务时,我们才能看到服务容器的真正威力。

让我们来看个例子:
假设我们有个新服务,NewsletterManager,它用于管理准备和分发一个邮件信息到一组地址。当然,my_mailer已经能够发送邮件信息了,所以我们将在NewsletterManager内部使用它。
假设它的类内容如下:

Symfony2 学习笔记之服务容器
namespace Acme\HelloBundle\Newsletter;

use Acme\HelloBundle\Mailer;

class NewsletterManager
{
protected $mailer; public function __construct(Mailer $mailer)
{
$this->mailer = $mailer;
} // ...
}
Symfony2 学习笔记之服务容器

上面的例子代码中没有使用服务容器,我们可以很容易的在controller内创建一个新的NewsletterManager实例。

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

这种方式是好的,但是如果我们决定NewsletterManager类需要第二个或者第三个构造函数参数呢?
我们可以重写代码并重新命名该类来实现,可是我们需要找到所有的使用NewsletterManager的地方修改它。这是很痛苦的事情,这时候服务容器就成了一个很诱人的选择。

YAML格式:

Symfony2 学习笔记之服务容器
# src/Acme/HelloBundle/Resources/config/services.yml
parameters:
# ...
newsletter_manager.class: Acme\HelloBundle\Newsletter\NewsletterManager services:
my_mailer:
# ...
newsletter_manager:
class: %newsletter_manager.class%
arguments: [@my_mailer]
Symfony2 学习笔记之服务容器

XML格式:

Symfony2 学习笔记之服务容器
<!-- src/Acme/HelloBundle/Resources/config/services.xml -->
<parameters>
<!-- ... -->
<parameter key="newsletter_manager.class">Acme\HelloBundle\Newsletter\NewsletterManager</parameter>
</parameters> <services>
<service id="my_mailer" ... >
<!-- ... -->
</service>
<service id="newsletter_manager" class="%newsletter_manager.class%">
<argument type="service" id="my_mailer"/>
</service>
</services>
Symfony2 学习笔记之服务容器

PHP代码格式:

Symfony2 学习笔记之服务容器
// src/Acme/HelloBundle/Resources/config/services.php
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference; // ...
$container->setParameter('newsletter_manager.class', 'Acme\HelloBundle\Newsletter\NewsletterManager'); $container->setDefinition('my_mailer', ... );
$container->setDefinition('newsletter_manager', new Definition(
'%newsletter_manager.class%',
array(new Reference('my_mailer'))
));
Symfony2 学习笔记之服务容器

在YAML配置文件中,@my_mailer告诉容器去查找一个名叫my_mailer的服务对象并把它传递给NewsletterManager的构造函数。在这种情况下,指定的服务my_mailer必须存在。如果不存在,将会抛出异常。你可以把你的依赖标记为可选,接下来说明。

可选依赖:setter注入
通过构造函数参数方式注入一个依赖在依赖已经存在并可用的情况下是一个完美的方式。但是当一个类的依赖是可选的时候,setter注入就成了更好的选择。

Symfony2 学习笔记之服务容器
namespace Acme\HelloBundle\Newsletter;

use Acme\HelloBundle\Mailer;

class NewsletterManager
{
protected $mailer; public function setMailer(Mailer $mailer)
{
$this->mailer = $mailer;
} // ...
}
Symfony2 学习笔记之服务容器

相应的配置上只需要一点改动:

YAML格式:

Symfony2 学习笔记之服务容器
# src/Acme/HelloBundle/Resources/config/services.yml
parameters:
# ...
newsletter_manager.class: Acme\HelloBundle\Newsletter\NewsletterManager services:
my_mailer:
# ...
newsletter_manager:
class: %newsletter_manager.class%
calls:
- [ setMailer, [ @my_mailer ] ]
Symfony2 学习笔记之服务容器

XML格式:

Symfony2 学习笔记之服务容器
<!-- src/Acme/HelloBundle/Resources/config/services.xml -->
<parameters>
<!-- ... -->
<parameter key="newsletter_manager.class">Acme\HelloBundle\Newsletter\NewsletterManager</parameter>
</parameters> <services>
<service id="my_mailer" ... >
<!-- ... -->
</service>
<service id="newsletter_manager" class="%newsletter_manager.class%">
<call method="setMailer">
<argument type="service" id="my_mailer" />
</call>
</service>
</services>
Symfony2 学习笔记之服务容器

PHP代码格式:

Symfony2 学习笔记之服务容器
// src/Acme/HelloBundle/Resources/config/services.php
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference; // ...
$container->setParameter('newsletter_manager.class', 'Acme\HelloBundle\Newsletter\NewsletterManager'); $container->setDefinition('my_mailer', ... );
$container->setDefinition('newsletter_manager', new Definition(
'%newsletter_manager.class%'
))->addMethodCall('setMailer', array(
new Reference('my_mailer')
));
Symfony2 学习笔记之服务容器

设置可选引用:
有时候,你的应用可能有一个可选的依赖,这就意味着这个依赖对于你的服务运行不是必须的。上面例子中,my_mailer服务必须存在,所以没有它是会抛出异常。我们来修改newsletter_manager服务定义,让这个依赖变为可选依赖。这样当它存在是容器会注入它,如果不存在时,什么也不做。

YAML格式:

Symfony2 学习笔记之服务容器
# src/Acme/HelloBundle/Resources/config/services.yml
parameters:
# ... services:
newsletter_manager:
class: %newsletter_manager.class%
arguments: [@?my_mailer]
Symfony2 学习笔记之服务容器

XML格式:

Symfony2 学习笔记之服务容器
<!-- src/Acme/HelloBundle/Resources/config/services.xml -->

<services>
<service id="my_mailer" ... >
<!-- ... -->
</service>
<service id="newsletter_manager" class="%newsletter_manager.class%">
<argument type="service" id="my_mailer" on-invalid="ignore" />
</service>
</services>
Symfony2 学习笔记之服务容器

PHP代码格式:

Symfony2 学习笔记之服务容器
// src/Acme/HelloBundle/Resources/config/services.php
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerInterface; // ...
$container->setParameter('newsletter_manager.class', 'Acme\HelloBundle\Newsletter\NewsletterManager'); $container->setDefinition('my_mailer', ... );
$container->setDefinition('newsletter_manager', new Definition(
'%newsletter_manager.class%',
array(new Reference('my_mailer', ContainerInterface::IGNORE_ON_INVALID_REFERENCE))
));
Symfony2 学习笔记之服务容器

在YAML配置文件中,@? 语法标示告诉服务容器该依赖是可选的。当然,NewsletterManager类也需要相应的修改一下构造函数:

public function __construct(Mailer $mailer = null)
{
// ...
}

Symfony核心和第三方bundle服务:
从Symfony2和所有第三方bundles的配置都通过容器获取他们的服务, 你可以很容易的访问他们或者在你自己的服务中使用他们。为了保持简洁,Symfoy2模式不需要controller也定义成服务。而是Symfony2把整个服务注入到所有的Controller中。比如,处理在用户Session中存在信息时,Symfony2 提供了一个session服务,你可以在一个标准controller中直接调用:

Symfony2 学习笔记之服务容器
public function indexAction($bar)
{
$session = $this->get('session');
$session->set('foo', $bar); // ...
}
Symfony2 学习笔记之服务容器

在Symfony2中,你将经常使用Symfoy或第三方bundles提供的服务来执行任务,比如渲染模板的templating, 发送邮件的mailer访问请求信息的request等。

我们可以进一步的在我们自己创建的服务中调用这些服务。让我们修改NewsletterManager使用真正的Symfony2 mailer服务。同时我们还为其传入模板引擎,让它通过一个模板生成邮件内容。

Symfony2 学习笔记之服务容器
namespace Acme\HelloBundle\Newsletter;

use Symfony\Component\Templating\EngineInterface;

class NewsletterManager
{
protected $mailer; protected $templating; public function __construct(\Swift_Mailer $mailer, EngineInterface $templating)
{
$this->mailer = $mailer;
$this->templating = $templating;
} // ...
}
Symfony2 学习笔记之服务容器

配置服务容器:

YAML格式:

services:
newsletter_manager:
class: %newsletter_manager.class%
arguments: [@mailer, @templating]

XML格式:

<service id="newsletter_manager" class="%newsletter_manager.class%">
<argument type="service" id="mailer"/>
<argument type="service" id="templating"/>
</service>

PHP代码格式:

Symfony2 学习笔记之服务容器
$container->setDefinition('newsletter_manager', new Definition(
'%newsletter_manager.class%',
array(
new Reference('mailer'),
new Reference('templating')
)
));
Symfony2 学习笔记之服务容器

高级容器配置:

到此我们看到,在容器中定义一个服务非常简单,通常包含一个服务的配置键和一些参数。然而,容器还有许多其它的可用工具帮助标志(tag)服务为特定的功能。 以创建更复杂的服务,在容器建立后执行操作。

设置服务为public/private
在定义服务的时候,你通常想在应用程序范围内访问它们,这些服务叫做public服务。比如doctrine服务在使用DoctrineBundle注册时就是一个公共服务,你可以按照如下方式访问:

$doctrine = $container->get('doctrine');

然而,在某些情况下你不想一个服务变为公共的。这种情况通常出现在你创建某个服务只是为另外一个服务作为输入参数时出现。private 私有服务,这些服务在调用的时候只能在参数行内通过 new PrivateFooBar()形式引用。

简单的说:一个服务当你不想它被你的代码直接访问时,它就是私有的了。

配置形式如下:

YAML格式:

services:
foo:
class: Acme\HelloBundle\Foo
public: false

XML格式:

<service id="foo" class="Acme\HelloBundle\Foo" public="false" />

PHP代码格式:

$definition = new Definition('Acme\HelloBundle\Foo');
$definition->setPublic(false);
$container->setDefinition('foo', $definition);

这时候你就不能再进行如此操作了:

$container->get('foo');

注意:服务默认情况下全部是公共的。如果一个服务被配置为private了,但是你还想引用它,那么你需要给它定义别名。

服务的别名:
当使用核心或者第三方bundles提供的服务时,你可能想用更加方便快捷的形式调用某些服务。你可以通过给他们定义别名来实现,甚至给私有服务定义别名。

YAML格式:

services:
foo:
class: Acme\HelloBundle\Foo
bar:
alias: foo

XML格式:

<service id="foo" class="Acme\HelloBundle\Foo"/>
<service id="bar" alias="foo" />

PHP代码格式:

$definition = new Definition('Acme\HelloBundle\Foo');
$container->setDefinition('foo', $definition); $containerBuilder->setAlias('bar', 'foo');

这时你可以通过别名来直接调用以前的私有服务了,比如:

$container->get('bar'); // 将返回以前私有服务 foo

要求必备文件:
有些情况下,你需要在加载服务前包含其它文件,这时你可以使用file命令实现:

YAML格式:

services:
foo:
class: Acme\HelloBundle\Foo\Bar
file: %kernel.root_dir%/src/path/to/file/foo.php

PHP代码格式:

$definition = new Definition('Acme\HelloBundle\Foo\Bar');
$definition->setFile('%kernel.root_dir%/src/path/to/file/foo.php');
$container->setDefinition('foo', $definition);

XML格式:

<service id="foo" class="Acme\HelloBundle\Foo\Bar">
<file>%kernel.root_dir%/src/path/to/file/foo.php</file>
</service>

注意:Symfony将在内部调用PHP函数require_once,这就意味着你的文件将每个请求都会被包括一次。

服务标签(Tags):
就像你在网络上发表博客可以设置标签“Symfony”或者“PHP”等一样,你在容器中配置的服务也可以被贴上标签。一个标签暗示这个服务是被用于特殊目的的。比如:

YAML格式:

services:
foo.twig.extension:
class: Acme\HelloBundle\Extension\FooExtension
tags:
- { name: twig.extension }

XML格式:

<service id="foo.twig.extension" class="Acme\HelloBundle\Extension\FooExtension">
<tag name="twig.extension" />
</service>

PHP代码格式:

$definition = new Definition('Acme\HelloBundle\Extension\FooExtension');
$definition->addTag('twig.extension');
$container->setDefinition('foo.twig.extension', $definition);

这里的twig.extension 标签就是一个专用标签,是TwigBundle在配置时使用的。通过给服务标注这个twig.extension标签,bundle就会知道foo.twig.extension 服务应该被注册为一个Twig的扩展。换句话说,Twig会查找所有标记为twig.extension的服务并自动把它们注册为扩展。

下面列出了Symfony2核心bundles的可用的标签:

assetic.filter
assetic.templating.php
data_collector
form.field_factory.guesser
kernel.cache_warmer
kernel.event_listener
monolog.logger
routing.loader
security.listener.factory
security.voter
templating.helper
twig.extension
translation.loader
validator.constraint_validator

以上就是有关Symfony2 中类似Spring容器的Services Container的基本知识。

参考URL:http://symfony.com/doc/current/book/service_container.html