使用 RelProxy 提高 Java 開發效率
RelProxy 旨在通過下列兩種方式提高開發效率:
可以在生產環境下修改使用者程式碼,而不需要重新載入整個應用。
提高開發效率,避免花費過多的時間載入應用且對效能不會有影響。
兩個目標都要求在你的應用中增加一些 RelProxy 程式碼,註冊成一種典型的監聽、回撥模式。這是一種“侵入”的方式。
如果你是一名Java 框架或獨立 Java 通用服務模組的開發者,可以將 RelProxy Java 嵌入到你的框架中,這樣能透明地為框架的終端使用者提供程式碼自動載入功能,只需要進行一些必要的配置,而無需呼叫 RelProxy API。
對使用 Java 版的 RelProxy,有兩種 API 可供呼叫:
JProxy 及其相關類:主要是靜態方法
Java 指令碼 API:基於介面
第二種方式更適合將 RelProxy 嵌入到你的 Java 框架中,這種方式是基於介面的,在你的 API 中無需暴露公共 RelProxy 類,因為在框架中會執行啟動程式。我將使用比較簡單的 API:JProxyScriptEngineFactory.create()。
JProxyScriptEngine 的功能與 Jproxy 相同,也就是說具有相同的方法。只是這種情況下,只需要使用介面。
一個簡單的例子是演示如何嵌入 RelProxy 的最好方式。這個例子是 RelProxy 的示例倉庫中包含的 RelProxyBuiltin(relproxy_builtin_ex 專案中)。它定義了兩個監聽器來實現註冊使用者端的程式碼,一個監聽器顯示選項(option),另一個執行選擇的行為。
這個迷你框架和示例使用 NetBeans 和 Maven 開發完成。
有兩個包:
com.innowhere.relproxy_builtin_ex :迷你框架。子包 com.innowhere.relproxy_builtin_ex.impl 只包含一個非公共的類。
com.innowhere.relproxy_builtin_ex_main :一個簡單的使用示例。
迷你框架(公共類和介面):
RelProxyBuiltinRoot.java
package com.innowhere.relproxy_builtin_ex; import com.innowhere.relproxy_builtin_ex.impl.RelProxyBuiltinImpl; public class RelProxyBuiltinRoot { private final static RelProxyBuiltinImpl SINGLETON = new RelProxyBuiltinImpl(); public static RelProxyBuiltin get() { return SINGLETON; } }
RelProxyBuiltin.java
package com.innowhere.relproxy_builtin_ex; import com.innowhere.relproxy.jproxy.JProxyScriptEngine; import java.io.InputStream; import java.io.PrintStream; public interface RelProxyBuiltin { public JProxyScriptEngine getJProxyScriptEngine(); public void addOutputListener(OutputListener listener); public void removeOutputListener(OutputListener listener); public int getOutputListenerCount(); public void addCommandListener(CommandListener listener); public void removeCommandListener(CommandListener listener); public int getCommandListenerCount(); public void runLoop(InputStream in,PrintStream out); }
OutputListener.java
package com.innowhere.relproxy_builtin_ex; import java.io.PrintStream; public interface OutputListener { public void write(PrintStream out); }
CommandListener.java
package com.innowhere.relproxy_builtin_ex; import java.io.PrintStream; public interface CommandListener { public void execute(String command,String input,PrintStream out); }
現在看一下實現細節,該類演示了怎樣簡單地內嵌 RelProxy:
RelProxyBuiltinImpl.java
package com.innowhere.relproxy_builtin_ex.impl; import com.innowhere.relproxy.jproxy.JProxyScriptEngine; import com.innowhere.relproxy.jproxy.JProxyScriptEngineFactory; import com.innowhere.relproxy_builtin_ex.CommandListener; import com.innowhere.relproxy_builtin_ex.OutputListener; import com.innowhere.relproxy_builtin_ex.RelProxyBuiltin; import java.io.InputStream; import java.io.PrintStream; import java.util.LinkedHashSet; import java.util.Scanner; public class RelProxyBuiltinImpl implements RelProxyBuiltin { protected JProxyScriptEngine jProxyEngine = null; protected LinkedHashSet<OutputListener> outListeners = new LinkedHashSet<OutputListener>(); protected LinkedHashSet<CommandListener> commandListeners = new LinkedHashSet<CommandListener>(); @Override public JProxyScriptEngine getJProxyScriptEngine() { if (jProxyEngine == null) jProxyEngine = (JProxyScriptEngine)JProxyScriptEngineFactory.create().getScriptEngine(); return jProxyEngine; } public JProxyScriptEngine getJProxyScriptEngineIfConfigured() { if (jProxyEngine == null || !jProxyEngine.isEnabled()) return null; return jProxyEngine; } @Override public void addOutputListener(OutputListener listener) { JProxyScriptEngine jProxy = getJProxyScriptEngineIfConfigured(); if (jProxy != null) { listener = jProxy.create(listener,OutputListener.class); } outListeners.add(listener); } @Override public void removeOutputListener(OutputListener listener) { JProxyScriptEngine jProxy = getJProxyScriptEngineIfConfigured(); if (jProxy != null) { listener = jProxy.create(listener,OutputListener.class); } outListeners.remove(listener); } @Override public int getOutputListenerCount() { return outListeners.size(); } @Override public void addCommandListener(CommandListener listener) { JProxyScriptEngine jProxy = getJProxyScriptEngineIfConfigured(); if (jProxy != null) { listener = jProxy.create(listener,CommandListener.class); } commandListeners.add(listener); } @Override public void removeCommandListener(CommandListener listener) { JProxyScriptEngine jProxy = getJProxyScriptEngineIfConfigured(); if (jProxy != null) { listener = jProxy.create(listener,CommandListener.class); } commandListeners.remove(listener); } @Override public int getCommandListenerCount() { return commandListeners.size(); } @Override public void runLoop(InputStream in,PrintStream out) { Scanner scanner = new Scanner(in); while(true) { out.print("Enter phrase:"); String input = scanner.nextLine(); out.println("Command list:"); for(OutputListener listener : outListeners) listener.write(out); out.print("Enter command (or quit):"); String command = scanner.nextLine(); if ("quit".equals(command)) break; for(CommandListener listener : commandListeners) listener.execute(command,input,out); } } }
這三個方法足以解釋怎樣啟動 RelProxy Java 引擎,怎樣簡單地使用指令監聽器來註冊熱載入。
RelProxyBuiltinImpl.java (部分)
@Override public JProxyScriptEngine getJProxyScriptEngine() { if (jProxyEngine == null) jProxyEngine = (JProxyScriptEngine)JProxyScriptEngineFactory.create().getScriptEngine(); return jProxyEngine; } public JProxyScriptEngine getJProxyScriptEngineIfConfigured() { if (jProxyEngine == null || !jProxyEngine.isEnabled()) return null; return jProxyEngine; } @Override public void addOutputListener(OutputListener listener) { JProxyScriptEngine jProxy = getJProxyScriptEngineIfConfigured(); if (jProxy != null) { listener = jProxy.create(listener,OutputListener.class); } outListeners.add(listener); }
公共方法 RelProxyBuiltin.getJProxyScriptEngine() 必須在啟動時執行,用於配置 RelProxy。如果沒有配置,RelProxy 就不起作用。
請記住,通過 create(…) 建立的代理物件需要能正確的執行 hashCode() 方法和 equals(Object) 方法,監聽器集合、監聽記錄依賴這兩個方法來區別監聽器物件。
這是基於控制檯的示例程式碼(名稱與 JUnit 類似,但確實不是 JUnit 的測試示例):
Main.java
package com.innowhere.relproxy_builtin_ex_main; import com.innowhere.relproxy.RelProxyOnReloadListener; import com.innowhere.relproxy.jproxy.JProxy; import com.innowhere.relproxy.jproxy.JProxyCompilerListener; import com.innowhere.relproxy.jproxy.JProxyConfig; import com.innowhere.relproxy.jproxy.JProxyDiagnosticsListener; import com.innowhere.relproxy.jproxy.JProxyInputSourceFileExcludedListener; import com.innowhere.relproxy.jproxy.JProxyScriptEngine; import com.innowhere.relproxy_builtin_ex.CommandListener; import com.innowhere.relproxy_builtin_ex.RelProxyBuiltin; import com.innowhere.relproxy_builtin_ex.RelProxyBuiltinRoot; import java.io.File; import java.lang.reflect.Method; import java.net.URL; import java.util.Arrays; import java.util.List; import javax.tools.Diagnostic; import javax.tools.DiagnosticCollector; import javax.tools.JavaFileObject; public class Main { public static void main(String[] args) throws Exception { new Main(); } public Main() { // Note: NetBeans Console window works bad (no input) with Maven Test tasks http://stackoverflow.com/questions/3035351/broken-console-in-maven-project-using-netbeans // this is why is not a really JUnit test. setUp(); try { mainTest(); } finally { tearDown(); } System.exit(0); } public void setUp() { URL res = this.getClass().getResource("/"); // .../target/classes/ // Use example of RelProxy in development time: String inputPath = res.getFile() + "/../../src/main/java/"; if (new File(inputPath).exists()) { System.out.println("RelProxy to be enabled, development mode detected"); } else { System.out.println("RelProxy disabled, production mode detected"); return; } JProxyInputSourceFileExcludedListener excludedListener = new JProxyInputSourceFileExcludedListener() { @Override public boolean isExcluded(File file, File rootFolderOfSources) { String absPath = file.getAbsolutePath(); if (file.isDirectory()) { return absPath.endsWith(File.separatorChar + "relproxy_builtin_ex"); } else { return absPath.endsWith(File.separatorChar + Main.class.getSimpleName() + ".java"); } } }; String classFolder = null; // Optional Iterable<String> compilationOptions = Arrays.asList(new String[]{"-source","1.6","-target","1.6"}); long scanPeriod = 1000; RelProxyOnReloadListener proxyListener = new RelProxyOnReloadListener() { @Override public void onReload(Object objOld, Object objNew, Object proxy, Method method, Object[] args) { System.out.println("Reloaded " + objNew + " Calling method: " + method); } }; JProxyCompilerListener compilerListener = new JProxyCompilerListener(){ @Override public void beforeCompile(File file) { System.out.println("Before compile: " + file); } @Override public void afterCompile(File file) { System.out.println("After compile: " + file); } }; JProxyDiagnosticsListener diagnosticsListener = new JProxyDiagnosticsListener() { @Override public void onDiagnostics(DiagnosticCollector<JavaFileObject> diagnostics) { List<Diagnostic<? extends JavaFileObject>> diagList = diagnostics.getDiagnostics(); int i = 1; for (Diagnostic diagnostic : diagList) { System.err.println("Diagnostic " + i); System.err.println(" code: " + diagnostic.getCode()); System.err.println(" kind: " + diagnostic.getKind()); System.err.println(" line number: " + diagnostic.getLineNumber()); System.err.println(" column number: " + diagnostic.getColumnNumber()); System.err.println(" start position: " + diagnostic.getStartPosition()); System.err.println(" position: " + diagnostic.getPosition()); System.err.println(" end position: " + diagnostic.getEndPosition()); System.err.println(" source: " + diagnostic.getSource()); System.err.println(" message: " + diagnostic.getMessage(null)); i++; } } }; RelProxyBuiltin rpbRoot = RelProxyBuiltinRoot.get(); JProxyScriptEngine engine = rpbRoot.getJProxyScriptEngine(); JProxyConfig jpConfig = JProxy.createJProxyConfig(); jpConfig.setEnabled(true) .setRelProxyOnReloadListener(proxyListener) .setInputPath(inputPath) .setJProxyInputSourceFileExcludedListener(excludedListener) .setScanPeriod(scanPeriod) .setClassFolder(classFolder) .setCompilationOptions(compilationOptions) .setJProxyCompilerListener(compilerListener) .setJProxyDiagnosticsListener(diagnosticsListener); engine.init(jpConfig); System.out.println("RelProxy running"); } public void tearDown() { RelProxyBuiltin rpbRoot = RelProxyBuiltinRoot.get(); JProxyScriptEngine engine = rpbRoot.getJProxyScriptEngine(); engine.stop(); System.out.println("RelProxy stopped"); } public void mainTest() { RelProxyBuiltin rpbRoot = RelProxyBuiltinRoot.get(); TestListener listener = new TestListener(); rpbRoot.addOutputListener(listener); assertTrue(rpbRoot.getOutputListenerCount() == 1); rpbRoot.removeOutputListener(listener); assertTrue(rpbRoot.getOutputListenerCount() == 0); rpbRoot.addOutputListener(listener); CommandListener commandListener = listener.getCommandListener(); rpbRoot.addCommandListener(commandListener); assertTrue(rpbRoot.getCommandListenerCount() == 1); rpbRoot.removeCommandListener(commandListener); assertTrue(rpbRoot.getCommandListenerCount() == 0); rpbRoot.addCommandListener(commandListener); rpbRoot.runLoop(System.in,System.out); } private static void assertTrue(boolean res) { if (!res) throw new RuntimeException("Unexpected Error"); } }
看一下這段程式碼:
Main.java (部分)
URL res = this.getClass().getResource("/"); // .../target/classes/ // Use example of RelProxy in development time: String inputPath = res.getFile() + "/../../src/main/java/"; if (new File(inputPath).exists()) { System.out.println("RelProxy to be enabled, development mode detected"); } else { System.out.println("RelProxy disabled, production mode detected"); return; } JProxyInputSourceFileExcludedListener excludedListener = new JProxyInputSourceFileExcludedListener() { @Override public boolean isExcluded(File file, File rootFolderOfSources) { String absPath = file.getAbsolutePath(); if (file.isDirectory()) { return absPath.endsWith(File.separatorChar + "relproxy_builtin_ex"); } else { return absPath.endsWith(File.separatorChar + Main.class.getSimpleName() + ".java"); } } };
我們獲取並註冊應用原始碼的根目錄,該程式碼“可能”會被重新載入。
我們需要排除框架程式碼,因為這顯然不是使用者的程式碼(不需要重新載入)。此外,還需要排除 Main.java 檔案,該檔案包含了測試程式碼,也不需要重新載入,只有 TestListener.java 類(與 Main.java 在同一資料夾下)需要(必需)重新載入。
最後 TestListener.java 類包含兩個監聽器,CommandListener 的實現採用匿名內部類的方式,主要目的是為了演示。
TestListener.java
package com.innowhere.relproxy_builtin_ex_main; import com.innowhere.relproxy_builtin_ex.CommandListener; import com.innowhere.relproxy_builtin_ex.OutputListener; import java.io.PrintStream; public class TestListener implements OutputListener { @Override public void write(PrintStream out) { out.println("uppercase"); out.println("lowercase"); } public CommandListener getCommandListener() { return new CommandListener() { @Override public void execute(String command,String text,PrintStream out) { if ("uppercase".equals(command)) out.println(text.toUpperCase()); else if ("lowercase".equals(command)) out.println(text.toLowerCase()); else out.println("Unknown command:" + command); } }; } }
先預定義可選項,然後執行 Main 類。為了校驗 RelProxy 是否起作用,可以在不停止程式的執行的基礎上增加一個新的可選項“same”。
@Override public void write(PrintStream out) { out.println("uppercase"); out.println("lowercase"); out.println("same"); // NEW } public CommandListener getCommandListener() { return new CommandListener() { @Override public void execute(String command,String text,PrintStream out) { if ("uppercase".equals(command)) out.println(text.toUpperCase()); else if ("lowercase".equals(command)) out.println(text.toLowerCase()); else if ("same".equals(command)) // NEW out.println(text); // NEW else out.println("Unknown command:" + command); } }; } }
下一篇文章中將處理包含當前“same”的行為,不需要停止控制檯應用。
ItsNat web 框架可能是第一個使用 RelProxy 技術的應用(版本 v1.4)。
注意:使用 RelProxy 0.8.7 或更高的版本,這個版本在嵌入方式上做了改進。
相關文章
- JAVA開發之簡化Dao層、提高開發效率Java
- iOS 提高開發效率iOS
- JAVA開發之簡化Dao層、提高開發效率(二)Java
- JAVA開發之簡化Dao層、提高開發效率(三)Java
- 用 FutureBuilder 提高開發效率Rebuild
- 提高開發效率小技巧
- 如何提高使用Java反射的效率?Java反射
- [Java]使用lombok提高編碼效率JavaLombok
- 如何提高App開發的效率?APP
- 從開發框架提高開發效率說起框架
- 在 Laravel 中使用 TypeScript 、CoffeeScript 、pug 提高開發效率LaravelTypeScript
- 提高iOS App開發效率的方法iOSAPP
- API開發平臺,提高API開發及管理效率API
- CoffeeScript和Sass提高Web開發效率Web
- 提高iOS開發效率的方法和工具iOS
- 如何大幅度提高 Mac 開發效率Mac
- 如何能提高CSS編寫技巧 提高Web前端開發效率CSSWeb前端
- ZT - 使用 IBM Debugger for AIX 來提高開發效率(4)IBMAI
- ZT - 使用 IBM Debugger for AIX 來提高開發效率(1)IBMAI
- Delphi提高開發效率之GExperts專家的使用說明
- 如何提高Java學習效率Java
- iOS開發經驗談:如何提高應用開發效率?iOS
- 【譯】提高React開發效率的5個工具React
- 提高 Linux 開發效率的 5 個工具Linux
- 快速提高Android開發效率的Web工具AndroidWeb
- Facebook工程師開發工具提高VR內容開發效率工程師VR
- 如何使用Git提高研發團隊工作效率?Git
- 華為雲 DAS,大幅提高資料庫開發效率資料庫
- 一些提高開發效率的小體會
- 提高開發效率之VS Code基礎配置篇
- 提高開發效率的 Eclipse 實用操作(2)Eclipse
- 提高python開發效率的10個小技巧Python
- 提高Android開發效率的9個Web工具AndroidWeb
- 對待Java程式設計,開發工程師如何提高效率?Java程式設計工程師
- 提高企業開發效率的優質工具:快速開發平臺
- Android開發必看-快速提高 Android 開發效率的 Web 工具AndroidWeb
- 【譯】使用這些 CSS 屬性選擇器來提高前端開發效率!CSS前端
- 移動開發時批處理壓縮圖片提高開發效率移動開發