Enterprise Java incremental hot deployment solution

Zhang Yulong 666666 2021-01-21 12:07:35
enterprise java incremental hot deployment


Before I explain : I haven't updated my blog for a long time , I have done a lot of things in the company this year , Including code analysis and hot deployment replacement , I haven't had time to write some articles , What a pity , Make up an introductory article while you go to bed at noon .

First of all, the hot deployment scenario is like this , The company has a lot of projects , Really BU There are about hundreds of projects in the division , Some projects can't be started properly , So some students are modifying the code , Or in the ordinary routine task development process is blind change , And then go to the company's code platform to release , The disgusting thing is here , Some projects run from build to release about 30 minute , So every time you modify the code to make it work, you need to 30 A period of 10 minutes , This greatly reduces the development efficiency of the company , Once inertia becomes a habit , It will be very difficult to change , So we really need one after modifying the code locally , An artifact that can take effect on the server in seconds , such , Our hot deployment plug-in was born .

Hot deployment is a hard nut to crack in the industry itself , It belongs to the category of reverse programming ,JVM Class loaded , Then hot deployment needs to be unloaded and reloaded ,Spring There is context registration ,spring Bean Execute initialization lifecycle , Hot deployment is about destroying classes , Reinitialize , There are so many details in the design , Several hot deployment models in the industry have different processing methods , Because of the huge amount of underlying detail that needs to be dealt with , So at present, it is almost impossible to find a hot deployment plug-in that completely covers all functions , Generally, the hot deployment plug-ins that you hear are mainly foreign projects, such as the commercial version jrebel, Open source springloaded, And the rough ones spring dev tools. At present, these projects are ready-made complex open source projects or closed commercial projects , I want to modify the project to match my own company , It's very difficult . Gossip , To enter the body

Preface 1 : What is hot deployment

So called hot deployment , Is to upgrade the software while the application is running , There's no need to restart the app . about Java For applications , Hot deployment is updating at run time Java Class file , Trigger at the same time spring Some column reloading procedures for . There is no need to restart in the process , And the modified code takes effect in real time

Preface ii : Why we need hot deployment

The programmer restarts the service locally every day 5-12 Time , A single time is about 3-8 minute , Every day to Cargo Deploy 3-5 Time , Single time duration 20-45 minute , Deployment is frequent and frequent 、 Time consuming . The plug-in provides local and remote hot deployment capabilities to enable code changes to take effect in seconds ,RD Daily work is mainly divided into two scenarios: development self-test and joint debugging , The role of hot deployment in each scenario is described below :




Preface 3 : Where is the difficulty of hot deployment , Why is there no easy-to-use open source tool in the industry

Hot deployment is not the same as hot restart , image tomcat perhaps spring boot tool dev This kind of hot restart is equivalent to loading the project directly , Poor performance , Hot deployment of incremental files is very difficult , Need to be compatible with various middleware and user writing , High technical threshold , Need to be right JPDA(Java Platform Debugger Architecture)、java agent、 Bytecode enhancement 、classloader、spring frame 、Mybatis Framework and other integration solutions and other technical principles, in-depth understanding can fully support various frameworks , In addition, we need IDEA Plug in development capabilities , Form a whole product solution . Now there's hot deployment , Code is a little girl dressed up !

Preface 4 : Why don't we spring boot devtools

Some friends asked me , Why not use it directly spring boot devtools, There are two reasons , First, it's only used in spring boot In the project , For ordinary java Project and spring xml Projects are not supported , The second most important point is that its thermal loading scheme is actually similar to tomcat The thermal loading is the same , It's just that it's hot loaded through nesting classloader To complete , This classloader Only load... At a time class file Changed class Binary , This will bring a question , In front of a very large project ( Start about 10min+) This situation , It looks very pale . In the final analysis, the reason lies in his reload The scope is simply too great , It's OK for some small projects , But the actual effect of some relatively large projects is very touching .

