symfony 6 默认使用 services.yaml 配置,核心结构含 defaults(启用 autowire/autoconfigure、服务私有)、resource 扫描 app\ 命名空间、controllers 子块启用参数注入;手动 arguments 仅用于自动装配失败场景,如标量参数或接口多实现。

Symfony 6 的依赖注入容器默认开箱即用,不需要手动初始化;但配置方式变了——services.yaml 是主战场,YAML 语法更严格,自动装配(autowire: true)是默认行为,手动配 arguments 反而是例外情况。
services.yaml 中的默认配置结构怎么写
新项目生成的 config/services.yaml 已预设合理基础,关键三段不能删:
-
defaults块控制全局行为:默认启用autowire和autoconfigure,且所有服务默认为private -
App\下的服务自动加载:通过resource: '../src/'扫描,类名即服务 ID(如App\Service\Logger) -
controllers子块显式启用controller.service_arguments标签,这是控制器方法参数注入的前提
别手动加 public: true——除非你真要从容器里 get() 某个服务(99% 不需要)。
什么时候必须手动写 arguments
自动装配失败时才出手,常见于:
- 构造函数参数类型模糊:比如两个
string参数,容器无法区分哪个是 host、哪个是 port - 依赖是标量或数组:如
__construct(string $apiUrl, array $options),必须用arguments显式传值 - 接口有多个实现:容器不知道该选
FileLogger还是DbLogger,得用@logger.file这类具体 ID 注入
示例:
App\Service\ApiClient:
arguments:
$apiUrl: '%env(APP_API_URL)%'
$timeout: 5
注意变量名 $apiUrl 必须和构造函数参数名完全一致(PHP 8+ 支持命名参数推导,但显式写更稳)。
私有服务在测试里怎么取
别改 public: true,那是反模式。直接用测试基类提供的特殊容器:
- 继承
WebTestCase或KernelTestCase - 调用
self::bootKernel()启动内核 - 用
static::$container->get(MyService::class)或static::getContainer()->get(MyService::class)
这个 static::$container 是测试专用容器,能绕过私有限制——它只在测试环境生效,不影响生产配置。
工厂服务 YAML 怎么写(替代 XML 内联写法)
YAML 不支持 XML 那种嵌套 <service></service>,必须拆成两步:
- 先定义工厂服务(如
knp_gaufrette.filesystem_map) - 再定义目标服务,用
factory引用它:factory: ['@knp_gaufrette.filesystem_map', 'get'] - 参数单独列在
arguments里,不是塞进 factory 字段
错误写法:factory: ['@fs_map', 'get', ['s3']](参数混进 factory 数组)
正确写法:
app.filesystem.s3:
class: Gaufrette\Filesystem
factory: ['@knp_gaufrette.filesystem_map', 'get']
arguments: ['s3']
这种拆分让服务可复用、可单独测试,也避免 YAML 解析器把参数当成数组字面量。
真正容易被忽略的是:自动装配不是万能的,它依赖 PHP 8 的属性反射和类注解完整性;一旦构造函数参数类型声明缺失或用了别名接口,autowire 就会静默失败——这时候看日志里的 Could not resolve argument 错误,比猜配置更有用。











