欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot啟動流程SpringApplication準(zhǔn)備階段源碼分析

 更新時間:2023年04月03日 14:01:01   作者:唐小碼  
這篇文章主要為大家介紹了SpringBoot啟動流程SpringApplication準(zhǔn)備階段源碼分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

SpringBoot啟動流程源碼分析一、入口參數(shù)研究和創(chuàng)建對象

我們在上一篇的時候主要針對入口參數(shù)和SpringApplication創(chuàng)建對象的過程進(jìn)行了研究,本篇主要針對執(zhí)行run方法之前的準(zhǔn)備過程進(jìn)行研究。

準(zhǔn)備階段分析

以下先看下SpringApplication的run()方法

package org.springframework.boot;
public ConfigurableApplicationContext run(String... args) {
   //1.計時器
   StopWatch stopWatch = new StopWatch();
   stopWatch.start();
   ConfigurableApplicationContext context = null;
   Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
   //2.headless配置
   configureHeadlessProperty();
   //3、獲取監(jiān)聽
   SpringApplicationRunListeners listeners = getRunListeners(args);
   listeners.starting();
   try {
   //應(yīng)用程序啟動的參數(shù)  
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
      //4、準(zhǔn)備環(huán)境
      ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
      //環(huán)境創(chuàng)建成功后,配置bean信息,決定是否跳過 BeanInfo 類的掃描,如果設(shè)置為 true,則跳過
      configureIgnoreBeanInfo(environment);
      //打印banner信息
      Banner printedBanner = printBanner(environment);
      context = createApplicationContext();
      exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
            new Class[] { ConfigurableApplicationContext.class }, context);
      prepareContext(context, environment, listeners, applicationArguments, printedBanner);
      refreshContext(context);
      afterRefresh(context, applicationArguments);
      //停止計時
      stopWatch.stop();
      //控制是否打印日志的,這里為true,即打印日志
      if (this.logStartupInfo) {
         new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
      }
      listeners.started(context);
      callRunners(context, applicationArguments);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, listeners);
      throw new IllegalStateException(ex);
   }
   try {
      listeners.running(context);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, null);
      throw new IllegalStateException(ex);
   }
   return context;
}

我將會根據(jù)執(zhí)行過程逐行進(jìn)行分析

1、StopWatch計時器

此類實則為計時器,如下對具體使用進(jìn)行分析

StopWatch stopWatch = new StopWatch();
//開始計時
stopWatch.start();
//停止計時
stopWatch.stop();

對于具體打印的上面寫的為

//將當(dāng)前類傳入StartupInfoLogger創(chuàng)建了一個對象
//然后調(diào)用logStarted打印日志
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
//創(chuàng)建一個Log類
protected Log getApplicationLog() {
   if (this.mainApplicationClass == null) {
      return logger;
   }
   return LogFactory.getLog(this.mainApplicationClass);
}
//調(diào)用log類的log.info()方法來打印日志
public void logStarted(Log log, StopWatch stopWatch) {
    if (log.isInfoEnabled()) {
            log.info(getStartedMessage(stopWatch));
    }
}
//打印詳細(xì)的日志
private StringBuilder getStartedMessage(StopWatch stopWatch) {
   StringBuilder message = new StringBuilder();
   message.append("Started ");
   message.append(getApplicationName());
   message.append(" in ");
   message.append(stopWatch.getTotalTimeSeconds());
   try {
      double uptime = ManagementFactory.getRuntimeMXBean().getUptime() / 1000.0;
      message.append(" seconds (JVM running for " + uptime + ")");
   }
   catch (Throwable ex) {
      // No JVM time available
   }
   return message;
}

這里可以看到stopWatch.getTotalTimeSeconds()方法就是來獲取實際的計時時間的。再者,通過這幾行代碼,我們也可以考慮下平常在寫代碼的時候,有幾種日志打印方式?SpringBoot是怎么集成日志框架的?

2、configureHeadlessProperty()

private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless";
private void configureHeadlessProperty() {
   System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
         System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}

這一部分代碼這樣理解吧,首先java.awt包提供了用于創(chuàng)建用戶界面和繪制圖形圖像的所有分類,那么 屬性SYSTEM_PROPERTY_JAVA_AWT_HEADLESS就一定會和用戶界面相關(guān)了。 這里將SYSTEM_PROPERTY_JAVA_AWT_HEADLESS設(shè)置為true,其實就是表示在缺少顯示屏、鍵盤或者鼠標(biāo)中的系統(tǒng)配置,如果將其設(shè)置為true,那么headless工具包就會被使用。

3、getRunListeners(args) 獲取監(jiān)聽

