-
任何一个类里面,只要有构造函数依赖的,都可以注入
-
服务提供者里面,registry方法是注册容器,boot方法是启动的时候进行的初始化工作,还有map方法,把服务提供者做一个映射
第一段
下面我做一个测试实现依赖注入
首先在
app
下面自己写一个类,vi /app/U.php
// app/U.php
<?php
namespace App;
class U
{
public function show()
{
return 'this is U show';
}
}
然后我们在控制器里面的构造函数里实现注入。这样,整个控制器都可以使用U.php类里面的所有函数了
<?php
namespace App\Http\Controllers;
class TestController extends Controller
{
protected $u;
public function __construct(\App\U $a)
{
$this->u = $a;
}
public function test()
{
return $this->u->show();
}
}
依赖注入打印结果
- 我们也可以直接绑定(
bind
),然后直接生产(make
)
Route::get('/test',function (){
App::bind('U',function(){
// 这里使用$this->app->bind()应该也可以,但是我这里测试不成功,就换成了App这个静态方法了
return new \App\U(); // 我们这里实例化那个类,就是绑定的哪个类
});
$res = App::make('U');
return $res->show();
});
第二段
- 但是我们如果能够直接用
别名
的方式来替代\App\U
就更好了。那么我们就需要起一个别名。为了方便区分理解,我们此处把别名叫做UA
第一步:把U
这个类,绑定(注册)至容器。
我们在
app/Providers/AppServiceProvider.php
里面注册U
这个类,起个名字为UA
.
public function register()
{
$this->app->bind('UA',function($app){
return new \App\U();
});
}
第二步,使用Facades
给绑定的UA
做成静态方法
首先我们还是可以在
app
下面自己写一个类,vi /app/UF.php
<?php
// app/UF.php
namespace App;
use Illuminate\Support\Facades\Facade;
class UF extends Facade
{
protected static function getFacadeAccessor()
{
return 'UA'; // 此处返回的是服务容器中绑定的名字,我们刚才把`U.php`类绑定成了`UA`
}
}
这个时候我们就可以使用静态方法UF
就能替代U.php
这个类了。不需要再在构造函数中注入了
<?php
namespace App\Http\Controllers;
class TestController extends Controller
{
public function test()
{
return \App\UF::show();
}
}
// 打印出来的结果,和依赖注入打印结果是一样的
image.png
image.png
第三段
- 我们也可以给
UF.php
这个类起别名F
很简单,只需要在第二段的基础上做修改。
在app/config/app.php
里面的aliases
数组里面添加'F' => \App\UF::class,
即可。
把`UF.php类`注册别名
- 这里我是为了方便大家区分,故意把注册的
U.php
类写成了,UA
,UF
,F
。其实三个名字都应该写成U
,这样才方便我们辨别。框架里面的都是统一的。比如下图的View
类别名
图1.png
图2.png
还有一个问题就是,如果我们需要看框架里面那些类怎么办的。我们每次打开只能打开到图1
的那种情况,我们可以看手册Facades
章节的Facade 类参考#
一些听课笔记也在这里写下
- 任何一个类里面,只要有构造函数依赖的,都可以注入
laravel中所谓的容器其实就是一个工厂,可以帮助我们创建对象。使用容器可以方便的实现创建某个接口的多个实现类。
容器剋在任何可以访问到app对象的地方绑定 (注册),但通常我们会在服务提供者的register方法中进行多个容器的绑定。
laravel框架默认为我们保留了一个服务提供者,AppServiceProvider.php 其中register是空白的,我们可以将自己的容器配置到里面。
Contracts 的好处是,我们的接口是一定的,但是我们的服务可以随便修改。这样做到了低耦合。
有了服务容器,我们才可以make 出来东西
没有在服务提供者里面注入的话,也make 不出来东西
服务提供者里面,register方法是注册容器,容器是帮我们创建对象的。不过,服务提供者本身,也需要被注册,就是在`app/config/app.php`里的`providers`数组里面
boot方法是启动的时候进行的初始化工作,还有map方法,把服务提供者做一个映射
laravel工厂模式
- 其实服务提供者,就是框架给我们提供了一个可配置的工厂
- 服务提供者是在框架里面为我们准备了一个类似于工厂的工具
- 服务容器,就是具体的帮助我们实现依赖注入,控制反转的工具
所谓的工厂模式,就是我们需要什么类,我们就绑定什么类。比如刚才我们绑定了
U
类,现在我们需要一个U2
类,我们就直接绑定U2
类.
但是此处的U2
类,应该是与U
类有一样的方法,一样的接口。这样,我们直接用F::方法
中的方法都不需要改变。适用场景,比如原来用了mysql
,现在改成orcal
public function register()
{
$this->app->bind('UA',function($app){
return new \App\U();
});
}
laravel的服务提供者provider
简单讲解
其实服务提供者本质就是
1.注册一个类
2.生成一个继承ServiceProvider的 Provider
在register 函数里面实现app的绑定这个类
3.在config/app.php的providers数组里面添加第二步生成的provider类
4.在使用的时候直接app('')或者app()->make('')即可
- 实例演示
1.生成Billing类
<?php
namespace App;
class Billing{
public function test()
{
return 'test';
}
}
2.
php artisan make:provider BillingProvider
修改里面的register函数
public function register()
{
$this->app->bind('billing',function (){
return new \App\Billing();
});
}
3. config/zpp.php里面
'providers' => [
.....
\App\Providers\BillingProvider::class,
],
4 使用
public function index()
{
$res = app('billing');
// 或者 $res = app()->make('billing');
dd($res->test());
}
- 如果想要实现
facede
也很简单,我们基于以上步骤来做
5.生成一个继承Facade的Bill类
<?php
namespace App;
use Illuminate\Support\Facades\Facade;
class Bill extends Facade {
protected static function getFacadeAccessor()
{
return 'billing';
}
}
// 函数的return 必须是 billing 代表的是 Billing这个类
其实相当于是 app()->bind('billing')
Facade这个类中有public static function __callStatic($method, $args) 函数
如果我们使用的方法不存在,就会执行这个静态方法
6.config/app.php里面
'aliases' => [
'Bill' => \App\Bill::class,
]
这个时候,我们就可以直接
public function index()
{
$res = Bill::test();
dd($res);
}
但是这种方式比provider多执行了一些东西,其实效率是偏慢的
服务容器
服务容器与容器与工厂
application是一个工厂
首先他要绑定一个类或者对象,然后才可以实例化使用
首先绑定一个类或者对象,同时给他起一个名字
$this->app->bind('\A', function ($app) {
//1.使用绑定方法,回调函数都要return一个类型的对象
return new App\User();
});
//然后当你需要这个对象的时候,你就make生产
$u = $this->app->make('A');
//$this->app->make('A') 等价于 $this->app['A'];
singleton()绑定默认只会创建一个对象
$this->app->bind('\A', function () {
return new App\User();
});
$u = $this->app['A'];
$u->title = 'abc';
$u2 = $this->app['A'];
dd($u2); //打印出'abc
如果是singleton()只能创建一个对象
使用绑定方法,回调函数都要return一个类型的对象
你也可以使用 instance方法绑定一个已经存在的对象至容器中。后面的调用都会从容器中返回指定的实例:
$this->app->bind('\A', function () {
return new App\User();
});
这句话还可以写成 $this->app->bind('A',App\User::class);
$api = new HelpSpot\API(new HttpClient);
$this->app->instance('HelpSpot\Api', $api);
我们来看一个app/bootstrap/app.php中的一个绑定
$app->singleton(
//这个只是一个接口,这个接口有很多的实现
Illuminate\Contracts\Http\Kernel::class,
//这个只是具体的实现这个接口的一个类
App\Http\Kernel::class
);
绑定接口至实现
$this->app->bind( 'App\Contracts\EventPusher', //接口
'App\Services\RedisEventPusher' //实现
);
依赖注入
一个工厂类来帮我们创建我们的对象的同时,把我们这个对象创建的时候需要的参数也一起注入进来
这个过程就叫做控制反转。把创建对象的责任交给容器,交给工厂,而不是我们自己
1.我们先创建两个类,比如在app目录下TA.php TB.php两个文件夹下两个类
namespace App;
class TA
{
private $title;
}
namespace App;
class TB
{
private $ta;
public function __construct(\App\TA $a){
$this->ta = $ta;
}
}
2.在app/bootstrap/app.php中写好一个绑定
绑定的时候,如果命名空间从根目录下引用"\",则此命名空间加不加引号都行
$app->bind( 'App\TA::class', '\App\TA::class'); //前面这个必须加引号,后面这个可写可不写
$app->bind( '\App\TB::class', \App\TB::class);
3.然后我们在路由页面随便做一个测试
Route::get('/test',function(){
$tb = $this->app->make( '\APP\TB::class' ); //引号可写可不写
dd($tb);
});
实现添加自己组建的流程
1.先在config/app.php里面的 providers 数组里面添加自己的 provider类
App\Library\Tool\ToolServiceProvider::class,
自己的provider类写在哪里都无所谓,只要跟数组中对上即可
2.在provider类里面 register 写入自己$this->app->singleton绑定的类,这个类是自己真正需要的功能类
singleton中第一个参数是随便的一个名称,第二个参数才是类名
3.创建一个facade类
protected static function getFacadeAccessor()
{
return \App\Library\Tool\source\Common::class;
}
}
这个return中的值,其实是刚才singleton 绑定的第一个参数
4.在config/app.php中的aliases数组里面添加别名
'Common' => App\Library\Tool\CommonFacade::class,
这个值对应的是3中的类名