Laravel驱动管理类Manager的分析和使用
Laravel驱动管理类Manager的分析和使用
第一部分 概念说明
第二部分 Illuminate\Support\Manager源码
第三部分 Manager类的使用
第一部分:概念解释
结合实际解释一下,啥是驱动:当我点了份外卖,那么外卖小哥无论如何都要讲外卖送到我的手中,我不会关心小哥走的是丝绸之路,还是强者之路,更不会关心他是骑着飞机、坦克还是大炮送来的。我只要我的外卖到我的手中。
归纳一下,我点外卖要就要得到外卖,这就是契约,这就是接口规定的功能。
小哥走什么路线,什么交通工具是他自己的实现,也就是各种驱动。
是不是和laravel的契约和服务提供者概念很相似呢?
只不过今天要讲解的Manager更加强调管理各个驱动,提前将所有的驱动全部注册好,在使用的时候直接解析或者切换。
说到这道友们应该理解了,Manager能做的事情Container和Provider能够做的更好。
那么Manager和Container、Provider的区别在哪呢?这里我只说明我的理解,Container和Provider更加的偏向框架层级,虽然也可以非侵入式的扩展和修改,但是相对Manager稍加麻烦,我将Manager看做一个小型的Container,里面包含了我要实现某个功能的各种驱动(实现),Manager更加的偏向业务逻辑层。
当要频繁切换一个功能实现的时候(他更像一个频繁更换内容,但是说明书不换的组件,俗话说的换汤不换药),我可能会选择Manager(比如发送短信,可以使用阿里大于,京东万象,飞鸽等等),因为他更加轻量。当要实现一个系统级的服务的时候,我会选择Container和Provider,比如上一篇中的日志服务。
第二部分:源码说明
# 直接上代码 挺简单的一个类,基本可以见名知意。未展示属性
<?php
namespace Illuminate\Support;
use Closure;
use Illuminate\Contracts\Container\Container;
use InvalidArgumentException;
// 值得注意的是 Manager是一个抽象类,一定要实现了其中的抽象方法getDefaultDriver才能实例化
// 我们观察构造方法中的参数,你会不会想到在Provider中挂载Manager是一个好方法呢?
public function __construct(Container $container)
{
$this->app = $container;
$this->container = $container;
$this->config = $container->make('config');
}
// 此类是一个抽象类 这个方法用来返回默认的驱动名
abstract public function getDefaultDriver();
public function driver($driver = null)
{
$driver = $driver ?: $this->getDefaultDriver();
if (is_null($driver)) {
throw new InvalidArgumentException(sprintf(
// 此处的static显然是实际调用该方法的类
'Unable to resolve NULL driver for [%s].', static::class
));
}
// 有点类似单例的写法
// 如果要解析的驱动已经解析过 那么直接返回
// 如果没有解析过 那么解析 并挂载到类中
if (! isset($this->drivers[$driver])) {
$this->drivers[$driver] = $this->createDriver($driver);
}
return $this->drivers[$driver];
}
// 创建指定驱动
// 要注意此类中传递的$driver就是指定驱动的名字
protected function createDriver($driver)
{
// First, we will determine if a custom driver creator exists for the given driver and
// if it does not we will check for a creator method for the driver. Custom creator
// callbacks allow developers to build their own "drivers" easily using Closures.
# 官方注释已经非常清晰了
# 如果要解析的驱动,是由我们手动通过键值对注册进来的 那么就调用对应的闭包
# 否则触发魔术方法__call
# 显然Manager本类中并不存在额外的方法,所以魔术方法调用的方法,也要我们在子类中实现
# 以上就是两种从Manager中返回驱动的方式了
if (isset($this->customCreators[$driver])) {
return $this->callCustomCreator($driver);
} else {
$method = 'create'.Str::studly($driver).'Driver';
if (method_exists($this, $method)) {
return $this->$method();
}
}
throw new InvalidArgumentException("Driver [$driver] not supported.");
}
// 上面说的通过此方法调用我们注册进来的闭包 从而返回驱动
protected function callCustomCreator($driver)
{
return $this->customCreators[$driver]($this->container);
}
// 这个就是注册闭包进来
// 你当然可以在业务逻辑中、甚至是指定的中间件中扩展你的Manager类
// 但我更喜欢在ServiceProvider的boot方法中进行扩展
public function extend($driver, Closure $callback)
{
$this->customCreators[$driver] = $callback;
return $this;
}
public function getDrivers()
{
return $this->drivers;
}
// __call魔术方法 从Manager中解析驱动的第二种方式
public function __call($method, $parameters)
{
return $this->driver()->$method(...$parameters);
}
第三部分:使用(依然通过日志这个不恰当例子进行展示)
1 创建契约
<?php
namespace App\Contracts;
interface ManageLog
{
public function logCertains($level, $foo);
}
2 创建日志组件,使用管理器管理
<?php
namespace App\Components\Log;
use Illuminate\Support\Manager;
class LogManager extends Manager
{
// 这是个类,并且你可以通过$this->app拿到容器实例,也就意味着你可以做很多事情
public function getDefaultDriver()
{
// 你也可以将返回的字符串写在配置中 等等
return 'elasticsearch';
}
// 展示魔术方法解析驱动
// $logManager->driver('elasticsearch')时触发
public function createElasticsearchDriver()
{
// 上一篇有简单示例
return '你的es日志驱动';
}
// 查看manager中的驱动
public function getCustomCreators()
{
return $this->customCreators;
}
}
3 创建不同的日志驱动
<?php
namespace App\Drivers\Log;
use App\Contracts\ManageLog;
use Monolog\Logger;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Processor\MemoryPeakUsageProcessor;
use Monolog\Processor\MemoryUsageProcessor;
class RotateDriver implements ManageLog
{
protected $logger;
public function __construct()
{
$logger = new Logger('manager');
$rotatingHandler = new RotatingFileHandler(storage_path('logs/test/manager.log'), 7);
$logger->pushHandler($rotatingHandler);
// 随便加点什么吧
$procesccor1 = new MemoryPeakUsageProcessor();
$procesccor2 = new MemoryUsageProcessor();
$logger->pushProcessor($procesccor1);
$logger->pushProcessor($procesccor2);
$this->logger = $logger;
}
public function logCertains($level, $foo)
{
$this->logger->{$level}($foo);
}
}
<?php
namespace App\Drivers\Log;
use App\Contracts\ManageLog;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
class StreamDriver implements ManageLog
{
protected $logger;
public function __construct()
{
$logger = new Logger('manager');
$streamHandler = new StreamHandler(storage_path('logs/test/manager.log'));
$logger->pushHandler($streamHandler);
$this->logger = $logger;
}
public function logCertains($level, $foo)
{
$this->logger->{$level}($foo);
}
}
4 创建服务提供者
#此处说明一下 为什么使用singleton进行绑定,因为我们执行了boot方法,并在其中扩展了我们的manager,也就修改了绑定到容器的manager,一旦在register方法中使用bind绑定的话,每次从容器中解析出来的都会是一个全新的manager,也就是说我们的boot方法相当于没有执行,也就自然不能够进行任何的操作了。其实laravel为了解决这个问题还有其他方法,请各位仔细查看服务提供者部分的文档,我这里选择在boot方法中对manager进行扩展,其实你可以在任何你喜欢的地方扩展。
php artisan make:provider LogManagerServiceProvider
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Components\Log\LogManager;
use App\Drivers\Log\StreamDriver;
use App\Drivers\Log\RotateDriver;
class LogManagerServiceProvider extends ServiceProvider
{
/**
* Register services.
*
* @return void
*/
public function register()
{
$this->app->singleton('logManager', function ($app) {
return new LogManager($app);
});
}
/**
* Bootstrap services.
*
* @return void
*/
public function boot()
{
// 扩展我们的logmanager
$this->app['logManager']->extend('stream', function () {
return new StreamDriver();
});
$this->app['logManager']->extend('rotate', function () {
return new RotateDriver();
});
// dd($this->app['logManager']->getCustomCreators());
}
}
5 注册服务
config/app.php
...
App\Providers\RouteServiceProvider::class,
// 注册自定义日志服务
App\Providers\LogServiceProvider::class,
// 注册日志管理服务
App\Providers\LogManagerServiceProvider::class,
6 使用测试
Route::get('logmanager', function () {
resolve('logManager')->driver('stream')->logCertains('emergency', 'something emergency');
resolve('logManager')->driver('rotate')->logCertains('debug', 'debug something');
});
以上代码比较简单,各位领会精神就好,大家可以结合前面说过的facade,仿照laravel原生Log服务实现各功能一致的log manager。
今天没有下集预告,发现错误欢迎指正,感谢!!!
Laravel驱动管理类Manager的分析和使用的更多相关文章
- DriverManager 驱动管理器类简介 JDBC简介(三)
驱动程序管理器是负责管理驱动程序的,驱动注册以后,会保存在DriverManager中的已注册列表中 后续的处理就可以对这个列表进行操作 简言之,驱动管理器,就是字面含义,主要负责就是管理 驱动 概述 ...
- Android 性能优化(16)线程优化:Creating a Manager for Multiple Threads 如何创建一个线程池管理类
Creating a Manager for Multiple Threads 1.You should also read Processes and Threads The previous le ...
- Cocos2d-X3.0 刨根问底(三)----- Director类源码分析
上一章我们完整的跟了一遍HelloWorld的源码,了解了Cocos2d-x的启动流程.其中Director这个类贯穿了整个Application程序,这章随小鱼一起把这个类分析透彻. 小鱼的阅读源码 ...
- Cocos2d-X3.0 刨根问底(六)----- 调度器Scheduler类源码分析
上一章,我们分析Node类的源码,在Node类里面耦合了一个 Scheduler 类的对象,这章我们就来剖析Cocos2d-x的调度器 Scheduler 类的源码,从源码中去了解它的实现与应用方法. ...
- [连载]《C#通讯(串口和网络)框架的设计与实现》-4.设备驱动管理器的设计
目 录 第四章 设备驱动管理器的设计... 2 4.1 接口定义... 2 4.2 设备容器... 7 4.3 ...
- Unity协程(Coroutine)管理类——TaskManager工具分享
博客分类: Unity3D插件学习,工具分享 源码分析 Unity协程(Coroutine)管理类——TaskManager工具分享 By D.S.Qiu 尊重他人的劳动,支持原创,转载请注明出处 ...
- C++智能指针管理类
1.程序员明确的进行内存释放 对于c++程序员,最头脑的莫过于对动态分配的内存进行管理了.c++在堆上分配的内存,需要程序员负责对分配的内存进行释放.但有时内存的释放看起来并不件很轻松的事,如下程序 ...
- Quartz管理类
package com.sihuatech.project.task.manager; import java.text.ParseException; import org.quartz.CronT ...
- 【Cocos2d-X开发学习笔记】第19期:动作管理类(CCActionManager)的使用
本系列学习教程使用的是cocos2d-x-2.1.4(最新版为3.0alpha0-pre) ,PC开发环境Windows7,C++开发环境VS2010 一.动作管理类 动作管理类CCActionMan ...
随机推荐
- Kubernetes 实战-Operator Finalizers 实现
原文链接:https://zdyxry.github.io/2019/09/13/Kubernetes-%E5%AE%9E%E6%88%98-Operator-Finalizers/ Finalize ...
- Java类库以及它的基本组织结构
Java 类库概念: Java 的应用程序编程接口 (API (Application Programming Interface) )以包的形式来组织,每个包提供了大量的相关类.接口和异常处理类, ...
- Arduboy基本操作(二)
Arduboy基本操作(二) 方向键控制物体移动 #include<Arduboy.h> Arduboy arduboy; int i,j; void setup() { arduboy. ...
- PCIe例程理解(一)用户逻辑模块(接收)仿真分析
前言 本文从例子程序细节上(语法层面)去理解PCIe对于事物层数据的接收及解析. 参考数据手册:PG054: 例子程序有Vivado生成: 为什么将这个内容写出来? 通过写博客,可以检验自己理解了这个 ...
- LeetCode 94 | 基础题,如何不用递归中序遍历二叉树?
今天是LeetCode专题第60篇文章,我们一起来看的是LeetCode的94题,二叉树的中序遍历. 这道题的官方难度是Medium,点赞3304,反对只有140,通过率有63.2%,在Medium的 ...
- 神奇的BUG系列-01
有时候遇见一个bug,感觉就是他了 其实他也不过是你职业生涯中写的千千万万个bug中的一员 你所要做的,是放下 日子还长,bug很多,不差这一个 就此别过,分手快乐 一辈子那么长,一天没放下键盘 你就 ...
- 【Spring注解驱动开发】二狗子让我给他讲讲@EnableAspectJAutoProxy注解
写在前面 最近,二狗子入职了新公司,新入职的那几天确实有点飘.不过慢慢的,他发现他身边的人各个身怀绝技啊,有Spring源码的贡献者,有Dubbo源码的贡献者,有MyBatis源码的贡献者,还有研究A ...
- 【Android】AndroidStudio(Eclipse)如何使用天天模拟器进行调试apk应用。
作者:程序员小冰,GitHub主页:https://github.com/QQ986945193 新浪微博:http://weibo.com/mcxiaobing 大家都知道,我们这些Android开 ...
- Fastlane- app自动编译、打包多个版本、上传到app store
Fastlane是一套使用Ruby写的自动化工具集,用于iOS和Android的自动化打包.发布等工作,可以节省大量的时间. Github:https://github.com/fastlane/fa ...
- 入门的艰难——关于LR的使用
这年头做一件事真是TM不容易啊.做测试也很纠结,不是都说商业工具很强大么,我去,这个不支持那个不支持的,这还有什么搞头,还非要按照你说的这个版本的才行,高一点的就crash了,结果连最初级的录制脚本都 ...