Container服务容器
服务容器是laravel中执行依赖注入的工具、用于存储各种注入到容器的类库对象,首先通过服务提供者绑定到容器,然后再通过反射机制获取里面的对象,获取的时候根据绑定的类型进行判断、例如有类、闭包、实例对象、会根据类型取获取
laravel 容器获取流程
laravel 中获取容器实例常用的有一下几种方式
- app() 辅助函数
- 服务名::方法 静态方法调用 如:App::basePath();
第一点: Facade是laravel框架里面比较特殊的一个特性,每个Facade都会与容器里面的一个实例对象关联,我们可以直接通过Facade类静态方法调用的形式来调用它关联的实例对象的方法。比如App这个Facade,调用App::basePath()的时候,实际相当于app()->basePath()。
这个底层机制也是依赖于php语言的特性才能实现的,需要在每一个Facade里面,设定一个静态成员并关联到一个服务的实例对象,当调用Facade类的静态方法的时候,解析出调用的方法名,再去调用关联的服务实例的同名方法,最后把结果返回。
我认为理解Facade能起到什么作用就够了,不一定要深究到它底层去了解实现的细节,毕竟在实际的开发中,不用Facade,也完全不影响laravel框架的使用。另外在实际编码中,要自定义一个Facade也非常容易,只要继承laravel封装的Facade基类即可:
<?php
namespace ThirdProvidersCasServerFacades;
use IlluminateSupportFacadesFacade;
use ThirdProvidersCasServerCasServerManager;
class CasServer extends Facade
{
protected static function getFacadeAccessor()
{
return CasServerManager::class;
}
}
实现Facade基类的getFacadeAccessor方法,laravel框架就知道这个Facade类该与哪个服务实例关联起来了。实际上这个getFacadeAccess方法,返回的名称就是后面要介绍的服务绑定名称。在laravel容器里面,一个服务实例,都会有一个固定的绑定名称,通过这个名称就能找到这个实例。所以为啥Facade类只要返回服务绑定名称即可。
我们可以看看App这个Facade类的代码:
<?php
namespace IlluminateSupportFacades;
/**
* @see IlluminateFoundationApplication
*/
class App extends Facade
{
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor()
{
return 'app';
}
}
它的getFacadeAccessor返回的就是一个字符串“app”,这个app就是laravel容器自己绑定自己时用的名称。
第二点:从上一点最后App这个Facade的源码可以看出,App这个Facade的全类名其实是:IlluminateSupportFacadesApp,那为什么我们在代码里面能够直接通过App这个简短的名称就能访问到呢:
<?php
Route::get('/', function () {
dd(App::basePath());
return '';
});
你看以上代码完全没有用到use或者完全限定的方式来使用IlluminateSupportFacadesApp。实际上App跟IlluminateSupportFacadesApp是完全等价的,只不过App比IlluminateSupportFacadesApp要简短很多,而且不需要use,所以用起来方便,那么它是怎么实现的?这跟laravel容器配置的别名有关系,在config/app.php中,
有一节aliases专门用来配置一些类型的别名:
'aliases' => [
'App' => IlluminateSupportFacadesApp::class,
'Artisan' => IlluminateSupportFacadesArtisan::class,
'Auth' => IlluminateSupportFacadesAuth::class,
'Blade' => IlluminateSupportFacadesBlade::class,
'Bus' => IlluminateSupportFacadesBus::class,
'Cache' => IlluminateSupportFacadesCache::class,
'Config' => IlluminateSupportFacadesConfig::class,
'Cookie' => IlluminateSupportFacadesCookie::class,
'Crypt' => IlluminateSupportFacadesCrypt::class,
'DB' => IlluminateSupportFacadesDB::class,
'Eloquent' => IlluminateDatabaseEloquentModel::class,
'Event' => IlluminateSupportFacadesEvent::class,
'File' => IlluminateSupportFacadesFile::class,
'Gate' => IlluminateSupportFacadesGate::class,
'Hash' => IlluminateSupportFacadesHash::class,
'Lang' => IlluminateSupportFacadesLang::class,
'Log' => IlluminateSupportFacadesLog::class,
'Mail' => IlluminateSupportFacadesMail::class,
'Notification' => IlluminateSupportFacadesNotification::class,
'Password' => IlluminateSupportFacadesPassword::class,
'Queue' => IlluminateSupportFacadesQueue::class,
'Redirect' => IlluminateSupportFacadesRedirect::class,
'Redis' => IlluminateSupportFacadesRedis::class,
'Request' => IlluminateSupportFacadesRequest::class,
'Response' => IlluminateSupportFacadesResponse::class,
'Route' => IlluminateSupportFacadesRoute::class,
'Schema' => IlluminateSupportFacadesSchema::class,
'Session' => IlluminateSupportFacadesSession::class,
'Storage' => IlluminateSupportFacadesStorage::class,
'URL' => IlluminateSupportFacadesURL::class,
'Validator' => IlluminateSupportFacadesValidator::class,
'View' => IlluminateSupportFacadesView::class
],
然后在laravel框架处理请求过程中,会通过IlluminateFoundationBootstrapRegisterFacades这个类来注册这些别名到全局环境里面:
<?php
namespace IlluminateFoundationBootstrap;
use IlluminateSupportFacadesFacade;
use IlluminateFoundationAliasLoader;
use IlluminateContractsFoundationApplication;
class RegisterFacades
{
/**
* Bootstrap the given application.
*
* @param IlluminateContractsFoundationApplication $app
* @return void
*/
public function bootstrap(Application $app)
{
Facade::clearResolvedInstances();
Facade::setFacadeApplication($app);
AliasLoader::getInstance($app->make('config')->get('app.aliases', []))->register();
}
}
所以我们才能直接通过别名,代替完整的类型名做同样的访问功能。如果你自己写了一些类,名称很长,并且在代码里面用的特别多,也可以考虑配置到config/app.php别名里面去,laravel会帮我们注册。
3)另外一种方式拿到laravel容器实例就是在服务提供者里面直接使用$this->app
服务提供者后面还会介绍,现在只是引入。因为服务提供者类都是由laravel容器实例化的,这些类都继承自IlluminateSupportServiceProvider,它定义了一个实例属性$app
abstract class ServiceProvider
{
/**
* The application instance.
*
* @var IlluminateContractsFoundationApplication
*/
protected $app;
laravel在实例化服务提供者的时候,会把laravel容器实例注入到这个app上面。所以我们在服务提供者里面,始终能通过app上面。所以我们在服务提供者里面,始终能通过this->$app访问到laravel容器实例,而不需要再使用app()函数或者App Facade了。
IOC (控制反转)
用户主动实例化对象、可以理解为控制正转;从容器中获取实例对象,由容器管理对象的完整的创建流程
DI (依赖注入)
依赖注入,例如A类需要B类提供的功能,它们就存在依赖关系,而注入只是把对象B交给对象A。
依赖注入有三种模式
构造器注入
class UserProvider{
protected $connection;
public function __construct( Connection $con ){
$this->connection = $con;
}
}
setter注入
class UserProvider{
protected $connection;
public function __construct(){
...
}
public function setConnection( Connection $con ){
$this->connection = $con;
}
}
接口注入
interface ConnectionInjector{
public function injectConnection( Connection $con );
}
class UserProvider implements ConnectionInjector{
protected $connection;
public function __construct(){
...
}
public function injectConnection( Connection $con ){
$this->connection = $con;
}
}