Symfony非默认机制启用依赖注入:ArgumentResolver使用问题
我来帮你解决这个Symfony依赖注入的问题。你遇到的ArgumentResolverInterface无法自动装配的问题,本质是因为它的默认实现依赖了一堆底层服务,Symfony的自动装配没法自动解析这些依赖——不过有几种靠谱的解决方式,我给你一步步讲:
首先,ArgumentResolverInterface的默认实现是Symfony内核自带的ArgumentResolver服务,它的服务ID是argument_resolver。你没法直接自动注入它,是因为它的构造函数需要多个依赖(比如参数元数据工厂、各种参数值解析器),这些不是简单的自动装配能搞定的。不过你可以通过显式配置服务或者注入容器来获取它:
方式A:显式配置服务注入(推荐)
在你的config/services.yaml里,给DataSourceController配置注入argument_resolver服务:
services: App\Controller\DataSourceController: arguments: $argumentResolver: '@argument_resolver'
然后修改控制器,直接注入ArgumentResolverInterface:
use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\JsonResponse; class DataSourceController { private $argumentResolver; public function __construct(ArgumentResolverInterface $argumentResolver) { $this->argumentResolver = $argumentResolver; } public function queryDatasource(string $hash, Request $request) { // 这里写你构造$datasource的逻辑,比如从某个服务或存储获取 $datasource = ...; // 用ArgumentResolver解析query方法的参数 $arguments = $this->argumentResolver->getArguments($request, [$datasource, 'query']); $data = $datasource->query(...$arguments); return new JsonResponse($data); } }
方式B:通过容器获取(不推荐,但快速可用)
如果你不想修改服务配置,可以临时注入ContainerInterface来获取argument_resolver服务:
use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\JsonResponse; class DataSourceController { private $argumentResolver; public function __construct(ContainerInterface $container) { $this->argumentResolver = $container->get('argument_resolver'); } public function queryDatasource(string $hash, Request $request) { // 构造$datasource的逻辑 $datasource = ...; $arguments = $this->argumentResolver->getArguments($request, [$datasource, 'query']); $data = $datasource->query(...$arguments); return new JsonResponse($data); } }
注意:这种方式不推荐,因为直接依赖容器会降低代码的可测试性,最好还是用显式注入的方式。
如果你的数据源需要依赖注入,更好的方式是把它们注册为Symfony服务,让容器直接管理它们的依赖,而不是手动实例化后再用ArgumentResolver处理。这样不仅能解决依赖注入的问题,还能享受Symfony服务的所有特性(比如自动装配、标签、生命周期管理等)。
步骤1:注册数据源服务
在config/services.yaml里注册你的数据源,比如给每个数据源加一个标签,方便后续定位:
services: # 注册单个数据源 App\DataSource\MyFirstDatasource: arguments: $databaseConnection: '@Doctrine\DBAL\Connection' # 示例依赖 tags: ['app.datasource'] # 自定义标签,用于分组 App\DataSource\MySecondDatasource: arguments: $apiClient: '@App\Service\ExternalApiClient' # 示例依赖 tags: ['app.datasource'] # 创建一个服务定位器,只包含带app.datasource标签的服务 App\DataSource\DatasourceLocator: parent: Symfony\Component\DependencyInjection\ServiceLocator tags: ['container.service_locator'] arguments: - !tagged_locator tag: 'app.datasource' index_by: 'hash' # 假设你的数据源有一个getHash()方法,或者直接用服务ID作为hash
步骤2:在控制器中使用服务定位器
然后修改控制器,注入DatasourceLocator来获取对应的数据源:
use App\DataSource\DatasourceLocator; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface; class DataSourceController { private $datasourceLocator; private $argumentResolver; public function __construct(DatasourceLocator $datasourceLocator, ArgumentResolverInterface $argumentResolver) { $this->datasourceLocator = $datasourceLocator; $this->argumentResolver = $argumentResolver; } public function queryDatasource(string $hash, Request $request) { // 从定位器获取已注入依赖的数据源 if (!$this->datasourceLocator->has($hash)) { throw new \RuntimeException("Datasource with hash $hash not found"); } $datasource = $this->datasourceLocator->get($hash); // 如果query方法需要Request或其他请求相关参数,还是可以用ArgumentResolver解析 $arguments = $this->argumentResolver->getArguments($request, [$datasource, 'query']); $data = $datasource->query(...$arguments); return new JsonResponse($data); } }
这样你的数据源本身的依赖已经由Symfony容器自动注入了,而ArgumentResolver只负责处理和请求相关的参数(比如Request对象、路由参数等),分工更清晰。
为什么ArgumentResolverInterface不能自动装配?因为它的默认实现Symfony\Component\HttpKernel\Controller\ArgumentResolver的构造函数需要以下依赖:
ArgumentMetadataFactoryInterface(用于解析方法参数元数据)- 一个
iterable类型的ArgumentValueResolverInterface集合(各种参数解析器,比如处理Request、服务、路由参数的解析器)
这些依赖都是Symfony内核内部聚合的,自动装配没法自动识别并注入这些集合,所以必须显式指定注入或者通过容器获取。
内容的提问来源于stack exchange,提问作者Victor

