OOPAA

          Focusing on OO, Patterns, Architecture, and Agile
          posts - 29, comments - 75, trackbacks - 0, articles - 0
            BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

          play! framework hot swap 淺析

          Posted on 2008-12-30 17:06 mingj 閱讀(4505) 評論(12)  編輯  收藏 所屬分類: framework 框架
          play! 最大的賣點(diǎn)就在于 hot swap,正如它自己宣稱的:
          reach your maximum productivity。play! 允許開發(fā)人員修改java文件,保存,然后刷新瀏覽器,立馬可以看到效果。不需要編譯,也不需要重啟服務(wù)器。
          Java 要想實(shí)現(xiàn)動態(tài)更新 class 文件,不外乎兩種手段:替換 classloader、替換 JVM。因?yàn)樘鎿Q JVM 引起的開銷更大,需要維護(hù) JVM 的堆、棧等運(yùn)行信息,所以 hot swap 通常是選擇替換 classloader。比如 grails 里面就是選擇替換 classloader,它會自己維護(hù)一個(gè)線程,定期輪詢源文件是否發(fā)生修改,以替換原來的 classloader。那么 play! 宣稱的 hot swap 又是怎么實(shí)現(xiàn)的呢?
          讓我們來看看play! 的內(nèi)部流程:
          1. play! 使用了 Apache Mina 作為底層的 http server,然后使用了自己關(guān)于 Mina IoHandler 接口的實(shí)現(xiàn)—— HttpHandler
          2. 當(dāng)瀏覽器發(fā)起一個(gè) request:
          2.1 Mina Server 生成一個(gè) Mina Request,轉(zhuǎn)發(fā)給 HttpHandler 的 messageReceived 方法
          2.2 play! 解析 Mina Request 和 Mina Session,包裝成自己的 Request 對象

          Request request = parseRequest(minaRequest, session);

          2.3 play! 檢測 Route 文件修改情況,根據(jù) Route 配置信息將 Route/Action 的信息賦給 Request 對象

          Router.detectChanges();
          Router.route(request);

          2.4 play! 根據(jù)當(dāng)前配置的開發(fā)模式來采用不同的策略調(diào)用 Action 來理 Request

          if (Play.mode == Play.Mode.DEV) {
          Invoker.invokeInThread(
          new MinaInvocation(session, minaRequest, minaResponse, request, response));
          }
          else {
          Invoker.invoke(
          new MinaInvocation(session, minaRequest, minaResponse, request, response));
          }

          2.5 如果 play! 當(dāng)前是 DEV 模式,invokeInThread方法會讓 invocation 對象代理 run() 方法

          public void run() {
          try {
          before();
          execute();
          after();
          }
          catch (Throwable e) {
          onException(e);
          }
          finally {
          _finally();
          }
          }

          咱們來看看 before() 方法:
          public static void before() {
          Thread.currentThread().setContextClassLoader(Play.classloader);
          if(!Play.id.equals("test")) {
          Play.detectChanges();
          if (!Play.started) {
          Play.start();
          }
          }
          //
          }

          在 Play 類的 detectChanges() 方法里面,有這么一句:
          classloader.detectChanges();

          哈哈,play! 修改源文件后,刷新瀏覽器即見效的奧秘就在這里了。再進(jìn)去看看 play! 自定義 classloader 的 detectChanges() 方法:

          public void detectChanges() {
          // Now check for file modification
          List<ApplicationClass> modifieds = new ArrayList<ApplicationClass>();
          for (ApplicationClass applicationClass : Play.classes.all()) {
          if (applicationClass.timestamp < applicationClass.javaFile.lastModified()) {
          applicationClass.refresh();
          modifieds.add(applicationClass);
          }
          }
          List
          <ClassDefinition> newDefinitions = new ArrayList<ClassDefinition>();
          Map
          <Class, Integer> annotationsHashes = new HashMap<Class, Integer>();
          for (ApplicationClass applicationClass : modifieds) {
          annotationsHashes.put(applicationClass.javaClass, computeAnnotationsHash(applicationClass.javaClass));
          if (applicationClass.compile() == null) {
          Play.classes.classes.remove(applicationClass.name);
          }
          else {
          applicationClass.enhance();
          BytecodeCache.cacheBytecode(applicationClass.enhancedByteCode, applicationClass.name, applicationClass.javaSource);
          newDefinitions.add(
          new ClassDefinition(applicationClass.javaClass, applicationClass.enhancedByteCode));
          }
          }
          try {
          HotswapAgent.reload(newDefinitions.toArray(
          new ClassDefinition[newDefinitions.size()]));
          }
          catch (ClassNotFoundException e) {
          throw new UnexpectedException(e);
          }
          catch (UnmodifiableClassException e) {
          throw new UnexpectedException(e);
          }
          // Check new annotations
          for (Class clazz : annotationsHashes.keySet()) {
          if (annotationsHashes.get(clazz) != computeAnnotationsHash(clazz)) {
          throw new RuntimeException("Annotations change !");
          }
          }
          // Now check if there is new classes or removed classes
          int hash = computePathHash();
          if (hash != this.pathHash) {
          // Remove class for deleted files !!
          for (ApplicationClass applicationClass : Play.classes.all()) {
          if (!applicationClass.javaFile.exists()) {
          Play.classes.classes.remove(applicationClass.name);
          }
          if(applicationClass.name.contains("$")) {
          Play.classes.classes.remove(applicationClass.name);
          }
          }
          throw new RuntimeException("Path has changed");
          }
          }

          HotswapAgent類的 reload 方法如下:
          public static void reload(ClassDefinition definitions) throws UnmodifiableClassException, ClassNotFoundException {
          instrumentation.redefineClasses(definitions);
          }

          讀到這里,也就弄清楚了 play! 怎么實(shí)現(xiàn) hot swap 的原理了,還是調(diào)用java.lang.instrument目錄下的類和方法來實(shí)現(xiàn)的 hot swap。不存在魔法,play! 還是選擇了替換 classloader,只不過這個(gè)替換動作發(fā)生在處理 http request 的時(shí)候,于是開發(fā)人員用起來就是“刷新瀏覽器就可以看見效果了”。



          評論

          # re: play! framework hot swap 淺析  回復(fù)  更多評論   

          2008-12-30 19:09 by 綿陽人才網(wǎng)
          very good! 綿陽 的 人才 我 wite...

          # re: play! framework hot swap 淺析  回復(fù)  更多評論   

          2008-12-30 19:25 by 太陽里的雪
          play! 框架的controller和model分包,及view下的html文件目錄組織怎么搞?

          # re: play! framework hot swap 淺析  回復(fù)  更多評論   

          2008-12-30 20:16 by 太陽里的雪
          用了一下,某些方面比Grails還要強(qiáng)~~

          # re: play! framework hot swap 淺析  回復(fù)  更多評論   

          2008-12-30 20:28 by mingj
          @太陽里的雪
          你說文件目錄結(jié)構(gòu)怎么搞是什么意思?
          是說怎么放置相應(yīng)的文件么?

          你可以看看/resources目錄下的application-skel文件夾
          這下面就是新建application的skeleton

          # re: play! framework hot swap 淺析  回復(fù)  更多評論   

          2008-12-30 21:22 by 太陽里的雪
          就是我要把controller和model要分包,好象包名必須以controllers和models開頭才行。

          另外一個(gè)問題就是怎么跟spring結(jié)合。

          # re: play! framework hot swap 淺析  回復(fù)  更多評論   

          2008-12-30 22:55 by shinewang
          @太陽里的雪
          最新的stable4里面有個(gè)SpringPlugin

          # re: play! framework hot swap 淺析  回復(fù)  更多評論   

          2008-12-30 23:50 by mingj
          @太陽里的雪
          是的,這是play! 要求的convention
          被寫死在Play類里面了

          # re: play! framework hot swap 淺析  回復(fù)  更多評論   

          2008-12-31 02:03 by 太陽里的雪
          今天試用了一下,play!模版處理能力目前實(shí)在是太差啦~~功能太少~~

          # re: play! framework hot swap 淺析  回復(fù)  更多評論   

          2008-12-31 10:46 by mingj
          @太陽里的雪
          現(xiàn)階段的play! 的確只能算是個(gè)玩具
          一些編程理念和普遍接受的理念大相徑庭

          稍后我還會推出其他的博文來分析play!種種之痛的
          敬請期待:)

          # re: play! framework hot swap 淺析  回復(fù)  更多評論   

          2008-12-31 19:59 by 夢想在這里起飛
          寫得不錯(cuò)啊,看看我這個(gè)
          開源的報(bào)表ireport項(xiàng)目web應(yīng)用
          http://ireport.cubebi.com


          # re: play! framework hot swap 淺析  回復(fù)  更多評論   

          2009-01-05 12:44 by ajf
          感興趣的朋友可以俺整地看看這個(gè)
          ajf agile java framework

          http://www.aygfsteel.com/ajf/archive/2009/01/03/249618.html

          # re: play! framework hot swap 淺析  回復(fù)  更多評論   

          2011-10-28 15:02 by 孫健
          @太陽里的雪
          可是擴(kuò)展及其方便 相比較JSP。

          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 金乡县| 彰武县| 宁安市| 马鞍山市| 高台县| 辰溪县| 普格县| 九寨沟县| 砚山县| 吉隆县| 建湖县| 顺义区| 岐山县| 荥阳市| 含山县| 滦平县| 阿坝县| 黎川县| 武宁县| 曲阳县| 高要市| 洛浦县| 建湖县| 天等县| 娄烦县| 新昌县| 资源县| 嘉义县| 鄂伦春自治旗| 白朗县| 志丹县| 麦盖提县| 高要市| 江川县| 楚雄市| 泸州市| 阳朔县| 抚顺市| 禄劝| 连州市| 民权县|