支付宝扫一扫付款
微信扫一扫付款
(微信为保护隐私,不显示你的昵称)
DI组件能够让你把程序中构建的对象以标准化和中心化的方式来呈现。
关于Dependency Injection(依赖注入)和service container(服务容器),请参考中文BOOK中的Service Container(服务容器)。
你可以通过下述两种方式安装:
通过composer安装(Packagist上的symfony/dependency-injection
)
你可能会有如下例的简单Mailer
类,并希望把它设为服务:
1 2 3 4 5 6 7 8 9 10 11 | class Mailer
{
private $transport;
public function __construct()
{
$this->transport = 'sendmail';
}
// ...
} |
现在你可将该类注册到容器中令其成为服务:
1 2 3 4 | use Symfony\Component\DependencyInjection\ContainerBuilder;
$container = new ContainerBuilder();
$container->register(‘mailer’, ‘Mailer’); |
对这个类进行一些改进,可以使它更灵活地让container来设置transport
属性。修改类的代码,将该属性注入构造器。
1 2 3 4 5 6 7 8 9 10 11 | class Mailer
{
private $transport;
public function __construct($transport)
{
$this->transport = $transport;
}
// ...
} |
这时你可以在容器中设置transport:
1 2 3 4 5 6 | use Symfony\Component\DependencyInjection\ContainerBuilder;
$container = new ContainerBuilder();
$container
->register('mailer', 'Mailer')
->addArgument('sendmail'); |
现在这个类变得更有适应性,因为你从代码的执行过程中分离出transport
,并将其转入容器。
你指定的transport传输方式,可能需要被其他服务知道。你可以避免在程序中“多处修改”这个值,只需把transport设为容器中的参数,然后引用这个“参数”到mailer
服务中的构造器参数中即可:
1 2 3 4 5 6 7 | use Symfony\Component\DependencyInjection\ContainerBuilder;
$container = new ContainerBuilder();
$container->setParameter('mailer.transport', 'sendmail');
$container
->register('mailer', 'Mailer')
->addArgument('%mailer.transport%'); |
现在,你可以对服务容器中的mailer
服务,注入其他的类。假设你有一个NewsletterManager
类:
1 2 3 4 5 6 7 8 9 10 11 | class NewsletterManager
{
private $mailer;
public function __construct(\Mailer $mailer)
{
$this->mailer = $mailer;
}
// ...
} |
在定义newsletter_manager
服务时,mailer
服务还不存在(译注:请参考下面代码,这句话是指,虽然“订阅管理器”这个服务定义了,但要传给它的构造参数,却不能是“mailer服务定义”,而必须是一个类实例)。使用Reference
类来通知容器,在newsletter manager初始化时,将mailer
服务注入:
1 2 3 4 5 6 7 8 9 10 11 12 13 | use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
$container = new ContainerBuilder();
$container->setParameter('mailer.transport', 'sendmail');
$container
->register('mailer', 'Mailer')
->addArgument('%mailer.transport%');
$container
->register('newsletter_manager', 'NewsletterManager')
->addArgument(new Reference('mailer')); |
如果NewsletterManager
并不需要Mailer,那么注入Mailer
服务可设为“可选”,你可以用setter注入来替代原有的constructor注入:
1 2 3 4 5 6 7 8 9 10 11 | class NewsletterManager
{
private $mailer;
public function setMailer(\Mailer $mailer)
{
$this->mailer = $mailer;
}
// ...
} |
现在你可以选择不将Mailer
注入到NewsletterManager
中。如果是这样的话,你可以在容器中调用setter方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 | use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
$container = new ContainerBuilder();
$container->setParameter('mailer.transport', 'sendmail');
$container
->register('mailer', 'Mailer')
->addArgument('%mailer.transport%');
$container
->register('newsletter_manager', 'NewsletterManager')
->addMethodCall('setMailer', array(new Reference('mailer'))); |
然后你可以从容器中得到newsletter_manager
服务:
1 2 3 4 5 6 7 | use Symfony\Component\DependencyInjection\ContainerBuilder;
$container = new ContainerBuilder();
// ...
$newsletterManager = $container->get('newsletter_manager'); |
尽管你可以从容器中直接取出服务,但最好避免这样做。例如,在NewsletterManager
中,你注入mailer
服务要好过从container中取出它来。如果你注入container并从中取出mailer
,这样会与这个特定容器绑定,导致类难以在别处复用。
若你基于某种原因,非要从注入的容器中来取得某个服务,从程序面来讲应该尽可能少地这样做。
前面例子中都是用PHP方式来设置的服务,你也可以使用配置文件来对Service进行设置。你可以使用Xml或Yaml来写入服务定义,而不是像上面那样用PHP来定义服务。通过把这些定义转移到一个或多个配置文件中,在多数情况下(除了极小的程序)这样做都有利于管理服务定义。为了实现这个,你需要安装Config组件。
加载一个xml配置文件:
1 2 3 4 5 6 7 | use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
$container = new ContainerBuilder();
$loader = new XmlFileLoader($container, new FileLocator(__DIR__));
$loader->load('services.xml'); |
如果你要加载YAML配置文件,你需要安装Yaml组件。
如果你确实需要用PHP创建服务,你也要把配置文件独立出来,并用类似的办法加载它:
1 2 3 4 5 6 7 | use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
$container = new ContainerBuilder();
$loader = new PhpFileLoader($container, new FileLocator(__DIR__));
$loader->load('services.php'); |
现在你可以用配置文件来设置newsletter_manager和mailer服务了:
1 2 3 4 5 6 7 8 9 10 11 12 | parameters:
# ...
mailer.transport: sendmail
services:
mailer:
class: Mailer
arguments: ['%mailer.transport%']
newsletter_manager:
class: NewsletterManager
calls:
- [setMailer, ['@mailer']] |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <?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="mailer.transport">sendmail</parameter>
</parameters>
<services>
<service id="mailer" class="Mailer">
<argument>%mailer.transport%</argument>
</service>
<service id="newsletter_manager" class="NewsletterManager">
<call method="setMailer">
<argument type="service" id="mailer" />
</call>
</service>
</services>
</container> |
1 2 3 4 5 6 7 8 9 10 11 | use Symfony\Component\DependencyInjection\Reference;
// ...
$container->setParameter('mailer.transport', 'sendmail');
$container
->register('mailer', 'Mailer')
->addArgument('%mailer.transport%');
$container
->register('newsletter_manager', 'NewsletterManager')
->addMethodCall('setMailer', array(new Reference('mailer'))); |
本文,包括例程代码在内,采用的是 Creative Commons BY-SA 3.0 创作共用授权。