最近做了一個java的項目,部門領導給了一套代碼讓我盡快掌握,說心里話本人真心不喜歡java的這種項目方式,各種配置各種xml文件簡直頭都大了,下面就將我遇到的其中一個我認為是坑的地方整理出來,希望能幫助到后面像我一樣的兄弟
功能需求說明:
使用Jsoup編寫了一套爬蟲程序,用來自動錄入網站的數據,之前測試都是寫在頁面中,手動的訪問頁面觸發爬蟲(后續一些問題就是因為這樣產生的),還有就是項目需要實現自動觸發也就是定時器
開發過程:
既然確定是定時器,操刀子就上直接百度java定時器,發現很多quartz、spring、spring-task、Timer ,發現Timer 這東西應該是最簡單粗暴的,於是在網上找到下面代碼
package tasklListener; import java.util.TimerTask; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import paypay.web.service.timers.ChannelDataSync; /** * 上下文監聽器,需要在web.xml中進行配置,請參見<listener></listener>結點 * * @author Administrator * */ public class MyContextListener implements ServletContextListener { private java.util.Timer timer = null; private ServletContext context = null; public void contextInitialized(ServletContextEvent event) { this.context = event.getServletContext(); timer = new java.util.Timer(true); event.getServletContext().log("定時器已啟動"); // 設定MyTask中任務每5秒執行一次,0表示馬上執行,可以改為2000,則表示2秒以后開始執行 // 以后都按后面指定的每5秒執行一次 timer.schedule(new MyTask(this.context), 60000, 60 * 60 * 2000); event.getServletContext().log("已經添加任務調度表"); } public void contextDestroyed(ServletContextEvent event) { timer.cancel(); this.context.log("定時器銷毀"); this.context = null; } private static class MyTask extends TimerTask { private static boolean isRunning = false; private ServletContext context = null; public MyTask(ServletContext context) { this.context = context; } // 下面的方法會按之前設定的每5秒執行一次,所以,此處不需要循環 public void run() { if (!isRunning) { isRunning = true; context.log("開始執行指定任務"); // TODO 添加自定義的詳細任務,以下只是示例 // 這里完成從數據庫取數據,然后存放到MySQL數據庫中 ChannelDataSync Dateuser=new ChannelDataSync(); java.text.DateFormat df = new java.text.SimpleDateFormat("yyyyMMdd") ; java.util.Date date = new java.util.Date() ; String datestr = df.format(new java.util.Date()); Dateuser.selectTSuserdata(datestr); isRunning = false; context.log("指定任務執行結束"); } else { context.log("上一次任務執行還未結束"); } } } }
建好了類,不對啊怎么觸發啊仔細看了一下注釋
/** * 上下文監聽器,需要在web.xml中進行配置,請參見<listener></listener>結點 * * @author Administrator * */
於是到web.xml中找到
<listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener>
那我就直接復制listener-class節點,然后寫入自己的包名+類名
<listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> <listener-class> com.shopping.zy.MyContextListener </listener-class> </listener>
編譯后報錯(大神別鄙視我,我是真的java新手,對於配置什么xm文件完全不會啊 )
看了一下報錯信息:cvc-complex-type.2.4.d: Invalid content was found starting with element 'listener-class'. No child element is expected at this point.
本人的英文很爛,但也大概看出來說是子元素有問題,既然是子元素不對,那就重新寫一個對象不就行了 ,於是配置文件改成:
<listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <listener> <listener-class> com.shopping.zy.MyContextListener </listener-class> </listener>
測試通過!
再接下來就是在MyContextListener類中做操作了 首先是在定時器的初始化方法contextInitialized中:
timer = new java.util.Timer(true); event.getServletContext().log("定時器已啟動"); //1000*60*60 1秒 *60=1分鍾 *2 等於開啟后60分鍾執行采集任務 //(1000*60*60)*12 (1000*60*60)=1個小時 *12表示沒12小時執行一次 int tempint=(1000*60*60)*(Integer.parseInt(sysconfig.getAotoupdate_step())); timer.schedule(new MyTask(this.context), 1000*60*60,(1000*60*60)*(Integer.parseInt(sysconfig.getAotoupdate_step())));
具體的可以參看代碼,schedule函數的參數是分別是,要調用的函數,延遲執行時間,執行間隔時間 我提供的代碼中最后的的執行間隔時間是動態在數據庫中取出的所以是動態的,你也可以直接寫成固定的
到這了你們肯定覺得沒什么坑啊,那么我就先來介紹一下第一個坑:
首先在MyContextListener是不可以直接使用srping的對象的,即使你聲明了對應的對象,但由於MyContextListener的啟動線程和spring不一致(我自己理解的,如果誰知道可以給我解釋一下),在MyContextListener類中使用spring對象是不會有實例化相關的注入對象的,這就坑爹了,總不能自己把所有的數據庫操作類重新做一遍吧,於是乎又是神器出山,繼續百度,找了半天發現一個帖子中的解決辦法:
ISysConfigService sysConfigService; WebApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(event.getServletContext()); sysConfigService = (ISysConfigService) context.getBean("sysConfigService"); SysConfig sysconfig= sysConfigService.getSysConfig();
這里里面有一點是需要注意的:context.getBean("sysConfigService")中的sysConfigService為實體類是我項目中的操作類,但是通過context.getBean()方法返回的對象類型必須是其對應的ISysConfigService接口,而不是其對象類型,
雖然不知道是為什么但我覺得這肯定跟spring的設計模式有關系,這是不是就是工廠類的設計模式啊,在此處坑了我很久也是今天我想寫這篇博文的一個誘因吧。這樣得到的對象是包括其本身的注入對象的。
接下來就是要實現調用我寫好的爬蟲函數了 ,這簡單啊 ,直接實例化類對象 然后調用方法就行了 ,由於類對象中沒有注入對象不需要使用上面的方法,說干就干
ManageHT mht=new ManageHT(); mht.zy_sr_cj(context);
mht.cj_start(context); mht.zy_collect_news();
三個方法分別是我對應的三個采集的函數,之前接收的是HttpServletRequest ,但是在MyContextListener中我不知道如何獲取,於是我就講context傳入了,如果你知道怎么獲取可以給我留言,這不是重點因為后續我使用了其他的辦法,
使用上面的方式訪問對應的函數會遇到和之前一樣的問題,對應的注入對象無法加載,弄了半天最后決定堅持我的簡單粗暴的原則,既然直接網頁訪問可以, MyContextListener不可以,那我就模擬一個網頁請求不就行了 還研究什么注入不注入對象的
說干就干:
public static String doGet(String url, String queryString, String charset, boolean pretty) { StringBuffer response = new StringBuffer(); HttpClient client = new HttpClient(); HttpMethod method = new GetMethod(url); try { if (StringUtils.isNotBlank(queryString)) //對get請求參數做了http請求默認編碼,好像沒有任何問題,漢字編碼后,就成為%式樣的字符串 method.setQueryString(URIUtil.encodeQuery(queryString)); client.executeMethod(method); if (method.getStatusCode() == HttpStatus.SC_OK) { BufferedReader reader = new BufferedReader(new InputStreamReader(method.getResponseBodyAsStream(), charset)); String line; while ((line = reader.readLine()) != null) { if (pretty) response.append(line).append(System.getProperty("line.separator")); else response.append(line); } reader.close(); } } catch (URIException e) { } catch (IOException e) { } finally { method.releaseConnection(); } return response.toString(); }
模擬get請求的操作,對於我的需求來說get/post沒什么區別,直接百度一段代碼,
最后的調用部分代碼變成:
String sr = doGet("http://localhost:8080/shopping/admin/testsr.htm", null, "UTF-8", true); Thread thread = Thread.currentThread(); thread.sleep(1000*60*10);//暫停10分鍾后程序繼續執行 String mr = doGet("http://localhost:8080/shopping/admin/zy_collect_mr.htm", null, "UTF-8", true); Thread threadsecond = Thread.currentThread(); threadsecond.sleep(1000*60*10);//暫停10分鍾后程序繼續執行 String news = doGet("http://localhost:8080/shopping/admin/zy_collect_news.htm", null, "UTF-8", true);
運行項目:執行定時器-執行任務-調用我的爬蟲頁面,看着數據一條一條進入到數據庫中,今天的努力沒白費,作為一個新手java程序員我想兩說,雖然我對java了解不深,但是java的一些機制確實導致了項目開發進度的緩慢(就是大牛你也得承認,java開發效率就是比.net、python、php)等慢多了 ,這只是我接觸java項目以后遇到的比較簡單的,之前焦頭爛額的也沒記錄下來,希望以后能把我遇到的問題總結出來,如果再有.net轉java的兄弟,希望能讓你少走一些彎路。