Springboot启动原理分析

这是一个SpringBoot项目的启动类

SpringApplication的run方法是将当前类和参数传递进去进行初始化操作

 run方法会返回一个SpringApplication实例,进行启动初始化。

接着往下看,new SpringApplication(primarySources) 这段代码,我们点击SpringApplication,可以看到如下图

 给核心变量赋值

在这么多变量中,我们找到核心的几个

//主应用程序类
private Class<?> mainApplicationClass;
//主要bean来源集合
private Set<Class<?>> primarySources;
//应用程序初始化器集合
private List<ApplicationContextInitializer<?>> initializers;
//应用程序监听器集合
private List<ApplicationListener<?>> listeners;
//应用程序类型
private WebApplicationType webApplicationType;
//创建一个新的SpringApplication实例,并从bean来源集合中加载信息
public SpringApplication(Class<?>... primarySources) {
       //空的资源加载器
       this((ResourceLoader)null, primarySources);
   }
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.sources = new LinkedHashSet();
        this.bannerMode = Mode.CONSOLE;
        this.logStartupInfo = true;
        this.addCommandLineProperties = true;
        this.addConversionService = true;
        this.headless = true;
        this.registerShutdownHook = true;
        this.additionalProfiles = Collections.emptySet();
        this.isCustomEnvironment = false;
        this.lazyInitialization = false;
        this.applicationContextFactory = ApplicationContextFactory.DEFAULT;
        this.applicationStartup = ApplicationStartup.DEFAULT;
        this.resourceLoader = resourceLoader;
        //判断来源bean集合是否为空
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
        //设置Web应用程序类型,可能是SERVLET程序,也可能是REACTIVE响应式程序,还有可能是NONE即非web应用程序
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        this.bootstrapRegistryInitializers = this.getBootstrapRegistryInitializersFromSpringFactories();         
        //设置ApplicationContextInitializer,应用程序初始化器。从META-INF/spring.factories文件中获取   this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        //设置ApplicationListener,应用程序监听器。从META-INF/spring.factories文件中获取
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }

判断应用程序类型 

deduceFromClasspath()该方法根据是否存在指定路径的类来推断应用程序类型。有NONE、REACTIVE、SERVLET三种,一般都是SERVLET类型。

源码如下

static WebApplicationType deduceFromClasspath() {
       //如果存在org.springframework.web.reactive.DispatcherHandler类型,并且不存在org.springframework.web.servlet.DispatcherServlet类型,并且不存在org.glassfish.jersey.servlet.ServletContainer类型,那么设置为REACTIVE,即响应式web应用程序 
        if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)) {
            return REACTIVE;
        } else {
            String[] var0 = SERVLET_INDICATOR_CLASSES;
            int var1 = var0.length;

            for(int var2 = 0; var2 < var1; ++var2) {
                String className = var0[var2];
                //如果不存在javax.servlet.Servlet类型,或者不存在org.springframework.web.context.ConfigurableWebApplicationContext类型,那么设置为NONE,即非web应用程序
                if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
                    return NONE;
                }
            }
            //否则设置为SERVLET,即给予servlet的web应用程序,一般都是SERVLET
            return SERVLET;
        }
    }

设置初始化器

        设置ApplicationContextInitializer初始化器,初始化的时候会使用到,在Spring上下文被刷新之前进行初始化的操作,例如注入Property Sources属性源或者是激活Profiles环境,这里会借助SpringFactoriesLoader工具类获取所有引入的jar包中和当前类路径下的META-INF/spring.factories文件中指定类型的实例,也就是ApplicationContextInitializer类型的实例。

        spring.factories 是Spirng boot提供的一种扩展机制,实际上spring.factories就是仿照Java中的SPI扩展机制来实现的Spring Boot自己的SPI机制,它是实现Spribf Boot的自动配置的基础。

        spring.factories该文件必须是 Properties 格式,其中 key 是接口或抽象类的完全限定名称,value 是逗号分隔的实现类名称列表。

设置初始化器源码

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

从META-INF/spring.factories中加载实例源码

//借助SpringFactoriesLoader获取所有引入的jar包中和当前类路径下的META-INF/spring.factories文件中指定类型的实例spring.factories 文件必须是 Properties 格式,其中 key 是接口或抽象类的完全限定名称,value 是逗号分隔的实现类名称列表。
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
        return this.getSpringFactoriesInstances(type, new Class[0]);
    }
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
        ClassLoader classLoader = this.getClassLoader();
        Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        //根据获取的类型全路径名反射创建实例
        List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }

