zoukankan      html  css  js  c++  java
  • SpringBoot启动分析1:SpringApplication的初始化

    前言:本次源码分析使用SpringBoot-2.2.5.RELEASE版本。

    1.1 Initializer初始化器

    1.1.1 Initializer概述

    初始化器的基类ApplicationContextInitializer是这么描述初始化器的:

    在Spring的上下文环境对象调用refresh()方法之前调用的回调接口,这些初始化器通常被用于web应用需要初始化应用上下文。
    

    实际上,ApplicationContextInitializer只提供了一个接口,接口定义如下:

    public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
    	void initialize(C applicationContext);
    }
    

    所以看出在上下文环境对象调用refresh()方法之前调用的回调接口就是各个实现了ApplicationContextInitializer实现类的initialize方法。

    1.1.2 Initializer实现方式

    基于SpringBoot的SPI

    如果查看过SpringBoot的启动源码那么会很清楚这种方式的实现方式,实际上它是通过SpringFactoriesLoader读取spring.factories配置文件中的实现来实例化Initializer,
    最后在必要的情况下调用这些Initializer的initialize方法。
    首先,创建一个Initializer实现:

    @Order(1)
    public class Order1Initializer implements ApplicationContextInitializer {
    
        private Logger log = LoggerFactory.getLogger(getClass());
    
        @Override
        public void initialize(ConfigurableApplicationContext applicationContext) {
            log.info("order1Initializer...");
        }
    }
    

    其次,在resources目录中新增META-INF目录并在该目录中添加spring.factories配置文件并添加以下配置:

    org.springframework.context.ApplicationContextInitializer=com.neckel.springboot.initializer.Order1Initializer
    

    最后启动SpringBoot:

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

    查看控制台的日志输出情况:

    2020-08-03 16:15:20.283  INFO 16108 --- [           main] c.n.s.initializer.Order1Initializer      : order1Initializer...
    2020-08-03 16:15:20.291  INFO 16108 --- [           main] com.neckel.springboot.Application        : Starting Application on PC-27 with PID 16108 (C:UsersAdministratorIdeaProjects
    eckel-springboot	argetclasses started by Administrator in C:UsersAdministratorIdeaProjects
    eckel-springboot)
    2020-08-03 16:15:20.292  INFO 16108 --- [           main] com.neckel.springboot.Application        : No active profile set, falling back to default profiles: default
    2020-08-03 16:15:20.794  INFO 16108 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
    ......
    

    基于启动类设置

    基于SpringBoot的SPI的方式,最终是将从spring.factories配置文件读取到的实现添加到SpringApplication中的initializers属性中,所以使用我们完全可以等SpringApplication初始化完成之后,收到将Initializer设置到initializers属性中。
    首先,创建一个Initializer实现:

    @Order(2)
    public class Order2Initializer implements ApplicationContextInitializer {
    
        private Logger log = LoggerFactory.getLogger(getClass());
    
        @Override
        public void initialize(ConfigurableApplicationContext applicationContext) {
            log.info("order2Initializer...");
        }
    }
    

    其次,在启动类中将该初始化器添加到SpringApplication中:

    @SpringBootApplication
    public class Application {
    
        public static void main(String[] args) {
            SpringApplication springApplication = new SpringApplication(Application.class);
            springApplication.addInitializers(new Order1Initializer());
            springApplication.run(args);
        }
    
    }
    

    最后启动SpringBoot,查看控制台的日志输出情况:

    2020-08-03 16:43:42.656  INFO 18272 --- [           main] c.n.s.initializer.Order1Initializer      : order1Initializer...
    2020-08-03 16:43:42.659  INFO 18272 --- [           main] c.n.s.initializer.Order2Initializer      : order2Initializer...
    2020-08-03 16:43:42.665  INFO 18272 --- [           main] com.neckel.springboot.Application        : Starting Application on PC-27 with PID 18272 (C:UsersAdministratorIdeaProjects
    eckel-springboot	argetclasses started by Administrator in C:UsersAdministratorIdeaProjects
    eckel-springboot)
    2020-08-03 16:43:42.665  INFO 18272 --- [           main] com.neckel.springboot.Application        : No active profile set, falling back to default profiles: default
    2020-08-03 16:43:43.188  INFO 18272 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
    ......
    

    基于配置文件配置

    初始化器的基类ApplicationContextInitializer有个实现类DelegatingApplicationContextInitializer,它用于读取配置文件中的context.initializer.classes来设置初始化器。
    首先,创建一个Initializer实现:

    @Order(3)
    public class Order3Initializer implements ApplicationContextInitializer {
    
        private Logger log = LoggerFactory.getLogger(getClass());
    
        @Override
        public void initialize(ConfigurableApplicationContext applicationContext) {
            log.info("order3Initializer...");
        }
    }
    

    其次,在全局配置文件中添加以下配置:

    context.initializer.classes=com.neckel.springboot.initializer.Order3Initializer
    

    最后启动SpringBoot,查看控制台的日志输出情况:

    2020-08-03 16:49:31.959  INFO 9756 --- [           main] c.n.s.initializer.Order3Initializer      : order3Initializer...
    2020-08-03 16:49:31.960  INFO 9756 --- [           main] c.n.s.initializer.Order1Initializer      : order1Initializer...
    2020-08-03 16:49:31.960  INFO 9756 --- [           main] c.n.s.initializer.Order2Initializer      : order2Initializer...
    2020-08-03 16:49:31.966  INFO 9756 --- [           main] com.neckel.springboot.Application        : Starting Application on PC-27 with PID 9756 (C:UsersAdministratorIdeaProjects
    eckel-springboot	argetclasses started by Administrator in C:UsersAdministratorIdeaProjects
    eckel-springboot)
    2020-08-03 16:49:31.966  INFO 9756 --- [           main] com.neckel.springboot.Application        : No active profile set, falling back to default profiles: default
    2020-08-03 16:49:32.445  INFO 9756 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
    ......
    

    1.1.3 可能存在的疑问

    在上方的三个Initializer初始化器中,它们都添加了@Order注解,该注解用于对它们进行排序,最终根据排序的顺序执行它们各自的initialize方法。至于如何排序后续会说明,但是这里存在疑问是Order3Initializer中的@Order(3)设置的优先级比其它两个初始化器还低,为什么还优先执行?答案在于Order3Initializer是通过全局配置文件配置被加载到SpringApplication中,它是由DelegatingApplicationContextInitializer读取全局配置文件中的context.initializer.classes属性配置,并且在DelegatingApplicationContextInitializer源码中可以看到它为读取到的初始化器设置了默认的优先级:

    public class DelegatingApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
    
          private static final String PROPERTY_NAME = "context.initializer.classes";
          // 该优先级已超越了Order1Initializer和Order2Initializer的优先级
          private int order = 0;
          ......
    }
    

    1.2 Listener监听器

    1.2.1 Listener概述

    这里的Listener指的是ApplicationListener类型的监听器,它是应用事件监听接口,是继承自Java标准观察者模式的EventListener接口。从Spring3.0之后,一个ApplicationListener实现可以声明自己所关心的事件类型。当一个ApplicationListener被注册到Spring ApplicationContext之后,应用运行时应用事件会陆续发生,对应响应类型的事件监听器会被调用。
    以下是EventListener接口和ApplicationListener接口的接口定义:

    # EventListener接口是JDK1.1提供的标准接口,所有的事件监听者都要实现这个接口
    public interface EventListener {}
    # ApplicationListener接口,继承自EventListener接口
    @FunctionalInterface
    public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
          void onApplicationEvent(E event);
    }
    

    在ApplicationListener接口中可以看到它处理的事件类型是ApplicationEvent,该事件抽象类是所有事件的基类,若要自定义事件则需要继承该事件基类,当自定义监听器触发监听后会调用自己的onApplicationEvent方法处理自定义的事件。

    1.2.2 Listener实现方式

    基于SpringBoot的SPI

    这种实现方式和Initializer初始化器一样,都是通过SpringFactoriesLoader读取spring.factories配置文件中的实现来实例化Listener。
    首先,创建一个Listener实现:

    @Order(1)
    public class Order1Listener implements ApplicationListener<ApplicationStartedEvent> {
    
        private Logger log = LoggerFactory.getLogger(getClass());
    
        public void onApplicationEvent(ApplicationStartedEvent event) {
            log.info("order1Listener...");
        }
    }
    

    其次,在resources目录中新增META-INF目录并在该目录中添加spring.factories配置文件并添加以下配置:

    org.springframework.context.ApplicationListener=com.neckel.springboot.listener.Order1Listener
    

    最后启动SpringBoot:

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

    查看控制台的日志输出情况:

    ......
    2020-08-06 23:33:57.499  INFO 8908 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.31]
    2020-08-06 23:33:57.586  INFO 8908 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
    2020-08-06 23:33:57.586  INFO 8908 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1500 ms
    2020-08-06 23:33:57.752  INFO 8908 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
    2020-08-06 23:33:57.897  INFO 8908 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
    2020-08-06 23:33:57.900  INFO 8908 --- [           main] com.neckel.source.Application            : Started Application in 2.232 seconds (JVM running for 2.639)
    2020-08-06 23:33:57.900  INFO 8908 --- [           main] c.neckel.source.listener.Order1Listener  : order1Listener...
    

    基于启动类设置

    基于SpringBoot的SPI的方式,最终是将从spring.factories配置文件读取到的实现添加到SpringApplication中的listeners属性中,所以我们完全可以跟Initializer一样等SpringApplication初始化完成之后,收到将Listener设置到listeners属性中。
    首先,创建一个Listener实现:

    @Order(2)
    public class Order2Listener implements ApplicationListener<ApplicationStartedEvent> {
    
        private Logger log = LoggerFactory.getLogger(getClass());
    
        public void onApplicationEvent(ApplicationStartedEvent event) {
            log.info("order2Listener...");
        }
    }
    

    其次,在启动类中将该监听器添加到SpringApplication中:

    @SpringBootApplication
    public class Application {
        public static void main(String[] args) {
            SpringApplication springApplication = new SpringApplication(Application.class);
            springApplication.addListeners(new Order2Listener());
            springApplication.run(args);
        }
    }
    

    最后启动SpringBoot,查看控制台的日志输出情况:

    2020-08-06 23:37:27.943  INFO 9132 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.31]
    2020-08-06 23:37:28.028  INFO 9132 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
    2020-08-06 23:37:28.029  INFO 9132 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1353 ms
    2020-08-06 23:37:28.218  INFO 9132 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
    2020-08-06 23:37:28.360  INFO 9132 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
    2020-08-06 23:37:28.362  INFO 9132 --- [           main] com.neckel.source.Application            : Started Application in 2.248 seconds (JVM running for 2.693)
    2020-08-06 23:37:28.363  INFO 9132 --- [           main] c.neckel.source.listener.Order1Listener  : order1Listener...
    2020-08-06 23:37:28.363  INFO 9132 --- [           main] c.neckel.source.listener.Order2Listener  : order2Listener...
    

    基于配置文件配置

    监听器的基类ApplicationListener有个实现类DelegatingApplicationListener,它用于读取配置文件中的context.listener.classes来设置监听器。
    首先,创建一个Listener实现:

    @Order(3)
    public class Order3Listener implements ApplicationListener<ApplicationStartedEvent> {
    
        private Logger log = LoggerFactory.getLogger(getClass());
    
        public void onApplicationEvent(ApplicationStartedEvent event) {
            log.info("order3Listener...");
        }
    }
    

    其次,在全局配置文件中添加以下配置:

    context.listener.classes=com.neckel.springboot.listener.Order3Listener
    

    最后启动SpringBoot,查看控制台的日志输出情况:

    2020-08-06 23:45:07.795  INFO 2500 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.31]
    2020-08-06 23:45:07.878  INFO 2500 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
    2020-08-06 23:45:07.878  INFO 2500 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1397 ms
    2020-08-06 23:45:08.018  INFO 2500 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
    2020-08-06 23:45:08.151  INFO 2500 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
    2020-08-06 23:45:08.153  INFO 2500 --- [           main] com.neckel.source.Application            : Started Application in 2.125 seconds (JVM running for 2.539)
    2020-08-06 23:45:08.154  INFO 2500 --- [           main] c.neckel.source.listener.Order3Listener  : order3Listener...
    2020-08-06 23:45:08.154  INFO 2500 --- [           main] c.neckel.source.listener.Order1Listener  : order1Listener...
    2020-08-06 23:45:08.154  INFO 2500 --- [           main] c.neckel.source.listener.Order2Listener  : order2Listener...
    

    跟Initializer初始化器一样三个Listener都添加了@Order注解,其中order3Listener优先被执行是因为order3Listener是通过全局配置文件配置被加载到SpringApplication中,它是由DelegatingApplicationListener读取全局配置文件中的context.listener.classes属性配置,并且在DelegatingApplicationListener源码中可以看到它为读取到的初始化器设置了默认的优先级:

    public class DelegatingApplicationListener implements ApplicationListener<ApplicationEvent>, Ordered {
    
    	private static final String PROPERTY_NAME = "context.listener.classes";
    	private int order = 0;
            ......
    }
    

    备注:另外还有一种实现方式后续说明。

    1.3 SpringApplication初始化

    1.3.1 SpringBoot启动入口

    想要启动一个SpringBoot应用很简单,只需要在包的根目录下创建一个类,并且在类上添加注解@SpringBootApplication标注其是一个SpringBoot应用即可:

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

    通过执行该启动类的Main方法即可启动一个SpringBoot应用,接下来整个SpringBoot的启动过程就是通过SpringApplication的run方法来进行。

    1.3.2 实例化SpringApplication

    SpringApplication类可用于从main方法引导和启动Spring应用程序,默认情况下该类将执行以下步骤来引导程序的启动:

    • 创建一个ApplicationContext上下文实例
    • 注册CommandLinePropertySource以将命令行参数公开为Spring属性
    • 刷新应用程序上下文并加载所有单例Bean

    该类的初始化由该类调用自身的run方法开始,再调用自身重载的run方法后得到初始化机会:

    public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
    	return run(new Class<?>[] { primarySource }, args);
    }
    public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    	return new SpringApplication(primarySources).run(args);
    }
    

    上面的new SpringApplication(...)就是开始初始化该类的实例,点击构造方法进入得到以下初始化源码:

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    	this.resourceLoader = resourceLoader;
    	Assert.notNull(primarySources, "PrimarySources must not be null");
    	this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    	this.webApplicationType = WebApplicationType.deduceFromClasspath();
    	setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    	this.mainApplicationClass = deduceMainApplicationClass();
    }
    

    核心的初始化分为以下三个步骤。

    初始化WebApplicationType

    应用类型的初始化通过WebApplicationType.deduceFromClasspath()来进行,点击进入其中查看对应源码:

    public enum WebApplicationType {
    	NONE,
    	SERVLET,
    	REACTIVE;
    	private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet","org.springframework.web.context.ConfigurableWebApplicationContext" };
    	private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";
    	private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";
    	private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
    	private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";
    	private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";
    	static WebApplicationType deduceFromClasspath() {
    		if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
    				&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
    			return WebApplicationType.REACTIVE;
    		}
    		for (String className : SERVLET_INDICATOR_CLASSES) {
    			if (!ClassUtils.isPresent(className, null)) {
    				return WebApplicationType.NONE;
    			}
    		}
    		return WebApplicationType.SERVLET;
    	}
    }
    

    可以看出,通过反射判断环境是否存在这个类来判断获取初始化的web应用类型。

    初始化Initializers

    用于读取所有META-INF/spring.factories文件中配置了ApplicationContextInitializer的初始化器:

    public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) {
    	this.initializers = new ArrayList<>(initializers);
    }
    

    可以看出它将从spring.factories文件中获取的配置进行实例化后,封装成一个集合对SpringApplication中的initializers集合属性进行初始化。

    初始化Listeners

    跟初始化Initializers类似也是通过读取所有META-INF/spring.factories文件中配置的ApplicationListener类型的监听器,最终配置被实例化之后封装成一个集合对SpringApplication中的listeners集合属性进行初始化:

    public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
    	this.listeners = new ArrayList<>(listeners);
    }
    

    值得注意的是,不管是初始化Initializers或者初始化Listeners,上面都省略了一个步骤,即读取配置和实例化的步骤,是因为它是一个通用读取的过程,以下详细讲解。
    要读取配置中的组件信息,需要调用通用方式getSpringFactoriesInstances并传入对应的参数类型,例如:

    # 读取ApplicationContextInitializer类型的配置信息
    getSpringFactoriesInstances(ApplicationContextInitializer.class)
    # 读取ApplicationListener类型的配置信息
    getSpringFactoriesInstances(ApplicationListener.class)
    

    这里列举了ApplicationContextInitializer类型和ApplicationListener类型在META-INF/spring.factories文件中的配置:

    # 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.cloud.CloudFoundryVcapEnvironmentPostProcessor,
    org.springframework.boot.context.FileEncodingApplicationListener,
    org.springframework.boot.context.config.AnsiOutputApplicationListener,
    org.springframework.boot.context.config.ConfigFileApplicationListener,
    org.springframework.boot.context.config.DelegatingApplicationListener,
    org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,
    org.springframework.boot.context.logging.LoggingApplicationListener,
    org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
    

    点击getSpringFactoriesInstances方法进入内部查看源码:

    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
          return getSpringFactoriesInstances(type, new 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;
    }
    

    可以看出最终它是由loadFactoryNames方法解析出配置并存放到Set集合中,最终将该集合作为参数传入createSpringFactoriesInstances方法,通过Set参数集合实例化出一个个实例。
    而这里的Set集合就是一个个的ApplicationContextInitializer或ApplicationListener所对应的组件信息,点击loadFactoryNames方法进入,需要注意的是参数type为要读取的配置类型:

    public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
    	String factoryTypeName = factoryType.getName();
    	return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
    }
    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 {
    			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 resource = new UrlResource(url);
    				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
    				Iterator var6 = properties.entrySet().iterator();
    				while(var6.hasNext()) {
    					Entry<?, ?> entry = (Entry)var6.next();
    					String factoryTypeName = ((String)entry.getKey()).trim();
    					String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
    					int var10 = var9.length;
    
    					for(int var11 = 0; var11 < var10; ++var11) {
    						String factoryImplementationName = var9[var11];
    						result.add(factoryTypeName, factoryImplementationName.trim());
    					}
    				}
    			}
    			cache.put(classLoader, result);
    			return result;
    		} catch (IOException var13) {
    			throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
    		}
    	}
    }
    

    通过以上的loadSpringFactories方法,它遍历了每个jar包中的META-INF/spring.factories文件,并把spring.factories文件中的每个配置全部读取出来放到一个map中,每个map的key就是spring.factories文件中对应的一个属性配置key,value则是这个key所对应的一些需要初始化的组件,如下图展示其中一个spring.factories文件内容,以及读取出来的组件信息,以ApplicationContextInitializer类型为例:

    # 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
    

    该配置对应读取出来的map信息为:

    最后在loadFactoryNames方法中通过factoryTypeName即ApplicationContextInitializer这个类型来筛选出其中的实现类,得到了以下结果:

    这就是ApplicationContextInitializer类型的Set集合,拿到该集合后需要将集合中的ApplicationContextInitializer类型的实现进行一个个实例化出来,所以调用createSpringFactoriesInstances方法并将上述解析出来的实现集合作为参数传入(另外还有一个重要的参数即SpringApplication,这个后续绑定监听器时有用):

    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 {
    			Class<?> instanceClass = ClassUtils.forName(name, classLoader);
    			Assert.isAssignable(type, instanceClass);
    			Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
    			T instance = (T) BeanUtils.instantiateClass(constructor, args);
    			instances.add(instance);
    		}
    		catch (Throwable ex) {
    			throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
    		}
    	}
    	return instances;
    }
    

    这段代码的意思就是将把spring.factories文件中的每个配置全部读取出来放到一个map中后,遍历该map的key,通过反射的方式实例化出来放到放instances集合中。
    至此,整个SpringApplication实例初始化完成,而核心的属性Initializers和Listeners会在后续启动做作为重要参数来初始化。

    1.4 Order注解实现

    在前面的不管是Initializers或Listeners中通过添加@Order注解并指定优先级数值可以决定它们的执行顺序。上面的createSpringFactoriesInstances方法中返回了读取的例如Listeners实现,后续会将这些实现进行排序:

    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;
    }
    

    AnnotationAwareOrderComparator

    AnnotationAwareOrderComparator拓展了OrderComparator接口,它不仅支持Ordered接口,还支持@Order注解以方便排序。AnnotationAwareOrderComparator类图如下:

    AnnotationAwareOrderComparator相较于父接口OrderComparator提供了通过读取注解元数据来进行排序的方法。

    排序源码分析(以排序Initializers初始化器为例)

    从上方可以知道实际排序的方法就是:

    AnnotationAwareOrderComparator.sort(instances);
    

    点击sort方法进入:

    public class AnnotationAwareOrderComparator extends OrderComparator {
        public static final AnnotationAwareOrderComparator INSTANCE = new AnnotationAwareOrderComparator();
    
        public static void sort(List<?> list) {
            if (list.size() > 1) {
                list.sort(INSTANCE);
            }
        }
    }
    

    可以看出调用了ArrayList的sort方法并传入了AnnotationAwareOrderComparator,目的在于后续排序的时候调用了AnnotationAwareOrderComparator的父类OrderComparator方法doCompare执行排序。省略ArrayList的sort方法直接进入OrderComparator方法doCompare源码:

    public class OrderComparator implements Comparator<Object> {
        private int doCompare(@Nullable Object o1, @Nullable Object o2, @NullableOrderSourceProvider sourceProvider) {
            boolean p1 = (o1 instanceof PriorityOrdered);
            boolean p2 = (o2 instanceof PriorityOrdered);
            if (p1 && !p2) {
                return -1;
            } else if (p2 && !p1) {
                return 1;
            }
            int i1 = getOrder(o1, sourceProvider);
            int i2 = getOrder(o2, sourceProvider);
            return Integer.compare(i1, i2);
        }
    }
    

    传递进来的参数Object o1和Object o2就是初始化器中的其中两个,开始对比这两者。点击getorder方法进入:

    public class OrderComparator implements Comparator<Object> {
        private int getOrder(@Nullable Object obj, @Nullable OrderSourceProvider sourceProvider) {
            Integer order = null;
            if ((obj != null) && (sourceProvider != null)) {
                Object orderSource = sourceProvider.getOrderSource(obj);
                if (orderSource != null) {
                    if (orderSource.getClass().isArray()) {
                        Object[] sources = ObjectUtils.toObjectArray(orderSource);
                        for (Object source : sources) {
                            order = findOrder(source);
                            if (order != null) {
                                break;
                            }
                        }
                    } else {
                        order = findOrder(orderSource);
                    }
                }
            }
            return ((order != null) ? order : getOrder(obj)); // 由于sourceProvider为空所以直接走这里的方法
        }
    }
    

    点击重载的getOrder方法:

    public class OrderComparator implements Comparator<Object> {
        protected int getOrder(@Nullable
        Object obj) {
            if (obj != null) { // 由于obj就是实际的初始化器所以这里不为空
                Integer order = findOrder(obj);
                if (order != null) {
                    return order;
                }
            }
            return Ordered.LOWEST_PRECEDENCE;
        }
    }
    

    跟踪findOrder方法进入:

    public class AnnotationAwareOrderComparator extends OrderComparator {
        @Override
        @Nullable
        protected Integer findOrder(Object obj) {
            Integer order = super.findOrder(obj);
            if (order != null) {
                return order;
            }
            return findOrderFromAnnotation(obj);
        }
    
        @Override
        @Nullable
        protected Integer findOrder(Object obj) {
            Integer order = super.findOrder(obj);
            if (order != null) {
                return order;
            }
            return findOrderFromAnnotation(obj);
        }
    
        @Nullable
        private Integer findOrderFromAnnotation(Object obj) {
            AnnotatedElement element = ((obj instanceof AnnotatedElement)
                ? (AnnotatedElement) obj : obj.getClass());
            MergedAnnotations annotations = MergedAnnotations.from(element,
                    SearchStrategy.TYPE_HIERARCHY);
            Integer order = OrderUtils.getOrderFromAnnotations(element, annotations);
            if ((order == null) && obj instanceof DecoratingProxy) {
                return findOrderFromAnnotation(((DecoratingProxy) obj).getDecoratedClass());
            }
            return order;
        }
    }
    
    

    这里首先调用父类OrderComparator的findOrder方法,通过getOrder()方法获取初始化器的order属性值,若不存在该属性值,则findOrderFromAnnotation方法从注解元数据中获取,若获取结果仍然为空则返回默认的order值Ordered.LOWEST_PRECEDENCE。

  • 相关阅读:
    四则运算出题系统,java
    Javaweb测试
    《构建之法》 读书笔记(6)
    使用ProcDump在程序没有响应时自动收集dump
    NASA关于如何写出安全代码的10条军规
    C#和C++中的float类型
    避免在C#中使用析构函数Finalizer
    C#性能优化的一些技巧
    从bug中学习怎么写代码
    Code Smell那么多,应该先改哪一个?
  • 原文地址:https://www.cnblogs.com/Json1208/p/13333911.html
Copyright © 2011-2022 走看看