1、 Overall design



2、 Walk into agent

instrument standard :https://docs.oracle.com/javase/8/docs/api/java/lang/instrument/package-summary.html?is-external=true

Class VirtualMachine:https://docs.oracle.com/javase/8/docs/jdk/api/attach/spec/com/sun/tools/attach/VirtualMachine.html#loadAgent-java.lang.String-

Interface ClassFileTransformer:https://docs.oracle.com/javase/8/docs/api/java/lang/instrument/ClassFileTransformer.html

2.1、JVM Static before startup Instrument

Javaagent yes java A parameter of the command . Parameters javaagent Can be used to specify a jar package , And to the java Package has a 2 Requirements :

  1. This jar Bag MANIFEST.MF File must be specified Premain-Class term .

  2. Premain-Class The specified class must implement premain() Method .

premain Method , Literally , Is running on main Class before function . When Java When the virtual machine starts , In execution main Function before ,JVM Will run first -javaagent specified jar In bag Premain-Class This class of premain Method .

Enter at the command line  java You can see the corresponding parameters , Among them is and java agent dependent :

-agentlib:<libname>[=< Options >] Load native agent Library <libname>, for example -agentlib:hprof
See also -agentlib:jdwp=help and -agentlib:hprof=help
-agentpath:<pathname>[=< Options >]
Load the native agent library by the full pathname
-javaagent:<jarpath>[=< Options >]
load Java Programming language agent , see also java.lang.instrument

The package provides some tools to help developers in Java Program runtime , Dynamic modification of Class type . among , A key component of using the package is Javaagent. From the name , It seems to be a Java Agency and so on , But in fact , His function is more like a Class Converter of type , He can accept external requests again at run time , Yes Class Change the type .

agent Load sequence diagram

essentially ,Java Agent It's a routine that follows a set of strict conventions Java class . The above said javaagent The command requires that the specified class must have premain() Method , And right premain The signature of the method also requires , The signature must satisfy the following two formats :

public static void premain(String agentArgs, Instrumentation inst)
public static void premain(String agentArgs)

JVM Will load first belt Instrumentation Signature method , Loading succeeded, ignoring the second type , If the first one doesn't , Load the second method . The logic is sun.instrument.InstrumentationImpl


2.2、Instrumentation Class common API

public interface Instrumentation {
// Add one more Class File Converter , Converters are used to change Class Binary stream data , Parameters canRetransform Set whether Retransformation is allowed .
void addTransformer(ClassFileTransformer transformer, boolean canRetransform);
// Before the class loads , Redefinition Class file ,ClassDefinition Represents a new definition of a class ,
If after the class loads , Need to use retransformClasses Methods redefine .addTransformer After method configuration , Subsequent class loading will be Transformer Intercept .
For classes that have already been loaded , It can be executed retransformClasses To re trigger this Transformer Interception of . Class loaded bytecode is modified , Unless it is... Again retransform, Otherwise it will not recover .
void addTransformer(ClassFileTransformer transformer);
// Delete a class Converter 
boolean removeTransformer(ClassFileTransformer transformer);
// Is it allowed to class retransform
boolean isRetransformClassesSupported();
// After the class loads , Redefinition Class. This is very important , The method is 1.6 After that, I joined , in fact , The method is update A class .
void retransformClasses(Class<?>... classes) throws UnmodifiableClassException;
// Is it allowed to class Redefinition 
boolean isRedefineClassesSupported();
// This method is used to replace the definition of the class , Instead of referencing the existing class file bytes , Just as you do when recompiling from source code to fix and continue debugging .
// Where you want to convert the bytes of an existing class file ( For example, in bytecode instrumentation ), You should use retransformClasses.
// This method can modify the method body 、 Constant pools and property values , But you can't add 、 Delete 、 Rename a property or method , You cannot modify the signature of a method 
void redefineClasses(ClassDefinition... definitions) throws ClassNotFoundException, UnmodifiableClassException;
// Access has been JVM Loaded class, Yes className May repeat ( There could be multiple classloader)
Class[] getAllLoadedClasses();

2.3、instrument principle :

instrument The underlying implementation of depends on JVMTI(JVM Tool Interface), It is JVM Some exposed interface collections for users to expand ,JVMTI It's based on event driven ,JVM Every time a certain logic is executed, the callback interface of some events will be called ( If any ), These interfaces allow developers to extend their logic .JVMTIAgent Is a use of JVMTI The exposed interface provides loading when the agent starts (agent on load)、 Agent through attach Form loading (agent on attach) And agent uninstall (agent on unload) Dynamic library of functions . and instrument agent It can be understood as a kind of JVMTIAgent Dynamic library , Another name is JPLISAgent(Java Programming Language Instrumentation Services Agent), That is to say, it is specially for java Language written instrumentation services to provide support for agents .

2.3.1、 Load on startup instrument agent The process :

