services类本质是静态工具类而非传统工厂类,它提供按需创建服务实例的静态方法,与容器协作:services负责创建逻辑和参数组装,容器负责生命周期管理、调度和缓存。

Services 类本质是工厂,但不是“工厂类”这个术语所指的通用设计模式实现;它是一组静态方法的集合,用于按需创建并返回服务实例。两者在 CI4 中不是并列选项,而是协作关系:你写工厂逻辑(比如 Services::database()),容器负责调度和缓存。
为什么 Services 不是传统意义上的工厂类
传统工厂类(如 DatabaseFactory)通常需要实例化、可继承、支持状态管理,而 CI4 的 Services 是一个纯静态工具类:
- 所有方法都是 public static
- 没有构造函数、不保存状态、不参与依赖注入链
- 它不被容器管理,也不被自动解析——你调用它,它就执行一次创建逻辑
- 返回的对象是否单例,取决于你是否在方法内部手动缓存(CI4 默认不缓存,除非显式用 static 变量或容器绑定)
$container->set() 和 Services::xxx() 该用哪个
选法取决于你对生命周期、复用性和配置灵活性的要求:
- 用
Services::xxx():适合“每次都要新实例”的场景,比如Services::email()—— 发邮件前重置收件人、主题,不能复用旧实例 - 用
$container->set('xxx', ...):适合需要单例、或需传入动态配置(如环境变量)的服务,比如日志处理器:$container->set('FileLogger', function ($container) { return new \App\Libraries\FileLogger( env('LOG_LEVEL', 'info'), env('LOG_PATH', WRITEPATH . 'logs/') ); }); - 混合使用更常见:在
Services::logger()内部调用$container->get('FileLogger'),把容器作为底层供给源,Services做封装入口
容易踩的坑:别让 Services 方法变成重复初始化黑洞
如果在 Services::cache() 里每次都 new CacheHandler(),又没做单例控制,那么每次调用 service('cache') 都会新建对象——这跟直接 new 没区别,还绕了一层。
- 正确做法是在方法内加 static $instance 缓存,或更推荐:用容器绑定 + shared(true)
- 注意 service('xxx') 默认不共享实例,除非你在 $container->set() 时显式设置为 shared
复杂点在于“谁该决定生命周期”
容器管生命周期(单例/每次新建/作用域),Services 管创建逻辑和参数组装。真正难的不是选哪个,而是厘清:这个服务是否需要被其他服务依赖?是否要支持运行时替换(比如测试时 mock)?如果答案是肯定的,就必须走容器绑定;否则 Services 静态方法够用,也更轻量。