获取全路径类名集合

        loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader)方法加载给定类型的全路径名,通过SpringFactoriesLoader从全部“META-INF/spring.factories”文件中根据给定的type类型获取所有对应的是实现类的全路径类名集合。这里的type就是ApplicationContextInitializer

对用到的全路径类名下的类进行实例化

         createSpringFactoriesInstances()该方法将会对上面方法加载的全部ApplicationContextInitializer的实现进行实例化,实际上就是一系列反射创建对象的过程。

private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names) {
        //用来存放实例化完成的类
        List<T> instances = new ArrayList(names.size());
        Iterator var7 = names.iterator();
        //遍历全路径类名
        while(var7.hasNext()) {
            String name = (String)var7.next();
            //根据全路径名获取class对象
            try {
                Class<?> instanceClass = ClassUtils.forName(name, classLoader);
                Assert.isAssignable(type, instanceClass);
                Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
                T instance = BeanUtils.instantiateClass(constructor, args);
                instances.add(instance);
            } catch (Throwable var12) {
                throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, var12);
            }
        }

        return instances;
    }

设置监听器

        setListeners设置ApplicationListener监听器。该方法和上面的设置初始化器setInitializers方法的逻辑是一样的,同样是从META-INF/spring.factories文件中获取,只不过上面获取的是ApplicationInitializer类型,而这里获取的是ApplicationListener类型的实例。

  spring-boot-autoconfigure.jar下的/META-INF/spring.factories文件中,名为org.springframework.context.ApplicationListener的key对应的value为包括一个监听器。

推断应用程序主类

        deduceMainApplicationClass()该方法推断应用程序主类,一般就是Spring boot项目的启动类,也就是调用main方法的类。

运行spring应用实例

        run()方法将会运行SpringApplication实例,并且创建并刷新一个新的ApplicationContext。该方法就是Spring boot项目启动的关键方法。

该方法主要有以下步骤:

1、从spring.factories文件中加载运行监听器的实现,比如EventPublishingRunListener

2、执行所有运行监听器实现的starting方法,进行早期的初始化操作,比如,EventPublishingRunListener的starting方法会向之前初始化的所有的ApplicationListener发送一个ApplicationStartingEvent事件,标志着SpringApplication的启动。

3、根据监听器和启动参数准备环境,查找项目的配置文件和激活的profile等信息。

4、打印Banner图标,printBanner()方法。

5、创建spring上下文容器实例

6、准备上下文容器prepareContext(),该方法是核心方法,将会将启动类注入到容器上下文内部的beanfactory中,有了这一步,后面就可以解析启动类的注解和各种配置,进而进行springboot的自动配置

自动配置时,依据@ComponentScan扫描到启动类下所有包下的标有组件注解的类,将其注入到IOC容器中,依据@EnableAutoConfiguration注解下的@Import注解导入AutoConfigurationImportSelector选择器,并使用selectImports()方法,去spring.factories文件中找KV键值对中K为xxxAutoConfiguartion的值,找到要注入IOC容器的类路径,再找出属性K,赋值后注册为Bean,放入IOC容器中。还有框架自带的某些类也会在此期间注册为IOC中的Bean。

如何注册为Bean?

        将每个需要注册为Bean的类的信息通过BeanDefinitionRegister接口注册为BeanDefinition到注册表中,再填充BeanDefinitionMap<BeanName,BeanDefinition实现类>,BeanDefinitionNames,存放所有要注册的BeanDefinition名称,后续根据BeanDefinitionNames通过反射获取Bean即可完成Bean的创建。

7、刷新上下文容器refreshContext(),该方法将会加载和解析容器中的bean以及各种配置

8、刷新后处理afterRefresh()。该方法是一个扩展方法,没有提供任何默认的实现,我们自定义的子类可以进行扩展,在afterRefresh()方法中运行callRunners()方法调用runner,即执行容器中的ApplicationRunner和CommandLineRunner类型bean的run方法。这也是一个扩展点,用于执行spring容器完全启动后需要做的逻辑。

9、应用SpringApplicationRunListener监听器的started方法。EventPublishingRunListener将会发出ApplicationStartedEvent事件,表明容器已启动

10、应用SpringApplicationRunListener监听器的running方法。EventPublishingRunListener将会发出ApplicationReadyEvent事件,表明容器已就绪,可以被使用了。    

 

                                                                                                                                                                                        

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值