【Spring Boot】(22)、Spring Boot启动配置原理

启动配置原理

重要的事件回调机制:

  • ApplicationContextInitializer

  • SpringApplicationRunListener

  • ApplicationRunner

  • CommandLineRunner

    前两者需要配置在META-INF/spring.factories中,而后两者只需要放在ioc容器中。


启动流程


1、创建SpringApplication对象

SpringApplication#run

public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
   return (new SpringApplication(sources)).run(args);
}

首先根据主类创建SpringApplication对象。

public SpringApplication(Object... sources) {
   //初始化对象
   initialize(sources);
}

2、进行初始化工作

SpringApplication#initialize

private void initialize(Object[] sources) {
   if (sources != null && sources.length > 0) {
       //保存主配置类
       this.sources.addAll(Arrays.asList(sources));
   }
   //判断是否是web应用,源码#2.1
   this.webEnvironment = deduceWebEnvironment();    

   //从类路径下找到META-INF/spring.factories配置的所有ApplicationContextInitializer,然后保存起来,源码#2.2
   setInitializers((Collection) getSpringFactoriesInstances(
               ApplicationContextInitializer.class));

   //从类路径下找到META-INF/spring.factories配置的所有ApplicationListener,同上
   setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

   //从多个主配置类中找到有main方法的主类,源码#2.3
   this.mainApplicationClass = deduceMainApplicationClass();
}

#2.1:SpringApplication#deduceWebEnvironment

private static final String[] WEB_ENVIRONMENT_CLASSES = new String[]{"javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext"};

private boolean deduceWebEnvironment() {
   for (String className : WEB_ENVIRONMENT_CLASSES) {
       if (!ClassUtils.isPresent(className, null)) {
           return false;
       }
   }
   return true;
}

WEB_ENVIRONMENT_CLASSES集合中遍历元素判断当前环境中是否有web应用的两个重要的依赖。


#2.2:SpringApplication#getSpringFactoriesInstances

从类路径下的所有jar包里的META-INF/spring.factories文件中获取所有type类型的类。

private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) {
   return getSpringFactoriesInstances(type, new Class<?>[] {});
}

private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
   //获取ClassLoader
   ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

   //使用ClassLoader根据type来获取所有的value值并返回name组成的集合,源码#2.2.1
  Set<String> names = new LinkedHashSet<String>(
       SpringFactoriesLoader.loadFactoryNames(type, classLoader));

   //根据name值利用反射机制得到name所在的类实例,源码#2.2.2
   List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
       classLoader, args, names);
   AnnotationAwareOrderComparator.sort(instances);
   return instances;
}

#2.2.1:SpringFactoriesLoader#loadFactoryNames

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
   String factoryClassName = factoryClass.getName();

   try {
       //从类路径下所有jar包中查找META-INF/spring.factories文件路径
       Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                   ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
       List<String> result = new ArrayList<String>();

       while(urls.hasMoreElements()) {
           URL url = (URL)urls.nextElement();

           //依次遍历所有的文件路径,从文件中获取key为classname对应value,并以逗号分隔加入到集合中
           Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
           String factoryClassNames = properties.getProperty(factoryClassName);
           result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
       }

       //返回类型集合
       return result;
   } catch (IOException var8) {
       throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
                   "] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
   }
}

#2.2.2:SpringApplication#createSpringFactoriesInstances

