依赖注入模式(Dependency Injection)
模式定义
依赖注入(Dependency Injection)是控制反转(Inversion of Control)的一种实现方式。
我们先来看看什么是控制反转。
当调用者需要被调用者的协助时,在传统的程序设计过程中,通常由调用者来创建被调用者的实例,但在这里,创建被调用者的工作不再由调用者来完成,而是将被调用者的创建移到调用者的外部,从而反转被调用者的创建,消除了调用者对被调用者创建的控制,因此称为控制反转。
要实现控制反转,通常的解决方案是将创建被调用者实例的工作交由 IoC 容器来完成,然后在调用者中注入被调用者(通过构造器/方法注入实现),这样我们就实现了调用者与被调用者的解耦,该过程被称为依赖注入。
依赖注入不是目的,它是一系列工具和手段,最终的目的是帮助我们开发出松散耦合(loose coupled)、可维护、可测试的代码和程序。这条原则的做法是大家熟知的面向接口,或者说是面向抽象编程。
<?php
namespace App\Controller;
use App\Service\UserService;
class IndexController
{
/**
* @var UserService
*/
private $userService;
// 通过在构造函数的参数上声明参数类型完成自动注入
public function __construct(UserService $userService)
{
$this->userService = $userService;
}
public function index()
{
$id = 1;
// 直接使用
return $this->userService->getInfoById($id);
}
}
通过 @Inject 注解注入,详见https://hyperf.wiki/2.0/#/zh-cn/di
抽象对象注入,详见https://hyperf.wiki/2.0/#/zh-cn/di
工厂对象注入,详见https://hyperf.wiki/2.0/#/zh-cn/di
/**
* Build an entry of the container by its name.
* This method behave like get() except resolves the entry again every time.
* For example if the entry is a class then a new instance will be created each time.
* This method makes the container behave like a factory.
*
* @param string $name entry name or a class name
* @param array $parameters Optional parameters to use to build the entry. Use this to force specific parameters
* to specific values. Parameters not defined in this array will be resolved using
* the container.
* @throws NotFoundException no entry found for the given name
* @throws InvalidArgumentException the name parameter must be of type string
*/
public function make(string $name, array $parameters = [])
{
$definition = $this->getDefinition($name);
if (! $definition) {
throw new NotFoundException("No entry or class found for '{$name}'");
}
return `$this->resolveDefinition($definition, $parameters)`;
}
make方法接受两个参数,类名称和一个参数数组,首先通过调用$definition = name)获得类名称的DI定义,然后通过definition, $parameters)解析对象,实际是调用Hyperf\Di\Resolver\ResolverDispatcher类的resolve方法
/**
* Resolve a definition to a value.
*
* @param DefinitionInterface $definition object that defines how the value should be obtained
* @param array $parameters optional parameters to use to build the entry
* @throws InvalidDefinitionException if the definition cannot be resolved
* @return mixed value obtained from the definition
*/
public function resolve(DefinitionInterface $definition, array $parameters = [])
{
if ($definition instanceof SelfResolvingDefinitionInterface) {
return $definition->resolve($this->container);
}
$guard = DepthGuard::getInstance();
return $guard->call($definition->getName(), function () use ($definition, $parameters) {
$resolver = $this->getDefinitionResolver($definition);
return $resolver->resolve($definition, $parameters);
});
}
在这里调用不同的解析器来解析对象,有两种解析器,ObjectResolver和FactoryResolver,如下:
/**
* Returns a resolver capable of handling the given definition.
*
* @throws RuntimeException no definition resolver was found for this type of definition
*/
private function getDefinitionResolver(DefinitionInterface $definition): ResolverInterface
{
switch (true) {
case $definition instanceof ObjectDefinition:
if (! $this->objectResolver) {
$this->objectResolver = new ObjectResolver($this->container, $this);
}
return $this->objectResolver;
case $definition instanceof FactoryDefinition:
if (! $this->factoryResolver) {
$this->factoryResolver = new FactoryResolver($this->container, $this);
}
return $this->factoryResolver;
default:
throw new RuntimeException('No definition resolver was configured for definition of type ' . get_class($definition));
}
}
例如,ObjectResolver的resolve方法
/**
* Resolve a definition to a value.
*
* @param DefinitionInterface $definition object that defines how the value should be obtained
* @param array $parameters optional parameters to use to build the entry
* @throws InvalidDefinitionException
* @throws DependencyException
* @return mixed value obtained from the definition
*/
public function resolve(DefinitionInterface $definition, array $parameters = [])
{
if (! $definition instanceof ObjectDefinition) {
throw InvalidDefinitionException::create(
$definition,
sprintf('Entry "%s" cannot be resolved: the class is not instanceof ObjectDefinition', $definition->getName())
);
}
return $this->createInstance($definition, $parameters);
}
private function createInstance(ObjectDefinition $definition, array $parameters)
{
// Check that the class is instantiable
if (! $definition->isInstantiable()) {
// Check that the class exists
if (! $definition->isClassExists()) {
throw InvalidDefinitionException::create($definition, sprintf('Entry "%s" cannot be resolved: the class doesn\'t exist', $definition->getName()));
}
throw InvalidDefinitionException::create($definition, sprintf('Entry "%s" cannot be resolved: the class is not instantiable', $definition->getName()));
}
$classReflection = null;
try {
$className = $definition->getClassName();
$classReflection = ReflectionManager::reflectClass($className);
$constructorInjection = $definition->getConstructorInjection();
$args = $this->parameterResolver->resolveParameters($constructorInjection, $classReflection->getConstructor(), $parameters);
$object = new $className(...$args);
} catch (NotFoundExceptionInterface $e) {
throw new DependencyException(sprintf('Error while injecting dependencies into %s: %s', $classReflection ? $classReflection->getName() : '', $e->getMessage()), 0, $e);
} catch (InvalidDefinitionException $e) {
throw InvalidDefinitionException::create($definition, sprintf('Entry "%s" cannot be resolved: %s', $definition->getName(), $e->getMessage()));
}
return $object;
}
从中可以看出,createInstance方法通过php反射机制获取相关类的构造函数,并依然通过容器完成构造函数中的参数的依赖注入,最后通过$object = new args)的方式完成对象的实例化。