動態編譯和動態運行代碼


代碼來源於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;\n" +
            "\n" +
            "public class UserService {\n" +
            "\n" +
            "    private IndexService service;\n" +
            "\n" +
            "    public void user(){\n" +
            "        service.query();\n" +
            "    }\n" +
            "\n" +
            "    public void setService(IndexService service){\n" +
            "        this.service = service;\n" +
            "    }\n" +
            "\n" +
            "}";

    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包解壓的操作

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM