浅析easyswoole源码Di原理与实现
依赖注入
简介:Dependency Injection 依赖注入
EasySwoole实现了简单版的IOC,使用 IOC 容器可以很方便的存储/获取资源,实现解耦。
使用依赖注入,最重要的一点好处就是有效的分离了对象和它所需要的外部资源,使得它们松散耦合,有利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。
在我们的日常开发中,创建对象的操作随处可见以至于对其十分熟悉的同时又感觉十分繁琐,每次需要对象都需要亲手将其new出来,甚至某些情况下由于坏编程习惯还会造成对象无法被回收,这是相当糟糕的。但更为严重的是,我们一直倡导的松耦合,少***原则,这种情况下变得一无是处。于是前辈们开始谋求改变这种编程陋习,考虑如何使用编码更加解耦合,由此而来的解决方案是面向接口的编程。
--摘自《searswoole开发文档》
本文将解析并实现下列功能。
- 1、singleton模式的巧妙实现。
- 2、通过Di方式实现简单的容器,方便存取资源,从而达到解耦的目的。
代码实现
文件结构
- \client.php 功能调用入口
- \AbstractInterface\Singleton.php 单例抽象接口
- \Lib\Redis.php redis服务类
- \Service\Di.php 依赖注入实现简单Ioc容器
单例实现过程解读
client.php
<?php /** * Created by PhpStorm. * User: zrj * Date: 19-1-14 * Time: 下午4:39 */ namespace App; define('APP_ROOT', dirname(__FILE__)); use Exception; use Yaconf; require_once APP_ROOT . '/Lib/Redis.php'; require_once APP_ROOT . '/Service/Di.php'; use App\Lib\Redis; use App\Service\Di; try { if (!Yaconf::has('easyswoole_api')) throw new Exception('项目配置ini文件不存在'); $redisConfig = Yaconf::get('easyswoole_api')['redis'] ?? []; if (empty($redisConfig)) throw new Exception('项目redis配置不存在'); $config = [ 'host' => (string)$redisConfig['host'] ?? '', 'port' => (int)$redisConfig['port'] ?? '', 'timeout' => (string)$redisConfig['timeout'] ?? '', 'password' => (string)$redisConfig['password'] ?? '', 'database' => (int)$redisConfig['database'] ?? '', ]; //直接访问 //Redis::getInstance($config)->set('hello', 'world'); //echo Redis::getInstance($config)->get('hello'); //Di::getInstance()->clear(); //注入容器 //--方法一、注入实例 //Di::getInstance()->set('redis', \App\Lib\Redis::getInstance($config)); //--方法二、容器内部实例化(支持单例模式) Di::getInstance()->set('redis', \App\Lib\Redis::class, $config); //通过容器获取实例 $redisInstance = Di::g 5b4 etInstance()->get('redis'); $redisInstance->set('dear', 'hui'); echo $redisInstance->get('hello') . PHP_EOL; echo $redisInstance->get('dear') . PHP_EOL; } catch (\Exception $e) { //throw $e; echo $e->getMessage(); }
\AbstractInterface\Singleton.php 单例抽象接口
namespace App\AbstractInterface; #声明为特性 Trait Singleton { private static $instance; public static function getInstance(...$args) { if(!isset(self::$instance)){ //通过static静态绑定来new绑定的对象 self::$instance=new static(...$args); } return self::$instance; } }
\Lib\Redis.php redis服务类
namespace App\Lib; require_once APP_ROOT . '/AbstractInterface/Singleton.php'; use App\AbstractInterface\Singleton; class Redis { //这里通过使用特性来扩展Redis类的能力 //借助Singleton中的getInstance来new本类,创建redis连接实例 //Redis::getInstance(...) use Singleton; private $connectInstance = null; /** * Redis constructor. * @param array $redisConfig * @throws \Exception */ private function __construct(array $redisConfig) { try { //通过yaconf读取配置ini if (!extension_loaded('redis')) throw new \Exception('Redis extension is not install'); $this-& 16ca gt;connectInstance = new \Redis(); $result = $this->connectInstance->connect($redisConfig['host'], $redisConfig['port'], $redisConfig['timeout']); $this->connectInstance->auth($redisConfig['password']); $this->connectInstance->select($redisConfig['database']); if ($result === false) throw new \Exception('Connect redis server fail'); } catch (\Exception $e) { throw $e; } } public function __call($name, $arguments) { return $this->connectInstance->$name(...$arguments); } }
Redis类引入Singleton特性,轻松实现单例子。
//通过Redis::getInstance就能获取实例
Redis::getInstance($config)->set('hello', 'world');
echo Redis::getInstance($config)->get('hello');
依赖注入的实现
\Service\Di.php 依赖注入实现简单Ioc容器
<?php /** * Created by PhpStorm. * User: zrj * Date: 19-1-14 * Time: 下午5:06 */ namespace App\Service; require_once APP_ROOT . '/AbstractInterface/Singleton.php'; require_once APP_ROOT . '/Lib/Redis.php'; use App\AbstractInterface\Singleton; //use App\Lib\Redis; class Di { use Singleton; private $container = []; //注入容器 public function set(string $key, $obj, array $arg = []): void { if (count($arg) == 1 && is_array($arg[0])) { $arg = $arg[0]; } $this->container[$key] = [ 'obj' => $obj, 'params' => $arg ]; } public function get(string $key) { if (!isset($this->container[$key])) return null; $result = $this->container[$key]; if (is_object($result['obj'])) { return $result['obj']; } elseif (is_callable($result['obj'])) {// 检测参数是否为合法的可调用结构(function,class method) return $result['obj']; } elseif (is_string($result['obj']) && class_exists($result['obj'])) { //通过反射方式获取实例化对象 $reflection = new \ReflectionClass($result['obj']); $constructor = $reflection->getConstructor(); $instance = null; if ($constructor->isPublic()) {//直接实例化 $instance = $reflection->newInstanceArgs($result['params']); } elseif ($constructor->isPrivate()) {//支持单例模式 if ($reflection->hasMethod('getInstance')) { $instance = $result['obj']::getInstance($result['params']); } } $this->container[$key]['obj'] = $instance; return $instance; } else { return $result['obj']; } } public function clear(): void { $this->container = []; } public function delete($key): void { unset($this->container[$key]); } }
在client.php中使用Di的使用
//向容器中注入实例
//Di::getInstance()->set('redis', \App\Lib\Redis::getInstance($config));
Di::getInstance()->set('redis', \App\Lib\Redis::class, $config);//通过容器获取实例
$redisInstance = Di::getInstance()->get('redis');$redisInstance->set('dear', 'hui');
echo $redisInstance->get('hello') . PHP_EOL;
echo $redisInstance->get('dear') . PHP_EOL;
总结
本案例涉及知识点:
1.通过Yaconf扩展高效获取配置信息
2.static静态绑定、trait特性
3.singleton模式的实现
4.Di依赖注入
附:Yaconf配置文件(easyswoole_api.ini)格式
redis.host = '192.168.1.99'
redis.port = 6379
redis.password = 'helloworld'
redis.database = 12
redis.timeout = 3
- ASP.NET 中 Session 实现原理浅析 [1] 会话的建立流程
- Java动态代理实现原理浅析
- 浅析 Linux 中的时间编程和实现原理一—— Linux 应用层的时间编程
- Netty实现原理浅析
- C++函数重载实现原理浅析
- Win32 临界区实现原理浅析
- 【转】浅析SkipList跳跃表原理及代码实现
- hashmap与Hashtable实现原理浅析
- Netty实现原理浅析
- 浅析 Linux 中的时间编程和实现原理,第 1 部分: Linux 应用层的时间编程
- 浅析AOP实现原理(1)静态代理
- Win32 临界区实现原理浅析
- C# 2.0 中Iterators的改进与实现原理浅析
- 浅析 Linux 中的时间编程和实现原理,第 2 部分: 硬件和 GLibC 库的细节
- jQuery内部原理和实现方式浅析
- 浅析JSONP技术原理及实现
- Java网络编程 - 浅析web服务器与浏览器的实现原理
- 浅析VUE双向绑定原理,实现数据监听并通知订阅者
- Netty构建分布式消息队列实现原理浅析
- ASP.NET 中 Session 实现原理浅析 [2] 状态管理器