Spring jcl及spring core源码深度解析

 更新时间:2022年11月02日 09:21:05   作者:Lemonade22  
这篇文章主要为大家介绍了Spring jcl及spring core源码深度解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

这两个内容源码虽然不算少,但是内容不太重要,在其他的 Module 里应用到了再做具体的学习。

1.spring-jcl

  • jcl 的全称为 Jakarta commons-logging,原是 apache 提供的一个抽象的日志框架,并不提供日志功能,若需要使用具体的日志则需要添加依赖的 jar 包,由于 jcl 的自我抛弃,不再进行维护了。但是框架总归是要记录日志的。所以 spring 5.0.x 框架封装了一个 jcl 框架 spring-jcl。

1.1.日志加载

spring-jcl 对外提供统一的接口,对日志的操作委托给具体的日志框架,5.0.2.RELEASE 版本中支持的日志如下:

private enum LogApi {LOG4J, SLF4J_LAL, SLF4J, JUL}

其中 JUL 为 java.util.logging ,JDK提供的基础日志功能,默认为 JUL,其他日志功能需要引入对应依赖。

静态块在类进行加载的时候就会尝试加载上述日志框架。当调用 LogFactory 工厂的 getLog 静态方法时,根据对应日志框架名称,创建对应日志类,部分源码如下:

public abstract class LogFactory {
    private static LogApi logApi = LogApi.JUL;
    static {
        ClassLoader cl = LogFactory.class.getClassLoader();
        try {
            // Try Log4j 2.x API
            cl.loadClass("org.apache.logging.log4j.spi.ExtendedLogger");
            logApi = LogApi.LOG4J;
        }
        catch (ClassNotFoundException ex1) {
            try {
                // Try SLF4J 1.7 SPI
                cl.loadClass("org.slf4j.spi.LocationAwareLogger");
                logApi = LogApi.SLF4J_LAL;
            }
            catch (ClassNotFoundException ex2) {
                try {
                    // Try SLF4J 1.7 API
                    cl.loadClass("org.slf4j.Logger");
                    logApi = LogApi.SLF4J;
                }
                catch (ClassNotFoundException ex3) {
                    // Keep java.util.logging as default
                }
            }
        }
    }
    public static Log getLog(String name) {
        switch (logApi) {
            case LOG4J:
                return Log4jDelegate.createLog(name);
            case SLF4J_LAL:
                return Slf4jDelegate.createLocationAwareLog(name);
            case SLF4J:
                return Slf4jDelegate.createLog(name);
            default:
                // Defensively use lazy-initializing delegate class here as well since the
                // java.logging module is not present by default on JDK 9. We are requiring
                // its presence if neither Log4j nor SLF4J is available; however, in the
                // case of Log4j or SLF4J, we are trying to prevent early initialization
                // of the JavaUtilLog adapter - e.g. by a JVM in debug mode - when eagerly
                // trying to parse the bytecode for all the cases of this switch clause.
                return JavaUtilDelegate.createLog(name);
        }
    }
}

该源码比较与 apache 的 jcl 简化了很多,核心类只有 LogFactory。

2.spring-core

spring核心包,主要包含 Spring 框架基本的核心工具类, Spring 的其他纽件都要用到这个包里的类, Core 模块是其他纽件的基本核心。

编译 spring-core 报错:找不到 DefaultNamingPolicy,Objenesis 类:为了避免第三方 class 的冲突,Spring 把最新的 cglib 和 objenesis 重新打包,并没有再源码提供这部分的代码,在官方文档里注释:这种重新打包技术避免了与应用程序级或第三方库和框架中不同 Objensis 版本之间的依赖关系的任何潜在冲突。

解决办法:在IDEA中打开右侧边栏的gradle,找到 Tasks --- other 模块,分别双击 cglibRepackJar 和 objenesisRepackJar 将两部分重新打包即可。或者也可以手动在 spring-core.gradle 配置文件中的 dependcies 配置项末尾添加:

