前段时间在做一个维护升级一个基于SSI的网站项目,叫做《网络课堂系统》,是广州市一所专科学院在使用的。这个项目是我所在的学院开发工作室两三年前接回来的项目,所以也经过了两三个年级的师兄的手最终传到我们手上。可想而知,这个项目的原代码已经非常凌乱了。原来打算直接重构整个项目的,但由于这是个正在使用中的项目,而我们又人手有限,那所学院的负责人Y要求我们先进行维护升级以满足使用需要后再考虑重构的事情。
这个项目的数据库有一张学期表,用来记录学期信息,并用一个字段指定哪个是当前学期。但是这张学期表的数据更新居然是靠手动修改数据库实现的,对于用户来说,这非常的不人性化,因此我得到了一个任务:实现一个操作简单的功能以代替这种非常不人性化的操作。
由于开发经验还不够等原因,我一开始是从管理员的方向去想。首先,我想在管理员页面添加一个按钮,当管理员点击这个按钮的时候进行检查更新数据库学期表。但是Y和我说管理员可能忘记点击,所以这个想法作罢。接着,我想写一个filter用来过滤登录信息,如果发现是管理员登录的话就检查更新数据库学期表。可是,这个想法还是没有实现,原因有二:一是Y说管理员不一定会在开学前登录一次,这可能会导致学期表没及时更新;二是我那时我发现无法用Spring对filter进行注入,实现起来要多走很多路。最后,我打算写一个Listener实现自动周期更新学期表。
后来经过测试,发现Spring也无法通过一般途径对Listener进行注入,查了相关资料发现这是因为filter、listener、servlet不由Spring管理。虽然麻烦,但我也决定做下去了。
当然,在实现的过程中,我也有不断的google查资料,并不是完全靠自己想出来的。
先来简单的介绍一下listener:listener是servlet监听器,它可以监听客户端的请求、服务端的操作等。要创建一个listener就必须实现相关的接口,这类接口有四个:ServletContextAttributeListener、ServletContextListener、HttpSessionAttributeListener、HttpSessionListener。
ServletContestener监听对ServletContext属性的操作,比如增删查改等。(ServletContext是servlet上下文的意思,整个web项目共享同一个ServletContext)
ServletContextListener用来监听ServletContext,而并非监听对ServletContext属性的操作。它有两个方法:当创建ServletContext时,调用contextInitialized (ServletContextEvent sce)方法;当销毁ServletContext时,调用contextDestroyed(ServletContextEvent sce)方法。
HttpSessionAttributeListener用来监听HttpSession中的属性的操作。它有三个方法:当在Session增加一个属性时,调用attributeAdded (HttpSessionBindingEvent se) 方法;当在Session删除一个属性时,调用attributeRemove(HttpSessionBindingEvent se)方法;当在Session属性被重新设置时,调用attributeReplaced(HttpSessionBindingEvent se) 方法。
HttpSessionListener 监听HttpSession的操作。有两个方法:当创建一个Session时,调用session Created(HttpSessionEvent se)方法;当销毁一个Session时,调用sessionDestroyed (HttpSessionEvent se)方法。
要实现我想实现的那个功能的话,就要写一个实现了ServletContextListener的Listener。
package com.course.online.listener;
import java.util.Timer;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import com.course.online.util.UpdateTerm;
public class TermListener implements ServletContextListener{
private Timer timer = null;
public void contextDestroyed(ServletContextEvent scEvent) {
// TODO Auto-generated method stub
timer.cancel();
System.out.println("停止学期更新!");
}
public void contextInitialized(ServletContextEvent scEvent) {
// TODO Auto-generated method stub
timer = new Timer(true);
/*24*60*60*1000*/
timer.schedule(new UpdateTerm(), 5000, 24*60*60*1000);
System.out.println("开始监听学期更新!");
}
}
先看一上面这篇Listener的代码,里面用到了一个Timer对象。这个Timer对象的作用是实现每相隔一段时间就执行某个操作一次。
当开始运行项目,ServletContext被创建时服务器调用contextInitialized(ServletContextEvent scEvent) 方法,生成一个Timer对象,并调用该对象的schedule(new UpdateTerm(), 5000, 24*60*60*1000)方法,其中参数“new UpdateTerm()”代表要定时执行的操作,“5000”代表第一次执行的时间为第5秒,“24*60*60*1000”代表执行周期时间为一天。
当项目结束运行,ServletContext被销毁的时候,服务器调用contextDestroyed(ServletContextEvent scEvent) 方法,该方法调用Timer对象的cancl()方法销毁Timer对象。
接着看一下UpdateTerm.java的代码,UpdateTerm.java是用来执行数据库更新的关键操作的:
package com.course.online.util;
import java.io.IOException;
import java.io.Reader;
import java.sql.SQLException;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimerTask;
import com.course.online.model.Term;
import com.ibatis.sqlmap.client.SqlMapClient;
public class UpdateTerm extends TimerTask{
private SqlMapClient sqlMapClient = null;
@Override
public void run(){
// TODO Auto-generated method stub
……
try {
Reader reader =
com.ibatis.common.resources.Resources.getResourceAsReader(
"com/course/online/listener/mapping/SqlMapConfig.xml");
sqlMapClient =
com.ibatis.sqlmap.client.SqlMapClientBuilder.buildSqlMapClient(reader);
reader.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
…… }
}
要把UpdateTerm对象传给Timer对象的schedule()方法的话,UpdateTerm对象必须继承TimerTask并重写其run()方法。因为schedule()方法执行后会自动调用run()。我把所有的相关操作都写在run()方法里面了,因为不用考虑代码重用的问题,那些代码完全只是为了这个listener而写的。其中大部分已经省略掉只留下其中一小段。
前面有说到因为filter、listener、servlet都不由Spring管理,所以Spring无法通过一般途径对listener进行注入。要解决这个问题,我到目前为止暂时懂得两种方法,暂时称为方法A和方法B。其中A被我用在了项目升级上面,但是这种方法相对笨和麻烦很多。
方法A的做法是:完全跳出Spring框架,单独为这个Listener写一个ibatis的配置文件,然后在UpdateTerm类里面的run()方法里以Reader reader =
com.ibatis.common.resources.Resources.getResourceAsReader(
"com/course/online/listener/mapping/SqlMapConfig.xml");
sqlMapClient =
com.ibatis.sqlmap.client.SqlMapClientBuilder.buildSqlMapClient(reader);
的方式去创建一个SqlMapClient对象来进行数据库操作。
方法B的做法和方法A类似,但是简便很多:可以在run()方法里面写入:
ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(servletContext);
TermManager tm = (TermsManager)ctx.getBean( "TermManager");
来实现TermManager的注入(TermManager包含各种对学期表的操作,这样就省去了原来很多写在UpdateTerm里面的方法)。虽然这只是对UpdateTerm进行注入而非对Listener,但这个方法对listener也同样适用的。其中参数servletContext这个参数可以由listener里面的ServletContextEvent对象的getServletContext()方法得到,然后当做参数传给UpdateTerm的构造方法就可以了。
方法B我还没测试过,如果细节上有错,还请多多包含。深知自己写的文章水平还很一般,欢迎高手们指出错误。
分享到:
相关推荐
这个定时器的实现通常涉及使用Java的定时器类`java.util.Timer`和`java.util.TimerTask`。 首先,我们关注`MyTimerTask.java`这个文件,它应该包含了定时任务的逻辑。`MyTimerTask`通常会继承自`java.util....
为了实现定时检测,项目可能使用了`System.Threading.Timer`或`System.Timers.Timer`类来设置周期性任务。定时器会按照预定间隔触发事件,执行链路检查逻辑。 5. **配置管理**: 程序需要配置不同用户的检测参数...
可以使用`java.util.Timer`或`java.util.concurrent.ScheduledExecutorService`来创建定时任务,或者使用Spring框架的`@Scheduled`注解。 总的来说,Web监听器是Java Web开发中不可或缺的一部分,它们提供了对Web...
- **定时器监听器**:为了实现在Web应用启动时自动启动定时器,可以使用Servlet监听器。 ```java public class TimerListener implements ServletContextListener { private Timer timer; private SampleTask ...
在Java编程领域,"java service"通常指的是使用Java来创建后台运行的服务程序,这些程序可以在操作系统级别作为服务启动,无需用户交互即可持续运行。本文将深入探讨如何编写Java服务程序,以及与之相关的类和概念。...
通过以上步骤,我们可以轻松地在Java Web环境中实现定时任务,确保任务按预期周期性地执行。然而,需要注意的是,这种方式的定时任务在Web容器停止时也会停止,如果需要独立于Web容器运行的定时任务,可以考虑使用 ...
- Java中可以使用`java.util.Timer`和`java.util.TimerTask`来实现定时任务,或者使用Java 5引入的`java.util.concurrent.ScheduledExecutorService`,后者提供了更强大的定时和调度功能。 6. **Java容器**: - ...
5. **监听器(Listener)**:监听任务或触发器的生命周期事件,并执行相应的动作。 6. **插件(Plugin)**:提供扩展Quartz功能的方法,如日志记录、任务定义加载等。 #### 四、快速入门 为了快速上手Quartz,开发者...
一个测试计划通常包括一个或多个线程组(ThreadGroups)、逻辑控制器(LogicController)、采样生成控制器(SampleGeneratingControllers)、监听器(Listener)、定时器(Timer)、断言(Assertions)和配置元素...
7. **计时器(Timer)**:找错字游戏需要计时功能,这通常涉及使用CountDownTimer或Handler类来实现定时更新。 8. **错误处理和异常捕获**:良好的错误处理机制是保证游戏稳定性的关键。源码可能包含对异常的捕获和...
这涉及到计时器(Timer)的使用、状态机的设计以及数据结构(如数组和字典)来存储游戏状态。 6. **数据库交互**:尽管是简单的农场游戏,但为了保存玩家进度,可能会有简单的数据库操作,如XML或SharedObject,...
11.5.2. 使用SimpleJdbcInsert来获取自动生成的主键 11.5.3. 指定SimpleJdbcInsert所使用的字段 11.5.4. 使用SqlParameterSource提供参数值 11.5.5. 使用SimpleJdbcCall调用存储过程 11.5.6. 声明SimpleJdbcCall...
11.5.2. 使用SimpleJdbcInsert来获取自动生成的主键 11.5.3. 指定SimpleJdbcInsert所使用的字段 11.5.4. 使用SqlParameterSource提供参数值 11.5.5. 使用SimpleJdbcCall调用存储过程 11.5.6. 声明SimpleJdbcCall...
- **Timer线程**:周期性地检查各个Group的状态,确保它们正常工作。 ##### 3.3 关键参数配置 MySQL线程池涉及的关键参数包括: - `thread_handling`:指定线程池模型类型。 - `thread_pool_size`:定义线程池中的...
在Java中,可以使用定时器(Timer)或者游戏循环(Game Loop)来实现动画效果。《忍者游戏》中的忍者动作、攻击特效等都是通过连续改变对象的位置和状态来实现的。同时,通过合理控制帧率,确保游戏运行流畅且不消耗...