quartz定时报错,连接失效出现问题

本文分析了C3P0连接池在与MySQL交互时出现的连接超时异常问题,并提出了解决方案,包括设置合理的空闲时间和增加验证查询。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

异常:

[2017-11-23 13:57:11] [WARN ] [com.mchange.v2.c3p0.impl.NewPooledConnection:425] - [c3p0] A PooledConnection that has already signalled a Connection error is still in use!

[2017-11-23 13:57:11] [WARN ] [com.mchange.v2.c3p0.impl.NewPooledConnection:426] - [c3p0] Another error has occurred [ com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: No operations allowed after connection closed. ] which will not be reported to listeners!
com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: No operations allowed after connection closed.
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:404)
at com.mysql.jdbc.Util.getInstance(Util.java:387)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:917)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:896)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:885)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:860)
at com.mysql.jdbc.ConnectionImpl.throwConnectionClosedException(ConnectionImpl.java:1235)
at com.mysql.jdbc.ConnectionImpl.checkClosed(ConnectionImpl.java:1230)
at com.mysql.jdbc.ConnectionImpl.setAutoCommit(ConnectionImpl.java:4833)
at com.mchange.v2.c3p0.impl.NewProxyConnection.setAutoCommit(NewProxyConnection.java:881)
at org.quartz.impl.jdbcjobstore.AttributeRestoringConnectionInvocationHandler.restoreOriginalAtributes(AttributeRestoringConnectionInvocationHandler.java:141)
at org.quartz.impl.jdbcjobstore.JobStoreSupport.cleanupConnection(JobStoreSupport.java:3600)
at org.quartz.impl.jdbcjobstore.JobStoreSupport.doRecoverMisfires(JobStoreSupport.java:3206)
at org.quartz.impl.jdbcjobstore.JobStoreSupport$MisfireHandler.manage(JobStoreSupport.java:3935)
at org.quartz.impl.jdbcjobstore.JobStoreSupport$MisfireHandler.run(JobStoreSupport.java:3956)

Caused by: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure


Mysql服务器默认的“wait_timeout”是8小时,也就是说一个connection空闲超过8个小时,Mysql将自动断开该connection。这就是问题的所在,在C3P0 pools中的connections如果空闲超过8小时,Mysql将其断开,而C3P0并不知道该connection已经失效,如果这时有Client请求connection,C3P0将该失效的Connection提供给Client,将会造成上面的异常。


分析过程:

而c3p0属性

<!--最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 -->
<property name="maxIdleTime">60</property>


quartz定时任务使用的是默认是maxIdleTime=0,永不丢弃。

从日志文件打印quartz数据库配置信息来看

characterEncoding = UTF - 8 & useSSL = TRUE,
 lastAcquisitionFailureDefaultUser -> NULL,
 maxAdministrativeTaskTime -> 0,
 maxConnectionAge -> 0,
 maxIdleTime -> 0,
 maxIdleTimeExcessConnections -> 0,
 maxPoolSize -> 100,
 maxStatements -> 0,
 maxStatementsPerConnection -> 120,

解决办法:1.查看源码看是否加上需要加上最大空闲时间 设置为60s

quartz的属性配置是discardIdleConnectionsSeconds

org.quartz.dataSource.myDS.discardIdleConnectionsSeconds60

2.validationQuery:每次返回连接时执行数据库sql查询到池中确保它仍然有效。

org.quartz.dataSource.myDS.validationQuerySELECT 1 FROM DUAL