dependencies {
    cglib("cglib:cglib:${cglibVersion}@jar")
    objenesis("org.objenesis:objenesis:${objenesisVersion}@jar")
    jarjar("com.googlecode.jarjar:jarjar:1.3")
    compile(files(cglibRepackJar))
    compile(files(objenesisRepackJar))
    compile(project(":spring-jcl"))
    optional("net.sf.jopt-simple:jopt-simple:5.0.4")
    optional("org.aspectj:aspectjweaver:${aspectjVersion}")
    optional("org.jetbrains.kotlin:kotlin-reflect:${kotlinVersion}")
    optional("org.jetbrains.kotlin:kotlin-stdlib:${kotlinVersion}")
    optional("io.projectreactor:reactor-core")
    optional("io.reactivex:rxjava:${rxjavaVersion}")
    optional("io.reactivex:rxjava-reactive-streams:${rxjavaAdapterVersion}")
    optional("io.reactivex.rxjava2:rxjava:${rxjava2Version}")
    optional("io.netty:netty-buffer")
    testCompile("io.projectreactor:reactor-test")
    testCompile("javax.xml.bind:jaxb-api:2.3.0")
    testCompile("org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}")
    testCompile("com.fasterxml.woodstox:woodstox-core:5.0.3") {
        exclude group: "stax", module: "stax-api"
    }
    compile fileTree(dir: 'libs', include : '*.jar') //添加该行
}

重新导入即可。

2.1.目录结构

asm:一个 Java 字节码操控框架。它能够以二进制形式修改已有类或者动态生成类。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类

cglib:一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为 jdk 的动态代理提供了很好的补充。通常可以使用 Java 的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,cglib 是一个好的选择

core:核心包

  • 根目录:别名注册、属性访问。
  • annotation:注解、元注解、合并的注解等。
  • codec:encode 和 decode 输入流 (主要用于编解码) 。
  • convert:主要是转换器服务,将一个类型转换位另外一个类型。
  • env:系统环境 (jdk环境参数:System.getProperties()、System.getenv())。
  • io:访问资源(主要是文件系统,也有字节流、jboss VFS)的工具
  • serializer:java 对象的字节流序列化和反序列化工具。
  • style:用来控制 java 对象输出为 String 的风格。
  • task:封装了一套同步和异步执行任务的 executor (并未使用线程池)。
  • type:访问 class meta 的工具。

lang:注解定义

objenesis:用于实例化一个特定 class 的对象,在类库中经常会有类必须拥有一个默认构造器的限制。Objenesis 通过绕开对象实例构造器来克服这个限制。常见使用场景有:

序列化,远程调用和持久化对象需要实例化并存储为到一个特殊的状态,而没有调用代码

代理,AOP 库和 Mock 对象,类可以被子类继承而子类不用担心父类的构造器

容器框架,对象可以以非标准的方式被动态实例化

util:工具类,这个包的工具类可以独立于 Spring 框架而存在;而 core 工具类主要还是为 Spring 框架所用,与 Spring 结合比较紧密。

2.2.源码说明

  • 这里的部分并不常用,或者说其原理不是很重要,简单说明。

2.2.1.asm类解读

*Visitor 抽象类:包含 AnnotationVisitor、ClassVisitor、FieldVisitor、MethodVisitor、ModuleVisitor 五个抽象类:

  • AnnotationVisitor:定义在解析注解时会触发的事件,如解析到一个基本值类型的注解、Enum 值类型的注解、Array 值类型的注解、注解值类型的注解等。
  • ClassVisitor:定义在读取 Class 字节码时会触发的事件,如类头解析完成、注解解析、字段解析、方法解析等。
  • FieldVisitor:定义在解析字段时触发的事件,如解析到字段上的注解、解析到字段相关的属性等。
  • MethodVisitor:定义在解析方法时触发的事件,如方法上的注解、属性、代码等。
  • ModuleVisitor:定义在访问 Module 时触发的事件,如访问包等。

*Writer:包含 AnnotationWriter、ClassWriter、FieldWriter、MethodWriter、ModuleWriter 五个类,分别继承上述对应的 *Visitor 抽象类,用于生成上述对应类型的二进制字节码。

  • ClassReader:类的读取解析。
  • Attribute:字节码中属性的类抽象。
  • ByteVector:字节码二进制存储的容器。
  • Opcodes:字节码指令的一些常量定义。
  • Type*:类型相关的常量定义以及一些基于其上的操作。

