• SpringBoot源码学习(一) SpringApplication构造器


    介绍

    此示例采用SpringBoot 2.2.13.RELEASE版本。

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    	<modelVersion>4.0.0</modelVersion>
    	<parent>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-parent</artifactId>
    		<version>2.2.13.RELEASE</version>
    		<relativePath/> <!-- lookup parent from repository -->
    	</parent>
    	<groupId>com.chinda</groupId>
    	<artifactId>read-spring</artifactId>
    	<version>0.0.1-SNAPSHOT</version>
    	<name>read-spring</name>
    	<description>Reading Spring Boot</description>
    
    	<properties>
    		<java.version>1.8</java.version>
    	</properties>
    
    	<dependencies>
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-web</artifactId>
    		</dependency>
    
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-devtools</artifactId>
    			<scope>runtime</scope>
    			<optional>true</optional>
    		</dependency>
    		<dependency>
    			<groupId>mysql</groupId>
    			<artifactId>mysql-connector-java</artifactId>
    			<scope>runtime</scope>
    		</dependency>
    		<dependency>
    			<groupId>org.projectlombok</groupId>
    			<artifactId>lombok</artifactId>
    			<optional>true</optional>
    		</dependency>
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-test</artifactId>
    			<scope>test</scope>
    			<exclusions>
    				<exclusion>
    					<groupId>org.junit.vintage</groupId>
    					<artifactId>junit-vintage-engine</artifactId>
    				</exclusion>
    			</exclusions>
    		</dependency>
    	</dependencies>
    
    	<build>
    		<plugins>
    			<plugin>
    				<groupId>org.springframework.boot</groupId>
    				<artifactId>spring-boot-maven-plugin</artifactId>
    				<configuration>
    					<excludes>
    						<exclude>
    							<groupId>org.projectlombok</groupId>
    							<artifactId>lombok</artifactId>
    						</exclude>
    					</excludes>
    				</configuration>
    			</plugin>
    		</plugins>
    	</build>
    
    </project>
    
    

    执行入口

    @SpringBootApplication
    public class ReadSpringApplication {
    
       public static void main(String[] args) {
          SpringApplication.run(ReadSpringApplication.class, args);
       }
    
    }
    

    SpringApplication构造器

    创建一个新的SpringApplication实例。应用程序上下文将从指定的主要来源加载Bean。

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    		// resourceLoader = null
    		this.resourceLoader = resourceLoader;
    		Assert.notNull(primarySources, "PrimarySources must not be null");
    		// new Class<?>[] {ReadSpringApplication.class}
    		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    		// 推断应用服务类型REACTIVE、NONE、SERVLET
    		this.webApplicationType = WebApplicationType.deduceFromClasspath();
    		/**
    		 * 设置应用上下文初始化相关实例
    		 * org.springframework.boot.context.config.DelegatingApplicationContextInitializer,
    		 * org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,
    		 * org.springframework.boot.context.ContextIdApplicationContextInitializer,
    		 * org.springframework.boot.devtools.restart.RestartScopeInitializer,
    		 * org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,
    		 * org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,
    		 * org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer,
    		 * org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
    		 */
    		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    		/**
    		 * 设置监听器
    		 * 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.context.logging.ClasspathLoggingApplicationListener,
    		 * org.springframework.boot.autoconfigure.BackgroundPreinitializer,
    		 * org.springframework.boot.context.config.DelegatingApplicationListener,
    		 * org.springframework.boot.builder.ParentContextCloserApplicationListener,
    		 * org.springframework.boot.devtools.logger.DevToolsLogFactory$Listener,
    		 * org.springframework.boot.ClearCachesApplicationListener,
    		 * org.springframework.boot.context.FileEncodingApplicationListener,
    		 * org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
    		 */
    		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    		// 推断主启动类: com.chinda.ReadSpringApplication
    		this.mainApplicationClass = deduceMainApplicationClass();
    	}
    

    从源码看,主要需要看this.webApplicationType = WebApplicationType.deduceFromClasspath();, getSpringFactoriesInstances(xxx.class), this.mainApplicationClass = deduceMainApplicationClass()

    根据路径推断应用类型

    deduceFromClasspath()

    static WebApplicationType deduceFromClasspath() {
       // `org.springframework.web.reactive.DispatcherHandler`能够被加载且`org.springframework.web.servlet.DispatcherServlet`不能够被加载且`org.glassfish.jersey.servlet.ServletContainer`不能够在加载,那么断定web应用类型是`REACTIVE`
       if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
           && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
           return WebApplicationType.REACTIVE;
       }
       // `javax.servlet.Servlet, org.springframework.web.context.ConfigurableWebApplicationContext`任意一个不能加载,那么断定web应用类型为`NONE`
       for (String className : SERVLET_INDICATOR_CLASSES) {
           if (!ClassUtils.isPresent(className, null)) {
               return WebApplicationType.NONE;
           }
       }
       return WebApplicationType.SERVLET;
    }
    
    /** 判断给定的类是否能够加载,不报错说明类路径下存在给定的类 */
    public static boolean isPresent(String className, @Nullable ClassLoader classLoader) {
       try {
           forName(className, classLoader);
           return true;
       }
       catch (IllegalAccessError err) {
           throw new IllegalStateException("Readability mismatch in inheritance hierarchy of class [" +
                                           className + "]: " + err.getMessage(), err);
       }
       catch (Throwable ex) {
           // Typically ClassNotFoundException or NoClassDefFoundError...
           return false;
       }
    }
    

    WebApplicationType 类型

    1. NONE: 该应用程序是Web应用程序运行,也不应启动嵌入式Web服务器。
    2. SERVLET: 该应用程序应作为基于Servlet的Web应用程序运行,并应启动嵌入式Servlet Web服务器。
    3. REACTIVE: 该应用程序应作为响应式Web应用程序运行,并应启动嵌入式的响应式Web服务器。

    获取工程实例

    getSpringFactoriesInstances(xxx.class)

    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
       ClassLoader classLoader = getClassLoader();
       // 获取指定类型的工厂名称集合, 去除重复名称
       Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
       // 根据名字类型创建工厂实例
       List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
       // 排序
       AnnotationAwareOrderComparator.sort(instances);
       return instances;
    }
    

    从源码可是看出,此方法主要做了三件事,loadFactoryNamescreateSpringFactoriesInstances, sort

    获取指定类型的工厂名称集合, 去除重复名称

    loadFactoryNames

    public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
       ClassLoader classLoaderToUse = classLoader;
       if (classLoaderToUse == null) {
          classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
       }
       String factoryTypeName = factoryType.getName();
       // 加载SpringFactories,根据名称过滤
       return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
    }
    

    loadSpringFactories源码

    private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
        // 从缓存中获取Spring工厂
       Map<String, List<String>> result = cache.get(classLoader);
       if (result != null) {
          return result;
       }
    
       result = new HashMap<>();
       try {
           // 加载META-INF/spring.factories文件
          Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
           // 解析spring.factories文件中的数据
          while (urls.hasMoreElements()) {
             URL url = urls.nextElement();
             UrlResource resource = new UrlResource(url);
             Properties properties = PropertiesLoaderUtils.loadProperties(resource);
             for (Map.Entry<?, ?> entry : properties.entrySet()) {
                String factoryTypeName = ((String) entry.getKey()).trim();
                String[] factoryImplementationNames =
                      StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
                for (String factoryImplementationName : factoryImplementationNames) {
                   result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
                         .add(factoryImplementationName.trim());
                }
             }
          }
    
          // Replace all lists with unmodifiable lists containing unique elements
          result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
                .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
           // 将spring.factories文件中的解析数据放入缓存
          cache.put(classLoader, result);
       }
       catch (IOException ex) {
          throw new IllegalArgumentException("Unable to load factories from location [" +
                FACTORIES_RESOURCE_LOCATION + "]", ex);
       }
       return result;
    }
    
    1. 从缓存中获取SpringFactories,若存在直接返回,不存在执行下面操作
    2. 加载META-INF/spring.factories文件
    3. 解析spring.factories文件中的数据
    4. 将解析出来的数据放入缓存

    spring.factories文件内容

    # Logging Systems
    org.springframework.boot.logging.LoggingSystemFactory=
    org.springframework.boot.logging.logback.LogbackLoggingSystem.Factory,
    org.springframework.boot.logging.log4j2.Log4J2LoggingSystem.Factory,
    org.springframework.boot.logging.java.JavaLoggingSystem.Factory
    
    # PropertySource Loaders
    org.springframework.boot.env.PropertySourceLoader=
    org.springframework.boot.env.PropertiesPropertySourceLoader,
    org.springframework.boot.env.YamlPropertySourceLoader
    
    # ConfigData Location Resolvers
    org.springframework.boot.context.config.ConfigDataLocationResolver=
    org.springframework.boot.context.config.ConfigTreeConfigDataLocationResolver,
    org.springframework.boot.context.config.StandardConfigDataLocationResolver
    
    # ConfigData Loaders
    org.springframework.boot.context.config.ConfigDataLoader=
    org.springframework.boot.context.config.ConfigTreeConfigDataLoader,
    org.springframework.boot.context.config.StandardConfigDataLoader
    
    # Run Listeners
    org.springframework.boot.SpringApplicationRunListener=
    org.springframework.boot.context.event.EventPublishingRunListener
    
    # Error Reporters
    org.springframework.boot.SpringBootExceptionReporter=
    org.springframework.boot.diagnostics.FailureAnalyzers
    
    # Application Context Initializers
    org.springframework.context.ApplicationContextInitializer=
    org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,
    org.springframework.boot.context.ContextIdApplicationContextInitializer,
    org.springframework.boot.context.config.DelegatingApplicationContextInitializer,
    org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,
    org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
    
    # Application Listeners
    org.springframework.context.ApplicationListener=
    org.springframework.boot.ClearCachesApplicationListener,
    org.springframework.boot.builder.ParentContextCloserApplicationListener,
    org.springframework.boot.context.FileEncodingApplicationListener,
    org.springframework.boot.context.config.AnsiOutputApplicationListener,
    org.springframework.boot.context.config.DelegatingApplicationListener,
    org.springframework.boot.context.logging.LoggingApplicationListener,
    org.springframework.boot.env.EnvironmentPostProcessorApplicationListener,
    org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
    
    # Environment Post Processors
    org.springframework.boot.env.EnvironmentPostProcessor=
    org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,
    org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor,
    org.springframework.boot.env.RandomValuePropertySourceEnvironmentPostProcessor,
    org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,
    org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor,
    org.springframework.boot.reactor.DebugAgentEnvironmentPostProcessor
    
    # Failure Analyzers
    org.springframework.boot.diagnostics.FailureAnalyzer=
    org.springframework.boot.context.config.ConfigDataNotFoundFailureAnalyzer,
    org.springframework.boot.context.properties.IncompatibleConfigurationFailureAnalyzer,
    org.springframework.boot.context.properties.NotConstructorBoundInjectionFailureAnalyzer,
    org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,
    org.springframework.boot.diagnostics.analyzer.BeanDefinitionOverrideFailureAnalyzer,
    org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,
    org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,
    org.springframework.boot.diagnostics.analyzer.BindValidationFailureAnalyzer,
    org.springframework.boot.diagnostics.analyzer.UnboundConfigurationPropertyFailureAnalyzer,
    org.springframework.boot.diagnostics.analyzer.ConnectorStartFailureAnalyzer,
    org.springframework.boot.diagnostics.analyzer.NoSuchMethodFailureAnalyzer,
    org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,
    org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,
    org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer,
    org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyNameFailureAnalyzer,
    org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyValueFailureAnalyzer,
    org.springframework.boot.diagnostics.analyzer.PatternParseFailureAnalyzer,
    org.springframework.boot.liquibase.LiquibaseChangelogMissingFailureAnalyzer
    
    # Failure Analysis Reporters
    org.springframework.boot.diagnostics.FailureAnalysisReporter=
    org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter
    

    根据名字类型创建工厂实例

    createSpringFactoriesInstances

    通过反射创建实例。

    排序AnnotationAwareOrderComparator.sort

    排序规则:@Order从小到大排序,没有order则按没排序之前的顺序。

    推断主应用类

    deduceMainApplicationClass

    private Class<?> deduceMainApplicationClass() {
       try {
          StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
          for (StackTraceElement stackTraceElement : stackTrace) {
             if ("main".equals(stackTraceElement.getMethodName())) {
                 // 存在main方法的类为主类
                return Class.forName(stackTraceElement.getClassName());
             }
          }
       }
       catch (ClassNotFoundException ex) {
          // Swallow and continue
       }
       return null;
    }
    
  • 相关阅读:
    一个常见数据库操作错误的分析(四)
    一个常见数据库操作错误的分析(六)
    vc6 配置使用 boost 1.34.1
    string tips
    greta 编译,配置,使用
    xml学习笔记(一)
    火狐插件Firebug的使用
    浅谈编写高性能的Javascript代码 【重要】
    WebConfig加密/解密
    微软发布3款SQL Injection(SQL 注入)攻击检测工具
  • 原文地址:https://www.cnblogs.com/chinda/p/14287575.html
Copyright © 2020-2023  润新知