主要体现在ClassLoader的loadClass()方法中,思路很简单:先检查是否已经被加载过,若没有加载则调用父类加载器的loadClass()方法,若父类加载器为空则默认使用启动类加载器作为父类加载器。如果父类加载器加载失败,抛出ClassNotFoundException异常后,调用自己的findClass()方法进行加载。
四、JavaCompiler动态编译
使用JavaCompiler进行动态编译。
//使用ToolProvider.getSystemJavaCompiler来得到一个JavaCompiler接口的实例。
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
//JavaCompiler中最核心的方法是run()。通过这个方法能编译java源代码。
int run(InputStream in, OutputStream out, OutputStream err, String… arguments)
参数分别用来为:
-
java编译器提供参数
-
得到Java编译器的输出信息
-
接收编译器的错误信息,
-
一个或多个Java源程式文件
如果run编译成功,返回 0。
如果前3个参数传入的是null
,那么run方法将以标准的输入、输出代替,即System.in
、System.out
和System.err
。如果我们要编译一个test.java
文件,并将使用标准输入输出,run的使用方法如下:
int results = tool.run(null, null, null, “D:\test\Student.java”);
五、通过URLClassLoader加载程序外的jar包,并进行动态编译
1、实体类Student
package com.guor.bean;
public class Student {
public Integer id;
public String name;
public void hello(String param) {
System.out.println(param);
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return “Student [id=” + id + “, name=” + name + “]”;
}
}
2、Java文件 -> class -> jar -> 动态编辑 -> 反射赋值
private void test01() throws Exception {
final String javaPath = “D:\test\java”;
final String studentPath = javaPath + “\Student.java”;
final String jarPath = “D:\test\jar\student-1.0.0.jar”;
final String packageStudentPath = “com.guor.bean.Student”;
// 将java源文件编译成.class字节码文件
String cmd = "javac " + studentPath;
System.out.println(cmd);
boolean execSysCmd = execCmd(cmd);
System.out.println(execSysCmd);
// 打成jar包
cmd = "jar -cvf " + jarPath + " " + javaPath;
System.out.println(cmd);
execSysCmd = execCmd(cmd);
System.out.println(execSysCmd);
/**
-
URLClassLoader:继承自SecureClassLoader,支持从jar文件和文件夹中获取class,
-
继承于classload,加载时首先去classload里判断是否由bootstrap classload加载过
*/
URL url = new URL(“file:” + jarPath);
URLClassLoader classLoader = new URLClassLoader(new URL[] { url },
Thread.currentThread().getContextClassLoader());
CusCompiler compiler = new CusCompiler(classLoader);
File file = new File(studentPath);
String beanTxt = FileUtils.readFileToString(file, “utf-8”);
Map<String, byte[]> results = compiler.compile(packageStudentPath, beanTxt);
Class<?> clazz = compiler.loadClass(packageStudentPath, results);
Object object = clazz.newInstance();
Method method = clazz.getDeclaredMethod(“setId”, Integer.class);
method.invoke(object, 1);
method = clazz.getDeclaredMethod(“setName”, String.class);
method.invoke(object, “哪吒”);
System.out.println(object);
method = clazz.getDeclaredMethod(“hello”, String.class);
method.invoke(object, “我命由我不由天”);
}
3、执行cmd命令
private boolean execCmd(String cmd) {
try {
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec(cmd);
InputStream es = proc.getErrorStream();
String line;
BufferedReader br;
br = new BufferedReader(new InputStreamReader(es, “GBK”));
StringBuffer buffer=new StringBuffer();
while ((line = br.readLine()) != null) {
buffer.append(line+“\n”);
}
} catch (Exception e) {
return false;
}
return true;
}
六、编译非文件形式源代码
1、通过JavaCompiler动态编译
public static void test() {
try {
String beanName = “Student”;
String appendTxt = “private String realName”;
AppendBeanUtil.appendBean(beanName, appendTxt);
//动态编译
JavaCompiler javac = ToolProvider.getSystemJavaCompiler();
String className = “D:\workspace\yygh_parent\myTest\src\main\java\com\guor\bean\Student.java”;
int status = javac.run(null, null, null, “-d”, System.getProperty(“user.dir”)+“\target\classes”,className);
if(status!=0){
System.out.println(“没有编译成功!”);
}
ClassLoader classLoader = Student.class.getClassLoader();
Class<?> classLoad = classLoader.loadClass(Student.class.getName());
Object newInstance = classLoad.newInstance();
Method setName = classLoad.getDeclaredMethod(“setName”, String.class);
setName.invoke(newInstance,“哪吒”);
System.out.println(“动态载入属性成功+++++”+newInstance);
} catch (Exception e) {
System.out.println(e);
}
}
2、springboot中动态编译工程内存在的bean
JDK 6 的编译器 API 的另外一个强大之处在于,它可以编译的源文件的形式并不局限于文本文件。JavaCompiler 类依靠文件管理服务可以编译多种形式的源文件。比如直接由内存中的字符串构造的文件,或者是从数据库中取出的文件。这种服务是由 JavaFileManager 类提供的。
在Java SE6中最佳的方法是使用StandardJavaFileManager类。这个类能非常好地控制输入、输出,并且能通过DiagnosticListener得到诊断信息,而DiagnosticCollector类就是listener的实现。新的 JDK 定义了 javax.tools.FileObject 和 javax.tools.JavaFileObject 接口。任何类,只要实现了这个接口,就可以被 JavaFileManager 识别。
使用StandardJavaFileManager步骤:
-
建立一个DiagnosticCollector实例
-
通过JavaCompiler.getStandardFileManager()方法得到一个StandardFileManager对象。
-
使用StandardFileManager获取需要编译的源代码。从文件或者字符流中获取源代码。
-
JavaCompiler.getTask()生成编译任务抽象。
-
通过CompilationTask.call()方法编译源代码。
-
关闭StandardFileManager。
代码实例:
(1)动态编译工程内存在的bean
private void test02() {
try {
String beanName = “Student”;
String appendTxt = “private String realName;”;
AppendBeanUtil.appendBean(beanName, appendTxt);
String class_name = “com.guor.bean.Student”;
URLClassLoader contextClassLoader = (URLClassLoader) Thread.currentThread().getContextClassLoader();
CusCompiler compiler = new CusCompiler(contextClassLoader);
String script = FileUtils.readFileToString(new File(“D:\workspace\yygh_parent\myTest\src\main\java\com\guor\bean\Student.java”),“utf-8”);
Map<String, byte[]> results = compiler.compile(class_name, script);
Class<?> clazz = compiler.loadClass(class_name, results);
System.out.println(“+++++++”+clazz);
Object newInstance = clazz.newInstance();
Method setName = clazz.getDeclaredMethod(“setRealName”, String.class);
setName.invoke(newInstance,“哪吒”);
System.out.println(“动态载入属性成功+++++”+newInstance);
} catch (Exception e) {
System.out.println(e);
}
}
(2)Compiler工具类
import javax.lang.model.element.Modifier;
import javax.lang.model.element.NestingKind;
import javax.tools.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.net.*;
import java.nio.CharBuffer;
import java.util.*;
import java.util.jar.JarEntry;
public class CusCompiler {
private final static Logger log = LoggerFactory.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 find(String packageName) {
List result = new ArrayList<>();
String javaPackageName = packageName.replaceAll(“\.”, “/”);
try {
Enumeration urls = classLoader.findResources(javaPackageName);
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
结语
小编也是很有感触,如果一直都是在中小公司,没有接触过大型的互联网架构设计的话,只靠自己看书去提升可能一辈子都很难达到高级架构师的技术和认知高度。向厉害的人去学习是最有效减少时间摸索、精力浪费的方式。
我们选择的这个行业就一直要持续的学习,又很吃青春饭。
虽然大家可能经常见到说程序员年薪几十万,但这样的人毕竟不是大部份,要么是有名校光环,要么是在阿里华为这样的大企业。年龄一大,更有可能被裁。
送给每一位想学习Java小伙伴,用来提升自己。
本文到这里就结束了,喜欢的朋友可以帮忙点赞和评论一下,感谢支持!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
图片转存中…(img-so8noYUw-1712627933687)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
结语
小编也是很有感触,如果一直都是在中小公司,没有接触过大型的互联网架构设计的话,只靠自己看书去提升可能一辈子都很难达到高级架构师的技术和认知高度。向厉害的人去学习是最有效减少时间摸索、精力浪费的方式。
我们选择的这个行业就一直要持续的学习,又很吃青春饭。
虽然大家可能经常见到说程序员年薪几十万,但这样的人毕竟不是大部份,要么是有名校光环,要么是在阿里华为这样的大企业。年龄一大,更有可能被裁。
送给每一位想学习Java小伙伴,用来提升自己。
[外链图片转存中…(img-ZBMEbTvy-1712627933687)]
本文到这里就结束了,喜欢的朋友可以帮忙点赞和评论一下,感谢支持!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!