You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 13:45:15