2010-07-09 90 views
19

我想知道是否有人可以提出建议:我有一个Quartz运行的计划作业每小时更新一个对象的数组列表的方案。如何在Java servlet上下文中获取和设置全局对象

但是我需要这个对象的数组列表对于由Tomcat创建的所有会话都是可见的。所以我在想,我在Quartz作业每小时的某个地方写这个对象,以便每个会话都可以访问它。

谁能说如何实现最佳效果?我想知道从Quartz作业写入servlet上下文的对象?另一种方法是让每个会话从数据库表中填充对象的数组列表。

谢谢

摩根先生。

+0

在这篇SOF文章中还有一篇有关这个挑战的讨论:http://stackoverflow.com/questions/10276041/retrieve-servletcontext-reference-in-quartz-scheduler – Steve 2012-05-21 16:15:33

+0

全局应用程序上下文就像Spring ? – PSyLoCKe 2017-04-10 21:01:43

回答

12

是的,我会将该列表作为应用程序范围的属性存储在ServletContext中。从数据库中提取数据可能效率较低,因为您只是每小时更新列表。创建ServletContextListener可能是必要的,以便为Quartz任务提供对ServletContext对象的引用。 ServletContext只能从JavaEE相关的类(如Servlets和Listener)中检索。

编辑: 在ServletContextListener中,当您创建作业时,可以通过将作业添加到JobDataMap中来将作业传递给作业。

public class MyServletContextListener implements ServletContextListener{ 
    public void contextInitialized(ServletContextEvent event){ 
    ArrayList list = new ArrayList(); 

    //add to ServletContext 
    event.getServletContext().setAttribute("list", list); 

    JobDataMap map = new JobDataMap(); 
    map.put("list", list); 
    JobDetail job = new JobDetail(..., MyJob.class); 
    job.setJobDataMap(map); 
    //execute job 
    } 

    public void contextDestroyed(ServletContextEvent event){} 
} 

//Quartz job 
public class MyJob implements Job{ 
    public void execute(JobExecutionContext context){ 
    ArrayList list = (ArrayList)context.getMergedJobDataMap().get("list"); 
    //... 
    } 
} 
+0

所以你说的是我有一个监听器,它有一个方法来填充对象;此侦听器在上下文初始化并填充对象时运行。当Quartz作业每小时运行一次时,可以通过侦听器访问该对象? – 2010-07-09 20:00:29

+1

查看来自另一个答案的评论,它看起来像你已经有一个ServletContextListener启动Quartz任务?你可以做的是在侦听器中创建列表,然后将该列表作为属性添加到ServletContext中,然后将该列表传递给任务。然后,您可以通过调用'ServletContext#getAttribute()'来访问Web应用程序中任何位置的列表。例如,在一个servlet中,你可以调用'(ArrayList)getServletContext()。getAttribute(“list”)'。确保你正确地同步你的列表,这样你就不会同时读/写它。 – Michael 2010-07-09 21:01:59

+0

这听起来不错,但有一个例外。我不确定是否可以通过Quartz的ScheduleController提交作业,然后将相同的列表传递给任务。 – 2010-07-09 21:18:04

0

那么,如果你使用静态字段,它们将对同一个类加载器加载的所有类都可见。我认为至少一个应用程序的servlet最终应该排位赛。但是,这确实很脏。

定义并保证是(更)全局的对象是ServletContext。这是构成一个应用程序的一部分的所有servlet之间共享的,即从相同的web.xml加载。有调用ServletContext的putget,它们允许你将它视为一个Map。

除此之外,您需要找到一个Tomcat服务器内所有Web应用程序通用的类。 Tomcat在装载机方面做了很多工作,我认为不同的Web应用程序将有不同的装载机。你可以通过编写你自己的课程并将该课程放在Tomcat的commonshared目录中来解决这个问题。如果我正确理解this description,那么这些类将一次提供给所有Web应用程序。最后,除了单个Tomcat服务器的限制之外,您还需要一些基于TCP/IP的机制来在JVM之间进行通信。但正如我理解你的问题,这不应该是必要的。

+0

但是你可以访问一个servlet之外的ServletContext吗?我提到的石英工作是暗示他们的接口。尽管我准备使用静态类。 – 2010-07-09 19:53:29

+0

嗯。这个Quartz作业是否作为Tomcat中的servlet或过滤器运行?如果是这样,是的。如果它在外部JVM中被启动,它将无法查看Tomcat的大脑。看看[这个Quartz文档](http://www.quartz-scheduler.org/docs/cookbook/ServletInitScheduler.html),它看起来像Quartz将会在你的应用程序中,并且一切都会好的。 – 2010-07-09 19:59:00

+0

至少Quartz可以访问您的ServletContext(在所描述的两种场景中)。我希望Quartz能够让API用户也能够访问该上下文。 – 2010-07-09 20:02:01

1

你可以尝试一些缓存解决方案,像EhCache你存储的值,并更新他们每隔一小时。它将处理并发问题。缓存对象本身可以存储在从Quartz作业写入ServletContext的好方法是将侦听器注册到您的作业中,该作业会通知有关更改后的值。因此,例如:

public class JobListener { 
    public void updateValue(Object newValue); 
} 

public class ServletContextCacheJobListener implements JobListener { 
    private ServletContext ctx; 
    public ServletContextJobListener(ServletContext ctx) { 
     this.ctx = ctx; 
    } 

    public void updateValue(Object newValue) { 
      Cache cache = (Cache) ctx.getAttribute("cache"); 
      cache.update("yourKey", newValue); 
    } 
} 

你的工作将有一个List<JobListener>,当你安排工作,你实例化的具体监听器,并将其添加到作业。

+0

不知道我明白你来自哪里。你能澄清吗?你是说我的Quartz作业应该有一个JobListener或ServletContextCacheJobListener的列表?如果后者,大概我会一次列出一个元素列表? – 2010-07-09 22:14:08

+0

阅读有关观察者(听众)模式。而'ServletContextCacheJobListener'是一个'JobListener'因为它实现了它。所以没有区别 – Bozho 2010-07-10 05:08:34