It seems that breakpoint, single step debugging is not hardcore enough, there is no number of people to see, this time to a core. This is again due to the growing popularity of ApaAS platforms. If ApaAS platforms choose Java as their in-platform business code, they not only face breakpoints outside the IDE, but also face the challenge of single-step debugging in order to achieve preview effects. You need to load the written Java source code dynamically into the Spring container and then call a method within the source code. This article is mainly to implement the Spring/Spring Boot runtime to compile the source code into a class bytecode array, and then the bytecode array through the custom class loader into a class object, and then the class object registered with the Spring container as a BeanDefinition. It then fetches the object directly, and finally calls the method specified in the object. I believe that other places on the Internet has been unable to find similar implementation, after all, like me to do this kind of specialized others do not have the original very few, most are reprinted under others, or write some of the Internet a lot of knowledge points, ha ha!

Personally, I think the common way of thinking in analyzing complex problems can be similar to the divide-and-conquer thought in the software field, which is to break down complex problems into small ones to solve them. Or you can use subtraction, where you solve a complex problem one small part at a time, and the remaining problems continue to be solved one small part at a time, and so on until the problem is solved. So the software world and the real world are indeed thought through, many thoughts can inspire our life, so I always believe that a programmer who is good at life, a good programmer who solves problems in life must be a good programmer, I admire this programmer very much.

We need to load the source code of a Java class directly into the Spring container and call it. The process is as follows:

1, first Java class source code dynamically compiled into byte arrays. Tools. jar is a tool that allows you to debug breakpoints and compile them at runtime. The tools.jar is a tool that allows you to debug breakpoints

2. Once you have the dynamically compiled bytecode array, you need to load the bytecode into the virtual machine and generate a Class object. This shouldn’t be too difficult, just by customizing a class loader

3. After getting the Class object, turn the Class into Spring’s Bean template object BeanDefinition. You might want to know a little bit about Spring if you look at the source code for spring startup.

4. Use Spring’s ApplicationContext object getBean to get the real object. Those of you who have used Spring know this

5. Call the specified method of the object. In order not to use reflection, the generated object usually inherits an explicit base class or implements an explicit interface, so that the peptide mechanism can receive a reference to the implementation class through the interface and then call the specified method directly.

First look at the implementation of dynamic compilation, the core source code is as follows

@author rongdi * @date 2021-01-06 */ public class DynamicCompiler {/** * Compile specified Java source code * @param JavaSrc Java source code * @return returns the fully qualified name of the class and the mapping of the compiled class bytecode byte array */ public static Map<String, byte[]> compile(String javaSrc) { Pattern pattern = Pattern.compile("public\\s+class\\s+(\\w+)"); Matcher matcher = pattern.matcher(javaSrc); if (matcher.find()) { return compile(matcher.group(1) + ".java", javaSrc); } return null; } /** * Compile specifies the Java source code * @param javaName Java file name * @param javaSrc Java source code content * @return returns the fully qualified name of the class and the mapping of the compiled class bytecode byte array */ public  static Map<String, byte[]> compile(String javaName, String javaSrc) { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager stdManager = compiler.getStandardFileManager(null, null, null); try (MemoryJavaFileManager manager = new MemoryJavaFileManager(stdManager)) { JavaFileObject javaFileObject = manager.makeStringSource(javaName, javaSrc); JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, Arrays.asList(javaFileObject)); if (task.call()) { return manager.getClassBytes(); } } catch (IOException e) { e.printStackTrace(); } return null; }}Copy the code

Then there is the implementation of the custom classloader

@author rongdi * @date 2021-01-06 */ public class DynamicClassLoader extends URLClassLoader { Map<String, byte[]> classBytes = new HashMap<String, byte[]>(); public DynamicClassLoader(Map<String, byte[]> classBytes) { super(new URL[0], DynamicClassLoader.class.getClassLoader()); this.classBytes.putAll(classBytes); } /** * public static Class<? > load(String javaSrc) throws ClassNotFoundException {** * Get the fully qualified name of the class and the byte array information of the class bytecode */ Map<String, byte[]> bytecode = dynamiccompiler.compile (javaSrc); if(bytecode ! DynamicClassLoader classLoader = new DynamicClassLoader(bytecode); Return classloader.loadClass (bytecode.keyset ().iterator().next()); } else { throw new ClassNotFoundException("can not found class"); } } @Override protected Class<? > findClass(String name) throws ClassNotFoundException { byte[] buf = classBytes.get(name); if (buf == null) { return super.findClass(name); } classBytes.remove(name); return defineClass(name, buf, 0, buf.length); }}Copy the code

The next step is to compile, load, and put the source code into the Spring container

