运行环境 springboot2.1.1 quarz2.3.0 jdk8 war包运行在tomcat9
11:00:57.624 [http-nio-8082-exec-2] ERROR c.k.f.w.e.GlobalExceptionHandler - [handleException,83] - Couldn't obtain triggers for job: connection closed
org.quartz.JobPersistenceException: Couldn't obtain triggers for job: connection closed
at org.quartz.impl.jdbcjobstore.JobStoreSupport.getTriggersForJob(JobStoreSupport.java:2190)
at org.quartz.impl.jdbcjobstore.JobStoreSupport$29.execute(JobStoreSupport.java:2176)
at org.quartz.impl.jdbcjobstore.JobStoreCMT.executeInLock(JobStoreCMT.java:245)
at org.quartz.impl.jdbcjobstore.JobStoreSupport.executeWithoutLock(JobStoreSupport.java:3785)
at org.quartz.impl.jdbcjobstore.JobStoreSupport.getTriggersForJob(JobStoreSupport.java:2173)
at org.quartz.core.QuartzScheduler.getTriggersOfJob(QuartzScheduler.java:1445)
at org.quartz.core.QuartzScheduler.deleteJob(QuartzScheduler.java:958)
at org.quartz.impl.StdScheduler.deleteJob(StdScheduler.java:301)
我出现的原因是因为没注意到项目本身有类继承了SpringBootServletInitializer(springboot war包部署需要继承这个类),然后配置打war包在tomcat部署时给@SpringApplication注解的类也继承了SpringBootServletInitializer.
因此导致tomcat在运行的时候创建了两个ApplicationContext(第二个ApplicationContext的parent是第一个ApplicationContext)
两个ApplicationContext都会创建一个SchedulerFactoryBean并执行afterPropertiesSet初始化Scheduler所以Scheduler会初始化两次,分别放入两个ApplicationContext,在依赖注入的时候会取第一个ApplicationContext里面的Scheduler.
在初始化Scheduler的时候会调用DBConnectionManager.addConnectionProvider往provides里面放入ConnectionProvider
ConnectionProvider是一个可以用来获取数据库连接的对象,provides是一个HashMap
DBConnectionManager是单例,所以对于两次初始化Scheduler用的是同一个实例,所以第二次初始化会把第一次放入provides里面的ConnectionProvider覆盖掉,所以这时如果从DBConnectionManager里面获取数据库连接的话获取到的是第二个ApplicationContext里面的连接
在执行Scheduler的某些方法时会调用JobStoreCMT.executeInLock方法,这个方法会从DBConnectionManager里面获取到数据库连接执行sql,也就是说获取的连接是第二个ApplicationContext的连接
执行完sql的后执行cleanupConnection清理连接,清理逻辑是从ConnectionHolder里面获取连接和当前执行sql的连接比较是否一样,一样就不关闭连接,不一样就关闭连接.而当前执行sql的连接是DBConnectionManager里面的属于第二次初始化Scheduler,而这里ConnectionHolder取到的是第一次初始化的Scheduler里面的连接,比较不一样,所以连接会被关闭。因此后面如果还执行其他sql 获取到的连接就会是一个被关闭的连接,所以就报connection closed(至于ConnectionHolder里面为什么会是第一次初始化的Scheduler里面的连接主要是因为代码Autowired注入的是第一次初始化的Scheduler,具体的可以自己了解)