您好,登錄后才能下訂單哦!
這期內容當中小編將會給大家帶來有關怎么用源碼分析Play Framework hotswap,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
Play Framework hotswap的賣點就在于 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 的時候,于是開發人員用起來就是“刷新瀏覽器就可以看見效果了”。
上述就是小編為大家分享的怎么用源碼分析Play Framework hotswap了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。