springboot启动过程原理附带问题

准备:

      预先了解的知识,  https://www.cnblogs.com/theRhyme/p/11057233.html

问题列表:

1:springboot启动过程中如何加载META-INF/spring.factories

2:springboot启动过程中都哪些地方用到了META-INF/spring.factories,分别起了什么作用。

3:ApplicationContextInitializer的作用,其子类都有哪些及作用分别是什么?

4:ApplicationListener的作用,其子类都有哪些及作用分别是什么,有多少种配置方式,二次调用是什么问题?

5:ApplicationEvent的子类有哪些?spring如何监听自定义event?

6:  SpringApplicationRunListener的作用,其子类都有哪些,并起哪些作用,已有哪些?

7:  webApplicationType 有几种?识别方式怎么实现的。

8:如何获取运行中的主程序类?

9:如何配置java.awt.headless 模式

10:org.springframework.util.MultiValueMap的使用方式及使用场景

11: springboot如何解析命令行参数?

12: 为什么SpringBoot中main方法执行完毕后程序不会直接退出?

解答:

1:springboot启动过程中如何加载META-INF/spring.factories

List<String> s = SpringFactoriesLoader.loadFactoryNames(type, classLoader);

1.1 使用的classLoader是什么?

classLoader=sun.misc.Launcher.AppClassLoader

2: 会读取到哪些spring.factories文件

  • ${project_home}/target/classes/META-INF/spring.factories
  • ${maven_repository}/org/springframework/spring-beans/5.2.4.RELEASE/spring-beans-5.2.4.RELEASE.jar!/META-INF/spring.factories
  • ${maven_repository}/org/springframework/boot/spring-boot-autoconfigure/2.2.5.RELEASE/spring-boot-autoconfigure-2.2.5.RELEASE.jar!/META-INF/spring.factories
  • ${maven_repository}/org/mybatis/spring/boot/mybatis-spring-boot-autoconfigure/2.1.1/mybatis-spring-boot-autoconfigure-2.1.1.jar!/META-INF/spring.factories
  • ${maven_repository}/com/alibaba/druid-spring-boot-starter/1.2.1/druid-spring-boot-starter-1.2.1.jar!/META-INF/spring.factories
  • ${maven_repository}/org/springframework/boot/spring-boot/2.2.5.RELEASE/spring-boot-2.2.5.RELEASE.jar!/META-INF/spring.factories
  • ${maven_repository}/com/github/pagehelper/pagehelper-spring-boot-autoconfigure/1.2.12/pagehelper-spring-boot-autoconfigure-1.2.12.jar!/META-INF/spring.factories

3: ApplicationContextInitializer的作用,其子类都有哪些及作用分别是什么?

0 = "org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer"
1 = "org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener"
2 = "org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer"
3 = "org.springframework.boot.context.ContextIdApplicationContextInitializer"
4 = "org.springframework.boot.context.config.DelegatingApplicationContextInitializer"
5 = "org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer"
6 = "org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer"
7 = "org.springframework.boot.devtools.restart.RestartScopeInitializer"

4:ApplicationListener的作用,其子类都有哪些及作用分别是什么,有多少种配置方式,二次调用是什么问题?

  • org.springframework.boot.devtools.restart.RestartApplicationListener
  • org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor
  • org.springframework.boot.context.config.ConfigFileApplicationListener
  • org.springframework.boot.context.config.AnsiOutputApplicationListener
  • org.springframework.boot.context.logging.LoggingApplicationListener
  • org.springframework.boot.autoconfigure.BackgroundPreinitializer
  • org.springframework.boot.context.logging.ClasspathLoggingApplicationListener
  • org.springframework.boot.context.config.DelegatingApplicationListener
  • org.springframework.boot.builder.ParentContextCloserApplicationListener
  • org.springframework.boot.ClearCachesApplicationListener
  • org.springframework.boot.context.FileEncodingApplicationListener
  • org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
  • org.springframework.boot.devtools.logger.DevToolsLogFactory.Listener

  4.2 ApplicationListener有好多种配置方式,参考网址

   4.3 ApplicationListener二次调用的问题,参考网址

5:ApplicationEvent的子类有哪些?spring如何监听自定义event

    5.1  ApplicationEvent的子类有哪些?我们参考网址

    5.2  spring如何监听自定义event?参考网址

6:  SpringApplicationRunListener的作用,其子类都有哪些,并起哪些作用,已有哪些?

  •   org.springframework.boot.context.event.EventPublishingRunListener

7: webApplicationType 有几种?识别方式怎么实现的。

    7.1 有三类,分别如下:

  • NONE:  The application should not run as a web application and should not start an embedded web server.
  • SERVLET:  The application should run as a servlet-based web application and should start an embedded servlet web server.
  • REACTIVE:  TThe application should run as a reactive web application and should start an embedded reactive web server.

   7.2 实现方式参考:WebApplicationType.deduceFromClasspath();

8:如何获取运行中的主程序类?

  8.1 参考SpringApplication.deduceMainApplicationClass。代码片段如下:

