IOC容器是实现依赖注入的便捷机制——TaylorOtwell文章转自:https://learnku.com/laravel/t...Laravel是最流行和最常用的开源现代Web应用框架之一今天。它提供了一些独特的功能,如EloquentORM、查询构建器、Homestead和其他仅在Laravel中可用的时髦功能。我喜欢Laravel的地方在于它独特的设计,就像建筑一样。Laravel底层使用了多种设计模式,如单例、工厂、构建器、门面、策略、提供者、代理等模式。随着知识的增长,我越来越发现Laravel的美妙之处。Laravel为开发者减轻了痛苦,带来了更多便利。学习Laravel不仅仅是学习如何使用不同的类,更是学习Laravel的哲学和优雅的语法。Laravel理念的一个重要组成部分是IoC容器,也称为服务容器。它是Laravel应用程序的核心部分,因此理解和使用IoC容器是我们必须掌握的一项重要技能。IoC容器是一个非常强大的类管理工具。它可以自动解析类。接下来我将尝试解释为什么它很重要以及它是如何工作的。首先说一下依赖倒置的原理。了解它有助于我们更好地理解IoC容器的重要性。这个原则指出:高层模块不应该依赖低层模块,两者都应该依赖抽象接口。抽象接口不应依赖于具体实现。具体实现应该依赖于抽象接口。简而言之:依靠抽象而不是具体类MySQLConnection{/***数据库连接*/publicfunctionconnect(){var_dump('MYSQLConnection');}}classPasswordReminder{/***@varMySQLConnection*/private$dbConnection;公共函数__construct(MySQLConnection$dbConnection){$this->dbConnection=$dbConnection;人们常常有一种误解,认为依赖倒置只是依赖注入的另一种说法。但实际上两者是不同的。在上面的代码示例中,虽然在PasswordReminder类中注入了MySQLConnection类,但它仍然依赖于MySQLConnection类。但是,高级模块PasswordReminder不应依赖于低级模块MySQLConnection。如果我们想把MySQLConnection改成MongoDBConnection,那我们就得手动修改PasswordReminder类构造函数中的依赖。PasswordReminder类应该依赖于抽象接口,而不是具体类。那么我们该怎么做呢?请看下面的例子:}}classPasswordReminder{/***@varDBConnection*/private$dbConnection;公共函数__construct(ConnectionInterface$dbConnection){$this->dbConnection=$dbConnection;}}通过上面的代码,如果我们要将MySQLConnection改为MongoDBConnection,完全不需要修改PasswordReminder类构造函数中的Dependency。因为现在PasswordReminder类依赖于接口,而不是具体类。如果您对接口的概念了解不多,可以阅读这篇文章。它将帮助您理解依赖倒置原理和IoC容器等。现在我将讨论IoC容器内部发生的事情。我们可以简单地将IoC容器理解为一个包含类依赖的容器。OrderRepositoryInterface接口:命名空间App\Repositories;interfaceOrderRepositoryInterface{publicfunctiongetAll();}DbOrderRepository类:命名空间App\Repositories;类DbOrderRepository实现OrderRepositoryInterface{functiongetAll(){return'Gettingallfrommysql';}}类:namespaceControllerApp\Http\Controllers;使用Illuminate\Http\Request;使用App\Http\Requests;使用App\Repositories\OrderRepositoryInterface;类OrdersControllerextendsController{protected$order;函数__construct(OrderRepositoryInterface$order){$this->order=$order;}publicfunctionindex(){dd($this->order->getAll());返回View::make(orders.index);}}路由:Route::resource('orders','OrdersController');现在,在浏览器中输入这个地址,报错。报错的原因是服务容器正在尝试实例化一个接口,无法实例化接口。要解决这个问题,只需将接口绑定到特定的类:在路由文件中添加以下代码行即可:App::bind('App\Repositories\OrderRepositoryInterface','App\Repositories\DbOrderRepository');现在刷新浏览器看看:我们可以这样定义一个容器类:classSimpleContainer{protectedstatic$container=[];publicstaticfunctionbind($name,Callable$resolver){static::$container[$name]=$resolver;}publicstaticfunctionmake($name){if(isset(static::$container[$name])){$resolver=static::$container[$name];返回$解析器();}thrownewException("容器中不存在绑定");}}在这里,我想告诉大家一个服务容器解决依赖是多么的简单。classLogToDatabase{publicfunctionexecute($message){var_dump('将消息记录到数据库:'.$message);}}classUsersController{protected$logger;公共函数__construct(LogToDatabase$logger){$this->logger=$logger;}publicfunctionshow(){$user='JohnDoe';$this->logger->execute($user);}}绑定依赖:SimpleContainer::bind('Foo',function(){returnnewUsersController(newLogToDatabase);});$foo=SimpleContainer::make('Foo');print_r($foo->show());输出:string(36)"Logthemessagestoafile:JohnDoe"Laravel的服务容器源码:publicfunctionbind($abstract,$concrete=null,$shared=false){$abstract=$this->归一化($抽象);$concrete=$this->normalize($concrete);如果(is_array($abstract)){list($abstract,$alias)=??$this->extractAlias($abstract);$this->alias($abstract,$alias);}$this->dropStaleInstances($抽象);如果(is_null($concrete)){$concrete=$abstract;}if(!$concreteinstanceofClosure){$concrete=$this->getClosure($abstract,$concrete);$this->bindings[$abstract]=compact('concrete','shared');如果($this->resolved($abstract)){$this->rebound($abstract);}}publicfunctionmake($abstract,array$parameters=[]){$abstract=$this->getAlias($this->normalize($abstract));如果(isset($this->instances[$abstract])){return$this->instances[$abstract];$concrete=$this->getConcrete($abstract);如果($this->isBuildable($concrete,$abstract)){$object=$this->build($concrete,$parameters);}else{$object=$this->make($concrete,$parameters);}foreach($this->getExtenders($abstract)作为$extender){$object=$extender($object,$this);}if($this->isShared($abstract)){$this->instances[$abstract]=$object;}$this->fireResolvingCallbacks($abstract,$object);$this->resolved[$abstract]=true;返回$对象;}publicfunctionbuild($concrete,array$parameters=[]){if($concreteinstanceofClosure){return$concrete($this,$parameters);}}$reflector=newReflectionClass($concrete);if(!$reflector->isInstantiable()){if(!empty($this->buildStack)){$previous=implode(',',$this->buildStack);$message="目标[$concrete]在构建[$previous]时不可实例化。";}else{$message="目标[$concrete]不可实例化。";}抛出新的BindingResolutionException($message);}$this->buildStack[]=$混凝土;$constructor=$reflector->getConstructor();如果(is_null($constructor)){array_pop($this->buildStack);返回新的$混凝土;}$dependencies=$constructor->getParameters();$parameters=$this->keyParametersByArgument($dependencies,$parameters);$instances=$this->getDependencies($dependencies,$parameters);array_pop($this->buildStack);返回$reflector->newInstanceArgs($instances);}如果想了解更多服务容器,可以看vendor/laravel/framwork/src/Illuminate/Container/Container.php简单绑定$this->app->bind('HelpSpot\API',function($app){returnnewHelpSpot\API($app->make('HttpClient'));});单例模式通过单例方法绑定到服务容器的类或接口,只会解析一次$this->app->singleton('HelpSpot\API',function($app){returnnewHelpSpot\API($app->make('HttpClient'));});绑定实例你也可以通过实例方法将一个特定的实例绑定到服务容器中。之后,它将始终返回绑定实例:$api=newHelpSpot\API(newHttpClient);$this->app->instance('HelpSpot\API',$api);如果没有绑定,PHP会使用反射机制来解析实例和依赖。如果想了解更多细节,可以查看Laravel服务容器练习代码的官方文档,可以从我的GitHub(喜欢的话请star)仓库获取。谢谢阅读。文章转自:https://learnku.com/laravel/t...更多文章:https://learnku.com/laravel/c...