使用Spring写应用程序的几点体会

来源:互联网 发布:python 拟合分布 编辑:程序博客网 时间:2024/05/21 09:12

     近几日使用Spring/hibernate更新以前一个老的应用程序,这个应用程序的用途是通过ftp来发送每天build出来的包。传送的相关数据,例如速度,成功与否等等由数据库管理,便于查询,监控和统计网络状况,服务器状况等等。应用程序分为两部分,一部分专门通过ftp发送包,另一部分用来查询相关数据,是个web站点。

     web站点已经更新完毕,用的是典型的SSH方式实现的。此次重点更新ftp发送工具这一部分,主要更新数据操作部分。(ftp发送部分是基于开源的jftp实现的,经过5年的运行,比较稳定,此次不在调整之列)

     从以前的使用经验看,发送工具经常会在收集数据时挂住不动,原因也不是很明确。所以首先把这部分功能移出工具,放到web站点上,通过spring的quartz来运行,使发送工具的功能更加单一,出错机会更加少。

     其次,将原有的jdbc操作方式升级为spring+hibernate,按照web站点的SSH实现经验,很快就实现升级。另外使用到几个特殊一点的 spring 类,concurrent.ThreadPoolTaskExecutor,quartz.MethodInvokingJobDetailFactoryBean,quartz.SimpleTriggerBean。

     其中ThreadPoolTaskExecutor是一个线程池,可以配置。我以前也试过apache的common-pool,费了老大力气,但效果不是很好。Spring的线程池通过MyEclipse的Visual VM观察,其线程启动运行具体任务之后,线程不退出而是处于等待状态,如果有新的调用,将立刻启动执行新的任务,这样提高了程序运行速度。发送工具需要同时发送文件到不同的几个ftp server,这个线程池刚好合适。

     使用quartz来执行定时的查询,看是否有新的文件需要发送,用以替代原来的while(true)守护线程,这样配置更加灵活,执行时间可配置。

     接下来开始测试工具是否能正常运行以满足需求了,遇到如下几个问题:

   1.      同时执行三个任务,发送同一个包到不同的ftp server,发送工具更新数据库时,三个ftp server的数据一样,这个很好找原因,按照DI注入的bean是个单例,启动的三条线程传递的数据,bean只记住了一个。但仅仅加上 scope=prototype并不起作用,必须要重新将applicationContext重新装载一遍,然后getBean()的时候,才能拿到新的bean。这里需要注意,重新装载applicationContext只要一次,不能每条线程都装载一次,这样很快就会导致内存不足。解决方法是使用单例类作为获得Context的工具类。
   2. public class SpringBeanUtil
          static class SpringBeanHolder{
              static SpringBeanUtil sbu=new SpringBeanUtil();
          }
          
          public static SpringBeanUtil getInstance(){
              return SpringBeanHolder.sbu;
          }
          
          public static ApplicationContext context;


          public SpringBeanUtil(){
              try {        
                  context = new FileSystemXmlApplicationContext(
                          "classpath:multiThreadAppContext.xml");        
              } catch (Throwable t) {
                  t.printStackTrace();
              }        
          }
      }
   3. 另外一个遇到的问题是,当线程启动开始传送文件后,quartz的cronjob总是不按时启动,而是短短的几秒中之内疯狂反复执行。这让人感觉很莫名其妙。经过多种方式的验证,最后终于发现,原来是applicationContext文件的问题,这个文件中包含了quartz的 cronjob,在启动线程重新装载的时候,这个cronjob一样被初始化,也按照它启动之后的时间来运行,外加原先获得bean的工具类不是单例类,就导致多个cronjob同时疯狂运行。解决方法,将applicationContext文件分成2部分,给线程用的文件中不包含cronjob的配置,同时精简重复的部分,节省一些内存。
   4. Transaction的问题,是在解决上一个问题时顺带走了一遍。使用xml文件配置的方式很不方便,还是用annotation的方式比较精确省心。这里遇到的问题是,先delete后save,delete不能正确执行,导致save总是提示重复数据不能插入。经过查询,原来delete是在session清空时才执行,这样在一个transaction提交时,delete才执行,而且是在save之后。解决方法,是在delete方法中调用flush方法,强制清空session。

     解决以上问题后,发送工具显得很是不错,数据库连接及时释放,内存占用稳定。原本打算放弃此次更新的,结果经过以上不懈地调试终达目标。