在路上

 找回密码
 立即注册
在路上 站点首页 学习 查看内容

使用RelProxy提高Java开发效率

2017-2-7 13:39| 发布者: zhangjf| 查看: 387| 评论: 0

摘要: RelProxy 旨在通过下列两种方式提高开发效率: 可以在生产环境下修改用户代码,而不需要重新加载整个应用。 提高开发效率,避免花费过多的时间加载应用且对性能不会有影响。 两个目标都要求在你的应用中增加一些 ...
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

  1. package com.innowhere.relproxy_builtin_ex;
  2. import com.innowhere.relproxy_builtin_ex.impl.RelProxyBuiltinImpl;
  3. public class RelProxyBuiltinRoot
  4. {
  5. private final static RelProxyBuiltinImpl SINGLETON = new RelProxyBuiltinImpl();
  6. public static RelProxyBuiltin get()
  7. {
  8. return SINGLETON;
  9. }
  10. }
复制代码

RelProxyBuiltin.java

  1. package com.innowhere.relproxy_builtin_ex;
  2. import com.innowhere.relproxy.jproxy.JProxyScriptEngine;
  3. import java.io.InputStream;
  4. import java.io.PrintStream;
  5. public interface RelProxyBuiltin
  6. {
  7. public JProxyScriptEngine getJProxyScriptEngine();
  8. public void addOutputListener(OutputListener listener);
  9. public void removeOutputListener(OutputListener listener);
  10. public int getOutputListenerCount();
  11. public void addCommandListener(CommandListener listener);
  12. public void removeCommandListener(CommandListener listener);
  13. public int getCommandListenerCount();
  14. public void runLoop(InputStream in,PrintStream out);
  15. }
复制代码

OutputListener.java

  1. package com.innowhere.relproxy_builtin_ex;
  2. import java.io.PrintStream;
  3. public interface OutputListener
  4. {
  5. public void write(PrintStream out);
  6. }
复制代码

CommandListener.java

  1. package com.innowhere.relproxy_builtin_ex;
  2. import java.io.PrintStream;
  3. public interface CommandListener
  4. {
  5. public void execute(String command,String input,PrintStream out);
  6. }
复制代码

现在看一下实现细节,该类演示了怎样简单地内嵌 RelProxy:

RelProxyBuiltinImpl.java

  1. package com.innowhere.relproxy_builtin_ex.impl;
  2. import com.innowhere.relproxy.jproxy.JProxyScriptEngine;
  3. import com.innowhere.relproxy.jproxy.JProxyScriptEngineFactory;
  4. import com.innowhere.relproxy_builtin_ex.CommandListener;
  5. import com.innowhere.relproxy_builtin_ex.OutputListener;
  6. import com.innowhere.relproxy_builtin_ex.RelProxyBuiltin;
  7. import java.io.InputStream;
  8. import java.io.PrintStream;
  9. import java.util.LinkedHashSet;
  10. import java.util.Scanner;
  11. public class RelProxyBuiltinImpl implements RelProxyBuiltin
  12. {
  13. protected JProxyScriptEngine jProxyEngine = null;
  14. protected LinkedHashSet<OutputListener> outListeners = new LinkedHashSet<OutputListener>();
  15. protected LinkedHashSet<CommandListener> commandListeners = new LinkedHashSet<CommandListener>();
  16. @Override
  17. public JProxyScriptEngine getJProxyScriptEngine()
  18. {
  19. if (jProxyEngine == null) jProxyEngine = (JProxyScriptEngine)JProxyScriptEngineFactory.create().getScriptEngine();
  20. return jProxyEngine;
  21. }
  22. public JProxyScriptEngine getJProxyScriptEngineIfConfigured()
  23. {
  24. if (jProxyEngine == null || !jProxyEngine.isEnabled())
  25. return null;
  26. return jProxyEngine;
  27. }
  28. @Override
  29. public void addOutputListener(OutputListener listener)
  30. {
  31. JProxyScriptEngine jProxy = getJProxyScriptEngineIfConfigured();
  32. if (jProxy != null)
  33. {
  34. listener = jProxy.create(listener,OutputListener.class);
  35. }
  36. outListeners.add(listener);
  37. }
  38. @Override
  39. public void removeOutputListener(OutputListener listener)
  40. {
  41. JProxyScriptEngine jProxy = getJProxyScriptEngineIfConfigured();
  42. if (jProxy != null)
  43. {
  44. listener = jProxy.create(listener,OutputListener.class);
  45. }
  46. outListeners.remove(listener);
  47. }
  48. @Override
  49. public int getOutputListenerCount()
  50. {
  51. return outListeners.size();
  52. }
  53. @Override
  54. public void addCommandListener(CommandListener listener)
  55. {
  56. JProxyScriptEngine jProxy = getJProxyScriptEngineIfConfigured();
  57. if (jProxy != null)
  58. {
  59. listener = jProxy.create(listener,CommandListener.class);
  60. }
  61. commandListeners.add(listener);
  62. }
  63. @Override
  64. public void removeCommandListener(CommandListener listener)
  65. {
  66. JProxyScriptEngine jProxy = getJProxyScriptEngineIfConfigured();
  67. if (jProxy != null)
  68. {
  69. listener = jProxy.create(listener,CommandListener.class);
  70. }
  71. commandListeners.remove(listener);
  72. }
  73. @Override
  74. public int getCommandListenerCount()
  75. {
  76. return commandListeners.size();
  77. }
  78. @Override
  79. public void runLoop(InputStream in,PrintStream out)
  80. {
  81. Scanner scanner = new Scanner(in);
  82. while(true)
  83. {
  84. out.print("Enter phrase:");
  85. String input = scanner.nextLine();
  86. out.println("Command list:");
  87. for(OutputListener listener : outListeners)
  88. listener.write(out);
  89. out.print("Enter command (or quit):");
  90. String command = scanner.nextLine();
  91. if ("quit".equals(command))
  92. break;
  93. for(CommandListener listener : commandListeners)
  94. listener.execute(command,input,out);
  95. }
  96. }
  97. }
