摘要: 觀察者模式,定義對象之間的一種一對多的依賴關(guān)系,當(dāng)對象的狀態(tài)發(fā)生改變時(shí),所有依賴于它的對象都得到通知并且被自動更新。觀察者模式在JDK中有現(xiàn)成的實(shí)現(xiàn),java.util.Obserable。
首先說下需求:通過ftp上傳約定格式的文件到服務(wù)器指定目錄下,應(yīng)用程序能實(shí)時(shí)監(jiān)控該目錄下文件變化,如果上傳的文件格式符合要求,將將按照每一行讀取解析再寫入到數(shù)據(jù)庫,解析完之后再將文件改名。(這個(gè)是原先已經(jīng)實(shí)現(xiàn)了的功能,請看我的一篇文章java利用WatchService實(shí)時(shí)監(jiān)控某個(gè)目錄下的文件變化并按行解析(注:附源代碼))
但項(xiàng)目上線一段時(shí)間后,發(fā)現(xiàn)再利用FileZilla登陸上傳文件,文件不能被解析,而重啟tomcat之后再上傳,又能解析,于是判定是監(jiān)控指定目錄的那個(gè)線程掛掉了,導(dǎo)致上傳后的文件不能被檢測到,故也不能被解析。之后查看日志也最終驗(yàn)證了我推斷。
所以關(guān)鍵的問題就是:如何監(jiān)聽線程,當(dāng)意外退出線程后進(jìn)行自動重啟,這也是本文所要利用觀察者模式實(shí)現(xiàn)的。
下面請看實(shí)現(xiàn)過程(尤其見紅色注解部分):
1、web.xml監(jiān)聽器配置文件監(jiān)控監(jiān)聽器,初始化創(chuàng)建一個(gè)監(jiān)控指定目錄的線程
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:root-context.xml</param-value> </context-param> <filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter> <filter-name>sitemesh</filter-name> <filter-class>com.opensymphony.sitemesh.webapp.SiteMeshFilter</filter-class> </filter> <filter-mapping> <filter-name>sitemesh</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <servlet> <servlet-name>appServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:servlet-context.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>appServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <!-- 配置spring監(jiān)聽器 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 配置監(jiān)控文件變化監(jiān)聽器 --> <listener> <listener-class>com.zealer.ad.listener.ThreadStartUpListenser</listener-class> </listener> <listener> <listener-class>com.zealer.ad.listener.SessionLifecycleListener</listener-class> </listener> <jsp-config> <taglib> <taglib-uri>/tag</taglib-uri> <taglib-location>/WEB-INF/tag/tag.tld</taglib-location> </taglib> </jsp-config> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <session-config> <session-timeout>45</session-timeout> </session-config> </web-app>
2、編寫一個(gè)觀察者實(shí)現(xiàn)類,用于監(jiān)聽“監(jiān)控指定目錄線程”,當(dāng)“監(jiān)控指定目錄線程”掛掉后,自動重啟該線程
ObserverListener Log log = LogFactory.getLog(ObserverListener. "WatchFilePathTask掛掉"= "WatchFilePathTask重啟"
3、編寫一個(gè)ThreadStartUpListenser類,實(shí)現(xiàn)ServletContextListener,tomcat啟動時(shí)創(chuàng)建后臺線程
ThreadStartUpListenser WatchFilePathTask r = Log log = LogFactory.getLog(ThreadStartUpListenser. = log.info("ImportUserFromFileTask is started!"
4、創(chuàng)建指定目錄文件變化監(jiān)控類WatchFilePathTask
WatchFilePathTask Observable Log log = LogFactory.getLog(WatchFilePathTask. String filePath = ConfigUtils.getInstance().getValue("userfile_path" ( watchService ="獲取監(jiān)控服務(wù)"+="@@@:Path:"+ String todayFormat = DateTime.now().toString("yyyyMMdd"= = existFiles.listFiles( ((todayFormat+".txt" ( != ImportUserFromFileTask task = (ImportUserFromFileTask) SpringUtils.getApplicationContext().getBean("importUserFromFileTask" WatchKey key = (= (WatchEvent<?> String fileName = ((todayFormat+".txt"= path.toFile().getAbsolutePath()+File.separator+"import filePath:"+ ImportUserFromFileTask task = (ImportUserFromFileTask) SpringUtils.getApplicationContext().getBean("importUserFromFileTask");"啟動線程導(dǎo)入用戶數(shù)據(jù)"+"已經(jīng)到這里來了"
5、創(chuàng)建解析用戶文件及導(dǎo)入數(shù)據(jù)庫線程,由WatchFilePathTask啟動
package com.zealer.ad.task;import com.zealer.ad.entity.AutoPutUser;import com.zealer.ad.entity.Bmsuser;import com.zealer.ad.service.AutoPutUserService;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.joda.time.DateTime;import java.io.BufferedReader;import java.io.File;import java.io.FileInputStream;import java.io.InputStreamReader;import java.util.Date;import javax.annotation.Resource;/** * 解析用戶文件及入庫線程,由WatchFilePathTask啟動 * @author cancer * */public class ImportUserFromFileTask extends Thread { private Log log = LogFactory.getLog(ImportUserFromFileTask.class); private String fileName; @Resource(name = "autoPutUserService") private AutoPutUserService autoPutUserService; @Override public void run() { File file = new File(fileName); if (file.exists() && file.isFile()) { log.debug(":@@@準(zhǔn)備開始休眠10秒鐘:" + file); //休眠十分鐘,防止文件過大還沒完全拷貝到指定目錄下,這里的線程就開始讀取文件 try { sleep(10000); } catch (InterruptedException e1) { e1.printStac
http://www.cnblogs.com/zishengY/p/7056948.html