ZF2中ServiceManagerConfig类的configureServiceManager方法作用解析
问题背景
我在阅读ZF2源码时遇到了ServiceManagerConfig类,对其中的configureServiceManager方法的作用存在疑惑。之前了解到这个方法负责配置ServiceManager实例,完成服务、别名、工厂、初始化器等的注册,但我搞不懂:ServiceManager本身就是用来管理服务的,为什么还要通过这个方法来配置它的服务注册相关内容?
ServiceManagerConfig类代码
class ServiceManagerConfig implements ConfigInterface { /* Services that can be instantiated without factories * @var array*/ protected $invokables = array('SharedEventManager' => 'Zend\\EventManager\\SharedEventManager', ); protected $factories = array( /* Service factories *@var array */ 'EventManager' => 'Zend\\Mvc\\Service\\EventManagerFactory', 'ModuleManager' => 'Zend\\Mvc\\Service\\ModuleManagerFactory', ); protected $abstractFactories = array(); /*Abstract factories * @var array*/ protected $aliases = array( /* Aliases * @var array */ 'Zend\\EventManager\\EventManagerInterface' => 'EventManager', ); /*Shared services,* Services are shared by default; this is primarily to indicate services that should NOT be shared @var array */ protected $shared = array( 'EventManager' => false, ); /* Constructor * Merges internal arrays with those passed via configuration * @param array $configuration */ public function __construct(array $configuration = array()) { if (isset($configuration['invokables'])) { $this->invokables = array_merge($this->invokables, $configuration['invokables']); } if (isset($configuration['factories'])) { $this->factories = array_merge($this->factories, $configuration['factories']); } if (isset($configuration['abstract_factories'])) { $this->abstractFactories = array_merge($this->abstractFactories, $configuration['abstract_factories']); } if (isset($configuration['aliases'])) { $this->aliases = array_merge($this->aliases, $configuration['aliases']); } if (isset($configuration['shared'])) { $this->shared = array_merge($this->shared, $configuration['shared']); } } /* Configure the provided service manager instance with the configuration in this class. * In addition to using each of the internal properties to configure the service manager, also adds an initializer to inject *ServiceManagerAware and ServiceLocatorAware classes with the service manager. * @param ServiceManager $serviceManager * @return void */ public function configureServiceManager(ServiceManager $serviceManager) { foreach ($this->invokables as $name => $class) { $serviceManager->setInvokableClass($name, $class); } foreach ($this->factories as $name => $factoryClass) { $serviceManager->setFactory($name, $factoryClass); } foreach ($this->abstractFactories as $factoryClass) { $serviceManager->addAbstractFactory($factoryClass); } foreach ($this->aliases as $name => $service) { $serviceManager->setAlias($name, $service); } foreach ($this->shared as $name => $value) { $serviceManager->setShared($name, $value); } $serviceManager->addInitializer(function ($instance) use ($serviceManager) { if ($instance instanceof EventManagerAwareInterface) { if ($instance->getEventManager() instanceof EventManagerInterface) { $instance->getEventManager()->setSharedManager( $serviceManager->get('SharedEventManager') ); } else { $instance->setEventManager($serviceManager->get('EventManager')); } } }); $serviceManager->addInitializer(function ($instance) use ($serviceManager) { if ($instance instanceof ServiceManagerAwareInterface) { $instance->setServiceManager($serviceManager); } }); $serviceManager->addInitializer(function ($instance) use ($serviceManager) { if ($instance instanceof ServiceLocatorAwareInterface) { $instance->setServiceLocator($serviceManager); } }); $serviceManager->setService('ServiceManager', $serviceManager); $serviceManager->setAlias('Zend\ServiceManager\ServiceLocatorInterface', 'ServiceManager'); $serviceManager->setAlias('Zend\ServiceManager\ServiceManager', 'ServiceManager'); } }
ServiceManager类中的调用代码
<?php namespace Zend\ServiceManager; use ReflectionClass; class ServiceManager implements ServiceLocatorInterface { /**@#+ Constants */ const SCOPE_PARENT = 'parent'; const SCOPE_CHILD = 'child'; protected $canonicalNames = array(); /*Lookup for canonicalized names. * @var array*/ protected $allowOverride = false; /** @var bool*/ protected $invokableClasses = array(); /* @var array*/ protected $factories = array(); /* @var string|callable|\Closure|FactoryInterface[] */ protected $abstractFactories = array(); /* @var AbstractFactoryInterface[] */ protected $delegators = array(); /* @var array[] */ protected $pendingAbstractFactoryRequests = array(); /* @var array */ protected $lastAbstractFactoryUsed = null; /* @var string*/ protected $lastCanonicalNameUsed = null; /*@var string */ protected $shared = array(); /* @var array */ protected $instances = array(); /* Registered services and cached values * @var array*/ protected $aliases = array(); /* @var array*/ protected $initializers = array(); /* @var array*/ protected $peeringServiceManagers = array(); /* @var ServiceManager[] */ protected $shareByDefault = true; /*Whether or not to share by default *@var bool*/ protected $retrieveFromPeeringManagerFirst = false; /** @var bool */ protected $throwExceptionInCreate = true; /* @var bool Track whether not to throw exceptions during create() */ /* @var array map of characters to be replaced through strtr */ protected $canonicalNamesReplacements = array('-' => '', '_' => '', ' ' => '', '\\' => '', '/' => ''); /*Constructor * @param ConfigInterface $config */ public function __construct(ConfigInterface $config = null) { if ($config) { $config->configureServiceManager($this); } } }
核心原因:职责分离与配置解耦
这是典型的职责单一原则和配置与逻辑分离的设计思路:
ServiceManager的核心职责
ServiceManager本身只专注于服务的运行时管理:比如处理服务的检索、实例化(根据工厂/可调用类规则)、共享实例缓存、别名映射、初始化器执行等。如果把服务注册、配置加载的逻辑也塞进它里面,会导致这个类职责过载,既管"用"又管"建",后续维护和扩展都会变得麻烦。ServiceManagerConfig的专属角色
ServiceManagerConfig是专门的配置处理类:- 它先把ZF2内置的默认配置和用户自定义的配置合并成统一的结构(比如
$invokables、$factories这些数组) - 再通过
configureServiceManager方法,把这些配置数据转化为ServiceManager内部的状态——也就是调用setInvokableClass、setFactory等方法,把配置规则注入到服务管理器中。
- 它先把ZF2内置的默认配置和用户自定义的配置合并成统一的结构(比如
灵活性与扩展性
这种设计让配置逻辑可以独立演化:- 你可以自定义实现
ConfigInterface的配置类,比如从YAML文件、数据库甚至远程接口加载配置,只要实现configureServiceManager方法,就能给ServiceManager配置服务 ServiceManager不需要关心配置的来源和加载方式,只需要接收配置好的规则,专注于自己的服务管理本职工作。
- 你可以自定义实现
代码细节验证
看ServiceManager的构造函数,它只接收一个ConfigInterface实例,然后调用其configureServiceManager($this)——这说明ServiceManager把配置的逻辑完全委托给了配置类,自己只提供配置的入口。
而configureServiceManager里的逻辑,就是把ServiceManagerConfig中合并好的配置项,逐个调用ServiceManager的对应方法完成注册,同时还添加了几个通用初始化器(比如给实现ServiceManagerAwareInterface的实例注入服务管理器),这些初始化逻辑也是配置的一部分,统一放在配置类里管理更清晰。
内容的提问来源于stack exchange,提问作者WWW

