Dancer2中如何让模块路由访问服务类实例?规避重复实例化与包变量
Dancer2 实现服务类单例注入的方案
1. 利用应用启动钩子初始化到上下文
可以通过 before_app 钩子在应用启动时一次性完成服务的复杂初始化,然后将实例存入应用的 user_data 区域,路由直接从这里获取即可,保证实例唯一且不会重复初始化。
示例代码:
# 应用启动时初始化服务 hook before_app => sub { my $app = shift; # 这里写复杂的初始化逻辑,比如加载配置、建立外部连接等 my $hello_service = Hello::Service->new( config => $app->config->{hello_service}, db_connection => DBI->connect('dbi:mysql:dbname', 'user', 'pass') ); # 存入应用上下文的用户数据区 $app->user_data->{hello_service} = $hello_service; }; # 路由中获取服务实例 get '/hello' => sub ($app) { my $hello_service = $app->user_data->{hello_service}; my $message = $hello_service->greet('world'); template 'hello', { message => $message }; };
before_app 钩子仅在应用启动阶段执行一次,既避免了重复初始化,也不会引入全局包变量。
2. 封装为 Dancer2 插件复用服务
如果需要在多个路由模块中共享服务,可以封装成 Dancer2 插件,插件内部通过懒加载维护单例实例,路由通过插件提供的接口获取。
示例代码:
# 自定义插件文件:Dancer2/Plugin/HelloService.pm package Dancer2::Plugin::HelloService; use Dancer2::Plugin; has _service => ( is => 'ro', lazy => 1, builder => '_build_service', ); sub _build_service { my $self = shift; # 复杂初始化逻辑 return Hello::Service->new( config => $self->config, # 其他依赖项 ); } register hello_service => sub { my $dsl = shift; return $dsl->app->plugin_setting('_service'); }; register_plugin; 1; # 应用配置文件中启用插件并配置参数 plugins: HelloService: api_key: 'your_api_key' api_endpoint: 'http://example.com/api' # 路由中调用插件获取服务 get '/hello' => sub { my $hello_service = hello_service(); my $message = $hello_service->greet('world'); template 'hello', { message => $message }; };
插件的 lazy 属性保证服务实例在首次调用时才初始化,且实例为应用级单例,封装性更强,适合多模块场景。
3. 通过容器管理依赖注入
Dancer2 支持结合容器(如 Bread::Board)进行依赖管理,可明确指定服务的单例生命周期,统一管理复杂依赖关系。
示例代码:
use Bread::Board; # 定义容器和服务 my $container = container 'MyApp' => as { service 'hello_service' => ( class => 'Hello::Service', dependencies => { config => dep('config/hello_service'), db_conn => dep('db_connection'), }, lifecycle => 'Singleton', # 明确指定单例生命周期 ); service 'config' => sub { shift->app->config }; service 'db_connection' => sub { DBI->connect('dbi:mysql:dbname', 'user', 'pass') }; }; # 将容器绑定到应用 hook before_app => sub { my $app = shift; $app->container($container); }; # 路由中从容器获取服务 get '/hello' => sub ($app) { my $hello_service = $app->container->resolve(service => 'hello_service'); my $message = $hello_service->greet('world'); template 'hello', { message => $message }; };
这种方式适合依赖关系复杂的场景,容器统一管控服务的创建和生命周期,避免手动维护实例的麻烦。
内容的提问来源于stack exchange,提问作者Jan
相关产品推荐
相关产品推荐

