This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.

preface

Most of our daily development involves writing.java files, compiling them into.class bytecode files using javac commands, then using the Java virtual machine to load the.class files into vm memory to generate class objects, and finally creating object instances from the class objects. The compilation phase of this process is called static compilation.

Dynamic compilation usually occurs when the program generates. Java files while the JVM process is running, and the JVM dynamically compiles the. Java files into. Class bytecode files.

role

  • Reflection has low performance. Dynamic compilation can be used instead of reflection to speed up program performance.
  • Many online coding, evaluation of small tools are written in the browser. Java source file, and then uploaded to the server, dynamic compilation, and then create object instances to run functions to achieve.

Dubbo dynamic compilation

The Dubbo framework dynamically generates adapter class source files for each extension interface, and then generates adapter class instance objects using dynamic compilation techniques.

In Dubbo, a Compiler’s SPI extension interface is provided. There are two extension interface implementation classes, JavassistCompiler (the default implementation class) and JdkCompiler.

@spi ("javassist") public interface Compiler {/** * compile Java source code ** @param code source code string * @param classLoader classLoader * @return The compiled Class object */ Class<? > compile(String code, ClassLoader classLoader); }Copy the code

Dubbo framework through ExtensionLoader# createAdaptiveExtensionClass generated source files and dynamic compilation for Class Class object.

private Class<? > createAdaptiveExtensionClass () {/ / generates extension interface to the corresponding adapter class String source String code = new AdaptiveClassCodeGenerator (type, cachedDefaultName).generate(); ClassLoader classLoader = findClassLoader(); / / get the compiler interface implementation class org.apache.dubbo.common.compiler.Com piler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension(); Return compiler.compile(code, classLoader); }Copy the code

Generate source: org.apache.dubbo.com mon. The extension. AdaptiveClassCodeGenerator# generate

Public String generate() {// Do not create adapter class source code if (! hasAdaptiveMethod()) { throw new IllegalStateException("No adaptive method exist on extension " + type.getName() + ", refuse to create the adaptive class!" ); } StringBuilder code = new StringBuilder(); // Add package information, import information, class definition information code.append(generatePackageInfo()); code.append(generateImports()); code.append(generateClassDeclaration()); Method[] methods = type.getmethods (); for (Method method : methods) { code.append(generateMethod(method)); } code.append("}"); return code.toString(); }Copy the code

In the example of MonitorFactory’s extension interface, the generated adapter source code string looks like this (with some formatting, the actual source code is compact and without newlines) :

package org.apache.dubbo.monitor; import org.apache.dubbo.common.extension.ExtensionLoader; public class MonitorFactory$Adaptive implements org.apache.dubbo.monitor.MonitorFactory { public org.apache.dubbo.monitor.Monitor getMonitor(org.apache.dubbo.common.URL arg0) { if (arg0 == null) throw new IllegalArgumentException("url == null"); org.apache.dubbo.common.URL url = arg0; String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol()); if (extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.monitor.MonitorFactory) name from url (" + url.toString() + ") use keys([protocol])"); org.apache.dubbo.monitor.MonitorFactory extension = (org.apache.dubbo.monitor.MonitorFactory) ExtensionLoader.getExtensionLoader(org.apache.dubbo.monitor.MonitorFactory.class).getExtension(extName); return extension.getMonitor(arg0); }}Copy the code

Perform dynamic compilation and return the Class object of the adapter Class, using the default extension JavassistCompiler as an example.

org.apache.dubbo.common.compiler.support.JavassistCompiler#doCompile

@Override public Class<? > doCompile(String name, String source) throws Throwable {CtClassBuilder Builder = new CtClassBuilder(); builder.setClassName(name); Matcher Matcher = import_pattern.matcher (source); while (matcher.find()) { builder.addImports(matcher.group(1).trim()); } // Handle the parent class matcher = extends_pattern.matcher (source); if (matcher.find()) { builder.setSuperClassName(matcher.group(1).trim()); Matcher = implements_pattern. matcher(source); if (matcher.find()) { String[] ifaces = matcher.group(1).trim().split("\,"); Arrays.stream(ifaces).forEach(i -> builder.addInterface(i.trim())); } body = source.subString (source.indexof ('{') + 1, source.length() -1); String[] methods = METHODS_PATTERN.split(body); String className = ClassUtils.getSimpleClassName(name); Arrays.stream(methods).map(String::trim).filter(m -> ! m.isEmpty()).forEach(method-> { if (method.startsWith(className)) { builder.addConstructor("public " + method); } else if (FIELD_PATTERN.matcher(method).matches()) { builder.addField("private " + method); } else { builder.addMethod("public " + method); }}); / / dynamic compilation, returns the adapter Class Class object this this. = ClassHelper getCallerClassLoader (getClass ()); CtClass cls = builder.build(classLoader); return cls.toClass(classLoader, JavassistCompiler.class.getProtectionDomain()); }Copy the code

After generating the Class object of the adapter Class, you can then create an object instance of the adapter Class through java.lang.Class#newInstance. A method that calls an extension interface in a program is actually a method of the adapter class.