依赖注入模式(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 = hypermesh怎么set选一个面_hyperfname)获得类名称的DI定义,然后通过hypermesh怎么set选一个面_php_02definition, $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 hypermesh怎么set选一个面_开发语言_03args)的方式完成对象的实例化。