2.2.2.core

annotation:注解、元注解、合并的注解等,注解相关的类和操作都在该包下,主要包含两大部分,一部分是关于 @AliasFor 注解的定义及其相关的使用说明,如AnnotationUtils,AnnotationAttributes 等类都是为了 @AliasFor 注解的使用做出的相关配套设施,另一部分是 @Order 类及其相关配套:

第一部分:@AliasFor 及其部分相关配套

@AliasFor:该注解相关信息必须通过 AnnotationUtils 加载,通常有以下几种用法:

别名,在注解定义中的属性上使用,如 @RequestMapping 注解的 path 和 value 属性,指定其中一个即指定了另一个:

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
    String name() default "";
    @AliasFor("path")
    String[] value() default {};
    @AliasFor("value")
    String[] path() default {};
    RequestMethod[] method() default {};
    String[] params() default {};
    String[] headers() default {};
    String[] consumes() default {};
    String[] produces() default {};
}

继承父注解的属性,不重写属性名,子注解的属性值的读写,其实是对父注解的属性值的读写,如@Service、@Controller 等价于 @Component:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component  //注意要声明父注解
public @interface Service {
    /**
     * The value may indicate a suggestion for a logical component name,
     * to be turned into a Spring bean in case of an autodetected component.
     * @return the suggested component name, if any (or empty String otherwise)
     */
    @AliasFor(annotation = Component.class)
    String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component  //注意要声明父注解
public @interface Controller {
    /**
     * The value may indicate a suggestion for a logical component name,
     * to be turned into a Spring bean in case of an autodetected component.
     * @return the suggested component name, if any (or empty String otherwise)
     */
    @AliasFor(annotation = Component.class)
    String value() default "";
}

继承父注解的属性,并重写属性名,需指定父注解的类型以及具体的属性,子注解的属性值的读写,其实是对父注解的属性值的读写,若两个都指明属性值,要求值必须相同,否则会报错。

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
@Documented
@Inherited
public @interface MyAnnotation {
    @AliasFor(attribute = "location")
    String value() default "";
    @AliasFor(attribute = "value")
    String location() default "";
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
@Documented
@Inherited
@MyAnnotation  //注意要声明父注解
public @interface SubMyAnnotation {
    @AliasFor(attribute = "value", annotation = MyAnnotation.class)
    String subValue() default "";
    @AliasFor(attribute = "location", annotation = MyAnnotation.class)
    String subLocation() default "";
//    subLocation属性写成下边这两种结果是一样的
//    @AliasFor(attribute = "value", annotation = MyAnnotation.class)
//    String subLocation() default "";
//    @AliasFor(value = "location", annotation = MyAnnotation.class)
//    String subLocation() default "";
//
}

注解的叠加复用,如@SpringBootApplication,在注解定义上配置需要复用的注解,并在指定属性上声明复用的注解属性 (本质上还是相当于实现了注解的继承功能):

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
    //复用@EnableAutoConfiguration注解的exclude属性
    @AliasFor(annotation = EnableAutoConfiguration.class)
    Class<?>[] exclude() default {};
    //复用@EnableAutoConfiguration注解的excludeName属性
    @AliasFor(annotation = EnableAutoConfiguration.class)
    String[] excludeName() default {};
    //复用@ComponentScan的basePackages属性
    @AliasFor(annotation = ComponentScan.class,attribute = "basePackages")
    String[] scanBasePackages() default {};
    //复用@ComponentScan的basePackageClasses属性
    @AliasFor(annotation = ComponentScan.class,attribute = "basePackageClasses")
    Class<?>[] scanBasePackageClasses() default {};
}

@AliasFor 注解需要通过 Spring 中提供的工具类 AnnotationUtils 或 AnnotatedElementUtils 来解析才能生效。AnnotatedElementUtils 内部还是调用的 AnnotationUtils。互为别名的属性值,使用的时候如果均赋值,同时又通过 AnnotationUtils 的 findAnnotation 方法获取属性值,那么会抛出异常。Spring 其实是自己实现了 jdk 动态的拦截器来实现别名功能。

@RequestMapping("/test")
public class App{}
@Test
public void test(){
    RequestMapping springAnnotationProxy = AnnotationUtils.findAnnotation(App.class, RequestMapping.class);
    System.out.println("springAnnotationProxy path:" + springAnnotationProxy.path());
    System.out.println("springAnnotationProxy value:" + springAnnotationProxy.value());
    RequestMapping jdkAnnotation = App.class.getAnnotation(RequestMapping.class);
    System.out.println("jdkAnnotation path:" + jdkAnnotation.path());
    System.out.println("jdkAnnotation value:" + jdkAnnotation.value());
}

debug 功能可以看出,springAnnotationProxy 的底层类型是 SynthesizedAnnotationInvocationHandler,其 value 和 path 属性值均为 test,而 jdkAnnotation 的底层类型是 AnnotationInvocationHandler,其只有 value 属性值为 test,path 属性为空。

AnnotationAttributes:本质上是一个 Map<String, Object> 集合,继承 LinkedHashMap,用于保存某个注解实例的全部属性 (属性名和对应的属性值) 。

AnnotationAwareOrderComparator:对 OrderComparator 的增强 (继承) ,可以对 Ordered 对象、使用 @Order 注解的对象进行比较。与OrderComparator 一样提供了如上两个静态的排序方法。

AnnotationUtils 中包含了很多解析注解的方法:

public static <A extends Annotation> A getAnnotation(Annotation ann, Class<A> annotationType)
public static <A extends Annotation> A getAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType)
public static <A extends Annotation> A getAnnotation(Method method, Class<A> annotationType)
public static Annotation[] getAnnotations(AnnotatedElement annotatedElement)
public static Annotation[] getAnnotations(Method method)
// 获取函数上或注解上的注解
        -   public static <A extends Annotation> A findAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType)
            public static <A extends Annotation> A findAnnotation(Method method, @Nullable Class<A> annotationType)
            public static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType)
            递归查找父注解
        -   public static boolean isAnnotationDeclaredLocally(Class<? extends Annotation> annotationType, Class<?> clazz)
            public static boolean isAnnotationInherited(Class<? extends Annotation> annotationType, Class<?> clazz)
            public static boolean isAnnotationMetaPresent(Class<? extends Annotation> annotationType, @Nullable Class<? extends Annotation> metaAnnotationType)
            是否声明了/继承了某个注解
        -   public static Map<String, Object> getAnnotationAttributes(Annotation annotation)
            public static Map<String, Object> getAnnotationAttributes(Annotation annotation, boolean classValuesAsString)
            public static AnnotationAttributes getAnnotationAttributes(@Nullable AnnotatedElement annotatedElement, Annotation annotation)
            获取注解的所有属性,返回 AnnotationAttributes
        -   public static Object getValue(Annotation annotation)
            public static Object getValue(@Nullable Annotation annotation, @Nullable String attributeName)
            public static Object getDefaultValue(Annotation annotation)
            public static Object getDefaultValue(@Nullable Annotation annotation, @Nullable String attributeName)
            public static Object getDefaultValue(Class<? extends Annotation> annotationType)
            public static Object getDefaultValue(@Nullable Class<? extends Annotation> annotationType, @Nullable String attributeName)
    获取注解的值以及默认值