复制代码

这三个方法足以解释怎样启动 RelProxy Java 引擎,怎样简单地使用指令监听器来注册热加载。

RelProxyBuiltinImpl.java (部分)

  1. @Override
  2. public JProxyScriptEngine getJProxyScriptEngine()
  3. {
  4. if (jProxyEngine == null) jProxyEngine = (JProxyScriptEngine)JProxyScriptEngineFactory.create().getScriptEngine();
  5. return jProxyEngine;
  6. }
  7. public JProxyScriptEngine getJProxyScriptEngineIfConfigured()
  8. {
  9. if (jProxyEngine == null || !jProxyEngine.isEnabled())
  10. return null;
  11. return jProxyEngine;
  12. }
  13. @Override
  14. public void addOutputListener(OutputListener listener)
  15. {
  16. JProxyScriptEngine jProxy = getJProxyScriptEngineIfConfigured();
  17. if (jProxy != null)
  18. {
  19. listener = jProxy.create(listener,OutputListener.class);
  20. }
  21. outListeners.add(listener);
  22. }
复制代码

公共方法 RelProxyBuiltin.getJProxyScriptEngine() 必须在启动时执行,用于配置 RelProxy。如果没有配置,RelProxy 就不起作用。

请记住,通过 create(…) 创建的代理对象需要能正确的执行 hashCode() 方法和 equals(Object) 方法,监听器集合、监听记录依赖这两个方法来区别监听器对象。

这是基于控制台的示例代码(名称与 JUnit 类似,但确实不是 JUnit 的测试示例):

