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

EJB中@Schedule的@Singleton注入@Produces对象失败,调度器未启动

解决EJB上下文定时清理HTTP连接池的问题

核心问题分析

你的调度器未启动是因为IdleConnectionMonitor不是EJB组件,容器无法识别@Schedule注解;而添加@Singleton后报错,是因为EJB容器要求单例Bean必须提供无参构造函数,同时你原代码中同时使用字段注入和带参构造的写法存在冲突。

解决方案步骤

1. 重构IdleConnectionMonitor类

将其声明为EJB单例组件,调整注入方式并保留无参构造:

@Singleton
@Startup
public class IdleConnectionMonitor {
    private static final Logger LOGGER = LoggerFactory.getLogger(IdleConnectionMonitor.class);
    
    @Inject
    private PoolingHttpClientConnectionManager poolingHttpClientConnectionManager;

    // 提供无参构造,满足EJB容器实例化要求
    public IdleConnectionMonitor() {}

    @Schedule(second = "30", persistent = false)
    public void cleanUpPool() {
        LOGGER.debug("PoolStats counters meaning: max: total connections count; free: available to be reused/free/idle; leased: currently executing requests; waiting: the requests being blocked awaiting a free connection");
        tracePoolStats("HTTP client pool stats in the beginning: ");
        tracePoolStats("HTTP client pool stats before recycling:");
        poolingHttpClientConnectionManager.closeExpiredConnections();
        poolingHttpClientConnectionManager.closeIdleConnections(30, TimeUnit.SECONDS);
        tracePoolStats("HTTP client pool stats after recycling:");
    }

    private void tracePoolStats(String messagePrefix) {
        if (LOGGER.isDebugEnabled()) {
            PoolStats poolStats = poolingHttpClientConnectionManager.getTotalStats();
            LOGGER.debug(messagePrefix + "(max/free/leased/waiting): {} | {} | {} | {}",
                    poolStats.getMax(), poolStats.getAvailable(), poolStats.getLeased(), poolStats.getPending());
        }
    }
}
  • @Singleton + @Startup:让容器在应用启动时就初始化该Bean,确保定时任务立刻生效
  • persistent=false:避免服务器重启后,容器执行停机期间错过的清理任务,适合此类时效性的操作
  • 移除带参构造,仅保留@Inject字段注入:避免EJB容器实例化冲突

2. 调整连接池生产者类RestServiceProducer

确保连接池在应用启动时就初始化,而非首次请求延迟创建:

@Singleton
@Startup
public class RestServiceProducer {
    private PoolingHttpClientConnectionManager poolingHttpClientConnectionManager;
    private static final int MAX_CONNECTIONS = 20;
    private static final int SOCKET_TIMEOUT_MS = 5000;

    @Produces
    @ApplicationScoped
    public PoolingHttpClientConnectionManager poolingHttpClientConnectionManager() {
        if (poolingHttpClientConnectionManager == null) {
            this.poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager();
            poolingHttpClientConnectionManager.setMaxTotal(MAX_CONNECTIONS);
            poolingHttpClientConnectionManager.setDefaultMaxPerRoute(MAX_CONNECTIONS);
            poolingHttpClientConnectionManager.setDefaultSocketConfig(
                    SocketConfig.copy(SocketConfig.DEFAULT)
                            .setSoTimeout(SOCKET_TIMEOUT_MS)
                            .build()
            );
        }
        return poolingHttpClientConnectionManager;
    }
}
  • @Singleton + @Startup:强制连接池在应用启动时完成初始化,避免定时任务执行时连接池未就绪
  • @ApplicationScoped:确保生成的连接池是应用级单例,符合连接池的复用需求

内容的提问来源于stack exchange,提问作者WesternGun

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 08:08:28