第二部分:@Order 及其部分相关配套

-   @Order:@Order 注解定义了类、方法和字段的优先级(排序情况),value 是可选的,默认为 Ordered.LOWEST_PRECEDENCE,即最低优先级。表示 Ordered 接口中的 order 属性。

-   OrderUtils:主要用于获取 @Order 注解的相关属性。

-   OrderComparator(根目录下):Ordered 对象的比较器,对外提供了两个静态的排序方法

public static void sort(List<?> list)、public static void sort(Object[] array)。

codec:编解码工具,不常用,略

convert:主要用于类型转换

TypeDescriptor:对java中所有数据类型的描述,包括 Collection、Map、自定义 Object、数组、基本数据类型及其包装类型。它被成对地用于GenericConversionService 中,表示从 src 类型到 target 类型的转换,会有一个相应的 converter 实现与之对应。为了提高性能,对基本数据类型及其包装类型做了缓存。

DefaultConversionService:对外提供的 API 的主要服务类

env:提供一个代表系统环境的 StandardEnvironment,包括对 System.getProperties()、System.getenv() 的访问。在 Spring Web 中StandardServletEnvironment 继承了 StandardEnvironment,又增加对 Servlet 容器系统参数的访问。

io:供外部调用的入口API主要是 FileSystemResourceLoader、ClassRelativeResourceLoader。spring web、spring context 等模块也会自定义覆盖/实现ResourceLoader的子类和子接口。

