Spring + Quartz:@PostConstruct 方法被调用了两次
Spring + Quartz: @PostConstruct method is called twice
我已经查看了 this 问题来解决我的问题,但没有什么可以帮助我的情况,或者我可能不完全理解那里的解决方案。
我正在执行多个 Quartz 计划的电子邮件任务,这些任务应该在应用程序启动时 运行,以及在 运行 时间动态计划、重新计划和取消计划。以下是我的配置 class.
@Configuration
@ComponentScan(basePackages="de.it2media.dps.statistics")
public class AppConfig{
@Autowired private AutowireCapableBeanFactory autowireCapableBeanFactory;
@Bean
public SchedulerFactoryBean schedulerFactoryBean(){
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
schedulerFactoryBean.setJobFactory(autowiringSpringBeanFactory());
return schedulerFactoryBean;
}
@Bean
public SpringBeanJobFactory autowiringSpringBeanFactory(){
return new SpringBeanJobFactory(){
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
Object job = super.createJobInstance(bundle);
autowireCapableBeanFactory.autowireBean(job);
return job;
}
};
}
@Bean
public Scheduler scheduler() throws SchedulerException{
Scheduler scheduler = schedulerFactoryBean().getScheduler();
return scheduler;
}
}
我的 @Controller
class 中有一个 @PostConstruct
方法,如下所示。
@Controller
@RequestMapping("/task")
public class TaskController extends BaseRemoteService implements TaskService {
@Autowired Scheduler scheduler;
@PostConstruct
private void afterPropertiesSet() throws Exception {
List<EmailTask> taskList;
taskList = getSavedTasks();
for (EmailTask emailTask : taskList) {
if (!scheduler.checkExists(new JobKey(emailTask.getTaskKey()))) {
JobDetail jobDetail = JobBuilder.newJob(EmailCronJob.class)
.withIdentity(emailTask.getTaskKey(), "emailTaskGroup").storeDurably(false).build();
jobDetail.getJobDataMap().put("emailTask", emailTask);
CronTrigger cronTrigger = TriggerBuilder.newTrigger().forJob(jobDetail)
.withIdentity(emailTask.getTaskKey(), "emailTaskGroup")
.withSchedule(CronScheduleBuilder.cronSchedule(emailTask.getCronExpression())).build();
scheduler.scheduleJob(jobDetail, cronTrigger);
}
}
}
}
还有我的 web.xml
文件。
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>de.it2media.dps.statistics.server.config.AppConfig</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>de.it2media.dps.statistics.server.config.MVCConfig</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/DPSStatistics/rpc/*</url-pattern>
</servlet-mapping>
我有另一个从 Spring 导入 MVC 配置的配置文件,但到目前为止我还没有需要在其中删除 bean。我不知道这是否真的很重要,但这里是。
@Configuration
@ComponentScan(basePackages="de.it2media.dps.statistics.server.controller")
@EnableWebMvc
public class MVCConfig {}
所以问题如题中所述,是@PostConstruct
方法被调用了两次。同样的 Quartz 作业都尝试创建两次,我得到以下异常。
[INFO] 2016/10/06 13:42:27.505 WARN [main] AnnotationConfigWebApplicationContext:549 - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'taskController': Invocation of init method failed; nested exception is org.quartz.ObjectAlreadyExistsException: Unable to store Job : 'emailTaskGroup.423946369', because one already exists with this identification.
[INFO] 2016/10/06 13:42:27.510 ERROR [main] DispatcherServlet:502 - Context initialization failed
[INFO] org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'taskController': Invocation of init method failed; nested exception is org.quartz.ObjectAlreadyExistsException: Unable to store Job : 'emailTaskGroup.423946369', because one already exists with this identification.
[INFO] at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:136) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:408) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1575) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:545) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.beans.factory.support.AbstractBeanFactory.getObject(AbstractBeanFactory.java:306) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:751) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:861) ~[spring-context-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:541) ~[spring-context-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext(FrameworkServlet.java:668) ~[spring-webmvc-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:634) ~[spring-webmvc-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:682) ~[spring-webmvc-4.3.3.RELEASE.jar:4.3.3.RELEASE]
我认为这个问题与 AutowireCapableBeanFactory
有关,我需要为 Quartz 的调度程序 class 提供应用程序上下文,但我不确定。
我完全被难住了。
@M。 Deinum 是对的。我唯一做的就是从 AppConfig.java 中的 @ComponentScan
中排除控制器包并且它有效。
@ComponentScan(basePackages="de.it2media.dps.statistics",
excludeFilters = { @Filter(Configuration.class), @Filter(Controller.class) })
我已经查看了 this 问题来解决我的问题,但没有什么可以帮助我的情况,或者我可能不完全理解那里的解决方案。
我正在执行多个 Quartz 计划的电子邮件任务,这些任务应该在应用程序启动时 运行,以及在 运行 时间动态计划、重新计划和取消计划。以下是我的配置 class.
@Configuration
@ComponentScan(basePackages="de.it2media.dps.statistics")
public class AppConfig{
@Autowired private AutowireCapableBeanFactory autowireCapableBeanFactory;
@Bean
public SchedulerFactoryBean schedulerFactoryBean(){
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
schedulerFactoryBean.setJobFactory(autowiringSpringBeanFactory());
return schedulerFactoryBean;
}
@Bean
public SpringBeanJobFactory autowiringSpringBeanFactory(){
return new SpringBeanJobFactory(){
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
Object job = super.createJobInstance(bundle);
autowireCapableBeanFactory.autowireBean(job);
return job;
}
};
}
@Bean
public Scheduler scheduler() throws SchedulerException{
Scheduler scheduler = schedulerFactoryBean().getScheduler();
return scheduler;
}
}
我的 @Controller
class 中有一个 @PostConstruct
方法,如下所示。
@Controller
@RequestMapping("/task")
public class TaskController extends BaseRemoteService implements TaskService {
@Autowired Scheduler scheduler;
@PostConstruct
private void afterPropertiesSet() throws Exception {
List<EmailTask> taskList;
taskList = getSavedTasks();
for (EmailTask emailTask : taskList) {
if (!scheduler.checkExists(new JobKey(emailTask.getTaskKey()))) {
JobDetail jobDetail = JobBuilder.newJob(EmailCronJob.class)
.withIdentity(emailTask.getTaskKey(), "emailTaskGroup").storeDurably(false).build();
jobDetail.getJobDataMap().put("emailTask", emailTask);
CronTrigger cronTrigger = TriggerBuilder.newTrigger().forJob(jobDetail)
.withIdentity(emailTask.getTaskKey(), "emailTaskGroup")
.withSchedule(CronScheduleBuilder.cronSchedule(emailTask.getCronExpression())).build();
scheduler.scheduleJob(jobDetail, cronTrigger);
}
}
}
}
还有我的 web.xml
文件。
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>de.it2media.dps.statistics.server.config.AppConfig</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>de.it2media.dps.statistics.server.config.MVCConfig</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/DPSStatistics/rpc/*</url-pattern>
</servlet-mapping>
我有另一个从 Spring 导入 MVC 配置的配置文件,但到目前为止我还没有需要在其中删除 bean。我不知道这是否真的很重要,但这里是。
@Configuration
@ComponentScan(basePackages="de.it2media.dps.statistics.server.controller")
@EnableWebMvc
public class MVCConfig {}
所以问题如题中所述,是@PostConstruct
方法被调用了两次。同样的 Quartz 作业都尝试创建两次,我得到以下异常。
[INFO] 2016/10/06 13:42:27.505 WARN [main] AnnotationConfigWebApplicationContext:549 - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'taskController': Invocation of init method failed; nested exception is org.quartz.ObjectAlreadyExistsException: Unable to store Job : 'emailTaskGroup.423946369', because one already exists with this identification.
[INFO] 2016/10/06 13:42:27.510 ERROR [main] DispatcherServlet:502 - Context initialization failed
[INFO] org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'taskController': Invocation of init method failed; nested exception is org.quartz.ObjectAlreadyExistsException: Unable to store Job : 'emailTaskGroup.423946369', because one already exists with this identification.
[INFO] at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:136) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:408) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1575) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:545) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.beans.factory.support.AbstractBeanFactory.getObject(AbstractBeanFactory.java:306) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:751) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:861) ~[spring-context-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:541) ~[spring-context-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext(FrameworkServlet.java:668) ~[spring-webmvc-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:634) ~[spring-webmvc-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:682) ~[spring-webmvc-4.3.3.RELEASE.jar:4.3.3.RELEASE]
我认为这个问题与 AutowireCapableBeanFactory
有关,我需要为 Quartz 的调度程序 class 提供应用程序上下文,但我不确定。
我完全被难住了。
@M。 Deinum 是对的。我唯一做的就是从 AppConfig.java 中的 @ComponentScan
中排除控制器包并且它有效。
@ComponentScan(basePackages="de.it2media.dps.statistics",
excludeFilters = { @Filter(Configuration.class), @Filter(Controller.class) })