<think>首先,用户的问题是关于Spring定时任务中获取Bean为null的问题,有时失败有时成功。用户提供了引用,我需要从中提取有用信息。 关键点: - 在定时任务中,通过继承QuartzJobBean等方式,尝试获取Bean时,有时为null,导致空指针异常。 - 引用[1]提到,在QuartzJobBean的executeInternal方法中,直接获取Bean可能为null,需要通过ApplicationContext手动获取。 - 引用[2]显示在Shiro配置类中注入Bean为null。 - 引用[3]是关于固定Cron表达式的定时任务示例。 - 引用[4]和[5]讨论Quartz Job中注入Spring Bean为null的问题,并提供了配置和解决方案。 用户的问题具体是:在Spring定时任务中获取Bean为null,有时失败有时成功。这表明问题可能不是一直发生,而是间歇性的,可能涉及线程安全或初始化问题。 我需要构建一个清晰的回答: - 分析问题原因。 - 提供解决方案。 - 确保回答结构化:介绍问题、原因分析、解决方案、总结。 - 生成相关问题。 - 使用中文。 - 遵循LaTeX格式:行内数学用$...$,独立公式用$$...$$(但在这个问题中可能不涉及数学公式,所以可能不需要)。 - 添加引用标识在末尾。 问题原因分析: 1. **Bean初始化问题**:定时任务可能在Spring容器完全初始化之前启动,导致Bean未准备好。 2. **线程上下文问题**:Quartz任务运行在独立的线程中,可能没有Spring的ApplicationContext绑定。 3. **依赖注入失效**:在非Spring管理的类中(如QuartzJobBean),@Autowired或@Resource可能不起作用。 4. **间歇性失败**:可能由于任务启动时机、线程调度或Bean加载顺序导致有时成功有时失败。 解决方案: - **手动获取ApplicationContext**:如引用[1]所示,通过JobExecutionContext获取SchedulerContext,然后获取ApplicationContext。 - **使用Spring的MethodInvokingJobDetailFactoryBean**:避免直接使用QuartzJobBean,而是让Spring管理Job。 - **确保Bean作用域**:Bean应该是单例或原型,但需确保线程安全。 - **配置Quartz与Spring集成**:正确配置SchedulerFactoryBean,设置applicationContextSchedulerContextKey。 - **延迟初始化**:确保任务在Spring上下文完全启动后执行。 从引用中提取的解决方案: - 引用[1]:在executeInternal中,通过scheduler.getContext().get("applicationContext")获取ApplicationContext。 - 引用[5]:提到解决方案,但未详细说明;在上下文中,可能是配置问题。 - 引用[4]:配置quartz.xml,使用JobDetailFactoryBean等。 最佳实践: 1. 在QuartzJobBean中,不要直接注入Bean,而是手动获取。 2. 使用Spring的@Scheduled注解代替Quartz,如果可能,因为@Scheduled方法在Spring上下文中运行。 3. 配置Quartz的JobFactory为SpringBeanJobFactory,这样Job实例由Spring创建,Bean可以注入。 针对间歇性问题: - 检查Bean的加载顺序。 - 添加日志以跟踪Bean获取过程。 - 确保ApplicationContext被正确设置到SchedulerContext中。 回答结构: 1. **介绍问题**:简述用户问题。 2. **原因分析**:解释为什么Bean为null,有时成功有时失败。 3. **解决方案**: - 方法1:手动获取ApplicationContext(参考引用[1])。 - 方法2:使用Spring管理Quartz Job(配置优化)。 - 方法3:使用@Scheduled注解简化。 4. **代码示例**:提供代码片段。 5. **总结**:强调关键点。 6. **相关问题**:生成几个相关问题。 生成相关问题: 1. 如何配置Spring与Quartz集成以避免Bean注入问题? 2. Spring定时任务中的Bean作用域有哪些注意事项? 3. 如何调试Spring定时任务中的间歇性空指针异常? 4. @Scheduled和Quartz在Spring中的区别是什么? 确保回答真实可靠:基于引用和Spring/Quartz最佳实践。 最终输出格式: - 用户可见层:回答结构清晰。 - 末尾添加引用标识。 - 相关问题部分。</think>### Spring 定时任务获取 Bean 为 null 的间歇性问题分析与解决方案 在 Spring 应用中,定时任务(如使用 Quartz 或 `@Scheduled`)获取 Bean 对象时出现空指针异常(NullPointerException),且问题有时发生有时正常,是一个常见但棘手的场景。这通常与 Spring 容器的初始化、线程上下文或配置问题相关。以下我将逐步分析原因并提供解决方案,帮助您彻底解决该问题。 #### 问题原因分析 Bean 获取失败有时成功有时失败,表明问题具有 **间歇性**,可能涉及以下核心原因: 1. **Spring 容器初始化顺序问题**: 定时任务可能在 Spring 上下文完全初始化前启动,导致 Bean 未加载完成。例如,Quartz Scheduler 在应用启动时立即激活,但某些 Bean 依赖(如数据库连接池)初始化较慢,引发随机失败[^1][^4]。 2. **线程上下文丢失**: Quartz 任务运行在独立线程池中,而非 Spring 管理的线程。如果未正确绑定 `ApplicationContext` 到任务线程,`@Autowired` 或 `@Resource` 注入会失效。引用[2]中 Shiro 配置类注入失败就是类似案例[^2]。 3. **Bean 作用域与线程安全**: 若 Bean 的作用域为 `prototype` 或未正确同步,多线程环境下(如 Quartz 的并发任务)可能导致某些线程获取到未完全初始化的 Bean 实例,引发间歇性 null[^5]。 4. **配置缺陷**: Quartz 的 `SchedulerFactoryBean` 未设置 `applicationContextSchedulerContextKey`,导致 `JobExecutionContext` 无法传递 `ApplicationContext`,引用[4]的配置遗漏了此关键项[^4]。 #### 解决方案 针对上述原因,提供以下可靠解决方案。**优先推荐方法1**,因为它直接解决间歇性问题且代码侵入性低。 ##### 方法1: 手动获取 ApplicationContext(推荐) 在 Quartz Job 的 `executeInternal` 方法中,通过 `JobExecutionContext` 手动获取 `ApplicationContext`,避免依赖注入。此方法确保每次执行都动态获取最新 Bean,解决初始化顺序问题。 **代码示例**: ```java import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.springframework.context.ApplicationContext; import org.springframework.scheduling.quartz.QuartzJobBean; public class MyQuartzJob extends QuartzJobBean { @Override protected void executeInternal(JobExecutionContext context) throws JobExecutionException { // 手动获取 ApplicationContext ApplicationContext appContext = (ApplicationContext) context.getScheduler() .getContext().get("applicationContext"); // 获取 Bean(例如 ConsultationDao) ConsultationDao consultationDao = appContext.getBean(ConsultationDao.class); // 执行业务逻辑 consultationDao.processData(); // 避免 NullPointerException } } ``` **关键配置**(在 Spring XML 或 Java Config 中): ```xml <bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="applicationContextSchedulerContextKey" value="applicationContext"/> <!-- 确保键名一致 --> <property name="triggers"> <list> <ref bean="cronTrigger" /> </list> </property> </bean> ``` **优势**: - 100% 避免间歇性 null,因为每次执行都重新获取 Bean。 - 兼容所有 Spring 版本,引用[1]和[5]已验证此方法[^1][^5]。 ##### 方法2: 使用 Spring 管理的 JobFactory 通过 `SpringBeanJobFactory` 让 Spring 创建 Job 实例,自动注入 Bean。但需注意:如果 Bean 作用域为 `prototype`,可能仍因线程竞争导致间歇性问题。 **配置示例**: ```java @Configuration public class QuartzConfig { @Bean public JobFactory springBeanJobFactory(ApplicationContext context) { SpringBeanJobFactory jobFactory = new SpringBeanJobFactory(); jobFactory.setApplicationContext(context); return jobFactory; } @Bean public SchedulerFactoryBean schedulerFactoryBean(JobFactory jobFactory) { SchedulerFactoryBean factory = new SchedulerFactoryBean(); factory.setJobFactory(jobFactory); return factory; } } ``` **Job 类调整**: ```java public class MyQuartzJob extends QuartzJobBean { @Autowired // 现在可以注入,但需确保单例作用域 private ConsultationDao consultationDao; @Override protected void executeInternal(JobExecutionContext context) { consultationDao.processData(); // 注入成功,但高并发时可能间歇性失败 } } ``` **适用场景**:低并发任务,Bean 作用域为 `singleton`[^4]。 ##### 方法3: 改用 Spring @Scheduled 注解(简化方案) 如果不需要 Quartz 的高级特性(如动态 Cron),使用 Spring 内置的 `@Scheduled` 更简单,Bean 注入天然安全。 **代码示例**: ```java import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @Component public class MyScheduledTask { @Autowired // 直接注入,无 null 风险 private ConsultationDao consultationDao; @Scheduled(cron = "0/5 * * * * ?") // 每5秒执行 public void runTask() { consultationDao.processData(); // 可靠执行 } } ``` **优势**: - 无需处理线程上下文,Spring 全权管理 Bean 生命周期。 - 引用[3]的固定 Cron 任务适用此方案[^3]。 #### 调试与预防措施 - **添加日志**:在 `executeInternal` 中记录 `ApplicationContext` 获取状态,监控失败时机(如应用启动时)。 - **延迟任务启动**:配置 `SchedulerFactoryBean` 的 `startupDelay` 属性(e.g., `startupDelay=10`),确保 Spring 初始化完成。 - **Bean 作用域检查**:将关键 Bean 设为 `singleton`,避免多线程竞争。 - **依赖验证**:确保所有 Bean 无循环依赖,使用 `@DependsOn` 明确初始化顺序。 #### 总结 间歇性 Bean null 问题主要由 Spring 容器初始化竞争或线程上下文丢失引起。**手动获取 ApplicationContext(方法1)是最可靠方案**,尤其适合高并发场景。配置优化后,任务稳定性将显著提升。如果任务简单,优先考虑 `@Scheduled`(方法3)以减少复杂度[^1][^4][^5]。 ### 相关问题 1. 如何配置 Quartz 与 Spring 集成以避免 Bean 注入问题? 2. Spring 定时任务中 Bean 的作用域(如 singleton/prototype)如何影响线程安全? 3. 调试 Spring 定时任务中间歇性空指针异常的最佳实践是什么? 4. @Scheduled 和 Quartz 在 Spring 中的优缺点及适用场景有哪些? [^1]: 关于 Spring MVC 定时任务中无法获取 Bean 容器中的对象,需手动获取 ApplicationContext。 [^2]: 解决注入 Spring 容器中的 Bean 对象为 null 问题,涉及线程上下文问题。 [^3]: 固定 Cron 表达式的定时任务示例,使用 @Scheduled 简化实现。 [^4]: 定时任务 Quartz 的 Job 中注入 Spring Bean 为 null 的配置问题。 [^5]: Quartz Job 中注入 Spring Bean 为 null 的解决方案,包括手动获取上下文。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值