动态Java代码注入

在本文中,我们将研究如何将Java代码动态加载到正在运行的jvm中。 该代码可能是全新的,或者我们可能想更改程序中某些现有代码的功能。

(在开始之前,您可能想知道为什么到底有人会这样做。显而易见的示例是规则引擎之类的东西。规则引擎希望为用户提供添加或更改规则的能力,而不必重新启动规则。您可以通过将DSL脚本作为规则注入规则库来执行此操作,这种方法的真正问题在于,必须对DSL脚本进行解释,使其运行起来极其缓慢。然后可以像程序中的任何其他代码一样编译和运行该程序,效率将提高几个数量级。

在《纪事报》中,我们在新的微秒微服务/算法容器的核心中使用了这个想法。

我们将要使用的库是Chronicle开源库Java-Runtime-Compiler 。

从下面的代码中您将看到,该库的使用极其简单-实际上,它实际上只需要几行。 创建一个CachedCompiler,然后调用loadFromJava。 (有关实际最简单的用例,请参见此处的文档。)

下面列出的程序执行以下操作:

  1. 创建一个线程,该线程每秒调用一次Strategy。 该战略的投入为10和20。
  2. 加载将两个数字相加的策略
  3. 等待3秒
  4. 加载从另一个数中减去一个数的策略

这是完整的代码清单:

package test;

import net.openhft.compiler.CachedCompiler;

/**
 * Loads the addingStrategy and then after 3s replaces it with the 
 * subtractingStrategy.
 */
public class DynamicJavaClassLoading {

    private final static String className = "test.MyClass";
    private final static String addingStrategy = "package test;\n" +
            "import test.DynamicJavaClassLoading.Strategy;\n" +
            "public class MyClass implements Strategy {\n" +
            "    public int compute(int a, int b) {\n" +
            "        return a+b;\n" +
            "    }\n" +
            "}\n";

    private final static String subtractingStrategy = "package test;\n" +
            "import test.DynamicJavaClassLoading.Strategy;\n" +
            "public class MyClass implements Strategy {\n" +
            "    public int compute(int a, int b) {\n" +
            "        return a-b;\n" +
            "    }\n" +
            "}\n";
    
    public static void main(String[] args) throws Exception {
        StrategyProxy strategy = new StrategyProxy();

        //Thread calling the strategy once a second
        Thread t = new Thread(() -> {
            while (true) {
                System.out.println(strategy.compute(10,20));
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();

        {
            ClassLoader cl = new ClassLoader() {
            };
            CachedCompiler cc = new CachedCompiler(null, null);
            Class aClass = cc.loadFromJava(cl, className, addingStrategy);

            Strategy runner = (Strategy) aClass.newInstance();
            strategy.setStratgey(runner);
        }

        Thread.sleep(3000);

        {
            ClassLoader cl = new ClassLoader() {
            };
            CachedCompiler cc = new CachedCompiler(null, null);
            Class aClass = cc.loadFromJava(cl, className, subtractingStrategy);

            Strategy runner = (Strategy) aClass.newInstance();
            strategy.setStratgey(runner);
        }
    }

    public interface Strategy{
        int compute(int a, int b);
    }

    public static class StrategyProxy implements Strategy{
        private volatile Strategy underlying;

        public void setStratgey(Strategy underlying){
            this.underlying = underlying;
        }

        public int compute(int a, int b){
            Strategy underlying = this.underlying;
            return underlying == null ? Integer.MIN_VALUE : underlying.compute(a, b);
        }
    }
}

这是输出(蓝色注释):

The strategy has not been loaded yet. underlying in the StrategyProxy is null so Integer.MIN_VALUE is returned
-2 1 4 7 4 8 3 6 4 8
The adding strategy has been loaded 10+20=30
30
30
30
After 3s the subtracting strategy is loaded. It replaces the adding strategy. 10-20=-10
-10
-10
-10
-10

-10

请注意,在每次加载策略时,在代码中我们都创建了一个新的ClassLoader和一个CachedCompiler。 这样做的原因是,ClassLoader一次只能加载一个特定类的一个实例。

如果仅使用该库来加载新代码,则可以这样做,而无需创建ClassLoader(即使用默认的ClassLoader)和CachedCompiler。

Class aClass = CompilerUtils.CACHED_COMPILER.loadFromJava(className, javaCode);

翻译自: https://www.javacodegeeks.com/2015/10/dynamic-java-code-injection.html

你可能感兴趣的:(java,编程语言,javascript,js,多线程)