利用javax.tools动态编译执行java代码

简介:

  inkfish原创,请勿商业性质转载,转载请注明来源(http://blog.csdn.net/inkfish )。

  参考:使用 javax.tools 创建动态应用程序

 

  javax.tools 包是一种添加到 Java SE 6 的标准 API,可以实现 Java 源代码编译,使您能够添加动态功能来扩展静态应用程序。本文将探查javax.tools包中提供的主要类,以Java表达式表示计算一个数值函数y=x*x+x。更多详情请参考《使用 javax.tools 创建动态应用程序》和javax.tools API docs

 

complier.CharSequenceCompiler源码:

package complier; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import javax.tools.DiagnosticCollector; import javax.tools.JavaCompiler; import javax.tools.JavaFileObject; import javax.tools.StandardLocation; import javax.tools.ToolProvider; import javax.tools.JavaCompiler.CompilationTask; /** * 编译{@link CharSequence}形式的源码,并实例化,返回一个实例。<br> * 用法示例(以编译MyInterface的一个实现类为例): * * <pre> * MyInterface instance = null; * JavaStringCompiler<MyInterface> compiler = new JavaStringCompiler<MyInterface>(null, null); * try { * Class<MyInterface> newClass = compiler.compile("com.mypackage.NewClass", * stringContaininSourceForNewClass, new Class<?>[] { MyInterface.class }); * instance = newClass.newInstance(); * } catch (JavaStringCompilerException ex) { * ex.printStackTrace(); * } catch (IllegalAccessException ex) { * ex.printStackTrace(); * } * instance.someOperation(someArgs); * </pre> */ public class CharSequenceCompiler<T> { /** 真正使用的编译器 */ private final JavaCompiler compiler; private final ClassLoaderImpl classLoader; /** 保存编译器编译中的诊断信息 */ private DiagnosticCollector<JavaFileObject> diagnostics; private final FileManagerImpl javaFileManager; /** 编译参数 */ private final List<String> options; /** * 构造一个新的实例,该实例持有指定的classloader * * @param loader * 应用的{@link ClassLoader} * @param options * 编译器的编译参数,具体可参考javac编译参数 * @throws IllegalStateException * 如果java编译器不能正确载入则抛出异常 */ public CharSequenceCompiler(ClassLoader loader, Collection<String> options) { compiler = ToolProvider.getSystemJavaCompiler(); if (compiler == null) { throw new IllegalStateException("系统java编译器无法找到,请确认类路径中已经包含tools.jar(注:JDK 6中默认自带,JRE 6中默认不带)。"); } if (loader == null) { classLoader = new ClassLoaderImpl(this.getClass().getClassLoader()); } else { classLoader = new ClassLoaderImpl(loader); } this.options = new ArrayList<String>(); if (options != null) { this.options.addAll(options); } diagnostics = new DiagnosticCollector<JavaFileObject>(); javaFileManager = new FileManagerImpl(compiler.getStandardFileManager(diagnostics, null, Charset .forName(Utils.ENCODING)), classLoader); } /** * 编译多个Java类的源码 * * @param classes * key为类的完全限定名,value为对应的源码。 * @return 编译后的类 * @throws CharSequenceCompilerException */ public synchronized Map<String, Class<T>> compile(Map<String, CharSequence> classes) throws CharSequenceCompilerException { //准备待编译文件 List<JavaFileObject> sources = new ArrayList<JavaFileObject>(); for (Entry<String, CharSequence> entry : classes.entrySet()) { String qualifiedClassName = entry.getKey(); CharSequence javaSource = entry.getValue(); if (javaSource != null) { int dotPos = qualifiedClassName.lastIndexOf('.'); String className = dotPos == -1 ? qualifiedClassName : qualifiedClassName .substring(dotPos + 1); String packageName = dotPos == -1 ? "" : qualifiedClassName.substring(0, dotPos); JavaFileObjectImpl source = new JavaFileObjectImpl(className, javaSource); sources.add(source); javaFileManager.putFileForInput(StandardLocation.SOURCE_PATH, packageName, className + ".java", source); } } //编译代码 CompilationTask task = compiler.getTask(null, javaFileManager, diagnostics, options, null, sources); Boolean result = task.call(); //返回编译结果 if ((result == null) || !result.booleanValue()) { throw new CharSequenceCompilerException("Compilation failed.", classes.keySet(), diagnostics); } try { Map<String, Class<T>> compiled = new HashMap<String, Class<T>>(); for (String qualifiedClassName : classes.keySet()) { compiled.put(qualifiedClassName, loadClass(qualifiedClassName)); } return compiled; } catch (ClassNotFoundException ex) { throw new CharSequenceCompilerException(classes.keySet(), ex, diagnostics); } catch (IllegalArgumentException ex) { throw new CharSequenceCompilerException(classes.keySet(), ex, diagnostics); } catch (SecurityException ex) { throw new CharSequenceCompilerException(classes.keySet(), ex, diagnostics); } } /** * 编译一个Java类。 * * @param qualifiedClassName * 类的完全限定名。 * @param javaSource * 编译的java类完整的源码。 * @param types * 0或多个类,用以检验被编译的类能否转换成这些类中任何一个。 * @return 编译后的类 * @throws CharSequenceCompilerException * 如果类无法被编译则抛出异常。 * @throws ClassCastException * 如果编译后的类无法转换成types中的任何一种类型,则抛出异常。 */ public synchronized Class<T> compile(String qualifiedClassName, CharSequence javaSource, Class<?>... types) throws CharSequenceCompilerException, ClassCastException { diagnostics = new DiagnosticCollector<JavaFileObject>(); Map<String, CharSequence> classes = new HashMap<String, CharSequence>(1); classes.put(qualifiedClassName, javaSource); Map<String, Class<T>> compiled = compile(classes); Class<T> newClass = compiled.get(qualifiedClassName); for (Class<?> type : types) { if (!type.isAssignableFrom(newClass)) { throw new ClassCastException(type.getName()); } } return newClass; } /** 载入Java类。 */ @SuppressWarnings("unchecked") private Class<T> loadClass(final String qualifiedClassName) throws ClassNotFoundException { return (Class<T>) classLoader.loadClass(qualifiedClassName); } }

 

complier.CharSequenceCompilerException源码:

package complier; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Set; import javax.tools.DiagnosticCollector; import javax.tools.JavaFileObject; @SuppressWarnings("serial") public class CharSequenceCompilerException extends Exception { /** 所有被编译的类的完整类名 */ private Set<String> classNames; transient private DiagnosticCollector<JavaFileObject> diagnostics; public CharSequenceCompilerException(Set<String> qualifiedClassNames, Throwable cause, DiagnosticCollector<JavaFileObject> diagnostics) { super(cause); setClassNames(qualifiedClassNames); setDiagnostics(diagnostics); } public CharSequenceCompilerException(String message, Set<String> qualifiedClassNames, DiagnosticCollector<JavaFileObject> diagnostics) { super(message); setClassNames(qualifiedClassNames); setDiagnostics(diagnostics); } public CharSequenceCompilerException(String message, Set<String> qualifiedClassNames, Throwable cause, DiagnosticCollector<JavaFileObject> diagnostics) { super(message, cause); setClassNames(qualifiedClassNames); setDiagnostics(diagnostics); } /** @return 返回编译出问题的类的全名称 */ public Collection<String> getClassNames() { return Collections.unmodifiableSet(classNames); } /** 得到异常的诊断信息 */ public DiagnosticCollector<JavaFileObject> getDiagnostics() { return diagnostics; } private void setClassNames(Set<String> qualifiedClassNames) { classNames = new HashSet<String>(qualifiedClassNames); } private void setDiagnostics(DiagnosticCollector<JavaFileObject> diagnostics) { this.diagnostics = diagnostics; } }

 

complier.ClassLoaderImpl源码:

package complier; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import javax.tools.JavaFileObject; import org.apache.commons.io.IOUtils; /** {@link ClassLoader}的一个实现,它map类名和JavaFileObjectImpl的实例。本类在{@link CharSequenceCompiler}和{@link FileManagerImpl}中被使用。 */ final class ClassLoaderImpl extends ClassLoader { private final Map<String, JavaFileObjectImpl> classes = new HashMap<String, JavaFileObjectImpl>(); ClassLoaderImpl(final ClassLoader parentClassLoader) { super(parentClassLoader); } @Override public InputStream getResourceAsStream(final String name) { if (name.endsWith(".class")) { String qualifiedClassName = name.substring(0, name.length() - ".class".length()) .replace('/', '.'); JavaFileObjectImpl file = classes.get(qualifiedClassName); if (file != null) { try { return new ByteArrayInputStream(IOUtils.toByteArray(file.openInputStream())); } catch (IOException ex) { } } } return super.getResourceAsStream(name); } protected void add(final String qualifiedClassName, final JavaFileObjectImpl javaFile) { classes.put(qualifiedClassName, javaFile); } /** @return 返回不可变的Collection,含有所有持有的{@link JavaFileObject}对象 */ protected Collection<JavaFileObjectImpl> files() { return Collections.unmodifiableCollection(classes.values()); } @Override protected Class<?> findClass(final String qualifiedClassName) throws ClassNotFoundException { JavaFileObject file = classes.get(qualifiedClassName); if (file != null) { try { byte[] bytes = IOUtils.toByteArray(file.openInputStream()); return defineClass(qualifiedClassName, bytes, 0, bytes.length); } catch (IOException ex) { } } // Workaround in Java 6. see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6434149 try { Class<?> c = Class.forName(qualifiedClassName); return c; } catch (ClassNotFoundException nf) { } return super.findClass(qualifiedClassName); } @Override protected synchronized Class<?> loadClass(final String name, final boolean resolve) throws ClassNotFoundException { return super.loadClass(name, resolve); } }

 

complier.FileManagerImpl源码:

package complier; import java.io.IOException; import java.net.URI; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.Set; import javax.tools.FileObject; import javax.tools.ForwardingJavaFileManager; import javax.tools.JavaFileManager; import javax.tools.JavaFileObject; import javax.tools.StandardLocation; import javax.tools.JavaFileObject.Kind; /** * {@link JavaFileManager}的一个实例,用于管理Java源代码和byte code。<br> * 所有的源码以{@link CharSequence}的形式保存在内存中,byte code以byte数组形式存放在内存中。 */ final class FileManagerImpl extends ForwardingJavaFileManager<JavaFileManager> { private final ClassLoaderImpl classLoader; private final Map<URI, JavaFileObject> fileObjects = new HashMap<URI, JavaFileObject>(); FileManagerImpl(JavaFileManager fileManager, ClassLoaderImpl classLoader) { super(fileManager); this.classLoader = classLoader; } @Override public ClassLoader getClassLoader(JavaFileManager.Location location) { return classLoader; } @Override public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException { FileObject o = fileObjects.get(uri(location, packageName, relativeName)); if (o != null) { return o; } return super.getFileForInput(location, packageName, relativeName); } @Override public JavaFileObject getJavaFileForOutput(Location location, String qualifiedName, Kind kind, FileObject outputFile) throws IOException { JavaFileObjectImpl file = new JavaFileObjectImpl(qualifiedName, kind); classLoader.add(qualifiedName, file); return file; } @Override public String inferBinaryName(Location loc, JavaFileObject file) { String result; if (file instanceof JavaFileObjectImpl) { result = file.getName(); } else { result = super.inferBinaryName(loc, file); } return result; } @Override public Iterable<JavaFileObject> list(Location location, String packageName, Set<Kind> kinds, boolean recurse) throws IOException { Iterable<JavaFileObject> result = super.list(location, packageName, kinds, recurse); ArrayList<JavaFileObject> files = new ArrayList<JavaFileObject>(); if ((location == StandardLocation.CLASS_PATH) && kinds.contains(JavaFileObject.Kind.CLASS)) { for (JavaFileObject file : fileObjects.values()) { if ((file.getKind() == Kind.CLASS) && file.getName().startsWith(packageName)) { files.add(file); } } files.addAll(classLoader.files()); } else if ((location == StandardLocation.SOURCE_PATH) && kinds.contains(JavaFileObject.Kind.SOURCE)) { for (JavaFileObject file : fileObjects.values()) { if ((file.getKind() == Kind.SOURCE) && file.getName().startsWith(packageName)) { files.add(file); } } } for (JavaFileObject file : result) { files.add(file); } return files; } void putFileForInput(StandardLocation location, String packageName, String relativeName, JavaFileObject file) { fileObjects.put(uri(location, packageName, relativeName), file); } private URI uri(Location location, String packageName, String relativeName) { return Utils.toURI(new StringBuilder(location.getName()).append('/').append(packageName).append('/') .append(relativeName).toString()); } }

 

complier.JavaFileObjectImpl源码:

package complier; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import javax.tools.FileObject; import javax.tools.JavaFileObject; import javax.tools.SimpleJavaFileObject; /** * {@link FileObject}和{@link JavaFileObject}的一个实现,它能持有java源代码或编译后的class。这个类可以用于: * <ol> * <li>存放需要传递给编译器的源码,这时使用的是{@link JavaFileObjectImpl#JavaFileObjectImpl(String, CharSequence)}构造器。</li> * <li>存放编译器编译完的byte code,这是使用的是{@link JavaFileObjectImpl#JavaFileObjectImpl(String, JavaFileObject.Kind)}</li> * </ol> */ final class JavaFileObjectImpl extends SimpleJavaFileObject { /** 如果kind == CLASS, 存储byte code,可以通过{@link #openInputStream()}得到 */ private ByteArrayOutputStream byteCode; /** 如果kind == SOURCE, 存储源码 */ private final CharSequence source; /** * 创建持有源码的实例 * * @param baseName * the base name * @param source * the source code */ JavaFileObjectImpl(final String baseName, final CharSequence source) { super(Utils.toURI(baseName + ".java"), Kind.SOURCE); this.source = source; } /** * 创建持有二进制byte code的实例 * * @param name * the file name * @param kind * the kind of file */ JavaFileObjectImpl(final String name, final Kind kind) { super(Utils.toURI(name), kind); source = null; } @Override public CharSequence getCharContent(final boolean ignoreEncodingErrors) throws UnsupportedOperationException { if (source == null) { throw new UnsupportedOperationException("getCharContent()"); } return source; } @Override public InputStream openInputStream() { return new ByteArrayInputStream(byteCode.toByteArray()); } @Override public OutputStream openOutputStream() { return (byteCode = new ByteArrayOutputStream()); } @Override public Writer openWriter() throws IOException { return new OutputStreamWriter(openOutputStream(), Utils.ENCODING); } }

 

complier.Utils源码:

package complier; import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.Charset; public abstract class Utils { public static final String ENCODING = Charset.defaultCharset().name(); /** 把String转换成URI,如果转换异常不抛出URISyntaxException,而直接抛出RuntimeException。 */ static URI toURI(String name) { try { return new URI(name); } catch (URISyntaxException e) { throw new RuntimeException(e); } } }

 

  以上代码为complier包中所有类,它对外暴露的主要方法是:CharSequenceCompiler<T>.compile(String qualifiedClassName, CharSequence javaSource, Class<?>... types) throws CharSequenceCompilerException, ClassCastException,通过它来动态编译字符串形式表示的java源代码。除此之外,包中其他方类和方法尽量使用默认访问权限,以避免他人误用以及隐藏实现细节。

  下面是测试package中的内容:

 

complier.test.Function源码:定义了一个接口,动态编译的所有类实现这个接口。

package complier.test; public interface Function { double doFunction(double x); }

 

complier.test.Function实现类的模板,方便类的生成。

package $packageName; import static java.lang.Math.*; public class $className implements complier.test.Function { @Override public double doFunction(double x){ return $expression; } }

 

complier.test.ExpressionCal源码:里面含有一个静态测试类ExpressionCal$Tester。

package complier.test; import java.io.IOException; import java.text.DecimalFormat; import java.util.Arrays; import java.util.Random; import javax.script.Bindings; import javax.script.Compilable; import javax.script.CompiledScript; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; import javax.script.SimpleBindings; import javax.tools.Diagnostic; import javax.tools.DiagnosticCollector; import javax.tools.JavaFileObject; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.exception.ExceptionUtils; import complier.CharSequenceCompiler; import complier.CharSequenceCompilerException; import complier.Utils; public class ExpressionCal { /** 包名前缀 */ private static final String PACKAGE_NAME = "javaxtools.compiler.test.runtime"; public static class Tester { public static void main(String[] args) throws ScriptException { //第一遍测试 test(); System.out.println("------Test Twice------/n"); test(); } public static void test() throws ScriptException { DecimalFormat df = new DecimalFormat("0.00"); int loop = 10 * 10000 - 1; String exp = "x*x+x"; double d = new Random().nextDouble() * 100; long start; //直接计算 start = System.nanoTime(); for (int i = 0; i < loop; i++) { @SuppressWarnings("unused") double a = d * d + d; } System.out.printf(exp.replace("x", df.format(d)) + "=%2.4f/n", d * d + d); System.out.printf("Time of direct cal %d loops: %10.2f微秒./n/n", loop + 1, (System.nanoTime() - start) / 1000d); //编译源码并计算 start = System.nanoTime(); Function func = new ExpressionCal().newFunction(exp); System.out.printf("Java src complain time: %10.2f微秒, /t", (System.nanoTime() - start) / 1000d); start = System.nanoTime(); for (int i = 0; i < loop; i++) { func.doFunction(d); } System.out.printf(exp.replace("x", df.format(d)) + "=%2.4f/n", func.doFunction(d)); System.out.printf("Complained source %d loops: %10.2f微秒./n/n", loop + 1, (System.nanoTime() - start) / 1000d); //内置Javascript计算 start = System.nanoTime(); ScriptEngine se = new ScriptEngineManager().getEngineByName("ECMAScript"); CompiledScript script = ((Compilable) se).compile("var x;" + exp); System.out.printf("JS complain time: %10.2f微秒, /t", (System.nanoTime() - start) / 1000d); start = System.nanoTime(); Bindings binding = new SimpleBindings(); for (int i = 0; i < loop; i++) { binding.put("x", d); script.eval(binding); } binding.put("x", d); System.out.printf(exp.replace("x", df.format(d)) + "=%2.4f/n", script.eval(binding)); System.out.printf("Javascript %d loops: %10.2f微秒./n", loop + 1, (System.nanoTime() - start) / 1000d); } } /** 类名后缀 */ private int classNameSuffix = 0; /** 随机数生成器,用于生成随机的包名和类名 */ private static final Random random = new Random(); /** 字符串形式的Java源文件内容 */ private String template; private static final String TEMPLATE_NAME = "Function.java.template"; private static final String TARGET_VERSION = "1.6"; private final CharSequenceCompiler<Function> compiler = new CharSequenceCompiler<Function>(getClass() .getClassLoader(), Arrays.asList(new String[] { "-target", TARGET_VERSION, "-encoding", Utils.ENCODING })); public Function newFunction(String expr) { StringBuilder errStr = new StringBuilder(); Function result = null; try { //生成唯一的包名和类名 final String packageName = PACKAGE_NAME + digits(); final String className = "C_" + (classNameSuffix++) + digits(); final String qName = packageName + '.' + className; //生成类的源码 final String source = fillTemplate(packageName, className, expr); //编译源码 Class<Function> compiledFunction = compiler.compile(qName, source, new Class<?>[] { Function.class }); result = compiledFunction.newInstance(); } catch (CharSequenceCompilerException ex) { errStr.append(log(ex.getDiagnostics())); ex.printStackTrace(); } catch (InstantiationException ex) { errStr.append(ExceptionUtils.getFullStackTrace(ex)).append("/n"); ex.printStackTrace(); } catch (IllegalAccessException ex) { errStr.append(ExceptionUtils.getFullStackTrace(ex)).append("/n"); ex.printStackTrace(); } catch (IOException ex) { errStr.append(ExceptionUtils.getFullStackTrace(ex)).append("/n"); ex.printStackTrace(); } if (errStr.toString().trim().length() > 0) { System.err.println(errStr.toString()); } return result; } /** @return 返回以'_'开头的随机16进制字符串 */ private String digits() { return '_' + Long.toHexString(random.nextLong()); } /** * 生成字符串形式的java源文件内容 * * @param packageName * 包名 * @param className * 类名 * @param expression * 表达式 * @return 字符串形式的java源文件内容 * @throws IOException */ private String fillTemplate(String packageName, String className, String expression) throws IOException { if (template == null) { template = IOUtils.toString(Function.class.getResourceAsStream(TEMPLATE_NAME), Utils.ENCODING); } String source = template.replace("$packageName", packageName)// .replace("$className", className)// .replace("$expression", expression); return source; } /** 记录{@link DiagnosticCollector}中的错误内容 */ private CharSequence log(final DiagnosticCollector<JavaFileObject> diagnostics) { final StringBuilder msgs = new StringBuilder(); for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()) { msgs.append(diagnostic.getMessage(null)).append("/n"); } return msgs; } }

 

ExpressionCal$Tester测试表达式x*x+x的运行,具体测试直接计算、java编译并计算、javascript编译并计算的效率,共测试三遍以观察其效率的变化,每一遍中每种方法又用for循环运行10万次计算,忽略打印输出的耗时。测试时间使用微秒为单位,精确度取决于System.nanoTime(),详见其java docs中的说明。测试结果如下:

44.53*44.53+44.53=2027.7451 Time of direct cal 100000 loops: 6461.72微秒. Java src complain time: 604570.13微秒, 44.53*44.53+44.53=2027.7451 Complained source 100000 loops: 5412.14微秒. JS complain time: 23354.09微秒, 44.53*44.53+44.53=2027.7451 Javascript 100000 loops: 8671081.10微秒. ------Test twice------ 7.67*7.67+7.67=66.4529 Time of direct cal 100000 loops: 670.48微秒. Java src complain time: 44715.18微秒, 7.67*7.67+7.67=66.4529 Complained source 100000 loops: 1397.38微秒. JS complain time: 2375.44微秒, 7.67*7.67+7.67=66.4529 Javascript 100000 loops: 8493123.29微秒. ------Test third times------ 74.34*74.34+74.34=5600.4535 Time of direct cal 100000 loops: 572.14微秒. Java src complain time: 39487.42微秒, 74.34*74.34+74.34=5600.4535 Complained source 100000 loops: 1375.04微秒. JS complain time: 1867.56微秒, 74.34*74.34+74.34=5600.4535 Javascript 100000 loops: 8624124.85微秒.

 

整理得到:

10万次计算
(单位:毫秒)
直接计算 java编译并计算 JS编译并计算
编译 计算 编译 计算
第一遍 6.46 604.57 5.41 23.35 8671.08
第二遍 0.67 44.71 1.4 2.38 8493.12
第三遍 0.57 39.49 1.38 1.87 8624.12

 

  可以看出,java直接计算速度超快,java编译并计算速度还是比直接计算慢1倍(不计编译时间),而JS的计算速度比直接计算慢4个数量级,简直惨不忍睹。第一次运行除JS计算外,均比较耗时,其中java第一次编译需要从磁盘上读取template文件,以后则均为内存操作。

  从测试结果看,如果需要运行一个固定的表达式,可以写死在Java程序中(废话),如果需要计算一个动态变化的表达式,如果计算次数较少(500次以下),JS较为划算,如果计算次数十分巨大,则需要考虑java编译并计算。

目录
相关文章
|
23小时前
|
Java API
Java Lambda表达式:简化代码、提升效率
【5月更文挑战第25天】本文将深入探讨Java中的Lambda表达式,解析它的概念、语法结构以及在实际编程中的应用。我们将通过实例来展示Lambda表达式如何帮助我们编写更简洁、更高效的代码,并讨论其对Java开发的影响和价值。
6 2
|
2天前
|
SQL Java 数据处理
实时计算 Flink版产品使用合集之在生产运行方式中是嵌入java代码还是在客户端
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStreamAPI、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
|
3天前
|
存储 Java 测试技术
滚雪球学Java(37):深入了解Java方法作用域和生命周期,让你写出更高效的代码
【5月更文挑战第12天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
12 0
滚雪球学Java(37):深入了解Java方法作用域和生命周期,让你写出更高效的代码
|
4天前
|
Java 编译器
滚雪球学Java(36):玩转Java方法重载和可变参数,让你的代码更灵活
【5月更文挑战第11天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
11 0
滚雪球学Java(36):玩转Java方法重载和可变参数,让你的代码更灵活
|
6天前
|
Java 测试技术
如何提高Java代码的可读性
Java是一种常用的编程语言,但是写出易懂且可读性高的代码却是一项挑战。本文将分享一些技巧和建议,帮助您提高Java代码的可读性和可维护性。
|
10天前
|
Java Kotlin
java调用kotlin代码编译报错“找不到符号”的问题
java调用kotlin代码编译报错“找不到符号”的问题
21 10
|
10天前
|
ARouter Java
Java注解之编译时注解
Java注解之编译时注解
16 3
|
10天前
|
前端开发 Java Spring
Java Web ——MVC基础框架讲解及代码演示(下)
Java Web ——MVC基础框架讲解及代码演示
18 1
|
10天前
|
设计模式 前端开发 网络协议
Java Web ——MVC基础框架讲解及代码演示(上)
Java Web ——MVC基础框架讲解及代码演示
10 0
|
10天前
|
Java
Java的取余如何编写代码
【5月更文挑战第9天】Java的取余如何编写代码
26 5