總體上可以分這三步

  • 獲取一個默認(rèn)的加載器
  • 根據(jù)類型獲取spring.factories中符合的類名
  • 創(chuàng)建類實例,返回

如下將跟下代碼

//獲取所有監(jiān)聽
SpringApplicationRunListeners listeners = getRunListeners(args);
//啟動監(jiān)聽
listeners.starting();

跳轉(zhuǎn)進(jìn)入getRunListeners方法

private SpringApplicationRunListeners getRunListeners(String[] args) {
   Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
   return new SpringApplicationRunListeners(logger,
         getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
  //3.1獲取類加載器
   ClassLoader classLoader = getClassLoader();
   // Use names and ensure unique to protect against duplicates
   //3.2 根據(jù)類型獲取spring.factories中符合的類名
   Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
   //3.3 創(chuàng)建類實例
   List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
   //對實例進(jìn)行排序
   AnnotationAwareOrderComparator.sort(instances);
   return instances;
}

SpringApplicationRunListeners類解讀

先看下SpringApplicationRunListeners類

/**
 * A collection of {@link SpringApplicationRunListener}.
 *
 * @author Phillip Webb
 */
class SpringApplicationRunListeners {
   private final Log log;
   private final List<SpringApplicationRunListener> listeners;
   SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) {
      this.log = log;
      this.listeners = new ArrayList<>(listeners);
   }

SpringApplicationRunListeners類內(nèi)部關(guān)聯(lián)了SpringApplicationRunListener的集合,說白了就是用List集合存儲了SpringApplicationRunListeners類,那么,我們就需要了解一下這個類是干嘛的

老規(guī)矩,先把源碼抬上來

/**
 *//可以理解為Spring Boot應(yīng)用的運行時監(jiān)聽器
 * Listener for the {@link SpringApplication} {@code run} method.
 *//SpringApplicationRunListener的構(gòu)造器參數(shù)必須依次為SpringApplication和String[]類型
 * {@link SpringApplicationRunListener}s are loaded via the {@link SpringFactoriesLoader}
 * and should declare a public constructor that accepts a {@link SpringApplication}
 * instance and a {@code String[]} of arguments.
 *//每次運行的時候?qū)?chuàng)建一個 SpringApplicationRunListener
  A new
 * {@link SpringApplicationRunListener} instance will be created for each run.
 *
 */
public interface SpringApplicationRunListener {
   /**
    * Called immediately when the run method has first started. Can be used for very
    * early initialization.
    */
    //Spring應(yīng)用剛啟動
   void starting();
   /**
    * Called once the environment has been prepared, but before the
    * {@link ApplicationContext} has been created.
    * @param environment the environment
    */
    //ConfigurableEnvironment準(zhǔn)備妥當(dāng),允許將其調(diào)整
   void environmentPrepared(ConfigurableEnvironment environment);
   /**
    * Called once the {@link ApplicationContext} has been created and prepared, but
    * before sources have been loaded.
    * @param context the application context
    */
    //ConfigurableApplicationContext準(zhǔn)備妥當(dāng),允許將其調(diào)整
   void contextPrepared(ConfigurableApplicationContext context);
   /**
    * Called once the application context has been loaded but before it has been
    * refreshed.
    * @param context the application context
    */
    //ConfigurableApplicationContext已裝載,但是任未啟動
   void contextLoaded(ConfigurableApplicationContext context);
   /**
    * The context has been refreshed and the application has started but
    * {@link CommandLineRunner CommandLineRunners} and {@link ApplicationRunner
    * ApplicationRunners} have not been called.
    * @param context the application context.
    * @since 2.0.0
    */
    //ConfigurableApplicationContext已啟動,此時Spring Bean已初始化完成
   void started(ConfigurableApplicationContext context);
   /**
    * Called immediately before the run method finishes, when the application context has
    * been refreshed and all {@link CommandLineRunner CommandLineRunners} and
    * {@link ApplicationRunner ApplicationRunners} have been called.
    * @param context the application context.
    * @since 2.0.0
    */
    //Spring應(yīng)用正在運行
   void running(ConfigurableApplicationContext context);
   /**
    * Called when a failure occurs when running the application.
    * @param context the application context or {@code null} if a failure occurred before
    * the context was created
    * @param exception the failure
    * @since 2.0.0
    */
    //Spring應(yīng)用運行失敗
   void failed(ConfigurableApplicationContext context, Throwable exception);
}

單純的看源碼,是一個簡單的接口,這時候我們可以看下作者給的注釋。理解部分就直接加到上面源碼中了。

再看下他的實現(xiàn)類EventPublishingRunListener

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
   private final SpringApplication application;
   private final String[] args;
   private final SimpleApplicationEventMulticaster initialMulticaster;
   public EventPublishingRunListener(SpringApplication application, String[] args) {
      this.application = application;
      this.args = args;
      this.initialMulticaster = new SimpleApplicationEventMulticaster();
      for (ApplicationListener<?> listener : application.getListeners()) {
         this.initialMulticaster.addApplicationListener(listener);
      }
   }

這里我們看到兩點:

  • 構(gòu)造器參數(shù)和他實現(xiàn)的接口(上面剛分析了)注釋中規(guī)定的一致
  • 將SpringApplication中的ApplicationListener實例列表全部添加到了SimpleApplicationEventMulticaster對象中

SimpleApplicationEventMulticaster是Spring框架的一個監(jiān)聽類,用于發(fā)布Spring應(yīng)用事件。因此EventPublishingRunListener實際充當(dāng)了Spring Boot事件發(fā)布者的角色。

這里我再跟進(jìn)源碼的時候發(fā)現(xiàn),針對SpringBoot的事件/監(jiān)聽機(jī)制內(nèi)容還是挺多的,我們在充分理解的時候需要先了解Spring的事件/監(jiān)聽機(jī)制,后面將兩個結(jié)合后單獨進(jìn)行對比分析。

3.1獲取類加載器getClassLoader()

ClassLoader classLoader = getClassLoader();
public ClassLoader getClassLoader() {
  if (this.resourceLoader != null) {
     return this.resourceLoader.getClassLoader();
  }
  return ClassUtils.getDefaultClassLoader();
}

這里的類加載器獲取首先是獲取resourceLoader的類加載器,獲取不到則獲取默認(rèn)的類加載器。 resourceLoader是資源加載器類,有具體的實現(xiàn)類。

3.2 根據(jù)類型獲取spring.factories中符合的類名

SpringFactoriesLoader.loadFactoryNames(type, classLoader)
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
//獲取類型名稱:org.springframework.context.ApplicationContextInitializer
   String factoryClassName = factoryClass.getName();
   return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}