package com.rdpaas.core.utils; import com.rdpaas.core.compiler.DynamicClassLoader; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.ConfigurableApplicationContext; * @author rongdi * @date 2021-02-06 */ public class ApplicationUtil {/** * @param applicationContext @param SRC/public static void Register (applicationContext applicationContext, String src) throws ClassNotFoundException { register(applicationContext, null, src); } @param applicationContext * @param beanName * @param SRC */ public static void register(ApplicationContext applicationContext, String beanName, String SRC) throws ClassNotFoundException {/** * use the dynamic Class loader to load Java source code to get the Class object */ Class<? > clazz = DynamicClassLoader.load(src); If (beanName == null) {beanName = clazz.getName(); beanName = clazz.getName(); Converting applicationContext ConfigurableApplicationContext} / * * * * / ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) applicationContext; / * * * get bean factory and converted into DefaultListableBeanFactory * / DefaultListableBeanFactory DefaultListableBeanFactory = (DefaultListableBeanFactory) configurableApplicationContext.getBeanFactory(); / * * * one thousand already has the BeanDefinition, remove, or a container startup couldn't call for many times, here don't use a * defaultListableBeanFactory destroySingleton (). DestroySingleton () cannot be used if the BeanDefinition registration is placed in beanDefinitionMap, not yet in singletonObjects. This is no effect of * / if (defaultListableBeanFactory. ContainsBeanDefinition (beanName)) { defaultListableBeanFactory.removeBeanDefinition(beanName); } /** * Convert Class objects to BeanDefinition */ BeanDefinitionBuilder BeanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz); / * * * to specify beanName registered BeanDefinition * / defaultListableBeanFactory generated above. RegisterBeanDefinition (beanName, beanDefinitionBuilder.getRawBeanDefinition()); } public static <T> T getBean(ApplicationContext ApplicationContext, String beanName) { return (T) ((ConfigurableApplicationContext) applicationContext).getBeanFactory().getBean(beanName); } public static <T> T getBean(ApplicationContext ApplicationContext, Class<T> clazz) { return (T) ((ConfigurableApplicationContext) applicationContext).getBeanFactory().getBean(clazz); }}Copy the code

Give some of the necessary test classes

package com.rdpaas.core.dao; import org.springframework.stereotype.Component; @author rongdi * @date 2021-01-06 */ @Component Public class TestDao {public String query(String) msg) { return "msg:"+msg; }}Copy the code
package com.rdpaas.core.service; import com.rdpaas.core.dao.TestDao; import org.springframework.beans.factory.annotation.Autowired; /** * a simple service abstraction class, which can also be an interface, mainly to bring the DAO into it. @author rongdi * @date 2021-01-06 */ public Abstract class TestService {@autoWired protected TestDao dao; public abstract String sayHello(String msg); }Copy the code

Finally, there is the entry class for the test

package com.rdpaas.core.controller; import com.rdpaas.core.service.TestService; import com.rdpaas.core.utils.ApplicationUtil; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; /** * @author rongdi * @date 2021-01-06 */ @controller public class DemoController implements ApplicationContextAware { private static String javaSrc = "package com;" + "public class TestClass extends com.rdpaas.core.service.TestService{" + " public String sayHello(String msg) {" + " Return dao. Query (MSG); + "}" + "}"; private ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } /** * test interface, which is actually to compile the Java source dynamically, load the bytecode to Class, load the Class into the Spring container, * get the object, * @return * @request Mapping("/test") @responseBody public String test() throws Exception { /** * The spring container gets an object * applicationUtil. register(applicationContext, javaSrc); */ ApplicationUtil.register(applicationContext,"testClass", javaSrc); /** * Get the beanName object from the spring context * TestService TestService = ApplicationUtil.getBean(applicationContext,TestService.class); */ TestService testService = ApplicationUtil.getBean(applicationContext,"testClass"); SayHello ("haha"); return testService.sayHello("haha"); }}Copy the code

If you think about it and get a little excited, you can do at least the following with this code

Open a portal to dynamically execute code, submit the content of the code to a POST interface, and execute the result directly

2. Now you have an APAAS platform where business logic is implemented in Java code, stored and put directly into the Spring container

3. With the breakpoint debugging in the previous article, you can now write logic using Java code on your own platform and support breakpoints and step debugging of your Java code

Well, this theme is drawing to a close again, if interested in my articles or need detailed source code, please support my WeChat public with the same number, convenient you can immediately received the article update, also let me have more power to maintain the strong passion, for we solve the problem of some online search less than, of course, if have what want me to study, Also can the article leave a message or the public number to send information. I’ll take the time to do some research for you if necessary.

 
Copy the code