Symfony 5中如何重写TraceableAdapter 替换框架默认实现
自定义Symfony TraceableAdapter 实现方案
你提到的CacheCollectorPass是Symfony在容器编译阶段自动给所有缓存适配器套上Traceable包装类的内置编译器Pass,默认直接硬编码了TraceableAdapter和TraceableTagAwareAdapter两个类作为包装实现,你可以通过调整容器编译逻辑完成替换,具体方案如下:
方案1:重写编译器Pass(完全替换默认实现)
- 第一步:创建自定义Traceable实现类,继承原有官方类保证兼容性,只重写你需要修改的逻辑即可,需要同时兼容普通缓存和标签缓存两种场景:
// src/Cache/Adapter/CustomTraceableAdapter.php namespace App\Cache\Adapter; use Symfony\Component\Cache\Adapter\TraceableAdapter; class CustomTraceableAdapter extends TraceableAdapter { // 示例:重写get方法添加自定义逻辑 public function get(string $key, callable $callback, float $beta = null, array &$metadata = null): mixed { // 自定义前置逻辑 $result = parent::get($key, $callback, $beta, $metadata); // 自定义后置逻辑 return $result; } }
// src/Cache/Adapter/CustomTraceableTagAwareAdapter.php namespace App\Cache\Adapter; use Symfony\Component\Cache\Adapter\TraceableTagAwareAdapter; class CustomTraceableTagAwareAdapter extends TraceableTagAwareAdapter { // 对应标签缓存的自定义逻辑 }
- 第二步:创建自定义编译器Pass,替换原有类选择逻辑
// src/DependencyInjection/Compiler/CustomCacheCollectorPass.php namespace App\DependencyInjection\Compiler; use Symfony\Component\Cache\Adapter\TagAwareAdapterInterface; use Symfony\Component\Cache\DependencyInjection\CacheCollectorPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use App\Cache\Adapter\CustomTraceableAdapter; use App\Cache\Adapter\CustomTraceableTagAwareAdapter; class CustomCacheCollectorPass extends CacheCollectorPass { public function process(ContainerBuilder $container) { if (!$container->hasDefinition('data_collector.cache')) { return; } foreach ($container->findTaggedServiceIds('cache.pool') as $id => $attributes) { $definition = $container->getDefinition($id); if ($definition->isAbstract()) { continue; } // 替换原有硬编码的类名,使用自定义实现 $recorderClass = is_subclass_of($definition->getClass(), TagAwareAdapterInterface::class) ? CustomTraceableTagAwareAdapter::class : CustomTraceableAdapter::class; $recorder = $container->register($id.'.recorder', $recorderClass) ->setDecoratedService($id) ->setPublic(false) ->addArgument($id.'.recorder.inner'); $container->getDefinition('data_collector.cache')->addMethodCall('addInstance', [$id, $recorder]); } } }
- 第三步:在Kernel中注册自定义Pass,先移除原有官方Pass避免重复执行
// src/Kernel.php use App\DependencyInjection\Compiler\CustomCacheCollectorPass; use Symfony\Component\Cache\DependencyInjection\CacheCollectorPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Kernel as BaseKernel; class Kernel extends BaseKernel { protected function build(ContainerBuilder $container): void { $container->removePass(CacheCollectorPass::class); $container->addCompilerPass(new CustomCacheCollectorPass()); } }
方案2:装饰现有Traceable服务(轻量需求适用)
如果不需要完全替换默认实现,只是需要追加额外逻辑,可以直接用装饰器模式处理所有Traceable服务,不需要修改编译器Pass:
- 在
config/services.yaml中添加全局装饰配置:
services: App\Cache\Decorator\TraceableAdapterDecorator: decorates: '*.recorder' arguments: ['@.inner']
装饰器实现对应的缓存接口,将大部分逻辑代理给内部的原生TraceableAdapter即可。
注意:以上两种方案默认仅在
dev环境生效,因为缓存数据收集器本身默认仅在dev环境加载,如需生产环境生效需要额外调整数据收集器的加载配置。
内容的提问来源于stack exchange,提问作者Taras Sachavskyi
相关产品推荐
相关产品推荐

