TYPO3 v10.4.21自定义FormFinisher并发执行实例化Repository异常问题
问题原因分析
- 根本问题是直接通过
GeneralUtility::makeInstance()实例化Extbase Repository类的方式不符合TYPO3规范。Extbase的Repository基类构造函数要求必须传入ObjectManagerInterface实例,你调用makeInstance时没有显式传参,正常情况下依赖DI容器的自动装配逻辑会自动补全参数,但并发场景下会触发竞态问题。 - 并发时复现的原因:TYPO3的DI容器需要先扫描类的元数据(构造函数参数、注解等)生成缓存,当两个请求几乎同时到达、而目标Repository的DI元数据缓存尚未生成时,第一个请求处于生成缓存的过程中,第二个请求读取到不完整的缓存信息,导致
makeInstance认为该类构造函数没有参数,最终触发参数缺失错误。本地环境通常预先生成了所有DI缓存、且OPcache优化更好,所以不会触发竞态。 - 这不是TYPO3核心Bug,是代码写法不规范导致的偶发问题,也不建议你手动给Repository构造函数传递ObjectManager实例,后续TYPO3版本可能修改Repository构造逻辑,自行传参的兼容性很差。
解决方法
- 方案1:构造函数依赖注入(推荐,适合TYPO3 10及以上版本)
表单Finisher支持原生DI注入,直接把依赖放到构造函数参数里即可,不需要手动调用makeInstance:
use TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager; use [NAMESPACE]\[ExtName]\Domain\Repository\ArticleRepository; protected $persistenceManager; protected $articleRepository; public function __construct( PersistenceManager $persistenceManager, ArticleRepository $articleRepository ) { parent::__construct(); $this->persistenceManager = $persistenceManager; $this->articleRepository = $articleRepository; }
- 方案2:inject方法注入(适合TYPO3 9及更早版本)
如果你的TYPO3版本不支持Finisher构造函数注入,改用inject注解方式注入:
/** * @param PersistenceManager $persistenceManager */ public function injectPersistenceManager(PersistenceManager $persistenceManager) { $this->persistenceManager = $persistenceManager; } /** * @param ArticleRepository $articleRepository */ public function injectArticleRepository(ArticleRepository $articleRepository) { $this->articleRepository = $articleRepository; } // 构造函数可以删除手动实例化的逻辑 public function __construct() { parent::__construct(); }
- 方案3:临时兼容写法(仅当必须手动实例化时使用)
如果你出于特殊需求必须手动实例化Repository,不要直接用makeInstance调用Repository类,改用ObjectManager获取:
$objectManager = GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\ObjectManager::class); $this->articleRepository = $objectManager->get(\[NAMESPACE]\[ExtName]\Domain\Repository\ArticleRepository::class);
额外优化建议
部署后先在后台清空所有缓存、或者调用CLI命令typo3 cache:flush预生成DI缓存,可以进一步降低竞态问题出现的概率。
内容的提问来源于stack exchange,提问作者Kathara
相关产品推荐
相关产品推荐

