如何无需手动指定参数动态调用带有Autowired参数的服务方法
如何无需手动指定参数动态调用带有Autowired参数的服务方法
嘿,我完全懂你现在的困扰——每次调用服务方法都要手动传参,方法参数一变就得改代码,太麻烦了;而且继承父类时构造函数要传一堆参数,代码看着乱糟糟的。下面给你几个实用的解决方案,帮你实现像依赖注入那样自动传递参数的动态调用:
方案一:利用Symfony的ArgumentResolver组件自动解析参数
Symfony自带的ArgumentResolverInterface本来是用来解析控制器方法参数的,我们可以直接用它来处理普通服务方法的参数注入,它会自动识别参数类型并从容器中获取对应的服务。
实现代码
use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface; use ReflectionMethod; class YourServiceCaller { private $argumentResolver; public function __construct(ArgumentResolverInterface $argumentResolver) { $this->argumentResolver = $argumentResolver; } public function callDynamic($service, string $methodName) { // 通过反射获取目标方法的信息 $reflectionMethod = new ReflectionMethod($service, $methodName); // 利用ArgumentResolver自动解析参数 $resolvedArgs = $this->argumentResolver->getArguments( // 这里如果方法不需要Request,可以传空Request,不影响其他服务参数的解析 new \Symfony\Component\HttpFoundation\Request(), fn() => [$service, $methodName] ); // 调用目标方法并返回结果 return call_user_func_array([$service, $methodName], $resolvedArgs); } }
使用方式
你只需要把目标服务和方法名传给callDynamic方法,它就会自动解析并注入所需的所有依赖参数,不用再手动传$entityManager、$translator这些参数了。
方案二:手动结合Reflection与容器解析参数
如果你不想依赖ArgumentResolver,也可以自己写逻辑,通过反射获取方法参数类型,再从容器中直接获取对应的服务。
实现代码
use Psr\Container\ContainerInterface; use ReflectionMethod; use ReflectionParameter; class DynamicMethodCaller { private $container; public function __construct(ContainerInterface $container) { $this->container = $container; } public function invoke($service, string $methodName) { $reflectionMethod = new ReflectionMethod($service, $methodName); $arguments = []; foreach ($reflectionMethod->getParameters() as $param) { /** @var ReflectionParameter $param */ $paramType = $param->getType(); // 如果参数是类类型,尝试从容器中获取对应服务 if ($paramType instanceof \ReflectionNamedType && !$paramType->isBuiltin()) { $className = $paramType->getName(); if ($this->container->has($className)) { $arguments[] = $this->container->get($className); continue; } } // 处理可选参数的默认值 if ($param->isOptional()) { $arguments[] = $param->getDefaultValue(); } else { throw new \RuntimeException(sprintf('无法解析必填参数: %s', $param->getName())); } } return call_user_func_array([$service, $methodName], $arguments); } }
说明
这个方法会遍历方法的每个参数,判断是否为可注入的类类型,自动从容器中拉取对应服务;对于可选参数则使用默认值,完全不用手动干预参数传递。
解决继承父类时构造函数参数冗余的问题
你提到的子类继承父类时要传一堆父类构造函数参数的问题,其实可以通过属性注入来彻底解决,不用再写冗长的构造函数:
父类示例(使用属性注入)
use Symfony\Component\DependencyInjection\Attribute\Autowire; class MainController { #[Autowire] private TranslatorInterface $translator; #[Autowire] private SystemTools $systemTools; #[Autowire] private KernelInterface $kernel; #[Autowire] private Permissions $permissions; #[Autowire] private ManagerRegistry $doctrine; #[Autowire] private RequestStack $requestStack; #[Autowire] private RouterInterface $router; }
子类示例
class LicensePackController extends MainController { // 子类只需要注入自己独有的服务即可,父类的服务会自动继承 #[Autowire] private SomeCustomService $customService; // 完全不需要写构造函数! }
这样一来,子类代码会变得非常简洁,不用再手动传递父类的所有构造参数。
备注:内容来源于stack exchange,提问作者Chopchop
相关产品推荐
相关产品推荐

