Heim  >  Artikel  >  Backend-Entwicklung  >  Interpretation des Laravel-Servicecontainers (IocContainer)

Interpretation des Laravel-Servicecontainers (IocContainer)

不言
不言Original
2018-07-06 15:06:221838Durchsuche

Dieser Artikel stellt hauptsächlich die Interpretation des Laravel-Servicecontainers (IocContainer) vor, der einen gewissen Referenzwert hat. Jetzt kann ich ihn mit allen teilen. Freunde in Not können sich darauf beziehen.

Der Kern von Laravel ist IocContainer. Dokument Es wird als „Service-Container“ bezeichnet. Der Service-Container ist ein leistungsstarkes Tool zum Verwalten von Klassenabhängigkeiten und zur Durchführung der Abhängigkeitsinjektion. Die Funktionsmodule in Laravel wie Route, Eloquent ORM, Request, Response usw. beziehen sich tatsächlich auf Bereitgestellt von Kernunabhängige Klassenmodule. Diese Klassen sind tatsächlich für den Laravel-Service-Container von der Registrierung bis zur Instanziierung verantwortlich und werden schließlich von uns verwendet.

Wenn Sie keine klare Vorstellung davon haben, was ein Service-Container ist, empfehle ich einen Blog-Beitrag, um mehr über die Besonderheiten des Service-Containers zu erfahren: Laravel's magischer Service-Container

Es gibt zwei Konzepte im Service-Container: Inversion of Control (IOC) und Dependency Injection (DI):

Dependency Injection und Inversion of Control sind unterschiedliche Beschreibungen derselben Sache und sie beschreiben sie aus unterschiedlichen Perspektiven . Die Abhängigkeitsinjektion wird aus der Perspektive der Anwendung beschrieben. Die Anwendung ist auf den Container angewiesen, um die benötigten externen Ressourcen zu erstellen und zu injizieren. Die Umkehrung der Kontrolle wird aus der Perspektive des Containers beschrieben. Der Container steuert die Anwendung, und der Container injiziert umgekehrt die von der Anwendung benötigten externen Ressourcen.

In Laravel bindet das Framework seine verschiedenen Dienste an den Service-Container. Wir können auch benutzerdefinierte Dienste an den Container binden. Wenn eine Anwendung einen bestimmten Dienst verwenden muss, löst der Dienstcontainer den Dienst auf, löst automatisch die Abhängigkeiten zwischen Diensten auf und übergibt ihn dann zur Verwendung an die Anwendung.

In diesem Artikel wird erläutert, wie Dienstbindung und -analyse in Laravel implementiert werden.

Dienstbindung

Häufig verwendete Methoden zum Binden von Diensten an Container sind Instanz, Bindung, Singleton und Alias. Schauen wir sie uns einzeln an.

Instanz

bindet ein vorhandenes Objekt an den Dienstcontainer. Wenn der Dienst anschließend nach Namen aufgelöst wird, gibt der Container immer die gebundene Instanz zurück.

$api = new HelpSpot\API(new HttpClient);
$this->app->instance('HelpSpot\Api', $api);

registriert das Objekt im Attribut $instnces des Service-Containers

[
     'HelpSpot\Api' => $api//$api是API类的对象,这里简写了
 ]

bind

Bindet den Service an den Service-Container

Es gibt drei Bindungsmethoden:

1.绑定自身
$this->app->bind('HelpSpot\API', null);

2.绑定闭包
$this->app->bind('HelpSpot\API', function () {
    return new HelpSpot\API();
});//闭包直接提供类实现方式
$this->app->bind('HelpSpot\API', function ($app) {
    return new HelpSpot\API($app->make('HttpClient'));
});//闭包返回需要依赖注入的类
3. 绑定接口和实现
$this->app->bind('Illuminate\Tests\Container\IContainerContractStub', 'Illuminate\Tests\Container\ContainerImplementationStub');

Im ersten Fall wird innerhalb der Bindungsmethode tatsächlich ein Abschluss für den Dienst über getClosure() generiert, bevor der Dienst gebunden wird Verfahren.

public function bind($abstract, $concrete = null, $shared = false)
{
    $abstract = $this->normalize($abstract);
    
    $concrete = $this->normalize($concrete);
    //如果$abstract为数组类似['Illuminate/ServiceName' => 'service_alias']
    //抽取别名"service_alias"并且注册到$aliases[]中
    //注意:数组绑定别名的方式在5.4中被移除,别名绑定请使用下面的alias方法
    if (is_array($abstract)) {
        list($abstract, $alias) = $this->extractAlias($abstract);

        $this->alias($abstract, $alias);
    }

    $this->dropStaleInstances($abstract);

    if (is_null($concrete)) {
        $concrete = $abstract;
    }
    //如果只提供$abstract,则在这里为其生成concrete闭包
    if (! $concrete instanceof Closure) {
        $concrete = $this->getClosure($abstract, $concrete);
    }

    $this->bindings[$abstract] = compact('concrete', 'shared');

    if ($this->resolved($abstract)) {
        $this->rebound($abstract);
    }
}


protected function getClosure($abstract, $concrete)
{
    // $c 就是$container,即服务容器,会在回调时传递给这个变量
    return function ($c, $parameters = []) use ($abstract, $concrete) {
        $method = ($abstract == $concrete) ? 'build' : 'make';

        return $c->$method($concrete, $parameters);
    };
}

bind registriert den Dienst im $bindings-Attribut des Service-Containers, ähnlich wie folgt:

$bindings = [
    'HelpSpot\API' =>  [//闭包绑定
        'concrete' => function ($app, $paramters = []) {
            return $app->build('HelpSpot\API');
        },
        'shared' => false//如果是singleton绑定,这个值为true
    ]        
    'Illuminate\Tests\Container\IContainerContractStub' => [//接口实现绑定
        'concrete' => 'Illuminate\Tests\Container\ContainerImplementationStub',
        'shared' => false
    ]
]

Singleton

public function singleton($abstract, $concrete = null)
{
    $this->bind($abstract, $concrete, true);
}

Singleton-Methode ist eine Variante der Bind-Methode. und um eine zu binden, muss die Klasse oder Schnittstelle nur einmal in den Container aufgelöst werden, und dann gibt der Dienst dieselbe Instanz für nachfolgende Aufrufe an den Container zurück

Alias

Registrieren Sie den Dienst und den Dienstalias bei der Container:

public function alias($abstract, $alias)
{
    $this->aliases[$alias] = $this->normalize($abstract);
}

Die Alias-Methode ist in der oben erwähnten Bindungsmethode nützlich. Sie registriert die entsprechende Beziehung zwischen dem Service-Alias ​​und der Service-Klasse im Attribut $aliases des Service-Containers.
Zum Beispiel:

$this->app->alias('\Illuminate\ServiceName', 'service_alias');

Nach dem Binden des Dienstes können Sie das Dienstobjekt bei Verwendung über

$this->app->make('service_alias');

analysieren, sodass Sie bei der Verwendung keine längeren Klassennamen schreiben müssen Die Erfahrung mit der Make-Methode wurde erheblich verbessert.

Service-Analyse

make: Analysieren Sie das Serviceobjekt aus dem Service-Container. Diese Methode empfängt den Klassennamen oder Schnittstellennamen, den Sie analysieren möchten, als Parameter

/**
 * Resolve the given type from the container.
 *
 * @param  string  $abstract
 * @param  array   $parameters
 * @return mixed
 */
public function make($abstract, array $parameters = [])
{
    //getAlias方法会假定$abstract是绑定的别名,从$aliases找到映射的真实类型名
    //如果没有映射则$abstract即为真实类型名,将$abstract原样返回
    $abstract = $this->getAlias($this->normalize($abstract));

    // 如果服务是通过instance()方式绑定的,就直接解析返回绑定的service
    if (isset($this->instances[$abstract])) {
        return $this->instances[$abstract];
    }

    // 获取$abstract接口对应的$concrete(接口的实现)
    $concrete = $this->getConcrete($abstract);

    if ($this->isBuildable($concrete, $abstract)) {
        $object = $this->build($concrete, $parameters);
    } else {
        //如果时接口实现这种绑定方式,通过接口拿到实现后需要再make一次才能
        //满足isBuildable的条件 ($abstract === $concrete)
        $object = $this->make($concrete, $parameters);
    }

    foreach ($this->getExtenders($abstract) as $extender) {
        $object = $extender($object, $this);
    }

    //如果服务是以singleton方式注册进来的则,把构建好的服务对象放到$instances里,
    //避免下次使用时重新构建
    if ($this->isShared($abstract)) {
        $this->instances[$abstract] = $object;
    }

    $this->fireResolvingCallbacks($abstract, $object);

    $this->resolved[$abstract] = true;

    return $object;
}

protected function getConcrete($abstract)
{
    if (! is_null($concrete = $this->getContextualConcrete($abstract))) {
        return $concrete;
    }

    // 如果是$abstract之前没有注册类实现到服务容器里,则服务容器会认为$abstract本身就是接口的类实现
    if (! isset($this->bindings[$abstract])) {
        return $abstract;
    }

    return $this->bindings[$abstract]['concrete'];
}

protected function isBuildable($concrete, $abstract)
{        
    return $concrete === $abstract || $concrete instanceof Closure;
}