我們繼續(xù)對loadSpringFactories追下去

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
    //從緩存里面獲取
    MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
    if (result != null) {
        return result;
    } else {
        try {
        //執(zhí)行classLoader.getResources("META-INF/spring.factories"),表示通過加載器獲取META-INF/spring.factories下的資源
            Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
            LinkedMultiValueMap result = new LinkedMultiValueMap();
            while(urls.hasMoreElements()) {
               //文件地址
                URL url = (URL)urls.nextElement();
                //從指定位置加載UrlResource
                UrlResource resource = new UrlResource(url);
                //加載里面的屬性,屬性見下圖
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                Iterator var6 = properties.entrySet().iterator();
                while(var6.hasNext()) {
                    Entry<?, ?> entry = (Entry)var6.next();
                    //獲取key值
                    String factoryClassName = ((String)entry.getKey()).trim();
                    //獲取value值
                    String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                    int var10 = var9.length;
//這里是將查詢出來的key作為result的key,value轉(zhuǎn)換成字符數(shù)組存放到result的value中
                    for(int var11 = 0; var11 < var10; ++var11) {
                        String factoryName = var9[var11];
                        result.add(factoryClassName, factoryName.trim());
                    }
                }
            }
            //將結(jié)果集存入緩存中
            cache.put(classLoader, result);
            return result;
        } catch (IOException var13) {
            throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
        }
    }
}

default V getOrDefault(Object key, V defaultValue) {
    V v;
    return (((v = get(key)) != null) || containsKey(key))
        ? v
        : defaultValue;
}

這個的意思是如果沒有,則獲取一個空的list

3.3創(chuàng)建實例createSpringFactoriesInstances()

這一步其實就是將上一步從META-INF/spring.factories加載進(jìn)來的資源進(jìn)行實例化。

private <T> List<T> createSpringFactoriesInstances()(Class<T> type, Class<?>[] parameterTypes,
      ClassLoader classLoader, Object[] args, Set<String> names) {
   List<T> instances = new ArrayList<>(names.size());
   for (String name : names) {
      try {
      //根據(jù)類加載器獲取指定類
         Class<?> instanceClass = ClassUtils.forName(name, classLoader);
         Assert.isAssignable(type, instanceClass);
         //根據(jù)參數(shù)獲取構(gòu)造器
         Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
         //根據(jù)傳入的構(gòu)造器對象以及構(gòu)造器所需的參數(shù)創(chuàng)建一個實例
         T instance = (T) BeanUtils.instantiateClass(constructor, args);
         //添加實例到集合中
         instances.add(instance);
      }
      catch (Throwable ex) {
         throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
      }
   }
   return instances;
}

