依赖注入下如何创建实例?控制器实例化位置存疑
解决控制器依赖注入(DI)的实用方案
嘿,我来给你捋捋怎么搞定这个问题——核心就是统一管理Template实例,让所有控制器不用各自去new,给你两种适配不同项目规模的方案:
1. 手动实现轻量依赖容器(适合小型项目)
如果你的项目还没用到成熟框架,自己写个简单的容器就能解决,完全不用额外装依赖:
第一步:写个依赖容器类,管理Template单例
这个容器的作用就是只创建一次Template,之后所有控制器都复用这个实例:
class Container { private $instances = []; // 获取Template单例 public function getTemplate() { if (!isset($this->instances['Template'])) { // 这里只初始化一次,后续都用同一个实例 $this->instances['Template'] = new Template(); } return $this->instances['Template']; } // 以后要加其他共享依赖(比如数据库连接),直接加类似方法就行 }
第二步:改造控制器,通过构造函数接收Template
把控制器里new Template的代码删掉,改成让外部把实例传进来:
class UserController { private $template; // 构造函数注入依赖,控制器自己不用管实例怎么来 public function __construct(Template $template) { $this->template = $template; } public function showUser() { return $this->template->render('user.html'); } } class PostController { private $template; public function __construct(Template $template) { $this->template = $template; } public function showPost() { return $this->template->render('post.html'); } }
第三步:路由分发时用容器创建控制器
在你的路由处理逻辑里,统一通过容器获取Template并传给控制器:
// 先初始化容器 $container = new Container(); // 假设你的路由是这么定义的 $routes = [ '/user' => UserController::class, '/post' => PostController::class, ]; // 处理当前请求 $currentRoute = $_SERVER['REQUEST_URI']; if (isset($routes[$currentRoute])) { $controllerClass = $routes[$currentRoute]; // 从容器拿Template实例,传给控制器 $controller = new $controllerClass($container->getTemplate()); // 调用控制器的对应方法(这里假设是默认的index方法,你可以根据路由调整) $controller->index(); }
这样不管你加多少个控制器,都只需要从容器里拿同一个Template实例,完全不用重复创建。
2. 用成熟DI容器框架(适合中大型项目)
如果项目规模不小,直接用现成的DI容器(比如PHP-DI、Symfony DI)会更省心,它们能自动帮你处理依赖注入,连手动传参都省了:
拿PHP-DI举个例子:
第一步:安装PHP-DI
composer require php-di/php-di
第二步:配置容器,声明Template为单例
创建个配置文件,告诉容器怎么创建Template:
// config/container.php return [ // 让Template以单例方式创建 Template::class => DI\create(Template::class)->singleton(), ];
第三步:路由中通过容器获取控制器
use DI\ContainerBuilder; // 构建容器 $builder = new ContainerBuilder(); $builder->addDefinitions('config/container.php'); $container = $builder->build(); // 路由分发 $currentRoute = $_SERVER['REQUEST_URI']; $routes = [ '/user' => UserController::class, '/post' => PostController::class, ]; if (isset($routes[$currentRoute])) { // 直接从容器拿控制器,PHP-DI会自动把Template实例注入进去 $controller = $container->get($routes[$currentRoute]); $controller->index(); }
这种方式下,你新增控制器只需要在构造函数里声明依赖就行,容器会自动识别并注入,完全不用手动处理,非常适合复杂项目。
几个关键注意点
- 别在控制器里new依赖:控制器只负责处理请求、调用视图,依赖的创建和管理交给容器,这样代码更易测试、改起来也方便。
- 优先用构造函数注入:像
Template这种控制器必须的依赖,用构造函数注入最清晰,确保控制器创建时就有需要的东西。 - 单例的适用场景:如果你的
Template是无状态的(比如只是封装Twig调用,没有全局变量),单例完全没问题;如果有状态,那可能需要每次创建新实例,但这种情况很少见。
内容的提问来源于stack exchange,提问作者yicoxo
相关产品推荐
相关产品推荐