private Class<?> deduceMainApplicationClass() {
        try {
            StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
            for (StackTraceElement stackTraceElement : stackTrace) {
                if ("main".equals(stackTraceElement.getMethodName())) {
                    return Class.forName(stackTraceElement.getClassName());
                }
            }
        }
        catch (ClassNotFoundException ex) {
            // Swallow and continue
        }
        return null;
    }
 

9:如何配置java.awt.headless 模式

参考SpringApplication.configureHeadlessProperty()。什么是 何时使用,如何使用参考如下链接

代码片段如下:

private void configureHeadlessProperty() {
        System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
                System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
    }
 

10:org.springframework.util.MultiValueMap的使用方式及使用场景


import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;

@Slf4j
public class MultiValueMapTest {

    /***
     * 从配置文件中加载
     * @throws IOException
     */
    @Test
    public  void test1() throws IOException {

        MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();

        InputStream inputStream  =  ClassLoader.getSystemResourceAsStream("multivaluemap.properties");
        Properties properties = new Properties();
        properties.load(inputStream);
        for (Map.Entry<?, ?> entry : properties.entrySet()) {
            String factoryTypeName = ((String) entry.getKey()).trim();
            for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                log.info("key==="+factoryTypeName+"-----------"+factoryImplementationName.trim());
                //log.info("----key={},v={}", factoryTypeName.trim(),factoryImplementationName.trim());
                map.add(factoryTypeName,factoryImplementationName.trim());
            }
        }

        printMap(map);
    }

    /**
     * 手动增加
     * @throws IOException
     */
    @Test
    public  void test2() throws IOException {
        MultiValueMap mailMap =  new LinkedMultiValueMap<String, String>();
        mailMap.add("name","test1");
        mailMap.add("name","test2");
        mailMap.add("name","test3");

        printMap(mailMap);
        //返回的类为 java.util.LinkedList
        String cname = mailMap.get("name").getClass().getCanonicalName();

        log.info("cname={}",cname);
        log.info("移除remove2={}",mailMap.remove("name"));

    }

    private void printMap(MultiValueMap<String, String> map){
        Set<String> keySet = map.keySet();
        List<String> tempList = null;
        String tempValue = null;
        for(String key : keySet){
            tempList = map.get(key);
            Iterator ii = tempList.iterator();
            while(ii.hasNext()){
                tempValue= (String) ii.next();
                log.info("-----key="+key+",value="+tempValue);
            }
        }
    }
}

11: springboot如何解析命令行参数?

 采用SimpleCommandLineArgsParser类来解析参数,根据是否是--开头,将--开头的参数放入optionArgs属性里,其他的放入nonOptionArgs属性里。

12: 为什么SpringBoot中main方法执行完毕后程序不会直接退出?

      因为有非守护线程在持续运行。具体参考链接。附带新知识点:守护线程与非守护线程的区别

补充知识:

classLoader可通过三种方式获得,而且可以依次降级。分别为:

1)Thread.currentThread().getContextClassLoader();
2)ClassUtils.class.getClassLoader();
3)ClassLoader.getSystemClassLoader();
 

其中1)的getSystemResources(“a.txt”)是从系统的根目录里查找资源

classLoader方法标识

获取classloader的方法 加载根目录资源 加载本目录下寻找资源 备注 Thread.currentThread().getContextClassLoader() getSystemResources(“a.txt”)

***********************           无                                                                                

getResources("a.txt")

无法从根目录和当前目录中加载到任何资源

ClassUtils.class.getClassLoader() getSystemResources(“a.txt”) ***********************           无                                   

getResources("a.txt")

无法从根目录和当前目录中加载到任何资源

ClassLoader.getSystemClassLoader() getSystemResources(“a.txt”) ***********************           无   

getResources("a.txt")

无法从根目录和当前目录中加载到任何资源

2, 四种常见的线程池详解,请点击链接

    2.1 知识点,共享的无界队列,自定义线程池。链接1链接2.

工具方法:

1:加载项目中的property文件

    1.1 采用PropertiesLoaderUtils方法

    1.2 采用 properties.load(inputStream)方法,具体代码如下


import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.springframework.core.io.UrlResource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Enumeration;
import java.util.Map;
import java.util.Properties;

@Slf4j
public class PropertiesLoaderUtilsTest {


    @Test
    public void loadProperties1() throws IOException {
        URL url =  ClassLoader.getSystemResource("a.properties");
        UrlResource resource = new UrlResource(url);
        Properties properties = PropertiesLoaderUtils.loadProperties(resource);
        printPropertys(  properties);
    }


    @Test
    public void loadProperties2() throws IOException {
        InputStream inputStream  =  ClassLoader.getSystemResourceAsStream("a.properties");
        Properties properties = new Properties();
        properties.load(inputStream);

        printPropertys(  properties);
    }
    private  void printPropertys(Properties properties){
        for (Map.Entry<?, ?> entry : properties.entrySet()) {
            String key = ((String) entry.getKey()).trim();
            //spring内部写法
            for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                log.info("key={},v={}", key.trim(),factoryImplementationName.trim());
            }
            //本人常规写法
            String v2 = (String) entry.getValue();
            log.info("-----------key={},v={}", key.trim(),v2);
        }
    }
}
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值