4、環(huán)境準(zhǔn)備prepareEnvironment

prepareEnvironment(listeners, applicationArguments)
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
      ApplicationArguments applicationArguments) {
   // Create and configure the environment
   //4.1 創(chuàng)建一個環(huán)境
   ConfigurableEnvironment environment = getOrCreateEnvironment();
   //4.2 配置環(huán)境
   configureEnvironment(environment, applicationArguments.getSourceArgs());
   //4.3 ConfigurationPropertySourcesPropertySource對象存入到第一位
   ConfigurationPropertySources.attach(environment);
   //listeners環(huán)境準(zhǔn)備(就是廣播ApplicationEnvironmentPreparedEvent事件)
   listeners.environmentPrepared(environment);
   // 將環(huán)境綁定到SpringApplication
   bindToSpringApplication(environment);
     // 如果是非web環(huán)境,將環(huán)境轉(zhuǎn)換成StandardEnvironment
   if (!this.isCustomEnvironment) {
      environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
            deduceEnvironmentClass());
   }
    // 配置PropertySources對它自己的遞歸依賴
   ConfigurationPropertySources.attach(environment);
   return environment;
}

4.1創(chuàng)建一個環(huán)境getOrCreateEnvironment

private ConfigurableEnvironment getOrCreateEnvironment() {
//有的話,直接返回
  if (this.environment != null) {
     return this.environment;
  }
  //這里我們在上面見到過,通過WebApplicationType.deduceFromClasspath()方法獲取的
  switch (this.webApplicationType) {
  case SERVLET:
     return new StandardServletEnvironment();
  case REACTIVE:
     return new StandardReactiveWebEnvironment();
  default:
     return new StandardEnvironment();
  }
}

這里創(chuàng)建了一個StandardServletEnvironment實例的環(huán)境 systemProperties用來封裝了JDK相關(guān)的信息 如下圖

systemEnvironment用來封轉(zhuǎn)環(huán)境相關(guān)的信息

封裝的還是挺詳細(xì)的哈。

4.2 配置環(huán)境

protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
   if (this.addConversionService) {
      ConversionService conversionService = ApplicationConversionService.getSharedInstance();
      environment.setConversionService((ConfigurableConversionService) conversionService);
   }
   configurePropertySources(environment, args);
   configureProfiles(environment, args);
}

setConversionService(ConfigurableConversionService conversionService)方法繼承于ConfigurablePropertyResolver接口, 該接口是PropertyResolver類型都將實現(xiàn)的配置接口。提供用于訪問和自定義將屬性值從一種類型轉(zhuǎn)換為另一種類型時使用的ConversionService的工具。PropertyResolver是用于針對任何底層源解析屬性的接口。

configurePropertySources(environment, args);當(dāng)前方法主要是將啟動命令中的參數(shù)和run 方法中的參數(shù)封裝為PropertySource。

protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
//獲取所有的屬性源,就是獲取4.1的ConfigurableEnvironment上獲取到的屬性
  MutablePropertySources sources = environment.getPropertySources();
  if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
     sources.addLast(new MapPropertySource("defaultProperties", this.defaultProperties));
  }
  //是否添加命令啟動參數(shù),addCommandLineProperties為true,表示需要添加,但是前提是你得配置了參數(shù)
  if (this.addCommandLineProperties && args.length > 0) {
     String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
     if (sources.contains(name)) {
        PropertySource<?> source = sources.get(name);
        CompositePropertySource composite = new CompositePropertySource(name);
        composite.addPropertySource(
              new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
        composite.addPropertySource(source);
        sources.replace(name, composite);
     }
     else {
        sources.addFirst(new SimpleCommandLinePropertySource(args));
     }
  }
}

configureProfiles(environment, args);環(huán)境配置

protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
//獲取激活的環(huán)境
  environment.getActiveProfiles(); // ensure they are initialized
  // But these ones should go first (last wins in a property key clash)
  Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
  profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
  //設(shè)置當(dāng)前的環(huán)境
  environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}

4.3 ConfigurationPropertySourcesPropertySource對象存入

public static void attach(Environment environment) {
   Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
   //獲取所有的屬性源,就是獲取4.1的ConfigurableEnvironment上獲取到的屬性
   MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources();
   //判斷是否有 屬性 configurationProperties
   PropertySource&lt;?&gt; attached = sources.get(ATTACHED_PROPERTY_SOURCE_NAME);
   if (attached != null &amp;&amp; attached.getSource() != sources) {
      sources.remove(ATTACHED_PROPERTY_SOURCE_NAME);
      attached = null;
   }
   if (attached == null) {
   // 將sources封裝成ConfigurationPropertySourcesPropertySource對象,并把這個對象放到sources的第一位置
      sources.addFirst(new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME,
            new SpringConfigurationPropertySources(sources)));
   }
}

