控制反转(Inversion of Control)
缩写为IoC
最常见的方式叫做依赖注入
简单说来,就是一个类把自己的的控制权交给另外一个对象,类间的依赖由这个对象去解决。
Laravel 中的使用
注入一个类:
App::bind('foo', function($app) { return new FooBar; });
这个例子的意思是创建一个别名为 foo 的类,使用时实际实例化的是 FooBar。
使用这个类的方法是:
$value = App::make('foo');
$value 实际上是 FooBar 对象。
如果希望使用单例模式来实例化类,那么使用:
App::singleton('foo', function() { return new FooBar; });
这样的话每次实例化后的都是同一个对象。
为了让依赖注入的代码不至于写乱,Laravel 搞了一个 服务提供器(Service Provider)的东西,它将这些依赖聚集在了一块,统一申明和管理,让依赖变得更加容易维护。
为了让 Laravel 中的核心类使用起来更加方便,Laravel实现了门面模式。Facade
$value = Cache::get('key');
这些静态调用实际上调用的并不是静态方法,而是通过 PHP 的魔术方法 __callStatic() 讲请求转到了相应的方法上。
那么如何讲我们前面写的服务提供器也这样使用呢?方法很简单,只要这么写:
use Illuminate\Support\Facades\Facade; class Foo extends Facade { protected static function getFacadeAccessor() { return ‘foo’; } }
这样我们就可以通过 Foo::test() 来调用我们之前真正的 FooBar 类的方法了。
总结一下创建自己类库的方法:
- 在 app/library/MyFoo 下创建类 MyFoo.php
- 在 app/library/MyFoo/providers 下创建 MyFooServiceProvider.php
- 在 app/library/MyFoo/facades 下创建 MyFooFacade.php
- 在 app/config/app.php 中添加 providers 和 aliases
工厂模式的升华 —— IoC 容器。
一个类需要绑定、注册至容器中,才能被“制造”。
laravel 称这个容器叫做服务容器,那么我们需要某个服务,就得先注册、绑定这个服务到容器,那么提供服务并绑定服务至容器的东西,就是 服务提供者(ServiceProvider)。服务提供者主要分为两个部分,register(注册) 和 boot(引导、初始化)
app/Http/Kernel.php 中的 $middleware 数组是全局中间件,也就是说,任何一条路由都会被应用这些中间件
有时候我们不需要全局中间件,这时候可以将某一个中间件注册至 app/Http/Kernel.php文件中的 $routeMiddleware 数组,
首先会经过全局中间件,然后就是我们在 app/Http/Kernel.php 的 $routeMiddleware 数组中定义的中间件。
return $next($request)将请求导向下一个中间件
用路由组,定义子域名变得十分容易:
- Route::group(['domain' => 'bbs.yourdomain.com']
比如我希望我的网站每一个用户都拥有自己的二级域名,类似于这样:userA.yourdomain.com,userB.yourdomain.com。这时候可以这样写:
- Route::group(['domain' => '{username}.myapp.com'], function()
- {
- Route::get('profile/{type}', function($username, $type)
- {
- //
- });
- });
class Container { protected $binds; protected $instances; public function bind($abstract, $concrete) { //Todo: 向 container 添加一种对象的的生产方式 //$abstract: 第一个参数 $abstract, 一般为一个字符串(有时候也会是一个接口), 当你需要 make 这个类的对象的时候, 传入这个字符串(或者接口), 这样make 就知道制造什么样的对象了 //$concrete: 第二个参数 $concrete, 一般为一个 Closure 或者 一个单例对象, 用于说明制造这个对象的方式 if ($concrete instanceof Closure) { $this->binds[$abstract] = $concrete; } else { $this->instances[$abstract] = $concrete; } } public function make($abstract, $parameters = []) { //Todo: 生产一种对象 //$abstract: 在bind方法中已经介绍过 //$parameters: 生产这种对象所需要的参数 if (isset($this->instances[$abstract])) { return $this->instances[$abstract]; } array_unshift($parameters, $this); return call_user_func_array($this->binds[$abstract], $parameters); } }
比如
Cache::get('key'); Route::get('/', 'HomeController@index');
Cache 和 Route 都是通过把他们各自的实现类 bind 到某个 Laravel 的 Container 后,那个 Container 所 make 出的一个实例。
到底是在哪进行 bind 操作的,又是 bind 到哪一个 Container 了,这个 Container 又是在什么地方 make 了他们?
IOC Container 组成了 Laravel 的架构,是 Laravel 的核心机制。在 Laravel 中,他们把这个叫做 Laravel 的 Service Container
该如何使用 Laravel 的 IOC Container( Service Container )?
#
如果想要使用 Laravel 的 IOC Container,也就是说想要用 IOC 的机制去 make 某种对象, 那么你就必须先 bind 这个对象的类到 Laravel 的 IOC Container 中, 才能把这种对象 make 出来。
至此,可以请出今天第二个角色了,Service Provider
在 Laravel 中,我们大体可以上有2种方式去使用 IOC Container:
- 通过 Service Provider 来使用IOC Container
- 不通过 Service Provider 直接使用 IOC Container
bind
App::bind('post', function ($app) { return App::make('App\Post'); });
最后 make
$post = App::make('post'); return $post->d; //将会返回 "123"
Route::get('/', 'HomeController@index');
你是无法找到对 Route 类的声明的,为啥呢,因为使用了别名。别名是 PHP 的一个特性( class_alias 方法 )。
Route 是如何配置成为别名的呢, 在 app/config/app.php 中, 我们可以看到 Laravel 把所有的别名配置都放在了这个数组中
'aliases' => [ ... 'Route' => Illuminate\Support\Facades\Route::class, ...
我们看到 Route 实际上是代表了 Illuminate\Support\Facades\Route::class 这个类, 我们找到这个类
namespace Illuminate\Support\Facades; /** * @see \Illuminate\Routing\Router */ class Route extends Facade { protected static function getFacadeAccessor() { return 'router'; } }
看到这个类之后,并没有找到之前 Route 调用的 get 方法,此时我们再看里面的这行注释,@see \Illuminate\Routing\Router,他提示我们去找这个位置,那我们就去找一下,我们又发现了一个 Router 类,而这个 Router 类中,是有 get 方法的,看起来这里似乎就是 Route 的真实身份了。
那 Laravel 是如何为 Illuminate\Support\Facades\Route::class 这个类找到他的真实身份的呢?
Facades。
Facade 的作用是用一个简单易记的语法,让你从 Laravel 的 IOC Container 中方便的 make 出你想要的类的对象。
在 Facade 类中,我们可以发现有一个 __callStatic()
魔术方法,这个方法的作用就是:如果你想要调用的静态方法在类的定义中并没有声明,那么就会执行 __callStatic()。在我们当前的情景中,静态方法 get 并没有被声明,那么当然,我们的类就会转而调用 __callStatic() 。
看 __callStatic() 的执行过程
static::resolveFacadeInstance("router");
返回 static::$app[$name]
返回了 static::$app['router'] 这个值。
而 $app 其实并没有 'router' 这个属性,那为什么可以这样调用呢? 是因为 Application 继承了 Container, 而 Container 又继承了 ArrayAccess 这个类。正是由于 ArrayAccess 的存在,以及 Container 实现了 ArrayAccess 的下面这个方法:
public function offsetGet($key) { return $this->make($key); }
所以,当我们使用 $app['router'] 时,实际上是执行了 $app->make('router')
就是从 $app 这个 IOC Container 中,make 了一个 router 的实例。
在 config/app.php 中,很容易就能找到跟我们的 Route 相关的, 也就是 App\Providers\RoutingServiceProvider::class,
命名空间位于 Illuminate\Contracts 之下,而这种接口在 Laravel 中就被称为 Contract。普通的 interface没什么2样
只是把框架里带命名空间的类变得更好记,比如:App => Illuminate\Support\Facades\App
什么是 Trait ?
其实说通俗一点,就是能把重复的方法拆分出去,通过 use 引入以达到代码复用的目的。
trait 有什么优势?来看一段代码:
class User extends Model { use Authenticate, SoftDeletes, Arrayable, Cacheable; ... }
这个用户模型类,我们引入了四个特性:注册与授权、软删除、数组式操作、可缓存。
- Laravel开发:Laravel核心——Ioc服务容器源码解析(服务器绑定)
服务容器的绑定 bind 绑定 bind 绑定是服务容器最常用的绑定方式,在 上一篇文章中我们讨论过,bind 的绑定有三种: 绑定自身 绑定闭包 绑定接口 今天,我们这篇文章主要从源码上讲解 Ioc ...
- NopCommerce架构分析(转载)
原文 一,NopCommerce架构分析之开篇 NopCommerce是.net开源项目中比较成熟的一款业务应用框架,也是电子商务系统中的典范.所以很想多学习一下里面的设计和实现方式. 二,NopCo ...
- 基于.NET MVC的高性能IOC插件化架构
基于.NET MVC的高性能IOC插件化架构 最近闲下来,整理了下最近写的代码,先写写架构,后面再分享几个我自己写的插件 最近经过反复对比,IOC框架选择了Autofac,原因很简单,性能出众,这篇博 ...
- Tomcat系统架构分析
Tomcat系统架构分析 关于这边blog呢,实际开发中并不会用到,但是我觉得还是很有必要认真的写一下.毕竟我们每天在本地撸码的时候使用的就是tomcat来做web服务器.一个常识就是说我们本地在to ...
- Laravel 核心--Facades 门面
Laravel 核心--Facades 门面 伊Summer 关注 0.1 2017.08.12 19:07* 字数 2017 阅读 1089评论 0喜欢 5 介绍 Facades 为应用的 IoC ...
- Laravel 核心概念
工欲善其事,必先利其器.在开发Xblog的过程中,稍微领悟了一点Laravel的思想.确实如此,这篇文章读完你可能并不能从无到有写出一个博客,但知道Laravel的核心概念之后,当你再次写起Larav ...
- Laravel核心解读--HTTP内核
Http Kernel Http Kernel是Laravel中用来串联框架的各个核心组件来网络请求的,简单的说只要是通过public/index.php来启动框架的都会用到Http Kernel,而 ...
- tomcat架构分析 (Session管理)
Session管理是JavaEE容器比较重要的一部分,在app中也经常会用到.在开发app时,我们只是获取一个session,然后向session中存取数据,然后再销毁session.那么如何产生se ...
- Flickr 网站架构分析
Flickr 网站架构分析 Flickr.com 是网上最受欢迎的照片共享网站之一,还记得那位给Windows Vista拍摄壁纸的Hamad Darwish吗?他就是将照片上传到Flickr,后而被 ...
随机推荐
- windows如何安装python zmq
百度windows python zmq 安装,真的是佩服百度还能活到今天,搜到的结果是各种yum 安装,各种jmq怎么安装,更有甚者直接整源代码编译,忍无可忍,所以自己写下这一篇自己探索出来的安装方 ...
- symbol(s) not found for architecture arm64
问题如下: 解决:更改环境 ok Standard architectures (armv7, arm7s)
- Excel设置下拉菜单并隐藏下拉菜单来源单元格内容
一.问题来源 做实验室的进展统计表,老师让加上开始时间和完成时间,时间格式:周几_上午(下午.晚上). 这样就可以了做下拉菜单,方便填写,而且格式统一,方便查看. 二.解决办法 2.1 下来菜单 红框 ...
- 我也来说说js的事件机制
原文链接:http://www.w3cfuns.com/notes/17398/8062de2558ef495ce6cb7679f940ae5c.html 学js,不懂事件机制,基本可以说学了js,就 ...
- 论坛中不同类型的贴的排序问题(涉及数据库的:CASE... END)
在论坛中,会有不同类型的帖子,比如说:普通贴.精华帖.置顶帖: 论坛的这些贴子会根据不同的类型进行排序,当然不仅仅只是看帖子类型,还有贴子的动态情况来进行排序. 在这里演示一下简单的帖子排序,我们只关 ...
- 关于批量插入数据之我见(100万级别的数据,mysql)
因前段时间去面试,问到怎样高效向数据库插入10万条记录,之前没处理过类似问题.也没看过相关资料,结果没答上来,今天就查了些资料.总结出三种方法: 測试数据库为mysql!!! 方法一: public ...
- /profile文件修改后立即生效
修改profile etc/profile文件是只读的,直接用vi或gedit打开修改后是无法保存的.要修改profile,需要取得root权限,(使用gedit编辑) $sudo gedit /et ...
- JAVA反射机制--怎么不受泛型束缚给数组赋值
在Java中,声明数组的时候我们经常会写泛型限制用户输入或者获取的数据.如:ArrayList<String> list = new ArrayList<>(); String ...
- Oracle学习——扫盲篇
前言 近期这几天一直在与Oracle数据库打交道.因为之前对Oracle的学习并不深入,仅仅是把Oracle当成一个数据源去使用.非常多东西了解的不是非常深,比方.数据库.数据库实例.表空间.用户.表 ...
- 为什么我获取不到这个css样式?js原生获取css样式总结
还是自己遇到的一个坑的总结吧!与其说是坑不如说自己学艺不精,让我先哭一会!! 需求 简单就是获取一个css的height (好吧 就是一个这么简单的需求) 实践 好吧 长时间的JQ 我已经对原生无能了 ...