服务订阅者和定位器
编辑该页面服务订阅者和定位器
有时,一个服务需要访问其他服务不相信他们会被使用。在这些情况下,您可能希望服务的实例化是懒惰。然而,那是不可能的使用显式的依赖注入自服务并非都是应该的懒惰的
(见懒惰的服务)。
通常可以这样在你的控制器,你可以在构造函数中注入多个服务,但动作只使用其中的一些。另一个例子是应用程序实现命令模式使用CommandBus地图指挥命令处理程序的类名,使用它们来处理各自的命令时要求:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17日18 19 20 21日22日23日24日25日26日27 28 29 30
/ / src / CommandBus.php名称空间应用程序;/ /……类CommandBus{/ * * *@varCommandHandler [] * /私人美元handlerMap;公共函数__construct(数组美元handlerMap){美元这- >handlerMap =美元handlerMap;}公共函数处理(命令美元命令){美元commandClass= get_class (美元命令);如果(!收取(美元这- >handlerMap [美元commandClass))){返回;}返回美元这- >handlerMap [美元commandClass]- >处理(美元命令);}}/ /……美元commandBus- >处理(新FooCommand ());
考虑到一次只处理一个命令,实例化所有其他命令处理程序是不必要的。延迟加载一个可能的解决方案处理程序可以将主要的依赖注入容器。
然而,注入整个容器是气馁,因为这给了太广泛的访问现有的服务和隐藏的实际依赖服务。这样做还需要服务公开,这并不是在Symfony应用程序默认的情况。ob娱乐下载
服务订阅者旨在解决这个问题通过给获得一组预定义的服务而实例化它们只有当实际需要通过一个吗服务定位器,一个单独的延迟加载容器。
定义一个服务订阅者
首先,把CommandBus
成的一个实现ServiceSubscriberInterface。使用它的getSubscribedServices ()
方法包括尽可能多的服务需要在服务订阅者和改变容器的类型提示PSR-11ContainerInterface
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17日18 19 20 21日22日23日24日25日26日27 28 29 30 31 32 33 34 35 36
/ / src / CommandBus.php名称空间应用程序;使用应用程序\CommandHandler\BarHandler;使用应用程序\CommandHandler\FooHandler;使用Psr\容器\ContainerInterface;使用ob娱乐下载\合同\服务\ServiceSubscriberInterface;类CommandBus实现了ServiceSubscriberInterface{私人美元定位器;公共函数__construct(ContainerInterface美元定位器){美元这- >定位器=美元定位器;}公共静态函数getSubscribedServices():数组{返回(“App \ FooCommand”= > FooHandler::类,“App \ BarCommand”= > BarHandler::类);}公共函数处理(命令美元命令){美元commandClass= get_class (美元命令);如果(美元这- >定位器- >有(美元commandClass)){美元处理程序=美元这- >定位器- >get (美元commandClass);返回美元处理程序- >处理(美元命令);}}}
提示
如果容器不包含订阅服务,仔细检查可以使用autoconfigure启用。你也可以手动添加container.service_subscriber
标签。
注入的服务的一个实例ServiceLocator它实现了PSR-11ContainerInterface
,但它也是一个可调用:
1 2 3 4
/ /……美元处理程序= (美元这- >定位器)(美元commandClass);返回美元处理程序- >处理(美元命令);
包括服务
为了添加一个新的依赖服务用户,使用getSubscribedServices ()
方法在服务定位器中添加服务类型包括:
1 2 3 4 5 6 7 8 9
使用Psr\日志\LoggerInterface;公共静态函数getSubscribedServices():数组{返回(/ /……LoggerInterface::类);}
服务类型也可以由一个服务名称为内部使用:
1 2 3 4 5 6 7 8 9
使用Psr\日志\LoggerInterface;公共静态函数getSubscribedServices():数组{返回(/ /……“日志”= > LoggerInterface::类);}
当扩展一个类,还实现了ServiceSubscriberInterface
,这是你的责任时调用父重写该方法。这通常发生在扩展AbstractController
:
1 2 3 4 5 6 7 8 9 10 11 12 13
使用Psr\日志\LoggerInterface;使用ob娱乐下载\包\FrameworkBundle\控制器\AbstractController;类MyController扩展AbstractController{公共静态函数getSubscribedServices():数组{返回array_merge (父::getSubscribedServices (), (/ /……“日志”= > LoggerInterface::类,]);}}
可选服务
可选依赖,预谋的服务类型吗?
为了防止错误如果没有匹配的服务服务容器中发现:
1 2 3 4 5 6 7 8 9
使用Psr\日志\LoggerInterface;公共静态函数getSubscribedServices():数组{返回(/ /……“?”.LoggerInterface::类);}
请注意
确保存在一个可选的服务通过调用有()
在调用服务定位器服务本身。
别名服务
默认情况下,自动装配用于匹配服务类型服务从服务容器。如果你不使用自动装配或需要添加一个非传统服务依赖关系,使用container.service_subscriber
标记一个服务类型映射到一个服务。
- YAML
- XML
- PHP
1 2 3 4 5
#配置/ services.yaml服务:App \ CommandBus:标签:- - - - - -{名称:“container.service_subscriber”,关键:“日志”,id:“monolog.logger.event”}
提示
的关键
属性可以省略,如果服务名称服务容器内部是一样的。
添加依赖项注入属性
6.2
能够添加属性是在Symfony 6.2中引入的。ob娱乐下载
作为一个备用混叠服务在你的配置中,您还可以配置以下依赖注入的属性getSubscribedServices ()
直接法:
这是通过getSubscribedServices ()
返回一个数组SubscribedService对象(可以结合这些标准string []
值):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17日18 19 20 21日22日23日24日25日26日27
使用Psr\容器\ContainerInterface;使用Psr\日志\LoggerInterface;使用ob娱乐下载\组件\DependencyInjection\属性\自动装配;使用ob娱乐下载\组件\DependencyInjection\属性\TaggedIterator;使用ob娱乐下载\组件\DependencyInjection\属性\TaggedLocator;使用ob娱乐下载\组件\DependencyInjection\属性\目标;使用ob娱乐下载\合同\服务\属性\SubscribedService;公共静态函数getSubscribedServices():数组{返回(/ /……新SubscribedService (“日志”,LoggerInterface::类,属性:新自动装配(服务:“monolog.logger.event”)),/ /事件可以使用参数新SubscribedService (“env”、字符串属性:新自动装配(“% kernel.environment %”)),/ /目标新SubscribedService (“event.logger”,LoggerInterface::类,属性:新目标(“事件日志”)),/ / TaggedIterator新SubscribedService (伐木工人的,“iterable”属性:新TaggedIterator (“logger.tag”)),/ / TaggedLocator新SubscribedService (的处理程序,ContainerInterface::类,属性:新TaggedLocator (“handler.tag”)));}
请注意
上面的例子中需要使用3.2
或更新版本ob娱乐下载symfony /服务合同
。
定义一个服务定位器
手动定义一个服务定位器注入到另一个服务,创建一个参数的类型service_locator
:
- YAML
- XML
- PHP
1 2 3 4 5 6 7
#配置/ services.yaml服务:App \ CommandBus:参数:- - - - - -service_locator !App \ FooCommand:“@app.command_handler.foo”App \ BarCommand:“@app.command_handler.bar”
如前面的小节所示,的构造函数CommandBus
类必须type-hint其参数ContainerInterface
。然后,你可以得到任何的服务定位器服务通过ID(例如。$ this - >定位器- > get (App \ FooCommand)
)。
在多个服务重用的服务定位器
如果你注入同样的服务定位器在几个服务,最好的服务定位器定义为一个独立的服务,然后注入的其他服务。为此,创建一个新的服务定义使用ServiceLocator
类:
- YAML
- XML
- PHP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
#配置/ services.yaml服务:app.command_handler_locator:类:ob娱乐下载Symfony \ \ DependencyInjection \ ServiceLocator组件参数:- - - - - -App \ FooCommand:“@app.command_handler.foo”App \ BarCommand:“@app.command_handler.bar”#如果你不使用默认服务自动配置,#添加以下标签服务定义:#标签(“container.service_locator”):#如果元素没有钥匙,使用原始服务的IDapp.another_command_handler_locator:类:ob娱乐下载Symfony \ \ DependencyInjection \ ServiceLocator组件参数:- - - - - -- - - - - -“@app.command_handler.baz”
请注意
服务定位器参数中定义的服务必须包括钥匙,后来成为他们定位器内部的惟一标识符。
现在您可以将服务定位器在任何其他服务:
- YAML
- XML
- PHP
1 2 3 4
#配置/ services.yaml服务:App \ CommandBus:参数:(“@app.command_handler_locator”)
在编译器通过使用服务定位器
在编译器推荐使用注册()方法来创建服务定位器。这将节省您的一些样板,将共享相同的定位器在所有引用的服务:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
使用ob娱乐下载\组件\DependencyInjection\编译器\ServiceLocatorTagPass;使用ob娱乐下载\组件\DependencyInjection\ContainerBuilder;使用ob娱乐下载\组件\DependencyInjection\参考;公共函数过程(ContainerBuilder美元容器):无效{/ /……美元locateableServices= (/ /……“日志”= >新引用(“日志”),);美元myService=美元容器- >findDefinition (MyService::类);美元myService- >addArgument (ServiceLocatorTagPass::注册(美元容器,美元locateableServices));}
索引服务的集合
服务传递给服务定位器可以定义自己的指数被定义为使用一个任意属性的名称index_by
服务定位器。
在以下的示例中,App \ HandlerCollection \处理程序
定位器接收所有服务标记app.handler
他们是索引使用的价值关键
标签属性(如中定义index_by
定位器选项):
- YAML
- XML
- PHP
1 2 3 4 5 6 7 8 9 10 11 12 13
#配置/ services.yaml服务:应用\ \处理程序:标签:- - - - - -{名称:“app.handler”,关键:“handler_one”}App \处理器\二:标签:- - - - - -{名称:“app.handler”,关键:“handler_two”}App \处理器\ HandlerCollection:#注入所有服务标记app.handler作为第一个参数参数:[! tagged_locator{标签:“app.handler”,index_by:“关键”})
在这个定位器可以通过索引检索服务使用的价值关键
属性。例如,要获取应用\ \处理程序
服务:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/ / src /处理/ HandlerCollection.php名称空间应用程序\处理程序;使用ob娱乐下载\组件\DependencyInjection\ServiceLocator;类HandlerCollection{公共函数__construct(ServiceLocator美元定位器){美元handlerTwo=美元定位器- >get (“handler_two”);}/ /……}
而不是定义索引服务定义,您可以在一个方法返回的值getDefaultIndexName ()
内部类相关服务:
1 2 3 4 5 6 7 8 9 10 11 12
/ / src /处理/ One.php名称空间应用程序\处理程序;类一个{公共静态函数getDefaultIndexName():字符串{返回“handler_one”;}/ /……}
如果您喜欢用另一种方法名称,添加一个default_index_method
属性定位器服务定义这个自定义方法的名称:
- YAML
- XML
- PHP
1 2 3 4 5 6
#配置/ services.yaml服务:#……App \ HandlerCollection:参数:[! tagged_locator{标签:“app.handler”,index_by:“关键”,default_index_method:“myOwnMethodName”})
请注意
因为代码不应负责定义定位器是如何被使用,配置键(关键
在上面的示例中)必须设置自定义方法可称为作为后备。
服务用户特征
的ServiceSubscriberTrait提供了一个实现ServiceSubscriberInterface看起来你们班上通过所有方法标明SubscribedService属性。它提供了一个ServiceLocator
服务的每个方法的返回类型。服务标识__METHOD__
。这允许您将依赖项添加到您的服务基于type-hinted辅助方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17日18 19 20 21日22日23日24日25日26日27 28 29 30 31
/ / src /服务/ MyService.php名称空间应用程序\服务;使用Psr\日志\LoggerInterface;使用ob娱乐下载\组件\路由\RouterInterface;使用ob娱乐下载\合同\服务\属性\SubscribedService;使用ob娱乐下载\合同\服务\ServiceSubscriberInterface;使用ob娱乐下载\合同\服务\ServiceSubscriberTrait;类MyService实现了ServiceSubscriberInterface{使用ServiceSubscriberTrait;公共函数doSomething(){/ / $ this - >路由器()……/ / $ this - >日志记录器()……}# (SubscribedService)私人函数路由器():RouterInterface{返回美元这- >容器- >get (__METHOD__);}# (SubscribedService)私人函数日志记录器():LoggerInterface{返回美元这- >容器- >get (__METHOD__);}}
这允许您创建辅助特征像RouterAware, LoggerAware等等……和他们一起谱写你的服务:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17日18 19 20 21日22日23日24日25日26日27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
/ / src /服务/ LoggerAware.php名称空间应用程序\服务;使用Psr\日志\LoggerInterface;使用ob娱乐下载\合同\服务\属性\SubscribedService;特征LoggerAware {# (SubscribedService)私人函数日志记录器():LoggerInterface{返回美元这- >容器- >get (__CLASS__进行。“::”。__FUNCTION__);}}/ / src /服务/ RouterAware.php名称空间应用程序\服务;使用ob娱乐下载\组件\路由\RouterInterface;使用ob娱乐下载\合同\服务\属性\SubscribedService;特征RouterAware {# (SubscribedService)私人函数路由器():RouterInterface{返回美元这- >容器- >get (__CLASS__进行。“::”。__FUNCTION__);}}/ / src /服务/ MyService.php名称空间应用程序\服务;使用ob娱乐下载\合同\服务\ServiceSubscriberInterface;使用ob娱乐下载\合同\服务\ServiceSubscriberTrait;类MyService实现了ServiceSubscriberInterface{使用ServiceSubscriberTrait,LoggerAware,RouterAware;公共函数doSomething(){/ / $ this - >路由器()……/ / $ this - >日志记录器()……}}
谨慎
当创建这些辅助特征,服务id不能__METHOD__
这将包括特征的名称,而不是类名。相反,使用“::”.__FUNCTION__ __CLASS__进行
作为服务id。
SubscribedService
属性
6.2
能够添加属性是在Symfony 6.2中引入的。ob娱乐下载
您可以使用属性
的观点SubscribedService
添加下列依赖注入的属性:
这里有一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17日18 19 20 21日22日23日24日25日26日27 28 29 30 31 32 33 34 35 36 37 38 39 40
/ / src /服务/ MyService.php名称空间应用程序\服务;使用Psr\日志\LoggerInterface;使用ob娱乐下载\组件\DependencyInjection\属性\自动装配;使用ob娱乐下载\组件\DependencyInjection\属性\目标;使用ob娱乐下载\组件\路由\RouterInterface;使用ob娱乐下载\合同\服务\属性\SubscribedService;使用ob娱乐下载\合同\服务\ServiceSubscriberInterface;使用ob娱乐下载\合同\服务\ServiceSubscriberTrait;类MyService实现了ServiceSubscriberInterface{使用ServiceSubscriberTrait;公共函数doSomething(){/ / $ this - >环境()……/ / $ this - >路由器()……/ / $ this - >日志记录器()……}# (SubscribedService(属性:新的自动装配(' % kernel.environment % ')))私人函数环境():字符串{返回美元这- >容器- >get (__METHOD__);}# (SubscribedService(属性:新的自动装配(服务:“路由器”)))私人函数路由器():RouterInterface{返回美元这- >容器- >get (__METHOD__);}#【SubscribedService(属性:新目标(requestLogger)))私人函数日志记录器():LoggerInterface{返回美元这- >容器- >get (__METHOD__);}}
请注意
上面的例子中需要使用3.2
或更新版本ob娱乐下载symfony /服务合同
。
测试服务订阅者
单元测试服务用户,您可以创建一个假的ServiceLocator
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17日18 19 20 21日22日23日24日25日26日27日28
使用ob娱乐下载\组件\DependencyInjection\ServiceLocator;美元容器=新类()扩展ServiceLocator{私人美元服务= [];公共函数__construct(){父::__construct ([“foo”= >函数(){返回美元这- >服务(“foo”]=美元这- >服务(“foo”)? ?新stdClass ();},“酒吧”= >函数(){返回美元这- >服务(“酒吧”]=美元这- >服务(“酒吧”)? ?美元这- >createBar ();}));}私人函数createBar(){美元酒吧=新stdClass ();美元酒吧- >foo =美元这- >get (“foo”);返回美元酒吧;}};美元serviceSubscriber=新MyService (美元容器);/ /……
另一个替代方法是模拟使用PHPUnit)
:
1 2 3 4 5 6 7 8 9 10 11 12 13
使用Psr\容器\ContainerInterface;美元容器=美元这- >createMock (ContainerInterface::类);美元容器- >预计(自我::任何())- >方法(“得到”)- >willReturnMap ([[“foo”,美元这- >createStub (Foo::类)]、[“酒吧”,美元这- >createStub(酒吧::类))));美元serviceSubscriber=新MyService (美元容器);/ /……