  1. Create and initialize JPLISAgent;

  2. monitor  VMInit  event , stay JVM Do the following after initialization :

    1. establish InstrumentationImpl object ;

    2. monitor ClassFileLoadHook event ;

    3. call InstrumentationImpl Of loadClassAndCallPremain Method , In this method, I will call javaagent in MANIFEST.MF In the specified Premain-Class Class premain Method ;

  3. analysis javaagent in MANIFEST.MF The parameters of the file , And according to these parameters to set JPLISAgent Some of the content in .

2.3.2、 Load at run time instrument agent The process :

adopt JVM Of attach Mechanism to request the target JVM Load the corresponding agent, The process is roughly as follows :

  1. Create and initialize JPLISAgent;

  2. analysis javaagent in MANIFEST.MF In the parameters of the ;

  3. establish InstrumentationImpl object ;

  4. monitor ClassFileLoadHook event ;

  5. call InstrumentationImpl Of loadClassAndCallAgentmain Method , In this method, I will call javaagent in MANIFEST.MF In the specified Agent-Class Class agentmain Method .

2.3.3、Instrumentation The limitations of

Most of the time , We use Instrumentation They all use their bytecode instrumentation , Or in general, class redefinition (Class Redefine) The function of , But there are the following limitations :

  1. premain and agentmain The time to modify bytecode in both ways is after class file loading , That is to say, it must have Class Parameters of type , You can't redefine a class that doesn't exist by using bytecode files and custom class names .

  2. Class bytecode modification is called class conversion (Class Transform), In fact, class transformation eventually returns to class redefinition Instrumentation#redefineClasses() Method , This method has the following limitations :

    1. The parent of the new class and the old class must be the same ;

    2. The number of interfaces implemented by new and old classes should be the same , And it's the same interface ;

    3. The new and old class accessors must be the same . The number of fields of the new class and the old class should be consistent with the field name ;

    4. New and old classes must be added or deleted by private static/final Embellished ;

