阅读时间:1 分钟
0 字
依赖注入
DuxLite 内置了依赖注入(DI)容器,基于 PHP-DI 实现。
设计理念
DuxLite 的依赖注入理念:依赖注入只在必要时使用,而不是过度依赖。
DuxLite 优先使用显式编程风格:
- 显式 > 隐式:优先使用明确的静态方法调用,如
App::db()
、App::cache()
- IDE 友好:显式调用能提供完整的代码提示和类型推断
- 可读性优先:代码意图清晰,依赖关系一目了然
php
// ✅ DuxLite 推荐:显式调用,IDE 完整提示
$users = App::db()->table('users')->get();
$cache = App::cache('redis');
$config = App::config('database');
// ❌ 过度依赖注入:增加复杂性
public function __construct(
private DatabaseManager $db,
private CacheManager $cache,
private Config $config
) {}
何时使用依赖注入:
- 框架内部服务管理
- 需要模拟测试的复杂服务
- 多态实现的服务切换
- 第三方库集成
何时使用静态方法:
- 框架核心功能访问
- 日常业务逻辑开发
- 简单直接的服务调用
基本使用
1. 获取容器
php
use Core\App;
// 获取容器实例
$container = App::di();
2. 注册服务
php
// 注册具体实例
App::di()->set('userService', new UserService());
// 使用工厂函数(推荐)
App::di()->set('userService', function() {
return new UserService(App::db(), App::cache());
});
3. 获取服务
php
// 获取服务
$userService = App::di()->get('userService');
// 检查服务是否存在
if (App::di()->has('userService')) {
$userService = App::di()->get('userService');
}
框架内置服务
DuxLite 框架自动注册了许多内置服务:
php
// 数据库服务
$db = App::db();
$db = App::di()->get('db');
// 缓存服务
$cache = App::cache();
$cache = App::cache('redis');
$cache = App::di()->get('cache.redis');
// 配置服务
$config = App::config('use');
$config = App::di()->get('config.use');
// 事件服务
$events = App::event();
$events = App::di()->get('events');
// 队列服务
$queue = App::queue();
$queue = App::queue('redis');
$queue = App::di()->get('queue.redis');
服务命名约定
基础服务
php
'db' // 数据库连接
'events' // 事件调度器
'permission' // 权限管理
'route' // 路由管理
'trans' // 翻译服务
带类型的服务
php
'cache.file' // 文件缓存
'cache.redis' // Redis 缓存
'queue.redis' // Redis 队列
'storage.local' // 本地存储
'config.use' // 应用配置
在模块中使用
在 App.php 中注册服务
php
class App extends AppExtend
{
public function init(Bootstrap $bootstrap): void
{
// 注册简单服务
App::di()->set('myModule.version', '1.0.0');
// 注册配置服务
App::di()->set('myModule.config', function() {
return App::config('mymodule');
});
}
public function register(Bootstrap $bootstrap): void
{
// 注册复杂服务
App::di()->set('userService', function() {
return new UserService(App::db(), App::cache());
});
// 注册带条件的服务
App::di()->set('paymentService', function() {
$config = App::config('payment');
switch ($config->get('provider')) {
case 'stripe':
return new StripePaymentService($config->get('stripe'));
case 'paypal':
return new PaypalPaymentService($config->get('paypal'));
default:
return new MockPaymentService();
}
});
}
}
控制器依赖注入
DuxLite 支持在控制器和中间件中使用依赖注入:
php
class UserController
{
public function __construct(
private UserService $userService,
private ValidatorService $validator
) {}
public function create(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
{
$data = $request->getParsedBody();
$this->validator->validate($data, UserCreateRules::class);
$user = $this->userService->create($data);
$response->getBody()->write(json_encode($user));
return $response->withHeader('Content-Type', 'application/json');
}
}
高级用法
1. 接口绑定
php
// 绑定接口到具体实现
App::di()->set(UserRepositoryInterface::class, function() {
return new DatabaseUserRepository(App::db());
});
// 使用接口名获取
$repository = App::di()->get(UserRepositoryInterface::class);
2. 条件服务注册
php
public function register(Bootstrap $bootstrap): void
{
// 根据环境注册不同服务
if (App::$debug) {
App::di()->set('debugService', function() {
return new DebugService();
});
}
// 根据配置注册服务
$config = App::config('features');
if ($config->get('email.enabled', false)) {
App::di()->set('emailService', function() {
return new EmailService(App::config('mail'));
});
}
}
3. 服务装饰器
php
public function register(Bootstrap $bootstrap): void
{
// 基础服务
App::di()->set('baseUserService', function() {
return new UserService(App::db());
});
// 装饰后的服务
App::di()->set('userService', function() {
$baseService = App::di()->get('baseUserService');
$cachedService = new CachedUserService($baseService, App::cache());
return new LoggedUserService($cachedService, App::log());
});
}
最佳实践
1. 服务命名规范
php
// ✅ 推荐的命名方式
App::di()->set('user.service', $userService);
App::di()->set('payment.gateway.stripe', $stripeGateway);
App::di()->set('cache.redis', $redisCache);
// ❌ 避免的命名方式
App::di()->set('UserService', $userService); // 大写开头
App::di()->set('user_service', $userService); // 下划线分隔
App::di()->set('us', $userService); // 过于简短
2. 使用工厂函数
php
// ✅ 工厂函数:有依赖的服务
App::di()->set('userService', function() {
return new UserService(App::db(), App::cache());
});
// ✅ 直接实例:简单配置
App::di()->set('api.version', '1.0');
App::di()->set('api.config', ['timeout' => 30]);
// ❌ 错误:有依赖但直接实例化
App::di()->set('userService', new UserService()); // 依赖未满足
3. 避免循环依赖
php
// ❌ 错误:循环依赖
App::di()->set('serviceA', function() {
return new ServiceA(App::di()->get('serviceB'));
});
App::di()->set('serviceB', function() {
return new ServiceB(App::di()->get('serviceA')); // 循环依赖
});
// ✅ 正确:使用事件解耦
App::di()->set('serviceA', function() {
return new ServiceA(App::event());
});
App::di()->set('serviceB', function() {
return new ServiceB(App::event());
});
4. 测试友好的设计
php
// 生产环境注册
public function register(Bootstrap $bootstrap): void
{
if (!App::di()->has('userRepository')) {
App::di()->set('userRepository', function() {
return new DatabaseUserRepository(App::db());
});
}
}
// 测试中可以注入 Mock
App::di()->set('userRepository', new MockUserRepository());
调试和故障排除
检查服务注册
php
// 检查特定服务是否已注册
if (App::di()->has('userService')) {
echo "UserService is registered\n";
}
// 获取所有已注册的服务名称
$services = App::di()->getKnownEntryNames();
foreach ($services as $serviceName) {
echo "Registered: $serviceName\n";
}
常见错误处理
php
// 服务未注册错误
try {
$service = App::di()->get('nonExistentService');
} catch (DI\NotFoundException $e) {
echo "Service not found: " . $e->getMessage();
}
// 正确:先检查再获取
if (App::di()->has('myService')) {
$service = App::di()->get('myService');
} else {
// 提供默认行为或抛出自定义异常
}
性能优化
延迟加载
php
// ✅ 延迟加载:只在需要时创建
App::di()->set('expensiveService', function() {
return new ExpensiveService(); // 只在第一次 get() 时创建
});
// ❌ 立即加载:在注册时就创建
App::di()->set('expensiveService', new ExpensiveService()); // 立即创建
缓存配置对象
php
// ✅ 缓存配置读取
App::di()->set('apiConfig', function() {
static $config = null;
if ($config === null) {
$config = App::config('api')->toArray(); // 只读取一次
}
return $config;
});
通过合理使用依赖注入,您可以构建更加模块化、可测试和可维护的应用程序。