• 动态编译和动态运行代码


    代码来源于https://github.com/hxulin/dynamic-compile-samples.git

    引入编译包

            <dependency>
                <groupId>com.itranswarp</groupId>
                <artifactId>compiler</artifactId>
                <version>1.0</version>
            </dependency>

    添加被调用的类

    package com.example.demo.dynamic;
    
    public class IndexService {
    
        public void query(){
            System.out.println("query");
        }
    }

    添加测试类

    package com.example.demo.dynamic;
    
    import com.itranswarp.compiler.JavaStringCompiler;
    
    import java.lang.reflect.Method;
    import java.util.Map;
    
    public class DynamicTest {
    
    
        public static final String code = "package com.example.demo.dynamic;
    " +
                "
    " +
                "public class UserService {
    " +
                "
    " +
                "    private IndexService service;
    " +
                "
    " +
                "    public void user(){
    " +
                "        service.query();
    " +
                "    }
    " +
                "
    " +
                "    public void setService(IndexService service){
    " +
                "        this.service = service;
    " +
                "    }
    " +
                "
    " +
                "}";
    
        public static void main(String[] args) throws Exception {
            JavaStringCompiler compiler = new JavaStringCompiler();
            Map<String, byte[]> compile = compiler.compile("UserService.java", code);
            Class<?> aClass = compiler.loadClass("com.example.demo.dynamic.UserService", compile);
    
            Method setService = aClass.getMethod("setService", IndexService.class);
            Object o = aClass.newInstance();
            setService.invoke(o,new IndexService());
            Method user = aClass.getMethod("user");
            user.invoke(o);
    
    
        }
    }

    使用jdk自带的比较复杂,所以使用已有的编译包

    补充,在后续的测试中,将测试代码添加到一个SpringBoot项目中,在idea中通过main方法启动项目,暴露接口传入java代码,可以编译,但是将springboot打包成jar启动后,传入java代码,编译失败,找不到符号

    目前有一种方法,已经测试成功可以运行,仅供测试用哈

    1、创建一个springboot项目,添加上面的编译依赖,

    项目主要用到的类

     2、dy包中的类

    package com.example.demo.dy;
    
    public class MyClassLoad extends ClassLoader {
        
         @Override
         protected Class<?> findClass(String name) throws ClassNotFoundException {
             MyJavaFileObject javaFileObject = MyJavaFileManager.fileObjects.get(name);
             if(javaFileObject != null){
                 byte[] compileByte = javaFileObject.getCompileByte();
                 return defineClass(name,compileByte,0,compileByte.length);
             }
             try {
                 return ClassLoader.getSystemClassLoader().loadClass(name);
             }catch (Exception e){
                 return super.findClass(name);
             }
         }
    
    }
    package com.example.demo.dy;
    
    import java.io.IOException;
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    
    import javax.tools.FileObject;
    import javax.tools.ForwardingJavaFileManager;
    import javax.tools.JavaFileManager;
    import javax.tools.JavaFileObject;
    
    public class MyJavaFileManager extends ForwardingJavaFileManager<JavaFileManager> {
        
         public static Map<String, MyJavaFileObject> fileObjects = new ConcurrentHashMap<>();
        
        public MyJavaFileManager(JavaFileManager fileManager) {
            super(fileManager);
        }
    
        @Override
        public JavaFileObject getJavaFileForInput(Location location, String className, JavaFileObject.Kind kind) throws IOException {
            JavaFileObject javaFileObject = fileObjects.get(className);
            if (javaFileObject == null){
                super.getJavaFileForInput(location,className,kind);
            }
            return javaFileObject;
        }
    
        @Override
        public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
            MyJavaFileObject javaFileObject = new MyJavaFileObject(className,kind);
            fileObjects.put(className,javaFileObject);
            return javaFileObject;
        }
    }
    package com.example.demo.dy;
    
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.OutputStream;
    import java.net.URI;
    
    import javax.tools.SimpleJavaFileObject;
    
    public class MyJavaFileObject extends SimpleJavaFileObject {
        
         private String source;
         private ByteArrayOutputStream outputStream;
    
         public MyJavaFileObject(String name,String source) {
             super(URI.create("String:///"+name),Kind.SOURCE);
             this.source = source;
         }
    
         public MyJavaFileObject(String name,Kind kind){
             super(URI.create("String:///"+name),kind);
             source = null;
         }
    
         @Override
         public OutputStream openOutputStream() throws IOException {
             outputStream = new ByteArrayOutputStream();
             return outputStream;
         }
    
         @Override
         public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
             if(source == null){
                 throw new IllegalArgumentException("source == null");
             }
             return source;
         }
    
         public byte[] getCompileByte(){
             return outputStream.toByteArray();
         }
    }    

    3、controller包中的类(SendController没用)

    package com.example.demo.controller;
    
    public interface UserService {
        
        public String user();
    
    }
    package com.example.demo.controller;
    
    import org.springframework.stereotype.Service;
    
    @Service
    public class IndexService {
    
        public String index() {
            return "indexService";
        }
    }
    package com.example.demo.controller;
    
    import java.io.IOException;
    import java.lang.reflect.Method;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    import java.util.Map;
    
    import javax.tools.DiagnosticCollector;
    import javax.tools.JavaCompiler;
    import javax.tools.JavaFileManager;
    import javax.tools.JavaFileObject;
    import javax.tools.ToolProvider;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RestController;
    
    import com.example.demo.dy.MyClassLoad;
    import com.example.demo.dy.MyJavaFileManager;
    import com.example.demo.dy.MyJavaFileObject;
    
    import com.itranswarp.compiler.JavaStringCompiler;
    
    @RestController
    public class IndexController {
        
        @Autowired
        private IndexService indexService;
        
    
        @GetMapping("index")
        public String index() {
            return "Success";
        }
        
        @PostMapping("compile")
        public String compile(@RequestBody String message) {
            JavaStringCompiler compiler = new JavaStringCompiler();
            try {
                Map<String, byte[]> compile = compiler.compile("UserServiceImpl.java", message,null);
                Class<?> loadClass = compiler.loadClass("com.example.demo.service.UserServiceImpl",compile);
                Object userService = loadClass.newInstance();
                Method method = loadClass.getMethod("setIndexService", IndexService.class);
                
                method.invoke(userService, indexService);
                
                if(userService instanceof UserService) {
                    UserService service = (UserService)userService;
                    return service.user();
                }
                return null;
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }finally {
                
            }
            
        }
        
        @PostMapping("compile1")
        public String compile1(@RequestBody String message) throws IOException {
            JavaStringCompiler compiler = new JavaStringCompiler();
            List<String> options = new ArrayList<>();
            options.add("-classpath");
            options.add("./BOOT-INF/classes/");
            try {
                Map<String, byte[]> compile = compiler.compile("UserServiceImpl.java", message,options);
                Class<?> loadClass = compiler.loadClass("com.example.demo.controller.UserServiceImpl",compile);
                Object userService = loadClass.newInstance();
                Method method = loadClass.getMethod("setIndexService", IndexService.class);
    
                method.invoke(userService, indexService);
    
                if(userService instanceof UserService) {
                    UserService service = (UserService)userService;
                    return service.user();
                }
                return null;
    
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }finally {
    
            }
    
        }
    
    
        @PostMapping("compile3")
        public String compile3(@RequestBody String message) {
            JavaStringCompiler compiler = new JavaStringCompiler();
            List<String> options = new ArrayList<>();
            String classpath = this.getClass().getClassLoader().getResource("").getPath();
            options.add("-cp");
            options.add(classpath);
            try {
                Map<String, byte[]> compile = compiler.compile("HelloWorld.java", message,options);
                Class<?> loadClass = compiler.loadClass("HelloWorld",compile);
    
                Method print = loadClass.getMethod("print");
                print.invoke(loadClass.newInstance());
    
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }finally {
    
            }
            return null;
    
        }
        
        @PostMapping("compile2")
        public String compile2(@RequestBody String message) {
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            DiagnosticCollector<JavaFileObject> collector = new DiagnosticCollector<>();
            JavaFileManager fileManager = new MyJavaFileManager(compiler.getStandardFileManager(collector,null,null));
            String current = this.getClass().getResource("").getPath();
            String classpath = this.getClass().getClassLoader().getResource("").getPath();
            List<String> options = new ArrayList<>();
    //        options.add("-target");
    //        options.add("1.8");
            System.out.println(classpath);
            System.out.println(current);
            options.add("-d");
            options.add(".");
            options.add("-cp");
            options.add(classpath);
            JavaFileObject javaFileObject = new MyJavaFileObject("UserServiceImpl.java",message);
            Boolean call = compiler.getTask(null, fileManager, collector, options, null, Arrays.asList(javaFileObject)).call();
            if(!call) {
                return "fail";
            }
            ClassLoader classLoader = new MyClassLoad();
            Class<?> loadClass = null;
            try {
                 loadClass = classLoader.loadClass("com.example.demo.service.UserServiceImpl");
                 Object userService = loadClass.newInstance();
                 Method method = loadClass.getMethod("setIndexService", IndexService.class);
                 method.invoke(userService, indexService);
                 if(userService instanceof UserService) {
                        UserService service = (UserService)userService;
                        return service.user();
                 }
                 
            } catch (Exception e) {
                e.printStackTrace();
            }
       
            
            return null;
        }
    }

    4、之后将项目打包,放到一个目录中,然后解压到当前目录下

    为啥要解压呢,主要是为了方便5中指定classpath(主要解决编译找不到符号的问题)

    目录结构

     5、测试接口--主要测试compile1,这个接口中在编译的时候添加了-classpath的参数,这样的话,动态编译的时候能够找到符号引用

    使用postman测试

    package com.example.demo.controller;
    
    import com.example.demo.controller.UserService;
    import com.example.demo.controller.IndexService;
    
    public class UserServiceImpl implements UserService {
        
        private IndexService indexService;
    
        public void setIndexService(IndexService indexService) {
            this.indexService = indexService;
        }
        
        @Override
        public String user() {
            return indexService.index();
        }
    
    }

     完,目前还不知道有啥问题,待后续补充吧

    6、还有一个

     就是上面的依赖的编译包,编译方法添加了个参数。

    补充:

    对compile2接口做调整

     

     添加了一个父类类加载器,不然在加载类的时候,类中关于其他类的引用加载不到

    补充:查看类能否被卸载

     添加gc方法调用

    虚拟机参数添加:

    -XX:+TraceClassUnloading

    然后查看日志打印,应该可有看到类的卸载信息

     补充:

    为了方便jar包部署,添加了一个在jar包启动完成后,进行jar包解压的操作

     

  • 相关阅读:
    第七次——例行报告
    贪吃蛇功能说明书(初稿)
    第六周——例行报告
    第五周——例行报告
    贪吃蛇界面设计初稿
    贪吃蛇需求分析
    软件工程第三次作业
    软件工程第二次作业
    软件工程第一次作业
    Python基础综合练习修改
  • 原文地址:https://www.cnblogs.com/nihaofenghao/p/12064011.html
Copyright © 2020-2023  润新知