serializer: java 对象的字节流序列化和反序列化

style:略

task:实现了两个适配器,分别是

并提供了一个简单的异步 TaskExecutor 的实现 SimpleAsyncTaskExecutor。它继承 CustomizableThreadCreator,用来创建线程并给线程命名(有意义的名字),需要注意的是它并没有使用线程池。ConcurrencyThrottleSupport 在 SimpleAsyncTaskExecutor 中用于控制并发,应用了生产者-消费者模式。

  • TaskExecutorAdapter:将 Executor 适配为 TaskExecutor
  • ExecutorServiceAdapter,将 TaskExecutor 适配为 Executor/ExecutorService。

type:对外暴露的、可直接使用的API接口为 CachingMetadataReaderFactory。基于 ASM,使用了访问者设计模式,同时使用 cache 来避免了对 class 的重复加载和解析。

根目录的部分其他类:

  • AliasRegistry、SimpleAliasRegistry:别名注册,保存的是Map from alias to canonical name,解决了别名循环的问题。
  • CollectionFactory:提供了几个静态函数,Collection createCollection(Class collectionType, int initialCapacity),Map createMap(Class mapType, int initialCapacity) 等等。
  • Constants:提供了对一个类中 public static final 且名字为大写的字段的访问。
  • ControlFlow、ControlFlowFactory:判断当前运行的堆栈中是否包含某个class或某个函数。
  • DecoratingClassLoader:供其他包来继承的一个抽象类,加载时可以让使用者有选择的排除掉某些class和某些package下的class。
  • ExceptionDepthComparator:用来比较exception和targetException深度的比较器。对外提供了一个静态工具方法:Class<? extends Throwable> findClosestMatch(Collection<Class<? extends Throwable>> exceptionTypes, Throwable targetException)
  • GenericCollectionTypeResolver:一个很有用、很强大的工具类,用来获取Collection中元素的类型、Map中的Key/Value类型。
  • GenericTypeResolver:工具类,获取函数的参数类型、返回类型。
  • LocalVariableTableParameterNameDiscoverer、ParameterNameDiscoverer:基于ASM,获取函数和构造器的参数名字。
  • SpringProperties:加载spring.properties到Properties中,并访问其中的属性。

