如何正确初始化依赖注入容器以避免属性值为 null
技术百科
碧海醫心
发布时间:2026-01-26
浏览: 次 本文解释了在依赖注入(di)场景中,因对象创建时机不当导致类属性获取不到容器中已注册服务的问题,并提供正确的初始化顺序与实践建议。
在使用自定义 DI 容器时,一个常见但容易被忽视的陷阱是:对象的构造函数中过早访问尚未初始化的服务。你遇到的 $this->router 为 null,而 $this->di->get('router') 却能正常返回值,其根本原因并非 DI 实现有误,而是 Cms 实例的创建发生在服务注册之前。
回顾你的 Bootstrap 文件原始逻辑:
$di = new DI();
$cms = new Cms($di); // ❌ 此时 $di 中尚未注册 'router'
$services = require 'config/services.php';
foreach ($services as $service) {
$provider = new $service($di);
$provider->init(); // ✅ 此处才真正执行 $di->set('router', $router)
}问题就在这里:Cms 构造函数中调用了 $this->di->get('router'),但此时 Provider::init() 还未执行,$di->container['router'] 根本不存在,has() 方法中的空合并操作 ?? null 直接返回 null —— 因此 $this->router 被赋值为 null 并永久固化在实例属性中,后续即使 DI 容器补上了该服务,也不会自动更新已存在的属性。
✅ 正确做法是:确保所有服务注册完成之后,再创建依赖这些服务的对象。修正后的启动流程如下:
try {
$di = new DI();
// ✅ 先完*部服务注册
$services = require 'config/services.php';
foreach ($services as $service) {
(new $service($di))->init();
}
// ✅ 再创建 Cms 实例(此时 'router' 已存在于 $di 中)
$cms = new Cms($di);
$cms->run();
} catch (\ErrorException $e) {
echo $e->getMessage();
}此外,还建议对 DI::get() 方法做健壮性增强,避免静默返回 null 导致难以调试:
public function get($key)
{
if (!array_key_exists($key, $this->container)) {
throw new \InvalidArgumentException("Service '{$key}' is not registered in DI container.");
}
return $this->container[$key];
}⚠️ 注意:当前 DI::get() 实际调用了 has(),而 has() 返回的是值或 null,这掩盖了“键不存在”的语义。应区分「键存在但值为 null」和「键不存在」两种情况——生产环境推荐显式校验并抛出异常,而非默认兜底。
最后,若希望 Cms 更具可测试性与松耦合性,还可考虑将 $router 改为延迟加载(Lazy Load)或通过方法注入替代构造注入:
public function getRouter(): Router
{
if ($this->router === null) {
$this->router = $this->di->get('router');
}
return $this->router;
}这样既保持了构造轻量,又规避了初

# ai
# 的是
# 上了
# 不存在
# 两种
# 自定义
# 而非
# 句话
# 还可
# 对象
# 值为
# 构造函数
# red
# this
# NULL
# php
# router
# bootstrap
# cms
# 还未
# 延迟加载
相关栏目:
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
AI推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
SEO优化<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
技术百科<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
谷歌推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
百度推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
网络营销<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
案例网站<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
精选文章<?muma echo $count; ?>
】
相关推荐
- Win11怎么设置任务栏对齐方式_Windows1
- Win11怎么制作U盘启动盘_Win11原版系统安
- c++如何使用std::bitset进行位图算法_
- Win11怎么设置任务栏透明_Windows11使
- C++中的constexpr和const有什么区别
- Windows10如何删除恢复分区_Win10 D
- PHP接收参数值为空怎么办_判断和处理空参数方法说
- 如何在 Django 中安全修改用户密码而不使会话
- Win11怎么关闭透明效果_Windows11个性
- Django 密码修改后会话失效的解决方案
- PHP怎么接收URL中的锚点参数_获取#后面参数值
- Win11摄像头无法使用怎么办_Win11相机隐私
- php485能和物联网模块通信吗_php485对接
- Windows Defender扫描失败怎么办_安
- Windows服务启动类型恢复方法_错误修改导致的
- Win10如何优化内存使用_Win10内存优化技巧
- Mac版Final Cut Pro入门_Mac视频
- Python字符串操作教程_切片拼接与格式化详解
- Windows怎样关闭开始菜单推荐广告_Windo
- Win10怎样设置闹钟贪睡时间 Win10闹钟贪睡
- php怎么捕获异常_trycatch结构处理运行时
- Win10怎么设置开机密码_Windows10账户
- php嵌入式日志记录怎么实现_php将硬件数据写入
- MAC怎么设置程序窗口永远最前_MAC窗口置顶插件
- Python抽象类与接口设计_规范说明【指导】
- Golang如何实现基本的用户注册_Golang用
- Python与Docker容器化部署实战_镜像构建
- Win10怎样安装Word样式库_Win10安装W
- c++怎么使用std::filesystem遍历文
- 如何开启Windows的远程服务器管理工具(RSA
- C#怎么使用委托和事件 C# delegate与e
- Win11开始菜单打不开_修复Windows 11
- php下载安装包怎么选_threadsafe与nt
- Windows10如何更改系统字体大小_Win10
- Windows10如何更改桌面背景_Win10个性
- Win11怎么设置默认PDF阅读器 Win11修改
- Win11怎么清理C盘系统错误报告_Win11清理
- 如何在Golang中处理数据库事务错误_回滚和日志
- Python集合操作技巧_高效去重解析【教程】
- c++协程和线程的区别 c++异步编程模型对比【核
- Python函数接口稳定性_版本演进解析【指导】
- Go语言中slice追加操作的底层共享机制解析
- Win11怎么格式化U盘_Win11系统U盘格式化
- XML的“混合内容”是什么 怎么用DTD或XSD定
- 如何使用 Python 合并文件夹内多个 Exce
- Flask 表单数据通过 SMTP 发送邮件的完整
- php会话怎么开启_session_start函数
- Win11怎么设置应用分屏_Windows11贴靠
- C++如何使用std::transform批量处理
- Win11怎么关闭专注助手 Win11关闭免打扰模

QQ客服