Quartz任务调度,访问Servlet Context容器中的数据

来源:互联网 发布:1password mac 买断版 编辑:程序博客网 时间:2024/06/05 01:05

Quartz是一种功能丰富的开源作业调度库,它可以在几乎任何Java应用程序集成,从最小的单机应用到最大的电子商务系统。 Quartz可以用来创建简单或复杂的任务,调度执行数以十计,数百计,甚至成千上万的任务。这些拥有某种Task的Job被定义为标准的Java组件,可以执行几乎任何你可以编程实现的事情。 Quartz调度包括了许多企业级功能,如JTA事务和集群支持。

Quartz是可免费使用,根据Apache2.0许可证授权。

官网地址:Quartz


问题背景:

因为做项目需要,对于登录次数超过一定数量的用户,系统要判定为恶意登录,应该对那个账号暂时性的锁定,锁定时间内此账号是不能再次进行登录请求的,这样可以有效减轻恶意登录情况。

之前想到的一个简单办法是session中计数,但是后来立马被自己否定了,原因有几个:

用户换一个浏览器session值就改变了,也就可以再次用被锁定的账号;

即便如此,session失效的时间是系统启动时就配置好了,不可控,比如我想锁定时间设置成3个小时。


解决方案:

采用Servlet Context上下文保存,用一个Map保存锁定用户信息,key为用户名,value为锁定开始时间,然后把Map保存在Servlet上下文中。采用Quartz任务调度定时扫描锁定列表,将达到解锁时间的用户移除。

因为之前未深入了解Quartz,遇到了一个问题,就是不知道怎么让任务中访问到Servlet Context对象,spring和Quartz继承后,在配置文件中定义的调度任务也没法访问到Servlet 上下文(至少是在我写此博文时,我还没想来)。查阅了官网使用文档,介绍的任务都是较为简单不用和Servlet上下文交互。x网上介绍Quartz和Servlet交互的博客不多,于是自己花时间研究源代码。

org.quartz.ee.servlet.QuartzInitializerListener或者org.quartz.ee.servlet.QuartzInitializerServlet类,原话是这么说的——A ServletContextListner that can be used to initialize Quartz.,也就是说采用ServletContextListner或者HttpServlet这两种方式来初始化Quartz调度器。

我在项目中采用的是第一种方式,继承QuartzInitializerListener监听器类,具体如下:

<span style="font-family:Microsoft YaHei;font-size:14px;">public class MyQuartzContextListener extends QuartzInitializerListener {static final Logger logger = LogManager.getLogger(MyQuartzContextListener.class);@Overridepublic void contextInitialized(ServletContextEvent sce) {super.contextInitialized(sce);ServletContext servletContext = sce.getServletContext();//scheduler factoryStdSchedulerFactory sFactory = (StdSchedulerFactory)servletContext.getAttribute(QUARTZ_FACTORY_KEY);Scheduler scheduler = null;try {scheduler = sFactory.getScheduler();//定义一个JobDetail JobDetail jobDetail = new JobDetail("lockedUserJobDetail", "lockedUserGroup", LockedUserMonitor.class);// 将ServletContext对象放到map中,然后从job中取出来,从而取得路径Map<String, Object> map = new HashMap<String, Object>();map.put("servletContext", servletContext);//将servlet上下文添加到JobDataMap中JobDataMap dateMap = new JobDataMap(map);jobDetail.setJobDataMap(dateMap);//触发器Trigger trigger = new CronTrigger("lockedUserCronTrigger", "lockedUserCronTrigger", "0 0/1 * ? * *");//关联任务和触发器scheduler.scheduleJob(jobDetail, trigger);//开启调度scheduler.start();} catch (SchedulerException e) {logger.error("调度器 MyQuartzContextListener", e);e.printStackTrace();} catch (ParseException e) {logger.error("调度器 CronTrigger表达式解析错误", e);e.printStackTrace();}}}</span>

在web.xml配置文件添加

<span style="font-family:Microsoft YaHei;font-size:14px;"><!-- 任务调度监听器 --><listener><listener-class>com.marketing.listener.MyQuartzContextListener</listener-class></listener></span>

然后是任务具体实现类,实现org.quartz.job.Job接口

<span style="font-family:Microsoft YaHei;font-size:14px;">public class LockedUserMonitor implements Job {static final Logger logger = LogManager.getLogger(MyQuartzContextListener.class);static final LongLOCKED_USER_LAST_TIME = 1000 * 60 * 60L;@SuppressWarnings("unchecked")@Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {ServletContext sevletContext = null;//job dataJobDataMap jobDataMap = context.getJobDetail().getJobDataMap();//提取servlet context 对象sevletContext = (ServletContext)jobDataMap.get("servletContext");Object lockedUsers = sevletContext.getAttribute(AgencyConstant.AGENCY_LOGIN_FAILED_USER_MAP);System.out.println("lockedUsers : "+ lockedUsers);if( lockedUsers != null ) {Map<String, Long> lockedUserMap = (HashMap<String,Long>)lockedUsers;checkLockedUsersStatus( lockedUserMap );}}/** * 将超时的用户接触锁定 * @param lockedUserMap */private void checkLockedUsersStatus( Map<String,Long> lockedUserMap ) { Set<Entry<String, Long>> userEntry = lockedUserMap.entrySet(); Iterator<Entry<String, Long>> userIt = userEntry.iterator(); Set<String> removeUsers = new HashSet<String>(); while( userIt.hasNext() ) { Entry<String, Long> item = userIt.next(); String username = item.getKey(); Long lockedTime = item.getValue(); Long difference = lockedTime - System.currentTimeMillis(); if( difference >= LOCKED_USER_LAST_TIME ) { removeUsers.add( username ); logger.info("当地时间:" + new Date(System.currentTimeMillis()) + ",用户 [" + username +"]接触登录锁定"); } } //移除已经锁定超过1小时的用户 Iterator<String> it = removeUsers.iterator(); while( it.hasNext() ) { lockedUserMap.remove( it.next() );  }}}</span>

在Job中,通过JobDetail的JobDataMap获取到之前添加进去的Servlet Context对象引用,这样就可以操作上下文了。


通过配置文件配置的任务调度器,很方便。但是我这个地方需要在任务中访问到Servlet上下文,对整个应用进行控制,想到的解决方案如上所示。


在此记录,仅供学习,大家多多交流,共同进步~

0 0