private <T> List<T> createSpringFactoriesInstances(Class<T> type,
           Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
           Set<String> names)
{
   List<T> instances = new ArrayList<T>(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;
}

#2.3:SpringApplication#deduceMainApplicationClass

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

以上代码完成SpringApplication对象创建。

所有的initializer集合,如图所示:

所有的listener集合,如图所示:

以上两个示意图中的集合数据要看具体依赖 ,依赖其他模块,可能就不一样了。


3、运行run方法

​SpringApplication#run
private ConfigurableApplicationContext run(String... args) {
   StopWatch stopWatch = new StopWatch();
   stopWatch.start();
   ConfigurableApplicationContext context = null;
   FailureAnalyzers analyzers = null;
   configureHeadlessProperty();

   //获取SpringApplicationRunListeners,从类路径下找到META-INF/spring.factories配置的所有SpringApplicationRunListener,源码#3.1
   SpringApplicationRunListeners listeners = getRunListeners(args);

   //回调所有的SpringApplicationRunListener的starting方法,源码#3.2
   listeners.starting();

   try {
       //封装命令行参数
       ApplicationArguments applicationArguments = new DefaultApplicationArguments(
           args);

       //准备环境:首先创建环境对象,创建完成后回调每个SpringApplicationRunListener对象的environmentPrepared方法,源码#3.3
       ConfigurableEnvironment environment = prepareEnvironment(listeners,
                                                                applicationArguments);

       //打印Banner,源码#3.4
       Banner printedBanner = printBanner(environment);

       //创建ApplicationContext,决定创建web的ioc容器还是普通的ioc容器,具体原理看原来文章(此处略)
       context = createApplicationContext();

       analyzers = new FailureAnalyzers(context);

       //准备上下文,将environment保存到ioc容器中,源码#3.5
       //prepareContext方法内部:
       //首先applyInitializers(context);用来回调之前保存的所有的ApplicationContextInitializer的initialize方法
       //然后listeners.contextPrepared(context);用来回调所有的SpringApplicationRunListener的contextPrepared方法
       //最后listeners.contextLoaded(context);再回调所有的SpringApplicationRunListener的contextLoaded方法
       prepareContext(context, environment, listeners, applicationArguments,
                      printedBanner);

       //刷新ioc容器:首先创建ioc容器并初始化,如果是web应用还会创建嵌入式的Servlet容器,具体原理看原来文章(此处略)
       refreshContext(context);

       //从ioc容器中获取所有的ApplicationRunner和CommandLineRunner类,源码#3.6
       //并先回调ApplicationRunner的run方法,再回调CommandLineRunner的run方法
       afterRefresh(context, applicationArguments);

       //执行所有的SpringApplicationRunListener的finished方法,源码#3.7
       listeners.finished(context, null);

       stopWatch.stop();
       if (this.logStartupInfo) {
           new StartupInfoLogger(this.mainApplicationClass)
               .logStarted(getApplicationLog(), stopWatch);
       }

       //整个SpringBoot应用启动完成后返回ioc容器
       return context;
   }
   catch (Throwable ex) {
       handleRunFailure(context, listeners, analyzers, ex);
       throw new IllegalStateException(ex);
   }
}

#3.1:SpringApplication#getRunListeners

private SpringApplicationRunListeners getRunListeners(String[] args) {
   Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
   return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
       SpringApplicationRunListener.class, types, this, args));
}

从类路径下找到所有META-INF/spring.factories文件中配置的所有SpringApplicationRunListener类,并保存到SpringApplicationRunListeners对象的listeners对象集合中

#3.2:SpringApplicationRunListeners#starting

public void starting() {
   for (SpringApplicationRunListener listener : this.listeners) {
       listener.starting();
   }
}

调用所有SpringApplicationRunListener对象的starting方法。

#3.3:SpringApplication#prepareEnvironment

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
   //获取或者创建环境,源码#3.3.1
   ConfigurableEnvironment environment = getOrCreateEnvironment();

   //配置环境对象,比如当前的profiles,源码#3.3.2
   configureEnvironment(environment, applicationArguments.getSourceArgs());

   //调用每个SpringApplicationRunListener对象的environmentPrepared方法,源码#3.3.3
   listeners.environmentPrepared(environment);
   if (!this.webEnvironment) {
       environment = new EnvironmentConverter(getClassLoader())
           .convertToStandardEnvironmentIfNecessary(environment);
   }

   //返回环境对象
   return environment;
}

#3.3.1:SpringApplication#getOrCreateEnvironment

private ConfigurableEnvironment getOrCreateEnvironment() {
  if (this.environment != null) {
      return this.environment;
  }
   if (this.webEnvironment) {
       return new StandardServletEnvironment();
   }
   return new StandardEnvironment();
}

