使用RelProxy提高Java开发效率

开发 后端
两个目标都要求在你的应用中增加一些 RelProxy 代码,注册成一种典型的监听、回调模式。这是一种“侵入”的方式。

RelProxy 旨在通过下列两种方式提高开发效率:

可以在生产环境下修改用户代码,而不需要重新加载整个应用。

提高开发效率,避免花费过多的时间加载应用且对性能不会有影响。

[[154552]]

两个目标都要求在你的应用中增加一些 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

2

3

4

5

6

7

8

9

10

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

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

1

2

3

4

5

6

package com.innowhere.relproxy_builtin_ex;

import java.io.PrintStream;

public interface OutputListener

{

    public void write(PrintStream out);

}

CommandListener.java

1

2

3

4

5

6

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

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 (部分)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

@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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

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 (部分)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

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”。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

@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 或更高的版本,这个版本在嵌入方式上做了改进。

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

 

责任编辑:王雪燕 来源: ImportNew - paddx
相关推荐

2010-08-12 18:12:58

ibmdwRational

2012-03-14 09:35:56

JavaMaveneclipse

2017-03-21 09:11:21

Android开发效率TemplateBui

2017-09-29 10:38:30

Android

2010-12-31 14:01:01

tomcatjava效率

2012-03-07 09:41:01

Java

2010-10-25 10:55:11

Oracle函数索引

2021-07-18 07:45:04

物联网资产IOT

2015-12-15 09:50:12

Linux开发效率工具

2023-11-22 08:26:03

HutoolJava工具集

2022-09-05 14:17:48

Javascript技巧

2015-07-20 10:54:47

IOS提高效率工具

2020-03-18 15:54:41

开发效率代码

2013-07-29 12:45:19

iOS开发经验iOS提高应用开发效率

2024-08-27 22:08:13

2012-03-06 14:38:23

Visual Stud

2015-08-04 10:51:26

vim效率技巧

2011-09-30 11:13:31

51CTO博客一周热门程序员

2019-10-12 10:09:41

Vue 组件对象

2019-10-18 09:08:35

Vue组件验证码
点赞
收藏

51CTO技术栈公众号