如何正确初始化依赖注入容器以避免属性值为 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; ?>
】
相关推荐
- 如何在Golang中捕获结构体方法错误_Golan
- Win11摄像头无法使用怎么办_Win11相机隐私
- 如何用正则与预处理高效拦截带干扰符的恶意域名
- Win11怎么关闭自动更新 Win11永久关闭系统
- Win11如何设置电源计划_Win11电源计划优化
- Windows10如何更改鼠标灵敏度_Win10鼠
- php报错怎么查看_定位PHP致命错误与警告的方法
- c# 服务器GC和工作站GC的区别和设置
- Windows10怎样设置家长控制_Windows
- Win10怎么卸载剪映_Win10彻底卸载剪映方法
- Win11怎么关闭搜索历史 Win11清除搜索框最
- ACF 教程:如何正确更新嵌套在多层 Group
- 如何在Golang中写入JSON文件_保存结构体数
- Windows Defender扫描失败怎么办_安
- php8.4匿名类怎么用_php8.4匿名类创建与
- Python与Docker容器化部署实战_镜像构建
- Win11怎么关闭透明效果_Windows11个性
- Win11怎么设置声音输出设备_Windows11
- 如何在包含多值的列中精准搜索指定演员?
- Win10怎么查看内存时序参数_Win10CPU-
- Python多进程教程_multiprocessi
- php8.4如何实现队列任务_php8.4redi
- Win10怎样安装Excel数据分析工具_Win1
- Python大型项目拆分策略_模块化解析【教程】
- Django 测试数据库表缺失与字段未创建问题的完
- Python生成器表达式内存优化_惰性计算说明【指
- WindowsUSB驱动安装异常怎么办_USB驱动
- 手机php文件怎么变成mp4_安卓苹果打开php转
- 如何在Golang中处理云原生事件_使用Event
- Win11怎么设置默认图片查看器_Windows1
- C#怎么使用委托和事件 C# delegate与e
- Win11怎么卸载Photos应用_Win11卸载
- c++协程和线程的区别 c++异步编程模型对比【核
- Win11怎么更改文件夹图标_自定义Win11文件
- mac怎么安装字体_MAC添加第三方字体与字体册管
- Windows10如何更改系统字体大小_Win10
- Win11怎么设置右键刷新选项_Windows11
- Win11怎么开启窗口对齐助手_Windows11
- 如何在 Go 应用中实现自动错误恢复与进程重启机制
- 如何在Golang中使用encoding/gob序
- 如何使用Golang实现云原生应用弹性伸缩_自动应
- Windows10如何更改日期格式_Win10区域
- 如何在 Python 测试中动态配置 @backo
- 微信短链接怎么还原php_用浏览器开发者工具抓包获
- 企业SEO优化选择网站建设模板的技巧
- C++中的constexpr和const有什么区别
- Win11怎么清理C盘系统错误报告_Win11清理
- php本地部署支持nodejs吗_php与node
- Windows 10怎么把任务栏放在屏幕上方_Wi
- Windows10系统服务优化指南_Win10禁用

QQ客服