在上面第2章节中deduceWebEnvironment()方法返回是否是web环境标识,如果是web环境,webEnvironment则为true,否则为false。在此getOrCreateEnvironment()方法中根据webEnvironment的值创建环境对象,如果webEnvironment为true,则创建StandardServletEnvironment类型的环境对象,否则创建StandardEnvironment类型的环境对象。

#3.3.2:SpringApplication#configureEnvironment

protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
   this.configurePropertySources(environment, args);

   //配置当前环境的profiles,默认为default
   this.configureProfiles(environment, args);
}

#3.3.3:SpringApplicationRunListeners#environmentPrepared

public void environmentPrepared(ConfigurableEnvironment environment) {
   for (SpringApplicationRunListener listener : this.listeners) {
       listener.environmentPrepared(environment);
   }
}

调用所有SpringApplicationRunListener对象的environmentPrepared方法。

#3.4:SpringApplication#printBanner

private Banner printBanner(ConfigurableEnvironment environment) {
   //判断当前bannerMode是否为OFF
   if (this.bannerMode == Banner.Mode.OFF) {
       return null;
   } else {
       ResourceLoader resourceLoader = (this.resourceLoader != null ? this.resourceLoader
               : new DefaultResourceLoader(getClassLoader()));

       //获取BannerPrinter打印对象
       SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(
               resourceLoader, this.banner);

       //根据BannerMode的值,来打印Banner,源码#3.4.1
       if (this.bannerMode == Mode.LOG) {
           return bannerPrinter.print(environment, this.mainApplicationClass, logger);
       }
       return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
   }
}

#3.4.1:SpringApplicationBannerPrinter#print

public Banner print(Environment environment, Class<?> sourceClass, PrintStream out) {
   //获取Banner对象
   Banner banner = getBanner(environment, this.fallbackBanner);

   //依次调用Banners集合中的Banner对象的printBanner来开始打印Banner,源码#3.4.1.1
   banner.printBanner(environment, sourceClass, out);
   return new PrintedBanner(banner, sourceClass);
}

private Banner getBanner(Environment environment, Banner definedBanner) {
   Banners banners = new Banners();

   //从image图片格式中获取banner对象,如果有则保存到banners集合中
   banners.addIfNotNull(getImageBanner(environment));

   //从text文本格式中获取banner对象,如果有则保存到banners集合中
   banners.addIfNotNull(getTextBanner(environment));

   //banners集合个数只有0,1,2三种情况,然后判断banners集合是否为空,如果不为空,则返回之前获取到的Banners集合对象
   if (banners.hasAtLeastOneBanner()) {
       return banners;
   }
   if (this.fallbackBanner != null) {
       return this.fallbackBanner;
   }
   return DEFAULT_BANNER;
}

static final String BANNER_LOCATION_PROPERTY = "banner.location";
static final String DEFAULT_BANNER_LOCATION = "banner.txt";

private Banner getTextBanner(Environment environment) {
   //首先从环境对象中获取banner.location的值,如果存在直接返回该值,否则使用默认的banner.txt文件作为location的值
   String location = environment.getProperty(BANNER_LOCATION_PROPERTY,
               DEFAULT_BANNER_LOCATION);

   //根据location的值获取Resource对象
   Resource resource = this.resourceLoader.getResource(location);

   //判断Resource是否存在,如果存在返回ResourceBanner,否则返回null
   if (resource.exists()) {
       return new ResourceBanner(resource);
   }
   return null;
}

static final String[] IMAGE_EXTENSION = new String[]{"gif", "jpg", "png"};
static final String BANNER_IMAGE_LOCATION_PROPERTY = "banner.image.location";

private Banner getImageBanner(Environment environment) {
   //首先从环境对象中获取banner.image.location的值,如果存在直接返回该值
   String location = environment.getProperty(BANNER_IMAGE_LOCATION_PROPERTY);

   //判断是否有值
   if (StringUtils.hasLength(location)) {
       //如果banner.image.location有值,则根据该值返回Resource对象,并判断是否存在,如果存在则返回ImageBanner,否则返回null
       Resource resource = this.resourceLoader.getResource(location);
       return (resource.exists() ? new ImageBanner(resource) : null);
   }
   //如果banner.image.location没有值或者未设置,则依次判断banner.gif,banner.jpg,banner.png三种文件是否存在,如果某个文件存在,则直接返回该文件的ImageBanner对象,否则返回null
   for (String ext : IMAGE_EXTENSION) {
       Resource resource = this.resourceLoader.getResource("banner." + ext);
       if (resource.exists()) {
           return new ImageBanner(resource);
       }
   }
   return null;
}

