接上文:秒杀系列03
先看看我们上文的代码
$db = new Db($dsn); $user = new User($db);
上面的写法的问题:
如果有很多依赖需要注入,比如mysql实例,redis实例,mongo实例,那么每次使用业务类时,都要写一次各个依赖的实例化过程,还是很繁琐。
IoC 容器解决了什么问题:
提前处理依赖关系(配置,注解),隐藏注入过程。
即使用业务类时,不需要再去手动实例化一堆依赖,容器会自动管理依赖关系了。
更重要的是,在框架加载时,就可以提前初始化容器,后续就可以直接使用业务类了。
对于一些常驻内存框架,我们还可以复用容器中的依赖。
手写一个简单的 IoC 容器
test.php
<?php class Dsn { private $dsn = "mysql:host=192.168.0.101;dbname=seckill;charset=utf8;user=seckill;password=BBHyiFWiXrpxji5S;"; public function getdsn() { return $this->dsn; } } class Db { private $pdo; public function __construct($dsn) { try { $this->pdo = new PDO($dsn); $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } catch (PDOException $e) { echo "mysql连接失败:" . $e->getMessage(); } } public function queryForRows($sql) { try { $stmt = $this->pdo->query($sql); return $stmt->fetchAll(PDO::FETCH_ASSOC); } catch (PDOException $e) { echo "查询失败:" . $e->getMessage(); } } } class User { private $db; public function __construct(Db $Db) // 这里直接注入Db的实例了 { $this->db = $Db; // 这里不在主动实例化Db了,直接用注入的Db实例 } public function getAllUsers() : array { return $this->db->queryForRows("select * from users"); } } class ClassFactory { private static $container = []; public static function set(string $key, callable $func) { self::$container[$key] = $func; } public static function get(string $key) { if (isset(self::$container[$key])) { return (self::$container[$key])(); // 执行匿名函数 } return null; } } // User 依赖 Db,Db 依赖 Dsn ClassFactory::set("Dsn", function () { return new Dsn(); }); ClassFactory::set("Db", function () { return new Db(ClassFactory::get("Dsn")->getdsn()); }); ClassFactory::set("User", function () { return new User(ClassFactory::get("Db")); }); ////////////////下面是业务处理///////////////// $user = ClassFactory::get("User"); var_dump($user->getAllUsers());
上面是我们手写的依赖关系,更好的方式是借助于配置,自动装配,或者借助于 注解
使用第三方库,简单的实现IoC
安装 https://php-di.org/doc/getting-started.html
// 进入到项目根目录下,执行
composer require php-di/php-di
test.php 代码:
<?php
require_once __DIR__ . "/vendor/autoload.php";
class Dsn {
private $dsn = "mysql:host=192.168.0.101;dbname=seckill;charset=utf8;user=seckill;password=BBHyiFWiXrpxji5S;";
public function getdsn() {
return $this->dsn;
}
}
class Db {
private $pdo;
public function __construct($dsn) {
try {
$this->pdo = new PDO($dsn);
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
echo "mysql连接失败:" . $e->getMessage();
}
}
public function queryForRows($sql) {
try {
$stmt = $this->pdo->query($sql);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
echo "查询失败:" . $e->getMessage();
}
}
}
class User {
private $db;
public function __construct(Db $Db) // 这里直接注入Db的实例了
{
$this->db = $Db; // 这里不在主动实例化Db了,直接用注入的Db实例
}
public function getAllUsers() : array {
return $this->db->queryForRows("select * from users");
}
}
$container = new DI\Container();
$container->set("Dsn", function () {
return new Dsn();
});
$container->set("Db", function (\DI\Container $c) { // \DI\Container $c 基于反射去获取container对象
return new Db($c->get("Dsn")->getdsn());
});
$container->set("User", function (\DI\Container $c) {
return new User($c->get("Db"));
});
////////////////////////////////////////////
$user = $container->get("User");
var_dump($user->getAllUsers());