我是靠谱客的博主 酷酷楼房,这篇文章主要介绍springboot 2 下 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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
package com.yrxd.telemarketing.service; import org.apache.log4j.Logger; import org.springframework.boot.loader.LaunchedURLClassLoader; import javax.lang.model.element.Modifier; import javax.lang.model.element.NestingKind; import javax.tools.*; import java.io.*; import java.net.*; import java.nio.CharBuffer; import java.util.*; import java.util.jar.JarEntry; /**  */ public class CusCompiler {   static Logger logger = Logger.getLogger(CusCompiler.class);   static class CustomJavaFileObject implements JavaFileObject {     private String binaryName;     private URI uri;     private String name;     public String binaryName() {       return binaryName;     }     public CustomJavaFileObject(String binaryName, URI uri) {       this.uri = uri;       this.binaryName = binaryName;       name = uri.getPath() == null ? uri.getSchemeSpecificPart() : uri.getPath();     }     @Override     public Kind getKind() {       return Kind.CLASS;     }     @Override     public boolean isNameCompatible(String simpleName, Kind kind) {       String baseName = simpleName + kind.extension;       return kind.equals(getKind()) && (baseName.equals(getName()) || getName().endsWith("/" + baseName));     }     @Override     public NestingKind getNestingKind() {       throw new UnsupportedOperationException();     }     @Override     public Modifier getAccessLevel() {       throw new UnsupportedOperationException();     }     @Override     public URI toUri() {       return uri;     }     @Override     public String getName() {       return name;     }     @Override     public InputStream openInputStream() throws IOException {       return uri.toURL().openStream();     }     @Override     public OutputStream openOutputStream() throws IOException {       throw new UnsupportedOperationException();     }     @Override     public Reader openReader(boolean ignoreEncodingErrors) throws IOException {       throw new UnsupportedOperationException();     }     @Override     public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {       throw new UnsupportedOperationException();     }     @Override     public Writer openWriter() throws IOException {       throw new UnsupportedOperationException();     }     @Override     public long getLastModified() {       return 0;     }     @Override     public boolean delete() {       throw new UnsupportedOperationException();     }   }   static class MemoryInputJavaFileObject extends SimpleJavaFileObject {     final String code;     MemoryInputJavaFileObject(String name, String code) {       super(URI.create(name.replaceAll("\.", "/") + Kind.SOURCE.extension), Kind.SOURCE);       this.code = code;     }     @Override     public CharBuffer getCharContent(boolean ignoreEncodingErrors) {       return CharBuffer.wrap(code);     }   }   static class MemoryOutputJavaFileObject extends SimpleJavaFileObject {     final String name;     Map<String, byte[]> class_out;     MemoryOutputJavaFileObject(String name, Map<String, byte[]> out) {       super(URI.create(name.replaceAll("\.", "/") + Kind.SOURCE.extension), Kind.CLASS);       this.name = name;       this.class_out = out;     }     @Override     public OutputStream openOutputStream() {       return new FilterOutputStream(new ByteArrayOutputStream()) {         @Override         public void close() throws IOException {           out.close();           ByteArrayOutputStream bos = (ByteArrayOutputStream) out;           class_out.put(name, bos.toByteArray());         }       };     }   }   static class SpringBootJarFileManager implements JavaFileManager {     private URLClassLoader classLoader;     private StandardJavaFileManager standardJavaFileManager;     final Map<String, byte[]> classBytes = new HashMap<>();     SpringBootJarFileManager(StandardJavaFileManager standardJavaFileManager, URLClassLoader systemLoader) {       this.classLoader = new URLClassLoader(systemLoader.getURLs(), systemLoader);       this.standardJavaFileManager = standardJavaFileManager;     }     @Override     public ClassLoader getClassLoader(Location location) {       return classLoader;     }     private List<JavaFileObject> find(String packageName) {       List<JavaFileObject> result = new ArrayList<>();       String javaPackageName = packageName.replaceAll("\.", "/");       try {         Enumeration<URL> urls = classLoader.findResources(javaPackageName);         while (urls.hasMoreElements()) {           URL ll = urls.nextElement();           String ext_form = ll.toExternalForm();           String jar = ext_form.substring(0, ext_form.lastIndexOf("!"));           String pkg = ext_form.substring(ext_form.lastIndexOf("!") + 1);           JarURLConnection conn = (JarURLConnection) ll.openConnection();           conn.connect();           Enumeration<JarEntry> jar_items = conn.getJarFile().entries();           while (jar_items.hasMoreElements()) {             JarEntry item = jar_items.nextElement();             if (item.isDirectory() || (!item.getName().endsWith(".class"))) {               continue;             }             if (item.getName().lastIndexOf("/") != (pkg.length() - 1)) {               continue;             }             String name = item.getName();             URI uri = URI.create(jar + "!/" + name);             String binaryName = name.replaceAll("/", ".");             binaryName = binaryName.substring(0, binaryName.indexOf(JavaFileObject.Kind.CLASS.extension));             result.add(new CustomJavaFileObject(binaryName, uri));           }         }       } catch (Exception e) {         e.printStackTrace();       }       return result;     }     @Override     public Iterable<JavaFileObject> list(Location location, String packageName, Set<JavaFileObject.Kind> kinds, boolean recurse) throws IOException {       Iterable<JavaFileObject> ret = null;       if (location == StandardLocation.PLATFORM_CLASS_PATH) {         ret = standardJavaFileManager.list(location, packageName, kinds, recurse);       } else if (location == StandardLocation.CLASS_PATH && kinds.contains(JavaFileObject.Kind.CLASS)) {         ret = find(packageName);         if (ret == null || (!ret.iterator().hasNext())) {           ret = standardJavaFileManager.list(location, packageName, kinds, recurse);         }       } else {         ret = Collections.emptyList();       }       return ret;     }     @Override     public String inferBinaryName(Location location, JavaFileObject file) {       String ret = "";       if (file instanceof CustomJavaFileObject) {         ret = ((CustomJavaFileObject)file).binaryName;       } else {         ret = standardJavaFileManager.inferBinaryName(location, file);       }       return ret;     }     @Override     public boolean isSameFile(FileObject a, FileObject b) {       throw new UnsupportedOperationException();     }     @Override     public boolean handleOption(String current, Iterator<String> remaining) {       return standardJavaFileManager.handleOption(current, remaining);     }     @Override     public boolean hasLocation(Location location) {       return location == StandardLocation.CLASS_PATH || location == StandardLocation.PLATFORM_CLASS_PATH;     }     @Override     public JavaFileObject getJavaFileForInput(Location location, String className, JavaFileObject.Kind kind) throws IOException {       throw new UnsupportedOperationException();     }     @Override     public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException {       throw new UnsupportedOperationException();     }     @Override     public FileObject getFileForOutput(Location location, String packageName, String relativeName, FileObject sibling) throws IOException {       throw new UnsupportedOperationException();     }     @Override     public void flush() throws IOException {     }     @Override     public void close() throws IOException {       classBytes.clear();     }     @Override     public int isSupportedOption(String option) {       return -1;     }     public Map<String, byte[]> getClassBytes() {       return new HashMap<String, byte[]>(this.classBytes);     }     @Override     public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind,                                                FileObject sibling) throws IOException {       if (kind == JavaFileObject.Kind.CLASS) {         return new MemoryOutputJavaFileObject(className, classBytes);       } else {         return standardJavaFileManager.getJavaFileForOutput(location, className, kind, sibling);       }     }   }   private static class MemoryClassLoader extends LaunchedURLClassLoader {     Map<String, byte[]> classBytes = new HashMap<>();     public MemoryClassLoader(Map<String, byte[]> classBytes, ClassLoader classLoader) {       super(new URL[0], classLoader);       this.classBytes.putAll(classBytes);     }     @Override     protected Class<?> findClass(String name) throws ClassNotFoundException {       System.out.println("findClass: " + name);       byte[] buf = classBytes.get(name);       if (buf == null) {         return super.findClass(name);       }       classBytes.remove(name);       return defineClass(name, buf, 0, buf.length);     }   }   public Class<?> loadClass(String name, Map<String, byte[]> classBytes) throws Exception {     ClassLoader loader = new ClassLoader() {       @Override       public Class<?> loadClass(String name) throws ClassNotFoundException {         Class<?> r = null;         if (classBytes.containsKey(name)) {           byte[] buf = classBytes.get(name);           r =  defineClass(name, buf, 0, buf.length);         } else {           r = systemClassLoader.loadClass(name);         }         return r;       }     };     return loader.loadClass(name);   }   private URLClassLoader systemClassLoader;   public CusCompiler(URLClassLoader loader) {     systemClassLoader = loader;   }   public Map<String, byte[]> compile(String className, String code) throws Exception {     JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();     StandardJavaFileManager stdManager = compiler.getStandardFileManager(null, null, null);     SpringBootJarFileManager springBootJarFileManager = new SpringBootJarFileManager(stdManager, systemClassLoader);     JavaFileObject javaFileObject = new MemoryInputJavaFileObject(className, code);     List<String> options = new ArrayList<>();     //options.addAll(Arrays.asList("-classpath", System.getProperty("java.class.path"), "-bootclasspath", System.getProperty("sun.boot.class.path"), "-extdirs", System.getProperty("java.ext.dirs")));     JavaCompiler.CompilationTask task = compiler.getTask(null, springBootJarFileManager, null, null, null, Arrays.asList(javaFileObject));     Boolean compileRet= task.call();     if (compileRet == null || (!compileRet.booleanValue())) {       throw new RuntimeException("java filter compile error");     }     for (String key : springBootJarFileManager.getClassBytes().keySet()) {       logger.info("class: " + key + " len: " + Integer.valueOf(springBootJarFileManager.getClassBytes().get(key).length).toString());     }     return springBootJarFileManager.getClassBytes();   } }


调用的时候 sample 如下:

复制代码
1
2
3
4
5
         class_name = "com.yrxd.telemarketing.script." + class_name;           //CusCompiler compiler = new CusCompiler((URLClassLoader)getClass().getClassLoader());           CusCompiler compiler = new CusCompiler((URLClassLoader)Thread.currentThread().getContextClassLoader());           Map<String, byte[]> results = compiler.compile(class_name, script);           Class<?> clazz = compiler.loadClass(class_name, results);


踩坑的地方如下:
0. 启动 springboot 的时候,加上 -Xbootclasspath/a:/usr/local/java/lib/tools.jar 
1. 由于 springboot 特殊的机制,它的启动 launcher 是可以从 fat jar 里面直接读取资源的,所以要充分利用这一点,编译的时候,利用 springboot 的loader 来查找资源,大胆的假设这个 loader 的类型为  URLClassLoader 
2. 尽量不要直接用springboot 启动的 loader, loader 里面的资源读过一次之后,后面读不出来,这样导致的后果是: 在 springboot 的 main 函数里面,直接动态加载会有问题,需要新建一个loader,继承springboot 的loader
3. 注意适用这种方式后,系统会有多个 loader 存在,系统的的loader 是无法loader 这里动态加载的class, 由此可能引发一系列的异常,需要 case by case分析
4. 同一个classloader,对于相同的 classname, 不能 defineclass 调用多次,否则会出异常
5. 参考:
  [http://atamur.blogspot.com/2009/10/using-built-in-javacompiler-with-custom.html](http://atamur.blogspot.com/2009/10/using-built-in-javacompiler-with-custom.html) 实现自定义的 filemanager
[https://github.com/michaelliao/compiler](https://github.com/michaelliao/compiler) 最开始找到的实现,不支持 springboot,通过把 springboot 解压后,也勉强可以用,但是在多线程的情况下,使用 mybatisplus 的lambdaQuery 会有问题
 

最后

以上就是酷酷楼房最近收集整理的关于springboot 2 下 java 代码 动态编译 动态加载 实现的全部内容,更多相关springboot内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(57)

评论列表共有 0 条评论

立即
投稿
返回
顶部