Durch Aufruf make Nachdem wir die Methode aussortiert hatten, stellten wir fest, dass die Funktion der Build-Methode darin besteht, das analysierte Serviceobjekt zu erstellen. Schauen wir uns den spezifischen Prozess der Objekterstellung an. (Die Reflexion von PHP-Klassen wird während des Konstruktionsprozesses verwendet, um die Abhängigkeitsinjektion von Diensten zu implementieren.)

public function build($concrete, array $parameters = [])
{
    // 如果是闭包直接执行闭包并返回(对应闭包绑定)
    if ($concrete instanceof Closure) {
        return $concrete($this, $parameters);
    }
    
    // 使用反射ReflectionClass来对实现类进行反向工程
    $reflector = new ReflectionClass($concrete);

    // 如果不能实例化,这应该是接口或抽象类,再或者就是构造函数是private的
    if (! $reflector->isInstantiable()) {
        if (! empty($this->buildStack)) {
            $previous = implode(', ', $this->buildStack);

            $message = "Target [$concrete] is not instantiable while building [$previous].";
        } else {
            $message = "Target [$concrete] is not instantiable.";
        }

        throw new BindingResolutionException($message);
    }

    $this->buildStack[] = $concrete;

    // 获取构造函数
    $constructor = $reflector->getConstructor();

    // 如果构造函数是空,说明没有任何依赖,直接new返回
    if (is_null($constructor)) {
        array_pop($this->buildStack);

        return new $concrete;
    }
    
    // 获取构造函数的依赖(形参),返回一组ReflectionParameter对象组成的数组表示每一个参数
    $dependencies = $constructor->getParameters();

    $parameters = $this->keyParametersByArgument(
        $dependencies, $parameters
    );

    // 构建构造函数需要的依赖
    $instances = $this->getDependencies(
        $dependencies, $parameters
    );

    array_pop($this->buildStack);

    return $reflector->newInstanceArgs($instances);
}

//获取依赖
protected function getDependencies(array $parameters, array $primitives = [])
{
    $dependencies = [];

    foreach ($parameters as $parameter) {
        $dependency = $parameter->getClass();

        // 某一依赖值在$primitives中(即build方法的$parameters参数)已提供
        // $parameter->name返回参数名
        if (array_key_exists($parameter->name, $primitives)) {
            $dependencies[] = $primitives[$parameter->name];
        } 
        elseif (is_null($dependency)) {
             // 参数的ReflectionClass为null,说明是基本类型,如'int','string'
            $dependencies[] = $this->resolveNonClass($parameter);
        } else {
             // 参数是一个类的对象, 则用resolveClass去把对象解析出来
            $dependencies[] = $this->resolveClass($parameter);
        }
    }

    return $dependencies;
}

//解析出依赖类的对象
protected function resolveClass(ReflectionParameter $parameter)
{
    try {
        // $parameter->getClass()->name返回的是类名(参数在typehint里声明的类型)
        // 然后递归继续make(在make时发现依赖类还有其他依赖,那么会继续make依赖的依赖
        // 直到所有依赖都被解决了build才结束)
        return $this->make($parameter->getClass()->name);
    } catch (BindingResolutionException $e) {
        if ($parameter->isOptional()) {
            return $parameter->getDefaultValue();
        }

        throw $e;
    }
}

Der Service-Container ist der Kern von Laravel. Er kann die Abhängigkeiten zwischen Objekten für uns durch Abhängigkeitsinjektion und Kontrolle lösen Inversion, spezifische Verhaltensweisen werden extern definiert (Route, Eloquent, das sind externe Module, und sie definieren ihre eigenen Verhaltensspezifikationen. Der Service-Container ist für diese Klassen von der Registrierung bis zur Instanziierung für Ihre Verwendung verantwortlich).

Damit eine Klasse von einem Container extrahiert werden kann, muss sie zunächst beim Container registriert werden. Da Laravel diesen Container als Service-Container bezeichnet, müssen wir, wenn wir einen Service benötigen, zuerst den Service registrieren und an den Container binden. Dann ist das Ding, das den Service bereitstellt und den Service an den Container bindet, der Service-Provider (ServiceProvider). Der Dienstanbieter ist hauptsächlich in zwei Teile unterteilt: Registrieren (Registrierung) und Booten (Booten, Initialisieren). Informationen zum Inhalt des Laravel-Dienstanbieters finden Sie in einer anderen Laravel-Kerninterpretation – Dienstanbieter (ServiceProvider).

Das Obige ist der gesamte Inhalt dieses Artikels. Ich hoffe, dass er für das Studium aller hilfreich ist. Weitere verwandte Inhalte finden Sie auf der chinesischen PHP-Website.

Verwandte Empfehlungen:

Laravel Core Interpretation Request

Das obige ist der detaillierte Inhalt vonInterpretation des Laravel-Servicecontainers (IocContainer). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn