Accessing Spring beans from Quartz jobs
来源:互联网 发布:谭浩强c语言第四版错误 编辑:程序博客网 时间:2024/05/16 14:58
The Spring Framework integrates with the Quartz scheduler in a way that makes Quartz much easier to use. Although in order to use Spring beans with your Quartz jobs you have to deviate slightly from the usual Spring "dependency injection" way of doing things. According to the Spring API this is necessary because Quartz itself is responsible for the lifecycle of its Jobs.
I was recently refactoring my use of Quartz and Spring in my feed aggregator web application. Rather than explain the internal workings of my application at this time, I will explain some features I discovered with reference to James Goodwill's recent simple example of using Quartz and Spring together. James shows how a "cron style" job can easily be created by configuring Quartz Job, trigger, SchedulerFactoryBean and loading up the application context. In James' example the Spring application context would look something like this:
<beans>
<!-- Define the Job Bean that will be executed. Our bean is named in the jobClass property. -->
<bean name="myJob" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="com.gsoftware.common.util.MyJob"/>
</bean>
<!-- Associate the Job Bean with a Trigger. Triggers define when a job is executed. -->
<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
<!-- see the example of method invoking job above -->
<property name="jobDetail" ref="myJob"/>
<property name="startDelay" value="2000"/>
<property name="repeatInterval" value="10000"/>
</bean>
<!-- A list of Triggers to be scheduled and executed by Quartz -->
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<propertyy name="triggers">
<list>
<ref bean="simpleTrigger"/>
</list>
</property>
</bean>
</beans>
Great stuff! You can pass static data into the Quartz job via the JobDetailBean using the JobDataMap mechanism but AFAICT you should not pass Spring beans through this means.
So what if I want my job to be able to access other Spring resources like data access layers etc.? Let us assume I have a data access object layer configured elsewhere in my Spring config (like the example below) and I want my Quartz job to be able to access it.
<!-- A DAO bean which itself may have dependencies on data sources and other stuff -->
<bean name="daoAccess" class="com.someplace.daoImpl">
<property name="dataSource">
...yadda..yadda..yadda...
</property>
</bean>
I discovered via the Quartz Method Invocation on Beans post on the Spring forum that you can pass a reference to the Spring application context via the SchedulerFactoryBean. Like the example shown below:
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<propertyy name="triggers">
<list>
<ref bean="simpleTrigger"/>
</list>
</property>
<property name="applicationContextSchedulerContextKey">
<value>applicationContext</value>
</property>
</bean>
You can then access the Spring application context inside the Quartz job. This means you can then access any identifiable beans, like the data access object layer bean, from the application context.
public class MyJob implements Job {
private static final String APPLICATION_CONTEXT_KEY = "applicationContext";
public void execute(JobExecutionContext context) throws JobExecutionException {
ApplicationContext appCtx = getApplicationContext(context);
MyDAO dao = (MyDAO) appCtx.getBean("daoAccess");
// place rest of your Job code here
System.out.println("EXECUTING QUARTZ JOB");
}
private ApplicationContext getApplicationContext(JobExecutionContext context )
throws Exception {
ApplicationContext appCtx = null;
appCtx = (ApplicationContext)context.getScheduler().getContext().get(APPLICATION_CONTEXT_KEY);
if (appCtx == null) {
throw new JobExecutionException(
"No application context available in scheduler context for key /"" + APPLICATION_CONTEXT_KEY + "/"");
}
return appCtx;
}
}
I mentioned before that I am using Spring and Quartz inside a web application. In this case I am loading the Spring application context via Spring's ContextLoaderListener in the web.xml. Using this particular method of Spring instantiation means that the Spring application context loaded is actually a WebApplicationContext with access to the ServletContext. In my web application it is very useful to be able to check the status of my job via a variable stored in the ServletContext. Armed with the above technique it is now quite easy to access the WebApplicationContext and therefore the underlying ServletContext.
public class MyJob implements Job {
private static final String APPLICATION_CONTEXT_KEY = "applicationContext";
public void execute(JobExecutionContext context) throws JobExecutionException {
ApplicationContext appCtx = getApplicationContext(context);
WebApplicationContext webCtx = null;
ServletContext srvCtx = null;
if (appCtx instanceof WebApplicationContext){
webCtx = (WebApplicationContext) appCtx;
srvCtx = webCtx.getServletContext();
srvCtx.setAttribute("foo", "bar");
}
// place rest of your Job code here
System.out.println("EXECUTING QUARTZ JOB");
}
private ApplicationContext getApplicationContext(JobExecutionContext context )
throws Exception {
... shown previously ...
}
}
I hope these features are useful to people. I sometimes worry that Spring hides it's beauty under a bushel a little too much but I suppose the problem is Spring provides such an embarrassment of riches it is impossible to highlight everything useful.
Incidentally, in this post I have been experimenting with Google's prettify.js syntax highlighter. Looks good to me, cheers Google!
<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">Then, simply configure the 'jobDetail' within the trigger as usual.
<property name="targetObject" ref="foo"/>
<property name="targetMethod" value="bar"/>
</bean>
<bean id="foo" class="example.Foo">
<!-- dependency inject properties and/or constructor args -->
</bean>
The SchedulerFactoryBean starts the scheduler. The SchedulerFactoryBean is an InitializingBean which means that once the Spring applicationContext is initialized the scheduler will start (this is autowiring!). In a web application you can use the ContextLoaderListener to initialize the applicationContext (I tend to use the terms "Spring container", BeanFactory and applicationContext to refer to the same thing although they are not technically exactly the same!). Alternatively, you can use something like:
Resource resource = new FileSystemResource("applicationContext.xml");
BeanFactory factory = new XmlBeanFactory(resource);
HTH
MarkNormally, the scheduled job will stop when your applicationContext is unloaded.
If you want it to stop programmatically you can also call the stop() method on the SchedulerFactoryBean. e.g. if your SchedulerFactoryBean had an id of schedulerFactoryBean you would do something like this:
SchedulerFactoryBean scheduler = (SchedulerFactoryBean) factory.getBean("schedulerFactoryBean");
scheduler.stop();
// Alternatively
// scheduler.destroy();
Standard disclaimer: I have never done this before!. Quartz job chaining looks very possible judging by the documentation.
You should be able to pass a list of TriggerListeners or JobListeners into the SchedulerFactoryBean and use these to invoke new jobs.
Since this job code will be outside of the Spring Framework control you have take some extra care and produce thread safe code (e.g. avoid SimpleDateFormat and such!).
Good luck with it.
<bean id="myScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<propertyy name="triggers">
<list>
<ref bean="simpleTrigger"/>
</list>
</property>
</bean>
How can we stop the scheduler (myScheduler) and kill all the jobs?
I'm guessing something like this would work:
((Scheduler) factory.getBean("myScheduler")).shutdown();
I do not think it is possible to derive this information from the ServletContext. Server name, server port and context path are properties of the HTTP servlet request.
e.g. I can access my blog using:
http://cse-mjmcl.cse.bris.ac.uk/blog/ and http://localhost/blog/ and the given values for server and port would change! These values are request specific.
For others facing the same error check that you call ctx.refresh() like in:
GenericApplicationContext ctx = new GenericApplicationContext();
XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(ctx);
xmlReader.setValidationMode(XmlBeanDefinitionReader.VALIDATION_NONE);
xmlReader.loadBeanDefinitions(new FileSystemResource(fileName));
ctx.refresh();
These jobs were added using misfire instruction SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW so ones that were misfired while application was down are attempted to be fired as soon as application and quartz are started.
Who is responsible to wire new application context to the Job? Quartz I guess, but I'm not sure if that is happening/implemented. If spring application context or beans can/should not be in job that is persisted (to avoid Quartz hanging application startup), is there a way to access web application context started by the server e.g. using some thread local variable?
- Accessing Spring beans from Quartz jobs
- spring配置出错: BeanFactory not initialized or already closed - call 'refresh' before accessing beans vi
- Accessing Lua from C#
- Spring From the Trenches: Injecting Property Values Into Configuration Beans
- 关于quartz-2.1.6.jar,quartz-oracle-2.1.6.jar,spring-beans-3.0.3整合
- Steve Jobs (from Wikipedia)
- Steve Jobs (From askmen)
- Accessing Windows Shares from OpenSolaris
- spring beans
- spring beans
- Spring Batch--jobs
- Spring boot文件上传blocked a frame with origin "http://xxx" from accessing a cross-origin frame.
- Accessing Microsoft Office Data from .NET Applications
- Accessing DDK Help from Visual Studio
- Accessing Lua global variables from c++
- Accessing a Fedora Logical Volume from Ubuntu
- ACCESSING THE CLOUD FROM COCOA TOUCH
- Accessing Fabric HA Groups from Java
- 比较好的SCHEMA校验吧!收藏
- LDAP服務器配置
- 《C#编程之道》 之 如何获取汉字的拼音首字母
- 中继器 集线器 网桥 交换机 路由器和网关
- BCD码与十进制之间的转换
- Accessing Spring beans from Quartz jobs
- 星际译王本地字典的安装
- infragistics 技术论坛
- 犯了一个低级错误(关于2T硬盘的。)
- JavaScript程序执行顺序问题总结
- 道德经
- ZigBee技术简介 -- ZigBee技术优势
- ZigBee协议栈说明(1)
- 判断一个数字是否是回文