写在前面
学了两个多月的laravel一直没有去研究他的核心概念,在文档上看到些名词 “服务容器”,“服务提供者”…整个人人都是懵的下面结合我这几天的学习谈谈我的理解。
laravel的核心架构:服务容器,服务提供者,门面,契约。
要理解上面概念首先我们得知道什么是服务
服务
如我们在开发时需要的各种功能 邮件发送,图像处理,验证码等等 这些可以称之为服务,因为他为我们提供了一些第三方功能。
如邮件服务为你提供了邮件发送服务,图像服务为你提供了有关图像处理的服务就像在社会的一些服务业。
服务容器
容器顾名思义装东西的器皿,而服务容器就是存放服务的地方。
在laravel中应用本身就是一个容器,容器管理调用各种服务
laravel在运行的时候会创建一个app对象 这个对象就是容器
所有的服务都是在app中调用的。
laravelchina对他的介绍
Laravel 服务容器是一个用于管理类的依赖和执行依赖注入的强大工具。
上面说到用于管理类的依赖和执行依赖注入的工具,什么意思?
首先我们得明白类与类之间是可以存在依赖关系的。
先来看一个例子
在没有laravel服务容器的依赖注入下我们要实现一个类的依赖
class Person{
public function __construct(){
echo "人类创建<br>";
}
}
class TV {
public function __construct(){
echo "Tv创建<br>";
}
}
class Family{
public $person;
public $tv;
public function __construct($person,$tv){
$this->person = $person;
$this->tv = $tv;
echo "Family创建<br>";
}
}
Route::get('test',function(){
$person = new Person();
$tv = new Tv();
$family = new Family($person,$tv);
});
如上代码,我们实现Family类依赖Person类和Tv类所以我们在其构造函数中传入了两个参数分别是person类和tv类,可以看到非常的麻烦 实现类的依赖我们要先在构造函数中接收其他两个类 在new Family的时候 我们还要先 new其他两个 在传入Family类 。
如果类的依赖关系比较简单这种方式还勉强可行,如果类的关系非常复杂怎么办?如Person类又依赖Head类 Head类又依赖Eye类 Eye类又依赖…如果这么多层依赖关系还是像上面那样做显然有点不合适。
接下来我们通过服务容器的依赖注入解决类的依赖问题
class Person{
public function __construct(){
echo "人类创建<br>";
}
}
class TV {
public function __construct(){
echo "Tv创建<br>";
}
}
class Family{
public $person;
public $tv;
public function __construct(Person $person,Tv $tv){
$this->person = $person;
$this->tv = $tv;
echo "Family创建<br>";
}
}
Route::get('test',function(Family $family){
});
上述代码我们在Family的构造函数中声明了两个参数的类型 分别为 Person ,Tv 当声明了参数类型的时候服务容器就知道我们在这里要生成什么样的对象,我们直接在运行函数注入了Family类,同样声明了family的类型,所以会在运行函数里生成Family对象,并且自动为我们处理依赖关系。
运行效果
上面是自动绑定依赖关系的方式 为构造函数声明对应的参数类型即可。
我们还可以手动绑定依赖关系方法如下
先去除构造函数的类型声明
App::bind('Family',function(){
return new Family(new Person ,new Tv());
});
//绑定后的类可直接依赖注入,自动处理依赖关系
Route::get('test',function(Family $family){
});
上面只是一个基本绑定
bind的第一个参数为 要绑定的类名,第二个参数为闭包函数返回类的实例
单例绑定
实现只创建一次对象。
App::singleton('Family',function(){
return new Family(new Person ,new Tv());
});
绑定基本值
App::when('Family')
->needs('$variable')
->give($value);
上述代表表示,当绑定family时,如果他需要一个参数则传递给他,这个参数可以是一个类也可以是变量
绑定接口
我们可以绑定某个类的接口实现,当注入接口类时会注入绑定该接口的那个类
interface Person{
public function walk();
}
class Human implements Person{
public function walk(){
echo "i can walk";
}
}
//接口绑定
App::bind('Person','Human');
Route::get('test',function(Person $person){
dd($person);
});
容器的各种绑定暂时先介绍到这里,具体可参照服务容器
服务提供者
顾名思义就是提供服务的“人”,可以给你提供一组服务
服务提供者可以使我们方便快捷的调用各种服务,因为他提供了一种机制使各种服务能够注册到app中也就是服务容器,当注册到app中,服务容器才能方便的管理和调用这些服务。
上面我们通过App::bind()....
方式绑定的类毫无章程,随随便便就执行了绑定,这样带来的问题显得代码混乱,且每个类没有一个具体的界定,如邮件发送类 验证码类等等,因此服务提供者应运而生,服务提供者专门对这些服务归类,如A服务提供者下面的服务实现某个功能 B服务提供者下面的服务实现某个功能。
创建服务提供者php artisan make:provider TestServiceProvider
创建的服务提供者存放在 \app\Providers
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class Person{
}
class TestProvier extends ServiceProvider
{
/**
* Register services.
*
* @return void
*/
public function register()
{
$this->app->bind('Person',function(){
return new Person();
});
}
/**
* Bootstrap services.3
*
* @return void
*/
public function boot()
{
//
}
}
服务提供者有两个方法,register会在laravel启动时调用,因此我们要在register中注册各种服务,boot方法会在所有服务提供者的register方法都走完时执行,因此可以在boot方法中实现类的依赖注入。
完成服务绑定后我们还要将服务提供者注册到服务容器(config/app下的providers数组)
打印刚才注册的方法
Route::get('test',function(){
dd(resolve('Person'));
});
延迟服务提供者
如果只是绑定服务到容器,可以选择延迟服务提供者,这样laravel启动时并不会立马延迟服务,而是等用到之后在加载
方法
要延迟加载提供者,需要实现\Illuminate\Contracts\Support\DeferrableProvider
接口并设置provides 方法。这个 provides 方法返回该提供者注册的服务容器绑定:
以laravel框架的CacheServiceProvider为例
<?php
namespace Illuminate\Cache;
use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Support\ServiceProvider;
use Symfony\Component\Cache\Adapter\Psr16Adapter;
class CacheServiceProvider extends ServiceProvider implements DeferrableProvider
{
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->app->singleton('cache', function ($app) {
return new CacheManager($app);
});
$this->app->singleton('cache.store', function ($app) {
return $app['cache']->driver();
});
$this->app->singleton('cache.psr6', function ($app) {
return new Psr16Adapter($app['cache.store']);
});
$this->app->singleton('memcached.connector', function () {
return new MemcachedConnector;
});
}
/**
* Get the services provided by the provider.
*
* @return array
*/
public function provides()
{
return [
'cache', 'cache.store', 'cache.psr6', 'memcached.connector',
];
}
}
门面
在laravel中可以使用可以使用 类名::方法名
的方式使用类,而传统的方式我们调用一个类需要先引入一个长长的类名 在new
在进行调用,比较麻烦而门面则相当于静态方法直接使用即可,这些能够直接使用的类称之为门面类,但是门面类不易使用过多这个手册上有明确说明。
各种Facades类
Facade | Class | 服务容器绑定 |
App | Illuminate\Foundation\Application | app |
Artisan | Illuminate\Contracts\Console\Kernel | artisan |
Auth | Illuminate\Auth\AuthManager | auth |
Auth | (Instance) Illuminate\Contracts\Auth\Guard | auth.driver |
Blade | Illuminate\View\Compilers\BladeCompiler | blade.compiler |
Broadcast | Illuminate\Contracts\Broadcasting\Factory | |
Broadcast (Instance) | Illuminate\Contracts\Broadcasting\Broadcaster | |
Bus | Illuminate\Contracts\Bus\Dispatcher | |
Cache | Illuminate\Cache\CacheManager | cache |
Cache | (Instance) Illuminate\Cache\Repository | cache.store |
Config | Illuminate\Config\Repository | config |
Cookie | Illuminate\Cookie\CookieJar | cookie |
Crypt | Illuminate\Encryption\Encrypter | encrypter |
DB | Illuminate\Database\DatabaseManager | db |
DB (Instance) | Illuminate\Database\Connection | db.connection |
Event | Illuminate\Events\Dispatcher | events |
File | Illuminate\Filesystem\Filesystem | files |
Gate | Illuminate\Contracts\Auth\Access\Gate | |
Hash | Illuminate\Contracts\Hashing\Hasher | hash |
Lang | Illuminate\Translation\Translator | translator |
Log | Illuminate\Log\LogManager | log |
Mail | Illuminate\Mail\Mailer | mailer |
Notification | Illuminate\Notifications\ChannelManager | |
Password | Illuminate\Auth\Passwords\PasswordBrokerManager | auth.password |
Password (Instance) | Illuminate\Auth\Passwords\PasswordBroker | auth.password.broker |
Queue | Illuminate\Queue\QueueManager | queue |
Queue (Instance) | Illuminate\Contracts\Queue\Queue | queue.connection |
Queue (Base Class) | Illuminate\Queue\Queue | |
Redirect | Illuminate\Routing\Redirector | redirect |
Redis | Illuminate\Redis\RedisManager | redis |
Redis (Instance) | Illuminate\Redis\Connections\Connection | redis.connection |
Request | Illuminate\Http\Request | request |
Response | Illuminate\Contracts\Routing\ResponseFactory | |
Response (Instance) | Illuminate\Http\Response | |
Route | Illuminate\Routing\Router | router |
Schema | Illuminate\Database\Schema\Builder | |
Session | Illuminate\Session\SessionManager | session |
Session (Instance) | Illuminate\Session\Store | session.store |
Storage | Illuminate\Filesystem\FilesystemManager | filesystem |
Storage (Instance) | Illuminate\Contracts\Filesystem\Filesystem | filesystem.disk |
URL | Illuminate\Routing\UrlGenerator | url |
Validator | Illuminate\Validation\Factory | validator |
Validator (Instance) | Illuminate\Validation\Validator | |
View | Illuminate\View\Factory | view |
View (Instance) | Illuminate\View\View |
实时的fade
我们可以在use类前加Facades
这样我们就可以像使用Facades类那样调用各种类方法不用实例化。
契约
契约用于规范服务提供者的格式,方法,参数等。
契约给服务提供者规增加了一定约束。所以在框架里面所有的契约都是接口,这样才能规范服务提供者。