laravel服务容器是什么

laravel中,服务容器是一个用于管理类依赖以及实现依赖注入的强有力工具。当应用程序需要使用某一个服务时,服务容器会将服务解析出来,并同时自动解决服务之间的依赖,然后交给应用程序使用。

laravel服务容器是什么

本教程操作环境:windows7系统、Laravel6版、Dell G3电脑。

什么是服务容器

Laravel 服务容器是一个用于管理类依赖以及实现依赖注入的强有力工具。依赖注入这个名词表面看起来花哨,实质上是指:通过构造函数,或者某些情况下通过「setter」方法将类依赖「注入」到类中。

Laravel中的功能模块比如 Route、Eloquent ORM、Request、Response等等等等,实际上都是与核心无关的类模块提供的,这些类从注册到实例化,最终被我们所使用,其实都是 laravel 的服务容器负责的。

服务容器中有两个概念控制反转(IOC)依赖注入(DI):

依赖注入和控制反转是对同一件事情的不同描述,它们描述的角度不同。依赖注入是从应用程序的角度在描述,应用程序依赖容器创建并注入它所需要的外部资源。而控制反转是从容器的角度在描述,容器控制应用程序,由容器反向的向应用程序注入应用程序所需要的外部资源。

在Laravel中框架把自带的各种服务绑定到服务容器,我们也可以绑定自定义服务到容器。当应用程序需要使用某一个服务时,服务容器会将服务解析出来,同时自动解决服务之间的依赖,然后交给应用程序使用。

下面探讨一下Laravel中的服务绑定和解析是如何实现的。

服务绑定

常用的绑定服务到容器的方法有instance, bind, singleton, alias。下面我们分别来看一下。

instance

将一个已存在的对象绑定到服务容器里,随后通过名称解析该服务时,容器将总返回这个绑定的实例。

$api = new HelpSpotAPI(new HttpClient);$this->app->instance('HelpSpotApi', $api);

会把对象注册到服务容器的$instnces属性里

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

bind

绑定服务到服务容器

有三种绑定方式:

1.绑定自身

$this->app->bind('HelpSpotAPI', null);

2.绑定闭包

Calliper 文档对比神器 Calliper 文档对比神器

文档内容对比神器

Calliper 文档对比神器 28 查看详情 Calliper 文档对比神器

$this->app->bind('HelpSpotAPI', function () {    return new HelpSpotAPI();});//闭包直接提供类实现方式$this->app->bind('HelpSpotAPI', function ($app) {    return new HelpSpotAPI($app->make('HttpClient'));});//闭包返回需要依赖注入的类

3. 绑定接口和实现

$this->app->bind('IlluminateTestsContainerIContainerContractStub', 'IlluminateTestsContainerContainerImplementationStub');

