symfony消息中间件需实现middlewareinterface,通过handle方法在消息处理前后执行单一职责(如事务、日志),必须调用$stack->next()传递消息;事务中间件需配合doctrine,在try-catch中控制begintransaction/commit/rollback;通过messenger.php按总线配置中间件链,避免全局启用;调试可用debug:messenger命令验证。

写 Symfony 消息中间件,核心是实现 MiddlewareInterface,并把它接入消息总线管道。它不是 HTTP 中间件,不处理请求响应,而是处理“消息”(比如命令或事件)在被处理器执行前后的逻辑。
明确中间件职责:只管一条消息的流转
每个消息中间件只做一件事,例如:开启数据库事务、记录日志、捕获异常、验证消息结构、添加追踪 ID。它不负责业务逻辑本身——那是处理器(Handler)的事。
- 必须实现
handle(Envelope $envelope, StackInterface $stack): Envelope方法 - 调用
$stack->next($envelope)才能将消息传给下一个中间件或最终处理器 - 可对入参
$envelope做增强(如加 stamp)、修改,也可对返回的$envelope做后置处理
写一个带事务的自定义中间件
这是最常见也最典型的用例,尤其配合 Doctrine ORM 使用:
namespace App\Messenger\Middleware;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Middleware\MiddlewareInterface;
use Symfony\Component\Messenger\Middleware\StackInterface;
use Symfony\Component\Messenger\Stamp\HandledStamp;
class TransactionMiddleware implements MiddlewareInterface
{
public function __construct(private EntityManagerInterface $em) {}
public function handle(Envelope $envelope, StackInterface $stack): Envelope
{
$this->em->beginTransaction();
try {
$envelope = $stack->next($envelope);
$this->em->commit();
} catch (\Throwable $e) {
$this->em->rollback();
throw $e;
}
return $envelope;
}
}
注意:不要手动调用 $handler(...),始终走 $stack->next();事务提交/回滚必须严格对应中间件生命周期。
注册到消息总线(非全局启用)
Symfony 允许为不同总线配置不同中间件链。推荐在 config/packages/messenger.php 中声明:
return static function (ContainerConfigurator $containerConfigurator): void {
$messenger = $containerConfigurator->extension('messenger', [
'buses' => [
'messenger.bus.default' => [
'middleware' => [
'App\Messenger\Middleware\TransactionMiddleware',
'doctrine_ping_connection',
'trim_quotes',
],
],
],
]);
};
这样只影响默认总线,不影响查询总线或异步总线,避免误伤读操作。
调试与验证中间件是否生效
运行命令快速确认:
-
php bin/console debug:messenger— 查看各总线挂载的中间件顺序 - 在中间件
handle()开头加dump('in transaction middleware');,发一条命令观察是否触发 - 故意抛异常,检查数据库是否回滚,验证事务完整性
不需要注解、不需要服务标签,只要类存在、类型正确、配置到位,框架自动识别并注入依赖。











