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:17
下一篇 2025年11月3日 19:43:39

相关推荐

  • php如何实现多进程编程?PHP多进程编程基础与实践

    PHP多进程编程主要依赖PCNT扩展,通过pcntl_fork()创建子进程实现并行处理,父进程需用pcntl_waitpid()回收子进程避免僵尸进程,结合信号处理可提升健壮性;实际项目中常用消息队列或Swoole等替代方案以增强扩展性与跨平台支持。 PHP实现多进程编程主要依赖PCNT(Proc…

    2025年12月10日
    000
  • Laravel 8 中更新多选框权限:使用 Sync 方法高效管理多对多关系

    或者,更简洁的方式,直接在blade模板中使用集合的contains方法: id }}” {{ $user->userPermissions->contains($permission->id) ? ‘checked’ : ” }}> 总结: 使用 Laravel 的 sy…

    2025年12月10日
    000
  • Laravel 中保持下拉列表选择状态的教程

    本文旨在解决 Laravel 应用中,在表单提交后下拉列表重置的问题。通过利用 Laravel 的请求对象,我们可以轻松地在页面刷新后保持用户在下拉列表中所做的选择,提升用户体验。本文将详细介绍如何实现这一功能,并提供示例代码和注意事项。 在 Laravel 应用中,表单提交后页面刷新,下拉列表恢复…

    2025年12月10日
    000
  • php如何实现一个简单的REST API?php构建RESTful API基础教程

    核心是通过PHP处理HTTP请求并返回JSON响应。需设计URI、选择HTTP方法、实现路由与数据处理。示例中根据GET请求返回用户信息,支持单个或全部用户查询,并返回对应状态码。POST请求通过解析php://input获取JSON数据,验证后创建新用户并返回201状态码。安全方面需过滤输入防止注…

    2025年12月10日
    000
  • php如何配置错误报告?php错误报告级别设置指南

    PHP错误报告配置需根据环境区分:开发时开启display_errors和E_ALL级别报告以快速调试,生产时关闭显示并记录日志,常用error_reporting控制级别,结合ini_set()或框架实现灵活管理。 PHP错误报告的配置核心在于控制哪些类型的错误被显示给用户或记录到日志文件,以及如…

    2025年12月10日
    000
  • Laravel 中表单提交后如何保持下拉列表的选中状态

    本文旨在解决 Laravel 应用中表单提交后下拉列表(select)重置的问题。通过利用 Laravel 提供的 request 对象和旧输入值功能,我们能够轻松地在页面刷新后保持用户在下拉列表中选择的选项,从而提升用户体验。本文将详细介绍如何在视图中正确地处理下拉列表的选中状态,并提供相应的代码…

    2025年12月10日
    000
  • Laravel 中下拉列表选择后重置问题的解决

    本文旨在解决 Laravel 应用中下拉列表在提交后重置的问题。通过利用 Laravel 的请求对象,我们将演示如何在页面刷新后保持用户在下拉列表中选择的选项,从而提升用户体验。文章将提供详细的代码示例和解释,帮助开发者轻松实现该功能。 在 Laravel 应用中,经常会遇到需要在表单提交后保持用户…

    2025年12月10日
    000
  • Laravel Redis队列任务清理指南:兼顾旧版本与Laravel 8+

    本文详细介绍了在Laravel应用中清空Redis队列任务的方法。对于Laravel 8及更高版本,可使用内置的php artisan queue:clear命令;而对于Laravel 8之前的版本,则需通过创建自定义Artisan命令来直接操作Redis键,实现队列任务的快速清理,尤其适用于开发环…

    2025年12月10日
    000
  • PHP怎么过滤GET参数_PHPGET参数安全过滤技巧

    过滤GET参数可防止SQL注入、XSS等攻击,保障数据安全;使用filter_input、htmlspecialchars、预处理语句等方法能有效过滤和转义用户输入,结合HTTPS、权限控制等措施全面提升安全性。 直接点说,PHP过滤GET参数是为了确保你的网站不被恶意攻击,比如SQL注入、XSS攻…

    2025年12月10日
    000
  • PHP如何使用命名空间_PHP命名空间(Namespace)的使用与解析

    命名空间为PHP代码提供“姓氏”以避免命名冲突,通过namespace声明定义作用域,使用use导入类或函数,支持别名处理同名元素,是现代PHP模块化、自动加载和团队协作的基础。 PHP命名空间,简单来说,就是为你的代码提供一个“姓氏”,避免不同家族(或说不同库、不同模块)中同名成员(类、接口、函数…

    2025年12月10日
    000
  • php如何实现定时任务_php实现计划任务的方法

    答案:PHP定时任务依赖系统调度工具如Cron或任务计划程序,通过绝对路径调用PHP解释器执行脚本,并重定向输出以避免日志堆积;为防止并发执行,可使用文件锁flock()机制;在复杂场景下,推荐结合消息队列(如RabbitMQ、Redis)与消费者进程实现解耦和高并发处理,利用Laravel Sch…

    2025年12月10日
    000
  • PHP如何防止SQL注入_PHP防范SQL注入攻击的核心策略

    防范SQL注入的核心是预处理语句,它通过将SQL逻辑与数据分离,确保用户输入始终作为数据处理;结合参数绑定,使用PDO或MySQLi扩展可有效阻止恶意SQL执行,从根本上避免注入风险。 PHP防范SQL注入的核心策略,毫无疑问是采用预处理语句(Prepared Statements)配合参数绑定(P…

    2025年12月10日
    000
  • Laravel AJAX DELETE 请求方法不匹配问题及解决方案

    本文旨在解决Laravel应用中,当尝试通过AJAX发送DELETE请求时,遇到的“GET method is not supported for this route”错误。我们将深入探讨导致此问题的原因,并提供一个结合AJAX和Laravel方法伪造(Method Spoofing)机制的完整解…

    2025年12月10日
    000
  • Laravel中显示选中项目的标题及其关联问题列表

    本教程将指导如何在Laravel应用中,通过优化控制器和视图逻辑,高效地在问题列表页面显示当前选中项目的标题,并正确地迭代展示该项目的所有关联问题。文章将重点介绍如何利用Eloquent模型传递数据,并采用findOrFail方法提升代码健壮性,确保用户体验和数据完整性。 理解当前挑战 在构建lar…

    2025年12月10日
    000
  • 在 Laravel Nova 中实现邮件附件发送功能

    本教程详细指导如何在 Laravel Nova 应用中,通过自定义 Action 实现带文件附件的邮件发送功能。我们将探讨如何在 Mailable 类中利用 attach() 方法,将存储在服务器上的文件作为附件发送给收件人,并提供具体的代码示例和注意事项,确保邮件能够成功携带附件送达。 在 lar…

    2025年12月10日
    000
  • Laravel Nova 中邮件附件的实现指南

    本教程详细阐述了如何在 Laravel Nova 动作中,通过 Mailable 类为发送的邮件添加文件附件。文章将深入解析 Laravel Nova 文件字段与邮件发送机制的集成,重点介绍 Mailable 的 attach() 方法,并提供获取文件路径、处理 MIME 类型以及整合代码的完整示例…

    2025年12月10日
    000
  • Laravel教程:在问题列表视图中高效展示项目详情

    本教程旨在指导您如何在Laravel应用中,当展示特定项目的相关问题列表时,同时高效地显示该项目的详细信息。我们将通过优化控制器的数据传递方式,确保父级模型(项目)及其关联子级模型(问题)都能在视图中正确访问,并探讨相关的最佳实践,从而构建出更清晰、高效且易于维护的应用。 理解需求:项目与问题关联展…

    2025年12月10日
    000
  • 在Laravel Nova中通过邮件发送附件的教程

    本教程详细介绍了如何在Laravel Nova中,通过Mailable类为通过Action触发的邮件添加文件附件。文章将指导用户利用Mailable的attach方法,结合Laravel的存储系统,正确获取并附加文件,确保邮件能够成功携带所需附件发送给收件人,并提供了关键注意事项。 背景分析:Lar…

    2025年12月10日
    000
  • 精确控制JavaScript定时任务:实现整点弹窗与桌面通知

    本文详细阐述了如何利用JavaScript精确控制定时任务,以实现在指定时间(例如每小时的整点)触发弹窗或发送桌面通知。通过结合短间隔定时器、日期对象判断和防重复触发机制,解决了传统setInterval无法实现整点触发的问题,并提供了完整的代码示例及桌面通知的实现方法。 1. 理解传统定时器的局限…

    2025年12月10日
    000
  • 在 Laravel Nova 中通过邮件发送文件附件的教程

    在 Laravel Nova 环境下,通过 Mailable 类为电子邮件添加文件附件是一项常见需求。本教程将详细指导您如何实现这一功能,涵盖从 Nova Resource 定义到 Mailable build 方法中附件功能的实现,包括如何获取文件路径、使用 attach 方法以及相关注意事项,确…

    2025年12月10日
    000

发表回复

登录后才能评论
关注微信