#3.4.1.1:SpringApplicationBannerPrinter#printBanner

@Override
public void printBanner(Environment environment, Class<?> sourceClass,
                       PrintStream out)
{
   for (Banner banner : this.banners) {
       //依次调用每个banner对象的pringBanner方法
       banner.printBanner(environment, sourceClass, out);
   }
}

ResourceBanner#printBanner

@Override
public void printBanner(Environment environment, Class<?> sourceClass,
                       PrintStream out)
{
   
try {
       String banner = StreamUtils.copyToString(
this.resource.getInputStream(),
                                                environment.getProperty(
"banner.charset", Charset.class,
                                                                        Charset.forName(
"UTF-8")));

       
for (PropertyResolver resolver : getPropertyResolvers(environment,
                                                             sourceClass)) {
           banner = resolver.resolvePlaceholders(banner);
       }
       out.println(banner);
   }
   
catch (Exception ex) {
       
//other code...
   }
}

ImageBanner#printBanner

@Override
public void printBanner(Environment environment, Class<?> sourceClass,
                       PrintStream out)
{
   String headless = System.getProperty("java.awt.headless");
   try {
       System.setProperty("java.awt.headless", "true");
       printBanner(environment, out);
   }
   //catch...finally...
}

#3.5:SpringApplication#prepareContext

private void prepareContext(ConfigurableApplicationContext context,
           ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
           ApplicationArguments applicationArguments, Banner printedBanner)
{
   context.setEnvironment(environment);
   postProcessApplicationContext(context);

   //回调所有的initializer对象的initialize方法,源码#3.5.1
   applyInitializers(context);

   //回调所有的listener对象的contextPrepared方法,源码#3.5.2
   listeners.contextPrepared(context);
   if (this.logStartupInfo) {
       logStartupInfo(context.getParent() == null);
       logStartupProfileInfo(context);
   }

   //other code...

   //回调所有的listener对象的contextLoaded方法,源码#3.5.3
   listeners.contextLoaded(context);
}

#3.5.1:SpringApplication#applyInitializers

protected void applyInitializers(ConfigurableApplicationContext context) {
   //依次遍历所有的ApplicationContextInitializer对象,调用其initialize方法
   for (ApplicationContextInitializer initializer : getInitializers()) {
       Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
           initializer.getClass(), ApplicationContextInitializer.class);
       Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
       initializer.initialize(context);
   }
}

#3.5.2:SpringApplicationRunListeners#contextPrepared

public void contextPrepared(ConfigurableApplicationContext context) {
   //依次遍历所有的SpringApplicationRunListener对象,调用其contextPrepared方法
   for (SpringApplicationRunListener listener : this.listeners) {
       listener.contextPrepared(context);
   }
}

#3.5.3 : SpringApplicationRunListeners#contextLoaded

public void contextPrepared(ConfigurableApplicationContext context) {
   //依次遍历所有的SpringApplicationRunListener对象,调用其contextPrepared方法
   for (SpringApplicationRunListener listener : this.listeners) {
       listener.contextPrepared(context);
   }
}

#3.6:SpringApplication#afterRefresh

protected void afterRefresh(ConfigurableApplicationContext context,
           ApplicationArguments args)
{
   //调用所有的xxxRunner的run方法
   callRunners(context, args);
}

private void callRunners(ApplicationContext context, ApplicationArguments args) {
   List<Object> runners = new ArrayList<Object>();
   //从ioc容器中获取所有的ApplicationRunner和CommandLineRunner类
   runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
   runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
   AnnotationAwareOrderComparator.sort(runners);

   //先回调所有ApplicationRunner对象的run方法,在回调所有CommandLineRunner对象的run方法
   for (Object runner : new LinkedHashSet<Object>(runners)) {
       if (runner instanceof ApplicationRunner) {
           callRunner((ApplicationRunner) runner, args);
       }
       if (runner instanceof CommandLineRunner) {
           callRunner((CommandLineRunner) runner, args);
       }
   }
}