    5. You can modify the method body .

In addition to the way above , If you want to redefine a class , We can consider the isolation based on classloader : Create a new custom class loader to define a new class with new bytecode , However, there is a limitation that the new class can only be called through reflection .

2.4、 In those years JVM and Hotswap Love and kill each other

Around method body Of hotSwap JVM It's been improving

1.4 Start JPDA Introduced hotSwap Mechanism (JPDA Enhancements), Realized debug At the time of the method body The dynamics of

reference :https://docs.oracle.com/javase/8/docs/technotes/guides/jpda/enhancements1.4.html

1.5 Begin to pass JVMTI Realized java.lang.instrument (Java Platform SE 8 ) Of premain The way , Realized agent The dynamics of the way (JVM Specified at startup agent)

reference :https://docs.oracle.com/javase/8/docs/api/java/lang/instrument/package-summary.html

1.6 added agentmain The way , Achieve runtime dynamism ( adopt The Attach API Bind to concrete VM).

reference :https://blogs.oracle.com/corejavatechtips/the-attach-api

Its basic realization is through JVMTI Of retransformClass/redefineClass Conduct method body Level bytecode update ,ASM、CGLib And so on are basically dynamic around these .

But for Class Of hotSwap There's been no movement ( such as Class add to method, add to field, Modifying inheritance relations and so on ), Why? ? Because it's complicated and it doesn't pay too much .

2.5、 How to solve Instrumentation The limitations of

because JVM Limit ,JDK7 and JDK8 It's not allowed to change the class structure , For example, new fields , Add methods and modify the parent class of the class , This is for spring It's fatal for the project , Suppose Xiao Gong wants to modify one spring bean, I added a new one @Autowired Field , There are a lot of such scenarios in practical application , So our support for this scenario is essential .

So how do we do it , Let's welcome the famous dcevm,dcevm(DynamicCode Evolution Virtual Machine) yes java hostspot Patch for ( Strictly speaking, it's a modification ), allow ( It's not unlimited ) Modify the loaded class file in the running environment . Currently, only method bodies are allowed to be modified in the virtual machine (method bodies),decvm, Can increase Delete class properties 、 Method , Even changing the parent of a class 、dcevm It's an open source project , follow GPL 2.0、 More about dcevm Introduction to :









3、 Hot deployment technology analysis

3.1、 File monitoring

When hot deployment starts, two directories will be predefined locally and remotely first ,/var/tmp/xxx/extraClasspath and /var/tmp/xxx/classes,extraClasspath Custom extensions for us classpath url,classes Listen to the directory for us , When there is a document change , adopt idea Plug in to deploy to remote / Local , Trigger agent My listening Directory , To continue with the hot load logic below , Why don't we just replace the user's classPath The following resource file , Because the business side considers war Bag api project , and spring boot project , It's all about jar Package to start , In this way, we can't modify users directly class Of documents , Even user projects we can modify , Directly operate the user's class, It will also bring a series of security problems , So we're using expansion classPath url To modify and add files , And there's a scene , Multiple business side projects introduce the same jar package , stay jar It's equipped with mybatis Of xml Annotation , We have no way to modify this situation directly jar Source files in package , By expanding the path, you don't need to pay attention to jar Package to modify jar A file in the package and xml, Isn't it cool , In the same way, this method can carry out the whole jar Hot swapping of packages ( In the scheme design ). Here's a brief introduction to the core listener ,

3.2、jvm class reload

JVM Bytecode bulk overload logic , Through the new bytecode binary stream and the old class Object to generate ClassDefinition Definition ,instrumentation.redefineClasses(definitions), To trigger JVM heavy load , The initialization time will be triggered after overloading spring Plug in registered transfrom, In the next chapter, let's talk about spring How to overload .

newly added class How do we guarantee that we can load into classloader In the context of ? Because the project is executed remotely , So the operating environment is complex , It could be jar Package mode start (spring boot), It could be an ordinary project , It could be war web project , In view of this situation, we have made a layer of classloader url expand

User classLoader It's frame custom classLoader Generally referred to as the , for example Jetty The project is WebAppclassLoader, among Urlclasspath For the current project lib Under the file , for example spring boot The project is also from the current project BOOT-INF/lib/, wait , Different frames have slightly different custom positions . So in this case We have to get the user's customization classloader, If it's started in the normal way , Like ordinary spring xml With the help of plus Release , There is no custom for this classloader, Is the default AppClassLoader, So we start the user project with agent Bytecode enhancement to get real users classloader.

What we do : Find the user's child classloader And then get it by reflection classloader The elements in Classpath, among classPath Medium URL Is the current project loading class All of the runtime required by class Environmental Science , And it includes the tripartite jar Package dependency, etc .

We get URL Array , Expand our custom classpath Add directory to URL The first bit of the array , So when there's a new one class when , We just need to put class The file is in the expansion classpath The corresponding package directory can be found below , When there are others bean Rely on the new class when , Will look for class files from the current directory .

Why not go straight to Appclassloader Strengthen ? It's a customization of the framework classloader Strengthen


Consider such a scenario , The framework custom class loader has ClassA, Then at this time, the user added a Class B Thermal loading is required ,B class There are A Reference relation of , If we enhance AppClassLoader when , initialization B When an instance ClassLoader.loadclass First of all, from the UserClassLoader Start looking for classB, Rely on the principle of parental appointment ,B Be being Appclassloader Loaded , because B Class dependent A, So at the moment AppClassLoader load B You can't find it , Report at this time ClassNotFoundException. That is to say, we must expand the top class loader when we expand the class loader , Only in this way can we achieve the effect we want .

3.3、spring bean heavy load

spring bean reload In the process ,bean The destruction and restart process of , Among them, the fine node involves more . The main contents are shown in the figure below :


First, when you modify java class D when , adopt spring classpathScan Scan to verify the currently modified bean Whether it is spring bean( Comment check ) And then trigger the destruction process (BeanDefinitionRegistry.removeBeanDefinition) This method will change the current spring In context bean D And dependence spring bean D Of Bean C Destroy them together , But the scope is only in the present spring Context , if C In the context of quilt Bean B rely on , Cannot update dependencies in a child context , here , When traffic comes in ,Bean B Relating to Bean C Or objects before hot deployment , So hot deployment failed , So we are spring During initialization , You need to maintain a parent-child context correspondence , When the sub context changes, if the scope of change involves Bean B when , The dependency in the child context needs to be updated again , So when there is multi context association, we need to maintain the multi context environment , And the current context entry needs reload. Entrance means :spring mvc controller,Mthrift and pigeon, For different flow inlets , We use different methods reload Strategy .RPC The main operation of the framework entry is to unbind the registry , Re registration , Reloading the startup process, etc , Yes Spring mvc controller It's mainly about unbinding and registering url Mappping To realize the change switching of traffic entry class


3.4、spring xml heavy load

When users modify / newly added spring xml when , Need to be right xml All in bean Overload



again reload after , take spring Restart after destruction .

Be careful :xml The way of modification has changed a lot , It may involve the whole situation Aop The configuration of the CPU and the contents related to the pre processor and post processor , The scope of influence is the overall situation , So at present, only the ordinary xml bean New tags / modify , Other capabilities should be gradually released as appropriate .

3.5、mybatis xml heavy load


4、 Remote decompilation

Right click the plug-in in the code - Remote decompilation can view the current classpath Here's the latest version of the latest compilation class file , How is this done , The core code is as follows :

agentString+= "try {\n" +
"\t\t\tjava.lang.ClassLoader classLoader = org.springframework.beans.factory.support.DefaultListableBeanFactory.class.getClassLoader ();\n" +
"\t\t\tjava.lang.Class clazz = classLoader.loadClass ( \"org.hotswap.agent.config.PluginManager\" );\n" +
"\t\t\tjava.lang.reflect.Method method = clazz.getDeclaredMethod ( \"enhanceUserClassLoader\",new java.lang.Class[0]);\n" +
"\t\t\tmethod.setAccessible ( true );\n" +
"\t\t\tmethod.invoke ( null, new Object[0]);\n" +
"\t\t} catch (java.lang.Exception e){\n" +
"\t\t\te.printStackTrace ( );\n" +

The above code starts on the user side DefaultListableBeanFactory when , Initialize all bean After that , In the method preInstantiateSingletons After that, the current user side classloader Reverse holding + Path enhancement .

public static void enhanceUserClassLoader(){
if(springbootClassLoader != null){
LOGGER.info ( " For users classloader enhanced ,springbootClassLoader:" + springbootClassLoader );
URLClassLoaderHelper.prependClassPath ( springbootClassLoader );
LOGGER.info ( " For users classloader The enhancement was successful ,springbootClassLoader:" + springbootClassLoader );

Enhanced by using reflection at code startup classloader, Let's take a look at the core method prependClassPath

public static void prependClassPath(ClassLoader classLoader){
LOGGER.info ( " user classloader enhance ,classLoader:" + classLoader );
if(!(classLoader instanceof URLClassLoader)){
URL[] extraClasspath = PropertiesUtil.getExtraClasspath ();
prependClassPath( (URLClassLoader) classLoader,extraClasspath);

among URL[] extraClasspath = PropertiesUtil.getExtraClasspath (); Here we get the user-defined classpath, Every time you add or modify class After that, the latest resource file will be put in .

public static void prependClassPath(URLClassLoader classLoader, URL[] extraClassPath) {
synchronized (classLoader) {
try {
Field ucpField = URLClassLoader.class.getDeclaredField("ucp");
URL[] origClassPath = getOrigClassPath(classLoader, ucpField);
URL[] modifiedClassPath = new URL[origClassPath.length + extraClassPath.length];
System.arraycopy(extraClassPath, 0, modifiedClassPath, 0, extraClassPath.length);
System.arraycopy(origClassPath, 0, modifiedClassPath, extraClassPath.length, origClassPath.length);
Object urlClassPath = createClassPathInstance(modifiedClassPath);
ExtraURLClassPathMethodHandler methodHandler = new ExtraURLClassPathMethodHandler(modifiedClassPath);
ucpField.set(classLoader, urlClassPath);
LOGGER.debug("Added extraClassPath URLs {} to classLoader {}", Arrays.toString(extraClassPath), classLoader);
} catch (Exception e) {
LOGGER.error("Unable to add extraClassPath URLs {} to classLoader {}", e, Arrays.toString(extraClassPath), classLoader);

Just focus on

URL[] origClassPath = getOrigClassPath(classLoader, ucpField);

URL[] modifiedClassPath = new URL[origClassPath.length + extraClassPath.length];

System.arraycopy(extraClassPath, 0, modifiedClassPath, 0, extraClassPath.length);

System.arraycopy(origClassPath, 0, modifiedClassPath, extraClassPath.length, origClassPath.length); These lines of code

First get the user side classloader in URLClassPath Of URLS, Then configure the user's extclasspath Set the path of to URLS The first place in the array , So every time I call URLClassLoader Of findResource Method will get the latest resource file .

5、 The features we support

The function point

Do you support

Modify method body content

New method body

New non static fields

New static field

spring bean Newly added @autowired annotation

stay spring Scanning package base package Next , New band @Service Of bean, And injection

newly added xml

Add and modify static blocks

Add and modify anonymous inner class

Add and modify inheritance class

Add and modify interface method

New generic methods

modify annotation sql(Mybatis)

modify xml sql(Mybatis)

Add and modify static blocks

New anonymous inner class , modify

New internal class , modify

newly added , Delete extend Parent class ,implement Interface

Add a method to the parent class or interface , Delete method

Generic methods , Generic classes

Multi file hot deployment

spring boot project

war Package items

modify spring xml ( Modify only bean label )

newly added @Configuration @Bean

pigeon Service Framework

@Transactional Note added / modify , Annotation parameter modification

serialize Framework support
dubbo alibaba
dubbo apache

Delete inherited class

enumeration Field modification

modify static field value

Iterative mining of other functions ing


6、 Powerful to suffocating multi file hot deployment and source code exchange  

Because of the space and literary talent, I'm in a hurry , There is no way to completely write out all kinds of strange and unexplained problems encountered in the process of hot deployment , And the ups and downs . More functional requirements iteration suggestions and agent Source code technology exchange can join QQ Group to communicate in detail ,QQ Group number :825199617



本文为[Zhang Yulong 666666]所创,转载请带上原文链接,感谢

  1. 【计算机网络 12(1),尚学堂马士兵Java视频教程
  2. 【程序猿历程,史上最全的Java面试题集锦在这里
  3. 【程序猿历程(1),Javaweb视频教程百度云
  4. Notes on MySQL 45 lectures (1-7)
  5. [computer network 12 (1), Shang Xuetang Ma soldier java video tutorial
  6. The most complete collection of Java interview questions in history is here
  7. [process of program ape (1), JavaWeb video tutorial, baidu cloud
  8. Notes on MySQL 45 lectures (1-7)
  9. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  10. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  11. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  12. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  13. 【递归,Java传智播客笔记
  14. [recursion, Java intelligence podcast notes
  15. [adhere to painting for 386 days] the beginning of spring of 24 solar terms
  16. K8S系列第八篇(Service、EndPoints以及高可用kubeadm部署)
  17. K8s Series Part 8 (service, endpoints and high availability kubeadm deployment)
  18. 【重识 HTML (3),350道Java面试真题分享
  19. 【重识 HTML (2),Java并发编程必会的多线程你竟然还不会
  20. 【重识 HTML (1),二本Java小菜鸟4面字节跳动被秒成渣渣
  21. [re recognize HTML (3) and share 350 real Java interview questions
  22. [re recognize HTML (2). Multithreading is a must for Java Concurrent Programming. How dare you not
  23. [re recognize HTML (1), two Java rookies' 4-sided bytes beat and become slag in seconds
  24. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  25. RPC 1: how to develop RPC framework from scratch
  26. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  27. RPC 1: how to develop RPC framework from scratch
  28. 一次性捋清楚吧,对乱糟糟的,Spring事务扩展机制
  29. 一文彻底弄懂如何选择抽象类还是接口,连续四年百度Java岗必问面试题
  30. Redis常用命令
  31. 一双拖鞋引发的血案,狂神说Java系列笔记
  32. 一、mysql基础安装
  33. 一位程序员的独白:尽管我一生坎坷,Java框架面试基础
  34. Clear it all at once. For the messy, spring transaction extension mechanism
  35. A thorough understanding of how to choose abstract classes or interfaces, baidu Java post must ask interview questions for four consecutive years
  36. Redis common commands
  37. A pair of slippers triggered the murder, crazy God said java series notes
  38. 1、 MySQL basic installation
  39. Monologue of a programmer: despite my ups and downs in my life, Java framework is the foundation of interview
  40. 【大厂面试】三面三问Spring循环依赖,请一定要把这篇看完(建议收藏)
  41. 一线互联网企业中,springboot入门项目
  42. 一篇文带你入门SSM框架Spring开发,帮你快速拿Offer
  43. 【面试资料】Java全集、微服务、大数据、数据结构与算法、机器学习知识最全总结,283页pdf
  44. 【leetcode刷题】24.数组中重复的数字——Java版
  45. 【leetcode刷题】23.对称二叉树——Java版
  46. 【leetcode刷题】22.二叉树的中序遍历——Java版
  47. 【leetcode刷题】21.三数之和——Java版
  48. 【leetcode刷题】20.最长回文子串——Java版
  49. 【leetcode刷题】19.回文链表——Java版
  50. 【leetcode刷题】18.反转链表——Java版
  51. 【leetcode刷题】17.相交链表——Java&python版
  52. 【leetcode刷题】16.环形链表——Java版
  53. 【leetcode刷题】15.汉明距离——Java版
  54. 【leetcode刷题】14.找到所有数组中消失的数字——Java版
  55. 【leetcode刷题】13.比特位计数——Java版
  56. oracle控制用户权限命令
  57. 三年Java开发,继阿里,鲁班二期Java架构师
  58. Oracle必须要启动的服务
  59. 万字长文!深入剖析HashMap,Java基础笔试题大全带答案
  60. 一问Kafka就心慌?我却凭着这份,图灵学院vip课程百度云