OOPAA

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

          play! framework hot swap 淺析

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

          Request request = parseRequest(minaRequest, session);

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

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

          2.4 play! 根據當前配置的開發模式來采用不同的策略調用 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! 當前是 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! 修改源文件后,刷新瀏覽器即見效的奧秘就在這里了。再進去看看 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! 怎么實現 hot swap 的原理了,還是調用java.lang.instrument目錄下的類和方法來實現的 hot swap。不存在魔法,play! 還是選擇了替換 classloader,只不過這個替換動作發生在處理 http request 的時候,于是開發人員用起來就是“刷新瀏覽器就可以看見效果了”。



          評論

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

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

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

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

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

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

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

          2008-12-30 20:28 by mingj
          @太陽里的雪
          你說文件目錄結構怎么搞是什么意思?
          是說怎么放置相應的文件么?

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

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

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

          另外一個問題就是怎么跟spring結合。

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

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

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

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

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

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

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

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

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

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

          2008-12-31 19:59 by 夢想在這里起飛
          寫得不錯啊,看看我這個
          開源的報表ireport項目web應用
          http://ireport.cubebi.com


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

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

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

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

          2011-10-28 15:02 by 孫健
          @太陽里的雪
          可是擴展及其方便 相比較JSP。
          主站蜘蛛池模板: 宜兰县| 翁源县| 云浮市| 安丘市| 开远市| 舒城县| 洞头县| 三明市| 南通市| 延吉市| 德化县| 同仁县| 岐山县| 鄄城县| 开原市| 彭阳县| 泸溪县| 汪清县| 赫章县| 珲春市| 礼泉县| 翼城县| 垫江县| 正镶白旗| 丽江市| 四子王旗| 文山县| 新竹县| 昌平区| 祁门县| 泰和县| 竹山县| 南雄市| 上栗县| 祁阳县| 兖州市| 水城县| 许昌市| 杭州市| 军事| 昌江|