ホームページ PHPフレームワーク Laravel laravelサービスコンテナとは何ですか

laravelサービスコンテナとは何ですか

Feb 14, 2022 pm 04:31 PM
laravel サービスコンテナ

laravel では、サービス コンテナはクラスの依存関係を管理し、依存関係の注入を実装するための強力なツールです。アプリケーションが特定のサービスを使用する必要がある場合、サービス コンテナはサービスを解決し、サービス間の依存関係を自動的に解決して、それをアプリケーションに渡して使用します。

laravelサービスコンテナとは何ですか

このチュートリアルの動作環境: Windows 7 システム、Laravel 6 バージョン、Dell G3 コンピューター。

サービスコンテナとは

Laravel サービスコンテナは、クラスの依存関係を管理し、依存関係の注入を実装するための強力なツールです。依存関係注入という用語は、表面的には派手に見えるかもしれませんが、本質的には、コンストラクター、場合によっては「セッター」メソッドを通じて、クラスの依存関係をクラスに「注入」することを意味します。

Laravel の Route、Eloquent ORM、Request、Response などの機能モジュールは、実際にはコアとは関係のないクラスモジュールによって提供されており、これらのクラスは登録からインスタンス化まで、最終的にはクラスモジュールとして提供されます。実際の使用はLaravelのサービスコンテナの責任です。

サービス コンテナには 2 つの概念があります 制御の反転 (IOC) 依存性注入 (DI) :

##依存性の注入と制御の反転は、同じものを異なる視点から説明したものです。依存関係の注入はアプリケーションの観点から説明されており、アプリケーションはコンテナーに依存して、必要な外部リソースを作成して注入します。制御の反転をコンテナの観点から説明すると、コンテナはアプリケーションを制御し、コンテナはアプリケーションが必要とする外部リソースをアプリケーションに逆注入します。

Laravel では、フレームワークがさまざまなサービスをサービス コンテナにバインドしますが、カスタム サービスをコンテナにバインドすることもできます。アプリケーションが特定のサービスを使用する必要がある場合、サービス コンテナはサービスを解決し、サービス間の依存関係を自動的に解決して、それをアプリケーションに渡して使用します。

サービスのバインディングと解析が Laravel でどのように実装されるかについて説明しましょう。

サービス バインディング

サービスをコンテナにバインドするために一般的に使用されるメソッドには、インスタンス、バインド、シングルトン、エイリアスなどがあります。個別に見てみましょう。

インスタンス

既存のオブジェクトをサービス コンテナにバインドします。その後サービスが名前によって解決されると、コンテナは常にバインドされたインスタンスを返します。

$api = new HelpSpot\API(new HttpClient);
$this->app->instance('HelpSpot\Api', $api);
ログイン後にコピー

オブジェクトはサービス コンテナの $instnces 属性に登録されます

[
     'HelpSpot\Api' => $api//$api是API类的对象,这里简写了
 ]
ログイン後にコピー

bind

サービスをサービスにバインドしますコンテナ

バインディング メソッドは 3 つあります:

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');
ログイン後にコピー

最初の状況を考慮して、実際には、bind メソッド内で、サービスをバインドする前に getClosure() を通じてサービスのクロージャが生成されます。bind メソッドのソース コードを見てみましょう。

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 は、次のようにサービスをサービス コンテナの $bindings 属性に登録します。

$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 メソッドは、バインド メソッドのバリアントでは、解析が 1 回だけ必要なクラスまたはインターフェイスをコンテナにバインドすると、サービスはその後のコンテナ呼び出しに対して同じインスタンスを返します

alias

サービスとサービス エイリアスをコンテナに登録する:

public function alias($abstract, $alias)
{
    $this->aliases[$alias] = $this->normalize($abstract);
}
ログイン後にコピー

alias メソッドは、上記のバインド メソッドで役立ち、サービス エイリアスとサービス クラスの対応関係を $ に登録します。サービスコンテナのaliases属性。

例:

$this->app->alias('\Illuminate\ServiceName', 'service_alias');
ログイン後にコピー

サービスをバインドした後、使用時に

$this->app->make('service_alias');
ログイン後にコピー

を通じてサービス オブジェクトを解析できるため、これらを長く記述する必要がなくなります。クラス名が変更され、make メソッドの使用感が大幅に向上しました。

サービス解析

make: サービス コンテナからサービス オブジェクトを解析します。このメソッドは、解析するクラス名またはインターフェイス名を受け取りますパラメータとして

