laravel 服务容器实现原理
前言
通过实现laravel 框架功能,以便深入理解laravel框架的先进思想。
什么是服务容器
服务容器是用来管理类依赖与运行依赖注入的工具。Laravel框架中就是使用服务容器来实现 ** 控制反转 ** 和 ** 依赖注入 **。
什么是控制反转(IoC)和依赖注入(DI)
控制反转(IoC) 就是说把创建对象的** 控制权 **进行转移,以前创建对象的主动权和创建时机是由自己把控的,而现在这种权力转移到第三方,也就是 ** Laravel ** 中的容器。
依赖注入(DI)则是帮助容器实现在运行中动态的为对象提供提依赖的资源。
概念容易不太容易让人理解,举个栗子:
//我们构建一个人的类和一个狗的类
class People
{
public $dog = null;
public function __construct()
{
$this->dog = new Dog();
}
public function putDog(){
return $this->dog->dogCall();
}
}
class Dog{
public function dogCall(){
return '汪汪汪';
}
}
这个人在遛狗,突然遇到了死对头,他于是放狗咬人
$people = new People();
$people->putDog();
在这个操作中,people类要执行putDog()这个方法,需要依赖Dog类,一般我们像上面一样,在people中利用构造函数来添加这个Dog依赖。如果使用控制反转 依赖注入则是这个样子
class People
{
public $dog = null;
public function __construct(Dog $Dog)
{
$this->dog = $dog;
}
public function putDog(){
return $this->dog->dogCall();
}
}
People类通过构造参数声明自己需要的 依赖类,由容器自动注入。这样就实现了程序的有效解耦,好处在这就不多说了。
Laravel容器依赖注入的实现
实现原理需要了解的知识点:
闭包(匿名函数):
匿名函数(Anonymous functions),也叫闭包函数(closures),允许 临时创建一个没有指定名称的函数
反射:PHP 5 以上版本具有完整的反射 API,添加了对类、接口、函数、方法和扩展进行反向工程的能力。 此外,反射 API 提供了方法来取出函数、类和方法中的文档注释
理解了闭包和反射的基本用法我们来看Laravel中是怎么实现容器的,下面代码是我对laravel框架容器部分代码的简化核心版:
lass Container
{
/**
* 容器绑定,用来装提供的实例或者 提供实例的回调函数
* @var array
*/
public $building = [];
/**
* 注册一个绑定到容器
*/
public function bind($abstract, $concrete = null, $shared = false)
{
if(is_null($concrete)){
$concrete = $abstract;
}
if(!$concrete instanceOf Closure){
$concrete = $this->getClosure($abstract, $concrete);
}
$this->building[$abstract] = compact("concrete", "shared");
}
//注册一个共享的绑定 单例
public function singleton($abstract, $concrete, $shared = true){
$this->bind($abstract, $concrete, $shared);
}
/**
* 默认生成实例的回调闭包
*
* @param $abstract
* @param $concrete
* @return Closure
*/
public function getClosure($abstract, $concrete)
{
return function($c) use($abstract, $concrete){
$method = ($abstract == $concrete)? 'build' : 'make';
return $c->$method($concrete);
};
}
/**
* 生成实例
*/
public function make($abstract)
{
$concrete = $this->getConcrete($abstract);
if($this->isBuildable($concrete, $abstract)){
$object = $this->build($concrete);
}else{
$object = $this->make($concrete);
}
return $object;
}
/**
* 获取绑定的回调函数
*/
public function getConcrete($abstract)
{
if(! isset($this->building[$abstract])){
return $abstract;
}
return $this->building[$abstract]['concrete'];
}
/**
* 判断 是否 可以创建服务实体
*/
public function isBuildable($concrete, $abstract)
{
return $concrete === $abstract || $concrete instanceof Closure;
}
/**
* 根据实例具体名称实例具体对象
*/
public function build($concrete)
{
if($concrete instanceof Closure){
return $concrete($this);
}
//创建反射对象
$reflector = new ReflectionClass($concrete);
if( ! $reflector->isInstantiable()){
//抛出异常
throw new \Exception('无法实例化');
}
$constructor = $reflector->getConstructor();
if(is_null($constructor)){
return new $concrete;
}
$dependencies = $constructor->getParameters();
$instance = $this->getDependencies($dependencies);
return $reflector->newInstanceArgs($instance);
}
//通过反射解决参数依赖
public function getDependencies(array $dependencies)
{
$results = [];
foreach( $dependencies as $dependency ){
$results[] = is_null($dependency->getClass())
?$this->resolvedNonClass($dependency)
:$this->resolvedClass($dependency);
}
return $results;
}
//解决一个没有类型提示依赖
public function resolvedNonClass(ReflectionParameter $parameter)
{
if($parameter->isDefaultValueAvailable()){
return $parameter->getDefaultValue();
}
throw new \Exception('出错');
}
//通过容器解决依赖
public function resolvedClass(ReflectionParameter $parameter)
{
return $this->make($parameter->getClass()->name);
}
}
容器的工作流程
接着上面遛狗的例子:
//实例化容器类
$app = new Container();
//向容器中填充Dog
$app->bind('Dog','App\Dog');
//填充People
$app->bind('People', 'App\People');
//通过容器实现依赖注入,完成类的实例化;
$people = $app->make('People');
//调用方法
echo $people->putDog();
上面示例中我们先实例化容器类,然后使用bind()方法 绑定接口和 生成相应的实例的闭包函数。然后使用make() 函数生成实例对象,在make()中会调用 isBuildable($concrete, $abstract) 来判断 给定的服务实体($concrete参数)是否可以创建,可以创建 就会调用 build($concrete) 函数 ,build($concrete) 函数会判断传的参数是 是** 闭包 还是 具体类名 **,如果是闭包则直接运行,如果是具体类名的话,则通过反射获取该类的构造函数所需的依赖,完成实例化。
** 重点理解 下面这几个函数中 反射的用法,应该就很好理解了 **
build($concrete)
getDependencies(array $dependencies)
resolvedNonClass(ReflectionParameter $parameter)
resolvedClass(ReflectionParameter $parameter) ```
## 最后 ##
> IoC 理解起来是有点难度,可能文中描述让你感觉不是很清楚,可以将文中代码 在php中用debug观察 运行状态。
理解了容器的具体实现原理,再去看Laravel中的相关实现,就会感觉豁然开朗。
laravel 服务容器实现原理的更多相关文章
- Laravel 服务容器、服务提供器、契约实例讲解
前言 刚开始看laravel服务容器.契约.服务提供器的确生涩难懂,不单单是概念繁多,而且实际的demo很难找(找是找到了,但难用啊),最后就隔一段时间看一遍,大概个十来遍,还真给看出个门道, ...
- laravel服务容器
laravel框架底层解析 本文参考陈昊<Laravel框架关键技术解析>,搭建一个属于自己的简化版服务容器.其中涉及到反射.自动加载,还是需要去了解一下. laravel服务容器 建立项 ...
- Laravel 服务容器实例教程 —— 深入理解控制反转(IoC)和依赖注入(DI)
容器,字面上理解就是装东西的东西.常见的变量.对象属性等都可以算是容器.一个容器能够装什么,全部取决于你对该容器的定义.当然,有这样一种容器,它存放的不是文本.数值,而是对象.对象的描述(类.接口)或 ...
- Laravel服务容器的绑定与解析
本篇文章给大家带来的内容是关于Laravel服务容器的绑定与解析,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 前言 老实说,第一次老大让我看laravel框架手册的那天早上,我 ...
- 关于使用 Laravel 服务容器的优势介绍
如果说laravel框架的核心是什么,那么无疑是服务容器.理解服务容器的概念,对于我们使用laravel太重要了,应该说是否理解服务容器的概念是区分是否入门laravel的重要条件.因为整个框架正是在 ...
- laravel服务容器 转
laravel框架底层解析 本文参考陈昊<Laravel框架关键技术解析>,搭建一个属于自己的简化版服务容器.其中涉及到反射.自动加载,还是需要去了解一下. laravel服务容器 建立项 ...
- laravel 服务容器实例——深入理解IoC模式
刚刚接触laravel,对于laravel的服务容器不是很理解.看了<Laravel框架关键技术解析>和网上的一些资料后对于服务容器有了一些自己的理解,在这里分享给大家 1.依赖 IoC模 ...
- laravel服务容器-----深入理解控制反转(IoC)和依赖注入(DI)
首先大家想一想什么是容器,字面意思就是盛放东西的东西,常见的变量,对象属性都是容器,一个容器能够装什么东西,完全在于你对这个容器的定义.有的容器不仅仅只是存文本,变量,而是对象,属性,那么我们通过这种 ...
- Laravel 服务容器,IoC,DI
DI DI 就是常说的依赖注入,那么究竟什么是依赖注入呢? 打个比方,电脑(非笔记本哈)需要键盘和鼠标我们才能进行操作,这个‘需要’换句话说就是‘依赖’键盘和鼠标. 那么,相应的,一个类需要另一个类才 ...
随机推荐
- 移动端测试方案--sptt
sptt sptt是移动端UI自动化测试的一种解决方案,全称为special tool of test.sptt提供了一套测试解决方案,并使用命令行完成相关操作,最终可集成在各种后续的流程中. spt ...
- MySQL 5.7贴心参数之 log_timestamps
写在前面 使用 MySQL 的过程中,经常会有人碰到这么一个问题,看错误日志.慢查询日志的时候,时间总是和本地时间对不上,差了 8 个小时,这样分析起来就相对麻烦了一些. 新改进 对于不知道是什么原因 ...
- MySQL中字符串与数字比较的坑
公司项目代码中,某枚举字段数据库表中类型是char(1),在代码中,误以为是TINYINT,所以用数字筛选,后来发现结果不对.发现了一个现象,用数字0筛选会把所有的记录给筛选出来. 经过排查发现是在M ...
- c标签和foreach循环不能加载
需要同时导入2个包: jstl.jar和standard.jar(大多数时候只会注意到jstl包,而忽视了standard包) 代码: c标签的写法 <%@ taglib prefix=&quo ...
- Excel 中使用sql语句查询
将Excel连接Oracle数据库 Excel选项板中"数据"—"自其他来源"下拉菜单中有有个可以连接其它数据库的选项"来自数据连接向导"和 ...
- Angularjs快速入门(四)-css类和样式
例子: .error{background-color:red;} .warning{background-color:yellow;} <div ng-controller='HeaderCo ...
- HTML5 进阶系列:拖放 API 实现拖放排序
前言 HTML5 中提供了直接拖放的 API,极大的方便我们实现拖放效果,不需要去写一大堆的 js,只需要通过监听元素的拖放事件就能实现各种拖放功能. 想要拖放某个元素,必须设置该元素的 dragga ...
- 蓝桥杯- 奇妙的数字-java
/* (程序头部注释开始) * 程序的版权和版本声明部分 * Copyright (c) 2016, 广州科技贸易职业学院信息工程系学生 * All rights reserved. * 文件名称: ...
- MongoDB3.4 shell CRUD操作
输入db,显示你正在操作的数据库:切换数据库,输入use dbName,如果数据库不存在的话会自动帮我们创建一个:使用show dbs可以显示所有可用的数据库. 测试数据在文末 插入文档 插入操作的行 ...
- UNIX 系统概述
UNIX体系结构(UNIX Architecture) 调用内核的接口叫做系统调用(system call,图1.1中的阴影部分),普通函数库是建立在系统调用接口的基础之上.应用(applicatio ...