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
相关产品推荐
相关产品推荐

