IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Java是动态语言吗?JavaCompiler实现动态编译,并通过反射赋值 -> 正文阅读

[Java知识库]Java是动态语言吗?JavaCompiler实现动态编译,并通过反射赋值

一、Java是动态语言吗?

1、动态语言

动态语言是指程序在运行时可以改变其结构:新的函数可以被引进,已有的函数可以被删除等在结构上的变化。比如JavaScript、Python都是典型的动态语言,而C、C++、Java等语言则不属于动态语言。

动态类型语言,就是类型的检查是在运行时做的,是不是合法的要到运行时才判断,例如JavaScript就没有编译错误,只要运行错误。

2、静态类型

静态类型语言的类型判断是在运行前判断(如编译阶段),比如java就是静态类型语言,静态类型语言为了达到多态会采取一些类型鉴别手段,如继承、接口,而动态类型语言却不需要。

(1)优点:?

在于其结构非常规范,便于调试,方便类型安全;

(2)缺点:

需要写更多的类型相关代码,导致不便于阅读、不清晰明了。动态类型语言的优点在于方便阅读,不需要写非常多的类型相关的代码;缺点自然就是不方便调试,命名不规范时会造成读不懂,不利于理解等。

3、为什么Java可以称之为"准动态语言"?

体现在以下几个方面:

  1.反射机制

  2.动态编译

  3.动态执行javascript代码

  4.动态字节码操作

  5.动态转换类型

Java的反射机制被视为Java为准动态语言的主要的一个关键性质,这个机制允许程序在运行时透过反射取得任何一个已知名称的class的内部信息,包括:

正在运行中的类的属性信息,正在运行中的类的方法信息,正在运行中的类的构造信息,正在运行中的类的访问修饰符,注解等等。

动态语言无时不刻在体现动态性,而静态语言也在通过其他方法来趋近于去弥补静态语言的缺陷。

二、了解ClassLoader

ClassLoader是类加载器,具体的作用就是将class文件加载到jvm虚拟机中,程序就可以正常运行了。但是,JVM启动的时候,并不会一次性的加载所有的class文件,而是根据需要动态的去加载。如果一次性加载全部的jar包,内存肯定会扛不住。

1、ClassLoader

所有类加载器的基类,它是抽象的,定义了类加载最核心的操作。所有继承classloader的加载器,都会先判断是否被父类加载器加载过,防止多次加载,防止加载冲突。

2、Bootstrap classLoader

位于java.lang.classload,所有的classload都要经过这个classload判断是否已经被加载过,采用native code实现,是JVM的一部分,主要加载JVM自身工作需要的类,如java.lang.、java.uti.等; 这些类位于$JAVA_HOME/jre/lib/rt.jar。Bootstrap ClassLoader不继承自ClassLoader,因为它不是一个普通的Java类,底层由C++编写,已嵌入到了JVM内核当中,当JVM启动后,Bootstrap ClassLoader也随着启动,负责加载完核心类库后,并构造Extension ClassLoader和App ClassLoader类加载器。

3、URLClassLoader

继承自SecureClassLoader,支持从jar文件和文件夹中获取class,继承于classload,加载时首先去classload里判断是否由bootstrap classload加载过,1.7 新增实现closeable接口,实现在try 中自动释放资源,但捕获不了.close()异常。

4、AppClassLoader

应用类加载器,继承自URLClassLoader,也叫系统类加载器(ClassLoader.getSystemClassLoader()可得到它),它负载加载应用的classpath下的类,查找范围System.getProperty(“java.class.path”),通过-cp或-classpath指定的类都会被其加载,没有完全遵循双亲委派模型的,它重要的是loadClass方法。

三、双亲委派机制

主要体现在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)

参数分别用来为:

  1. java编译器提供参数
  2. 得到Java编译器的输出信息
  3. 接收编译器的错误信息,
  4. 一个或多个Java源程式文件

如果run编译成功,返回? 0。

如果前3个参数传入的是null,那么run方法将以标准的输入、输出代替,即System.inSystem.outSystem.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步骤:

  1. 建立一个DiagnosticCollector实例
  2. 通过JavaCompiler.getStandardFileManager()方法得到一个StandardFileManager对象。
  3. 使用StandardFileManager获取需要编译的源代码。从文件或者字符流中获取源代码。
  4. JavaCompiler.getTask()生成编译任务抽象。
  5. 通过CompilationTask.call()方法编译源代码。
  6. 关闭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<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);
            }
        }
    }

    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()) {
            log.info("class: " + key + " len: "
                    + Integer.valueOf(springBootJarFileManager.getClassBytes().get(key).length).toString());
        }
        return springBootJarFileManager.getClassBytes();
    }
}

往期精彩内容:

Java知识体系总结

Spring框架总结

超详细的springBoot学习笔记

常见数据结构与算法整理总结

Java设计模式:23种设计模式全面解析

Java面试题总结(附答案)

Linux知识体系总结

Redis知识体系总结

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-07-04 19:22:40  更:2021-07-04 19:23:14 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/5 12:23:24-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码