针对第一种情况,其实在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 = [    'HelpSpotAPI' =>  [//闭包绑定        'concrete' => function ($app, $paramters = []) {            return $app->build('HelpSpotAPI');        },        'shared' => false//如果是singleton绑定,这个值为true    ]            'IlluminateTestsContainerIContainerContractStub' => [//接口实现绑定        'concrete' => 'IlluminateTestsContainerContainerImplementationStub',        'shared' => false    ]]

singleton

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

singleton 方法是bind方法的变种,绑定一个只需要解析一次的类或接口到容器,然后接下来对于容器的调用该服务将会返回同一个实例

alias

把服务和服务别名注册到容器:

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

alias 方法在上面讲bind方法里有用到过,它会把把服务别名和服务类的对应关系注册到服务容器的$aliases属性里。

例如:

$this->app->alias('IlluminateServiceName', 'service_alias');

绑定完服务后在使用时就可以通过

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

将服务对象解析出来,这样make的时候就不用写那些比较长的类名称了,对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(引导、初始化)

【相关推荐:laravel视频教程】

以上就是laravel服务容器是什么的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/227393.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
银与绯海岸探险全部剧情触发攻略
上一篇 2025年11月3日 19:43:21
如何在一条 MySQL 语句中同时使用内置命令 (G & g) 和分号 (;)?
下一篇 2025年11月3日 19:43:29

相关推荐

  • composer require-dev和require有什么不同_Composer Require与Require-Dev区别解析

    require用于声明项目运行必需的依赖,如框架、数据库组件和第三方SDK,这些包会随项目部署到生产环境;2. require-dev用于声明仅在开发和测试阶段需要的工具,如PHPUnit、PHPStan、Faker等,不会默认部署到生产环境;3. 安装时composer install根据环境决定…

    2026年5月10日
    1000
  • 开源免费PHP工具 PHP开发效率提升利器

    推荐开源免费PHP开发工具以提升效率:VS Code、Sublime Text轻量高效,PhpStorm专业强大;调试用Xdebug、Kint、Ray;依赖管理选Composer;代码质量工具包括PHPStan、Psalm、PHP_CodeSniffer;数据库管理可用%ignore_a_1%MyA…

    2026年5月10日
    000
  • 深入理解 Laravel Session::put:避免常见陷阱与实现表单限流

    本文旨在深入探讨 laravel 框架中 `session::put` 方法的正确用法及其常见误区。针对用户在实现表单提交限流时遇到的问题,详细阐述了 `session::put` 必须提供键值对的原理,并提供了如何在控制器中利用会话机制有效防止重复提交的实战代码示例。通过本文,读者将掌握 lara…

    2026年5月10日
    000
  • Voyager 中关联关系的翻译问题解决方案

    本文档旨在解决在使用 TCGVoyager 管理后台时,关联模型无法正确翻译的问题。主要针对 Laravel 项目中,使用 Voyager 1.4 版本以及 Laravel 8.0 版本,并且已经配置多语言支持的情况下,如何确保关联关系中的可翻译字段能够根据当前应用语言环境进行正确翻译。通过修改 B…

    2026年5月10日
    000
  • 优化 Laravel Eloquent 查询:高效构建用户排行榜数据

    本教程详细讲解如何优化 Laravel Eloquent 查询以高效生成基于关联记录计数的排行榜。通过识别并消除冗余的 whereHas 子句,并巧妙利用 withCount 的条件闭包,我们能显著提升查询性能,大幅缩短数据获取时间,从而改善用户体验并降低数据库负载。 在 laravel 应用开发中…

    2026年5月10日
    000
  • 告别重复:使用Laravel Precognition统一前后端API验证

    本文旨在解决在Laravel后端与前端API交互中,如何高效复用后端验证规则的挑战。传统方案常限于表单元素,难以覆盖所有API请求。通过引入Laravel Precognition,开发者能够实现后端验证逻辑在前端的无缝应用,避免规则重复编写,从而提升开发效率与代码一致性,确保所有API请求的数据完…

    2026年5月10日
    200
  • Laravel Session::put 正确用法详解与常见误区规避

    本文详细探讨了 laravel 中 `session::put` 方法的正确用法,特别指出在仅提供键名而未指定值时可能导致会话数据未被正确设置的问题。通过示例代码,阐述了如何为会话数据赋予明确的值,并演示了如何正确地检查和获取会话数据,以确保会话管理功能按预期工作,有效避免常见的会话操作错误。 La…

    2026年5月10日
    000
  • PHP中批量为嵌套数组元素添加公共属性的教程

    本教程将详细介绍在php中如何高效地为包含多个关联数组的集合中的每个子数组添加一个或多个新的公共键值对。我们将探讨使用循环和数组合并函数实现这一目标的方法,并提供清晰的代码示例,帮助开发者处理此类数据结构转换。 在PHP开发中,我们经常会遇到处理复杂数据结构的需求,其中一种常见场景是拥有一个由多个关…

    2026年5月10日
    000
  • PHP框架的社区支持存在哪些痛点?

    php框架社区支持的痛点包括:文档匮乏或过时(1)、响应缓慢(2)、社区分散(3)。实战案例表明这些痛点可能导致开发进度受阻。改善方法包括:提供全面的文档、建立响应迅速的官方论坛、创建一个集成的社区平台。 PHP 框架社区支持存在的痛点及实战案例 PHP 框架为 Web 开发提供了强大的基础,但其社…

    2026年5月10日
    100
  • Laravel 8中Firebase Storage文件条件删除策略与实践

    本文针对Laravel 8环境下Firebase Storage无法直接按目录批量或条件删除文件的限制,提出了一套基于元数据管理的解决方案。通过在数据库中记录文件信息,结合Laravel的Artisan命令和Cron任务,实现对过期文件的精准识别与逐个删除,确保存储资源的有效管理。 Firebase…

    2026年5月10日
    000
  • php怎么安装_在云服务器上部署PHP环境的步骤

    答案:在云服务器上部署PHP环境需搭建LEMP栈(Linux+Nginx+MySQL+PHP-FPM),依次更新系统、安装Nginx、MariaDB、PHP-FPM及扩展,配置Nginx解析PHP并测试,最后通过权限控制、安全配置、防火墙和HTTPS等措施保障环境安全稳定。 在云服务器上部署PHP环…

    2026年5月10日
    000
  • Laravel 产品多图上传错误:foreach() 参数类型问题解决方案

    本文旨在解决 Laravel 应用中产品多图上传时遇到的 “foreach() argument must be of type array|object, null given” 错误。通过检查并确保循环遍历的变量为数组类型,避免因空值导致的错误,并提供代码示例和注意事项,…

    2026年5月10日
    200
  • PHP源码命令行工具开发_PHP源码命令行工具开发教程

    答案是使用PHP开发命令行工具需依托CLI SAPI,结合Composer管理依赖,并推荐采用Symfony Console等组件库来构建。首先确保PHP支持CLI模式,通过编写基础脚本并利用$argv和getopt()处理参数,但更优方式是引入Symfony Console组件进行命令定义与输入输…

    2026年5月10日
    000
  • PHP怎么运行创建_php脚本创建与执行流程解析

    PHP脚本需在服务器环境中通过解释器运行,不能双击执行。首先搭建环境(如XAMPP),然后编写.php文件并保存至服务器根目录,接着通过浏览器访问或命令行执行php命令运行脚本,服务器会调用PHP解释器解析代码并返回结果。 PHP脚本的运行依赖于服务器环境和解释器,不是直接像可执行程序那样双击运行。…

    2026年5月10日
    100
  • php中get_parent_class获取父类名_php在继承链中定位父类的应用场景

    get_parent_class函数用于获取类的父类名称,接收类名字符串返回父类名或false。示例中Dog类继承Animal,调用get_parent_class(__CLASS__)输出Animal。应用场景一:条件性调用父类方法,如构造函数中判断是否存在父类并调用其方法,提升灵活性。应用场景二…

    2026年5月10日
    100
  • 使用Laravel Blade动态渲染带标题的表格数据

    本文旨在详细指导如何在Laravel Blade模板中,利用`@foreach`循环和正确的索引策略,高效且准确地从嵌套数组结构中提取数据,并将其渲染成一个结构清晰、内容匹配的HTML表格,避免数据重复和错位问题。 在Web开发中,经常需要根据后端提供的数据动态生成HTML表格。特别是在处理具有行标…

    2026年5月10日
    000
  • Laravel模型中实现多语言数据自动过滤:重写newQuery()方法

    本教程详细介绍在laravel多语言应用中,如何通过重写模型(model)的`newquery()`方法,实现数据查询时自动根据当前应用语言环境进行过滤。这种方法提供了一种优雅且dry(don’t repeat yourself)的解决方案,避免了在每次数据查询时手动添加语言条件,确保了…

    2026年5月10日
    000
  • php学习有哪些

    PHP 学习途径:入门途径:在线教程:Codecademy、Udemy、Coursera 等书籍:《Head First PHP & MySQL》、《PHP in Action》官方文档:PHP 官方文档进阶学习:框架:Laravel、CodeIgniter 等数据库:MySQL、Postg…

    2026年5月10日
    100
  • 在 Laravel 中同时存储原始图片和 WebP 转换图片

    本文详细介绍了在 Laravel 应用中如何高效地处理图片上传,实现同时保存原始图片(如 JPG/PNG)及其 WebP 转换版本。通过利用 PHP 原生 GD 库功能,我们能够克服 Intervention Image 在特定场景下的路径写入问题,确保原始图片和优化后的 WebP 格式文件都能正确…

    2026年5月10日
    000
  • 解决AJAX响应中PHP输出JSON后出现多余HTML的问题

    本文旨在解决PHP脚本通过AJAX响应返回JSON数据时,出现JSON数据后方意外附带HTML内容的问题。通过在PHP脚本中JSON编码输出后立即使用die()或exit()函数,可以有效阻止后续不必要的输出,确保客户端接收到纯净、可解析的JSON响应,从而避免解析错误,提升前后端通信的健壮性。 理…

    2026年5月10日
    000

发表回复

登录后才能评论
关注微信