總結(jié)

準(zhǔn)備階段主要干了如下幾件事情

  • 設(shè)置headless為true(表示可以在缺少顯示屏、鍵盤或者鼠標(biāo)時候的系統(tǒng)配置)
  • 文件META-INF\spring.factories中獲取SpringApplicationRunListener接口的實現(xiàn)類EventPublishingRunListener,主要發(fā)布SpringApplicationEvent。
  • 創(chuàng)建Environment并設(shè)置比如環(huán)境信息,系統(tǒng)屬性,輸入?yún)?shù)和profile等信息
  • 打印Banner信息

本文僅為個人能力范圍內(nèi)理解,旨在分享出來和大家討論技術(shù),共同努力,共同進(jìn)步!

參考:《SpringBoot編程思想》

以上就是SpringBoot啟動流程SpringApplication準(zhǔn)備階段源碼分析的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot SpringApplication 啟動的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • springboot集成mqtt的實踐開發(fā)

    springboot集成mqtt的實踐開發(fā)

    本篇文章主要介紹了springboot集成mqtt的實踐開發(fā),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-08-08
  • 詳細(xì)解讀java同步之synchronized解析

    詳細(xì)解讀java同步之synchronized解析

    synchronized關(guān)鍵字是Java里面最基本的同步手段,下面我們來一起學(xué)習(xí)一下
    2019-05-05
  • 通過源代碼分析Mybatis的功能流程詳解

    通過源代碼分析Mybatis的功能流程詳解

    這篇文章主要介紹了通過源代碼分析Mybatis的功能,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-08-08
  • 解析java稀疏數(shù)組如何幫助我們節(jié)省內(nèi)存提升性能

    解析java稀疏數(shù)組如何幫助我們節(jié)省內(nèi)存提升性能

    這篇文章主要為大家介紹了java稀疏數(shù)組如何幫助我們節(jié)省內(nèi)存提升性能解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-11-11
  • java實現(xiàn)大文本文件拆分

    java實現(xiàn)大文本文件拆分

    這篇文章主要為大家詳細(xì)介紹了java實現(xiàn)大文本文件拆分,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-05-05
  • 淺談一下Java中集合的迭代方式

    淺談一下Java中集合的迭代方式

    這篇文章主要介紹了淺談一下Java中集合的迭代方式,可以幫助我們學(xué)習(xí),理解函數(shù)式編程,需要的朋友可以參考下
    2023-04-04
  • Java可重入鎖的實現(xiàn)原理與應(yīng)用場景

    Java可重入鎖的實現(xiàn)原理與應(yīng)用場景

    今天小編就為大家分享一篇關(guān)于Java可重入鎖的實現(xiàn)原理與應(yīng)用場景,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-01-01
  • Spring?MVC請求轉(zhuǎn)發(fā)與請求重定向的示例詳解

    Spring?MVC請求轉(zhuǎn)發(fā)與請求重定向的示例詳解

    轉(zhuǎn)發(fā)指服務(wù)器接收請求后,從一個資源跳轉(zhuǎn)到另一個資源中,請求轉(zhuǎn)發(fā)是一次請求,不會改變?yōu)g覽器的請求地址,這篇文章主要介紹了Spring?MVC請求轉(zhuǎn)發(fā)與請求重定向的相關(guān)知識,需要的朋友可以參考下
    2023-09-09
  • SpringBoot中實現(xiàn)定時任務(wù)的4種方式詳解

    SpringBoot中實現(xiàn)定時任務(wù)的4種方式詳解

    這篇文章主要介紹了SpringBoot中實現(xiàn)定時任務(wù)的4種方式詳解,在Springboot中定時任務(wù)是一項經(jīng)常能用到的功能,實現(xiàn)定時任務(wù)的方式有很多,今天來介紹常用的幾種,需要的朋友可以參考下
    2023-11-11
  • 圖文教程教你IDEA中的Spring環(huán)境搭建+簡單入門

    圖文教程教你IDEA中的Spring環(huán)境搭建+簡單入門

    這篇文章主要介紹了圖文教程教你IDEA中的Spring環(huán)境搭建+簡單入門,Spring的環(huán)境搭建使用Maven更加方便,需要的朋友可以參考下
    2023-03-03

最新評論