Symfony 4 中服务装饰器通过配置让新类包裹原始服务实现功能增强,如 CachedMailer 装饰 mailer.default,需实现相同接口、构造函数首参命名 $decorated,并在 services.yaml 中用 decorates 和 decoration_inner_name 避免循环引用。

在 Symfony 4 中,服务装饰器(Service Decoration)是官方支持的、不修改原始服务即可增强其行为的标准方式。它不是“写个新类然后手动替换”,而是通过容器配置让新类包裹原服务,实现日志、缓存、权限校验等横切逻辑。
装饰器类需实现相同接口并接收被装饰服务
假设你要装饰 Symfony 默认的 mailer.default 服务(类型为 Symfony\Component\Mailer\MailerInterface),步骤如下:
- 新建装饰器类,如 App\Service\CachedMailer,实现 MailerInterface
- 构造函数第一个参数必须命名为 $decorated(这是 Symfony 约定,不可改名),类型提示为 MailerInterface,用于接收原始邮件服务
- 后续参数可注入其他依赖,如 CacheInterface 或 LoggerInterface
- 在方法中调用 $this->decorated->send(...) 执行原始逻辑,前后可加缓存、日志等处理
services.yaml 中正确声明装饰关系
在 config/services.yaml 中添加以下配置:
App\Service\CachedMailer:
decorates: 'mailer.default'
decoration_inner_name: 'app.decorated_mailer'
arguments:
$decorated: '@app.decorated_mailer'
$cache: '@cache.app'
- decorates 指明要装饰哪个现有服务 ID(这里是 mailer.default)
- decoration_inner_name 是关键:它为原始服务生成一个别名(如 app.decorated_mailer),避免循环引用
- arguments 中通过 @app.decorated_mailer 注入原始服务,不能写 @mailer.default
- 该服务默认为私有(public: false),外部只能通过接口类型提示获取装饰后实例
验证与使用
装饰完成后,任何地方类型提示 MailerInterface,注入的都是你的 CachedMailer 实例:
- 控制器中:public function send(#[Autowire] MailerInterface $mailer) → 得到装饰器
- 运行 bin/console debug:container --types | grep MailerInterface,确认实现类已变
- 若需保留原始服务供内部调用,可在装饰器里保存 $this->decorated 并按需使用


