#3.7:SpringApplicationRunListeners#finished

public void finished(ConfigurableApplicationContext context, Throwable exception) {
   //依次遍历所有的SpringApplicationRunListener对象,调用其finished方法
   for (SpringApplicationRunListener listener : this.listeners) {
       callFinishedListener(listener, context, exception);
   }
}

private void callFinishedListener(SpringApplicationRunListener listener,
           ConfigurableApplicationContext context, Throwable exception)
{
   try {
       listener.finished(context, exception);
   }
   catch (Throwable ex) {
       //other code...
   }
}

这样整个SpringBoot应用启动就完成并返回ioc容器。


测试用例

分别编写四个事件回调类的子类:

ApplicationContextInitializer子类:HelloApplicationContextInitializer

public class HelloApplicationContextInitializer implements ApplicationContextInitializer {

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        System.out.println("HelloApplicationContextInitializer...initialize...");
    }
}

SpringApplicationRunListener子类:HelloSpringApplicationRunListener

public class HelloSpringApplicationRunListener implements SpringApplicationRunListener {

    //SpringApplicationRunListener子类必须有一个有参构造函数,参数分别是SpringApplication和String[]
    public HelloSpringApplicationRunListener(SpringApplication springApplication, String[] args){
        System.out.println("HelloSpringApplicationRunListener...constructor...");
    }

    @Override
    public void starting() {
        System.out.println("HelloSpringApplicationRunListener...starting...");
    }

    @Override
    public void environmentPrepared(ConfigurableEnvironment environment) {
        System.out.println("HelloSpringApplicationRunListener...environmentPrepared...");
    }

    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {
        System.out.println("HelloSpringApplicationRunListener...contextPrepared...");
    }

    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {
        System.out.println("HelloSpringApplicationRunListener...contextLoaded...");
    }

    @Override
    public void finished(ConfigurableApplicationContext context, Throwable exception) {
        System.out.println("HelloSpringApplicationRunListener...finished...");
    }
}

ApplicationRunner子类:HelloApplicationRunner

@Component
public class HelloApplicationRunner implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("HelloApplicationRunner...run...");
    }
}

CommandLineRunner子类:HelloCommandLineRunner

@Component
public class HelloCommandLineRunner implements CommandLineRunner {

    @Override
    public void run(String... args) throws Exception {
        System.out.println("HelloCommandLineRunner...run...");
    }
}

 在resources目录下新建META-INF文件夹,并新建一个文件,文件名为spring.factories,其内容为:

org.springframework.context.ApplicationContextInitializer=\
  org.com.cya.spring.boot.springboot07starter.initilizer.HelloApplicationContextInitializer

org.springframework.boot.SpringApplicationRunListener=\
  org.com.cya.spring.boot.springboot07starter.listener.HelloSpringApplicationRunListener

启动服务器之后,查看控制台输出的信息(忽略Spring Boot打印信息):

HelloSpringApplicationRunListener...constructor...
HelloSpringApplicationRunListener...starting...
HelloSpringApplicationRunListener...environmentPrepared...
HelloApplicationContextInitializer...initialize...
HelloSpringApplicationRunListener...contextPrepared...
HelloSpringApplicationRunListener...contextLoaded...
HelloApplicationRunner...run...
HelloCommandLineRunner...run...
HelloSpringApplicationRunListener...finished...

其结果跟源码分析过程一致。


====================打个广告,欢迎关注====================

QQ:
412425870
微信公众号:Cay课堂

csdn博客:
http://blog.csdn.net/caychen
码云:
https://gitee.com/caychen/
github:
https://github.com/caychen

点击群号或者扫描二维码即可加入QQ群:

328243383(1群)




点击群号或者扫描二维码即可加入QQ群:

180479701(2群)



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值