2.2.3.util

  • BooleanComparator:对 Boolean 数据进行排序(顺序和逆序)用到的比较器;
  • ComparableComparator:适配 Comparable 的 Comparator。
  • CompoundComparator:组合模式。多个 Comparator 组合成一个 Comparator
  • InstanceComparator:基于任意类顺序比较对象。
  • InvertibleComparator:可逆的比较器 (装饰者设计模式)。
  • NullSafeComparator:将一个只能对非 null 对象排序的比较器,装饰成一个可以对 null 对象排序的比较器 (装饰者设计模式)。
  • ClassUtils:class 工具类。包括装载 class,访问 class 名字、包名、函数、构造器、class类型是否是基本类型、接口等等。
  • CollectionUtils:封装了少数几个集合工具的静态方法。
  • CompositeIterator:多个迭代期组合到一起的迭代期,使用了组合设计模式。
  • ConcurrentReferenceHashMap:与 ConcurrentHashMap 不同的是,它的 key 和 value 存的是是软引用或弱引用 (可在构造器中指定)
  • PropertiesPersister、DefaultPropertiesPersister:实现了对 Properties 的加载和存储。
  • FileCopyUtils:在File、byte[]、IO流之间的转换。
  • FileSystemUtils:对文件、文件夹的递归删除和递归拷贝。
  • LinkedCaseInsensitiveMap:对 key 大小写不敏感的 LinkedHashMap 实现。
  • LinkedMultiValueMap:一个 key 映射多个 value 的 Map 结构。
  • MethodInvoker:将方法、object、class、方法参数封装进来,以供该方法的调用。
  • NumberUtils:将 String 转为制定类型的 Number。
  • ObjectUtils:实现了 基本数据类型及其数组的 hashcode,基本数据类型及其数组的 toString() 方法等等。
  • PropertyPlaceholderHelper:提供了两个重载函数 replacePlaceholders,用来替换字符串 value 中中的占位符。
  • ReflectionUtils:提供了访问field、method、函数调用等工具方法。
  • SerializationUtils:java 序列化和反序列化
  • ResourceUtils:用于将资源位置解析为文件系统中的文件的实用工具方法。
  • StopWatch:对任务执行时间的统计,包括总时间、每个任务的执行时间。
  • StreamUtils:IO 字节流和字节数组之间的拷贝。
  • StringUtils:字符串为空、长度、trim 操作、等等。
  • SystemPropertyUtils:与 PropertyPlaceholderHelper 不同的是,它使用 System.getProperty() 来替换占位符。
  • TypeUtils:boolean isAssignable(Type lhsType, Type rhsType)

2.2.4.cglib&langobjenesis

  • 这里主要引用了一些 jar 包以及部分类的声明,内容很少,不再赘述。

以上就是Spring jcl及spring core源码深度解析的详细内容,更多关于Spring jcl spring core解析的资料请关注脚本之家其它相关文章!

相关文章

  • Spring boot actuator端点启用和暴露操作

    Spring boot actuator端点启用和暴露操作

    这篇文章主要介绍了Spring boot actuator端点启用和暴露操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • java在cmd中乱码的问题解决

    java在cmd中乱码的问题解决

    本文深入探讨了在使用Java命令行cmd时可能出现的中文乱码问题,并提供了两种解决方案,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧
    2024-03-03
  • java RSAUtils 加密工具类操作

    java RSAUtils 加密工具类操作

    这篇文章主要介绍了java RSAUtils 加密工具类操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-08-08
  • Jenkins初级应用之Invoke Phing targets插件配置

    Jenkins初级应用之Invoke Phing targets插件配置

    这篇文章主要为大家介绍了Jenkins初级应用之Invoke Phing targets的插件配置,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步早日升职加薪<BR>
    2022-04-04
  • java堆排序概念原理介绍

    java堆排序概念原理介绍

    在本篇文章里我们给大家分享了关于java堆排序的概念原理相关知识点内容,有需要的朋友们可以学习下。
    2018-10-10
  • JetBrains IntelliJ IDEA 2020安装与使用教程详解

    JetBrains IntelliJ IDEA 2020安装与使用教程详解

    这篇文章主要介绍了JetBrains IntelliJ IDEA 2020安装与使用教程,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-06-06
  • SpringBoot Admin使用及心跳检测原理分析

    SpringBoot Admin使用及心跳检测原理分析

    这篇文章主要介绍了SpringBoot Admin使用及心跳检测原理分析,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • Java servlet后端开发超详细教程

    Java servlet后端开发超详细教程

    Servlet指在服务器端执行的一段Java代码,可以接收用户的请求和返回给用户响应结果,下面这篇文章主要给大家介绍了关于Java.servlet生命周期的相关资料,需要的朋友可以参考下
    2023-02-02
  • spring boot 即时重新启动(热更替)使用说明

    spring boot 即时重新启动(热更替)使用说明

    这篇文章主要介绍了spring boot 即时重新启动(热更替)的相关资料,需要的朋友可以参考下
    2017-12-12
  • IDEA安装后找不到.vmoptions文件的问题及解决

    IDEA安装后找不到.vmoptions文件的问题及解决

    这篇文章主要介绍了IDEA安装后找不到.vmoptions文件的问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-04-04

最新评论