Main.java

  1. package com.innowhere.relproxy_builtin_ex_main;
  2. import com.innowhere.relproxy.RelProxyOnReloadListener;
  3. import com.innowhere.relproxy.jproxy.JProxy;
  4. import com.innowhere.relproxy.jproxy.JProxyCompilerListener;
  5. import com.innowhere.relproxy.jproxy.JProxyConfig;
  6. import com.innowhere.relproxy.jproxy.JProxyDiagnosticsListener;
  7. import com.innowhere.relproxy.jproxy.JProxyInputSourceFileExcludedListener;
  8. import com.innowhere.relproxy.jproxy.JProxyScriptEngine;
  9. import com.innowhere.relproxy_builtin_ex.CommandListener;
  10. import com.innowhere.relproxy_builtin_ex.RelProxyBuiltin;
  11. import com.innowhere.relproxy_builtin_ex.RelProxyBuiltinRoot;
  12. import java.io.File;
  13. import java.lang.reflect.Method;
  14. import java.net.URL;
  15. import java.util.Arrays;
  16. import java.util.List;
  17. import javax.tools.Diagnostic;
  18. import javax.tools.DiagnosticCollector;
  19. import javax.tools.JavaFileObject;
  20. public class Main
  21. {
  22. public static void main(String[] args) throws Exception
  23. {
  24. new Main();
  25. }
  26. public Main()
  27. {
  28. // Note: NetBeans Console window works bad (no input) with Maven Test tasks http://stackoverflow.com/questions/3035351/broken-console-in-maven-project-using-netbeans
  29. // this is why is not a really JUnit test.
  30. setUp();
  31. try
  32. {
  33. mainTest();
  34. }
  35. finally
  36. {
  37. tearDown();
  38. }
  39. System.exit(0);
  40. }
  41. public void setUp()
  42. {
  43. URL res = this.getClass().getResource("/"); // .../target/classes/
  44. // Use example of RelProxy in development time:
  45. String inputPath = res.getFile() + "/../../src/main/java/";
  46. if (new File(inputPath).exists())
  47. {
  48. System.out.println("RelProxy to be enabled, development mode detected");
  49. }
  50. else
  51. {
  52. System.out.println("RelProxy disabled, production mode detected");
  53. return;
  54. }
  55. JProxyInputSourceFileExcludedListener excludedListener = new JProxyInputSourceFileExcludedListener()
  56. {
  57. @Override
  58. public boolean isExcluded(File file, File rootFolderOfSources)
  59. {
  60. String absPath = file.getAbsolutePath();
  61. if (file.isDirectory())
  62. {
  63. return absPath.endsWith(File.separatorChar + "relproxy_builtin_ex");
  64. }
  65. else
  66. {
  67. return absPath.endsWith(File.separatorChar + Main.class.getSimpleName() + ".java");
  68. }
  69. }
  70. };
  71. String classFolder = null; // Optional
  72. Iterable<String> compilationOptions = Arrays.asList(new String[]{"-source","1.6","-target","1.6"});
  73. long scanPeriod = 1000;
  74. RelProxyOnReloadListener proxyListener = new RelProxyOnReloadListener() {
  75. @Override
  76. public void onReload(Object objOld, Object objNew, Object proxy, Method method, Object[] args) {
  77. System.out.println("Reloaded " + objNew + " Calling method: " + method);
  78. }
  79. };
  80. JProxyCompilerListener compilerListener = new JProxyCompilerListener(){
  81. @Override
  82. public void beforeCompile(File file)
  83. {
  84. System.out.println("Before compile: " + file);
  85. }
  86. @Override
  87. public void afterCompile(File file)
  88. {
  89. System.out.println("After compile: " + file);
  90. }
  91. };
  92. JProxyDiagnosticsListener diagnosticsListener = new JProxyDiagnosticsListener()
  93. {
  94. @Override
  95. public void onDiagnostics(DiagnosticCollector<JavaFileObject> diagnostics)
  96. {
  97. List<Diagnostic<? extends JavaFileObject>> diagList = diagnostics.getDiagnostics();
  98. int i = 1;
  99. for (Diagnostic diagnostic : diagList)
  100. {
  101. System.err.println("Diagnostic " + i);
  102. System.err.println(" code: " + diagnostic.getCode());
  103. System.err.println(" kind: " + diagnostic.getKind());
  104. System.err.println(" line number: " + diagnostic.getLineNumber());
  105. System.err.println(" column number: " + diagnostic.getColumnNumber());
  106. System.err.println(" start position: " + diagnostic.getStartPosition());
  107. System.err.println(" position: " + diagnostic.getPosition());
  108. System.err.println(" end position: " + diagnostic.getEndPosition());
  109. System.err.println(" source: " + diagnostic.getSource());
  110. System.err.println(" message: " + diagnostic.getMessage(null));
  111. i++;
  112. }
  113. }
  114. };
  115. RelProxyBuiltin rpbRoot = RelProxyBuiltinRoot.get();
  116. JProxyScriptEngine engine = rpbRoot.getJProxyScriptEngine();
  117. JProxyConfig jpConfig = JProxy.createJProxyConfig();
  118. jpConfig.setEnabled(true)
  119. .setRelProxyOnReloadListener(proxyListener)
  120. .setInputPath(inputPath)
  121. .setJProxyInputSourceFileExcludedListener(excludedListener)
  122. .setScanPeriod(scanPeriod)
  123. .setClassFolder(classFolder)
  124. .setCompilationOptions(compilationOptions)
  125. .setJProxyCompilerListener(compilerListener)
  126. .setJProxyDiagnosticsListener(diagnosticsListener);
  127. engine.init(jpConfig);
  128. System.out.println("RelProxy running");
  129. }
  130. public void tearDown()
  131. {
  132. RelProxyBuiltin rpbRoot = RelProxyBuiltinRoot.get();
  133. JProxyScriptEngine engine = rpbRoot.getJProxyScriptEngine();
  134. engine.stop();
  135. System.out.println("RelProxy stopped");
  136. }
  137. public void mainTest()
  138. {
  139. RelProxyBuiltin rpbRoot = RelProxyBuiltinRoot.get();
  140. TestListener listener = new TestListener();
  141. rpbRoot.addOutputListener(listener);
  142. assertTrue(rpbRoot.getOutputListenerCount() == 1);
  143. rpbRoot.removeOutputListener(listener);
  144. assertTrue(rpbRoot.getOutputListenerCount() == 0);
  145. rpbRoot.addOutputListener(listener);
  146. CommandListener commandListener = listener.getCommandListener();
  147. rpbRoot.addCommandListener(commandListener);
  148. assertTrue(rpbRoot.getCommandListenerCount() == 1);
  149. rpbRoot.removeCommandListener(commandListener);
  150. assertTrue(rpbRoot.getCommandListenerCount() == 0);
  151. rpbRoot.addCommandListener(commandListener);
  152. rpbRoot.runLoop(System.in,System.out);
  153. }
  154. private static void assertTrue(boolean res)
  155. {
  156. if (!res) throw new RuntimeException("Unexpected Error");
  157. }
  158. }