/**
 * 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;
}
ログイン後にコピー

makeメソッドを整理してみると、buildメソッドの役割は解析されたサービスオブジェクトを構築することであることが分かりましたので、オブジェクトを構築する具体的な処理を見てみましょう。 (PHP クラスのリフレクションは、サービスの依存関係注入を実装する構築プロセス中に使用されます)

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;
    }
}
ログイン後にコピー

サービス コンテナは、laravel の核心であり、依存関係注入を通じてオブジェクト間の相互依存関係を解決できます。制御反転の場合、特定の動作は外部で定義されます (Route、Eloquent、これらは外部モジュールであり、独自の動作仕様を定義します。サービス コンテナは、登録から使用のためのインスタンス化まで、これらのクラスを担当します)。

クラスをコンテナーによって抽出するには、まずクラスをコンテナーに登録する必要があります。 laravelではこのコンテナをサービスコンテナと呼んでいますので、サービスが必要な場合には、まずサービスを登録してコンテナにバインドし、サービスを提供し、コンテナにサービスをバインドするのがサービスプロバイダ(ServiceProvider)です。サービスプロバイダーは、主に register (登録) と boot (ブート、初期化) の 2 つの部分に分かれています。

[関連する推奨事項:

laravel ビデオチュートリアル]

以上がlaravelサービスコンテナとは何ですかの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

Laravelで電子メールの送信が失敗したときに返品コードを取得する方法は? Laravelで電子メールの送信が失敗したときに返品コードを取得する方法は? Apr 01, 2025 pm 02:45 PM

Laravelの電子メールの送信が失敗したときに戻りコードを取得する方法。 Laravelを使用してアプリケーションを開発する場合、検証コードを送信する必要がある状況に遭遇することがよくあります。そして実際には...

Laravelスケジュールタスクは実行されません:スケジュール:実行コマンドの後にタスクが実行されていない場合はどうすればよいですか? Laravelスケジュールタスクは実行されません:スケジュール:実行コマンドの後にタスクが実行されていない場合はどうすればよいですか? Mar 31, 2025 pm 11:24 PM

LaravelスケジュールタスクRAN RANSPONSIVEトラブルシューティングRALAVELのスケジュールタスクスケジューリングを使用すると、多くの開発者がこの問題に遭遇します。スケジュール:実行...

Laravelでは、検証コードが電子メールで送信できない状況に対処する方法は? Laravelでは、検証コードが電子メールで送信できない状況に対処する方法は? Mar 31, 2025 pm 11:48 PM

Laravelの電子メールの検証コードの送信の障害を処理する方法は、Laravelを使用することです...

DCAT管理者にデータを追加するためにクリックのカスタムテーブル関数を実装する方法は? DCAT管理者にデータを追加するためにクリックのカスタムテーブル関数を実装する方法は? Apr 01, 2025 am 07:09 AM

DCATを使用するときにDCATADMIN(Laravel-Admin)にデータを追加するためにカスタムクリックのテーブル関数を実装する方法...

Laravel Redis接続共有:選択方法が他の接続に影響するのはなぜですか? Laravel Redis接続共有:選択方法が他の接続に影響するのはなぜですか? Apr 01, 2025 am 07:45 AM

Laravel FrameworkでRedis接続の共有の影響とLaravelフレームワークとRedisを使用する際のメソッドを選択すると、開発者は問題に遭遇する可能性があります。

Laravel Multi-Tenant Extension Stancl/Tenancy:テナントデータベース接続のホストアドレスをカスタマイズする方法は? Laravel Multi-Tenant Extension Stancl/Tenancy:テナントデータベース接続のホストアドレスをカスタマイズする方法は? Apr 01, 2025 am 09:09 AM

Laravel Multi-Tenant拡張機能パッケージStancl/Tenancyのカスタムテナントデータベース接続Laravel Multi-Tenant ExtensionパッケージStancl/Tenancyを使用したマルチテナントアプリケーションを構築する際の...

バングラ部分モデル検索のlaravelEloquent orm) バングラ部分モデル検索のlaravelEloquent orm) Apr 08, 2025 pm 02:06 PM

LaravelEloquentモデルの検索:データベースデータを簡単に取得するEloquentormは、データベースを操作するための簡潔で理解しやすい方法を提供します。この記事では、さまざまな雄弁なモデル検索手法を詳細に紹介して、データベースからのデータを効率的に取得するのに役立ちます。 1.すべてのレコードを取得します。 ALL()メソッドを使用して、データベーステーブルですべてのレコードを取得します:useapp \ models \ post; $ post = post :: all();これにより、コレクションが返されます。 Foreach Loopまたはその他の収集方法を使用してデータにアクセスできます。

Laravelの地理空間:インタラクティブマップと大量のデータの最適化 Laravelの地理空間:インタラクティブマップと大量のデータの最適化 Apr 08, 2025 pm 12:24 PM

700万のレコードを効率的に処理し、地理空間技術を使用したインタラクティブマップを作成します。この記事では、LaravelとMySQLを使用して700万を超えるレコードを効率的に処理し、それらをインタラクティブなマップの視覚化に変換する方法について説明します。最初の課題プロジェクトの要件:MySQLデータベースに700万のレコードを使用して貴重な洞察を抽出します。多くの人は最初に言語をプログラミングすることを検討しますが、データベース自体を無視します。ニーズを満たすことができますか?データ移行または構造調​​整は必要ですか? MySQLはこのような大きなデータ負荷に耐えることができますか?予備分析:キーフィルターとプロパティを特定する必要があります。分析後、ソリューションに関連している属性はわずかであることがわかりました。フィルターの実現可能性を確認し、検索を最適化するためにいくつかの制限を設定しました。都市に基づくマップ検索

See all articles