复制代码

看一下这段代码:

Main.java (部分)

  1. URL res = this.getClass().getResource("/"); // .../target/classes/
  2. // Use example of RelProxy in development time:
  3. String inputPath = res.getFile() + "/../../src/main/java/";
  4. if (new File(inputPath).exists())
  5. {
  6. System.out.println("RelProxy to be enabled, development mode detected");
  7. }
  8. else
  9. {
  10. System.out.println("RelProxy disabled, production mode detected");
  11. return;
  12. }
  13. JProxyInputSourceFileExcludedListener excludedListener = new JProxyInputSourceFileExcludedListener()
  14. {
  15. @Override
  16. public boolean isExcluded(File file, File rootFolderOfSources)
  17. {
  18. String absPath = file.getAbsolutePath();
  19. if (file.isDirectory())
  20. {
  21. return absPath.endsWith(File.separatorChar + "relproxy_builtin_ex");
  22. }
  23. else
  24. {
  25. return absPath.endsWith(File.separatorChar + Main.class.getSimpleName() + ".java");
  26. }
  27. }
  28. };
复制代码

我们获取并注册应用源代码的根目录,该代码“可能”会被重新加载。

我们需要排除框架代码,因为这显然不是用户的代码(不需要重新加载)。此外,还需要排除 Main.java 文件,该文件包含了测试代码,也不需要重新加载,只有 TestListener.java 类(与 Main.java 在同一文件夹下)需要(必需)重新加载。

最后 TestListener.java 类包含两个监听器,CommandListener 的实现采用匿名内部类的方式,主要目的是为了演示。

TestListener.java

  1. package com.innowhere.relproxy_builtin_ex_main;
  2. import com.innowhere.relproxy_builtin_ex.CommandListener;
  3. import com.innowhere.relproxy_builtin_ex.OutputListener;
  4. import java.io.PrintStream;
  5. public class TestListener implements OutputListener
  6. {
  7. @Override
  8. public void write(PrintStream out)
  9. {
  10. out.println("uppercase");
  11. out.println("lowercase");
  12. }
  13. public CommandListener getCommandListener()
  14. {
  15. return new CommandListener()
  16. {
  17. @Override
  18. public void execute(String command,String text,PrintStream out)
  19. {
  20. if ("uppercase".equals(command))
  21. out.println(text.toUpperCase());
  22. else if ("lowercase".equals(command))
  23. out.println(text.toLowerCase());
  24. else
  25. out.println("Unknown command:" + command);
  26. }
  27. };
  28. }
  29. }
复制代码

先预定义可选项,然后执行 Main 类。为了校验 RelProxy 是否起作用,可以在不停止程序的运行的基础上增加一个新的可选项“same”。

  1. @Override
  2. public void write(PrintStream out)
  3. {
  4. out.println("uppercase");
  5. out.println("lowercase");
  6. out.println("same"); // NEW
  7. }
  8. public CommandListener getCommandListener()
  9. {
  10. return new CommandListener()
  11. {
  12. @Override
  13. public void execute(String command,String text,PrintStream out)
  14. {
  15. if ("uppercase".equals(command))
  16. out.println(text.toUpperCase());
  17. else if ("lowercase".equals(command))
  18. out.println(text.toLowerCase());
  19. else if ("same".equals(command)) // NEW
  20. out.println(text); // NEW
  21. else
  22. out.println("Unknown command:" + command);
  23. }
  24. };
  25. }
  26. }
复制代码

下一篇文章中将处理包含当前“same”的行为,不需要停止控制台应用。

ItsNat web 框架可能是第一个使用 RelProxy 技术的应用(版本 v1.4)。

注意:使用 RelProxy 0.8.7 或更高的版本,这个版本在嵌入方式上做了改进。

原文链接: dzone 翻译: ImportNew.com - paddx
译文链接: http://www.importnew.com/17015.html

最新评论

小黑屋|在路上 ( 蜀ICP备15035742号-1 

;

GMT+8, 2025-7-9 03:00

Copyright 2015-2025 djqfx

返回顶部