SpringApplication源码剖析(一)——构造方法

SpringApplication源码剖析(一)——构造方法

之前通过看了AbstractApplicationContext.class、DefaultListableBeanFactory.class、AbstractBeanFactory.class几个类基本了解到了SpringIoC相关特性的原理具体代码实现。从这篇文章开始将打算了解以下SpringBoot应用的启动过程。SpringBoot应用入口是SpringApplication.run()方法,因此从这个方法为入口,看一下相关的源码。

看源码

看这个源码前,我一般会先到spring官网上看看有没有相关的资料,把一些定义先有个初步的概念,比如@SpringApplication注解、BeanPostProcessor、SpringBoot starter等;对定义有了印象后,会带着些疑问去看源码;最后会逐步调试看一下运行结果。比如在看SpringApplication源码时,我会使用下列例子:

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

打断点逐步看输出结果。

构造方法

在阅读run()方法的源码时,发现有些类属性是在构造方法中初始化的,因此先写一篇关于构造方法的笔记,记录以下几个后面会用到的比较特殊的属性。
构造方法源码:

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;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        this.bootstrappers = new ArrayList(this.getSpringFactoriesInstances(Bootstrapper.class));
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }
    this.sources = new LinkedHashSet();

spring应用的源source?

    this.bannerMode = Mode.CONSOLE;

banner的模式,banner是指SpringBoot启动的时候打印的:
banner
一共三种枚举:LOG、CONSOLE、OFF

    this.addCommandLineProperties = true;

若addCommandLineProperties设置为true,则如果启动时命令行传入的参数不为空,则把参数以SimpleCommandLinePropertySource对象实例形式添加到environment.getPropertySources()中
相关代码:

protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
        MutablePropertySources sources = environment.getPropertySources();
        DefaultPropertiesPropertySource.ifNotEmpty(this.defaultProperties, sources::addLast);
        if (this.addCommandLineProperties && args.length > 0) {
            String name = "commandLineArgs";
            if (sources.contains(name)) {
                PropertySource<?> source = sources.get(name);
                CompositePropertySource composite = new CompositePropertySource(name);
                composite.addPropertySource(new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
                composite.addPropertySource(source);
                sources.replace(name, composite);
            } else {
                sources.addFirst(new SimpleCommandLinePropertySource(args));
            }
        }
    }
    this.addConversionService = true;

相关代码主要有两处:
1、设置环境变量时,若addConversionService为true,则把sharedInstance注入到ConfigurableEnvironment中

protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
        if (this.addConversionService) {
            ConversionService conversionService = ApplicationConversionService.getSharedInstance();
            environment.setConversionService((ConfigurableConversionService)conversionService);
        }

        this.configurePropertySources(environment, args);
        this.configureProfiles(environment, args);
    }

2、若addConversionService为true,则把sharedInstance注入到beanFactory中

protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
       ......
        if (this.addConversionService) {
            context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
        }
    }
    this.registerShutdownHook = true;

若设置为true,则起一条线程监听当前线程发生shutdown时调用AbstractApplicationContext.doClose()方法。
相关代码:

private void refreshContext(ConfigurableApplicationContext context) {
        if (this.registerShutdownHook) {
            try {
                context.registerShutdownHook();
            } catch (AccessControlException var3) {
            }
        }

        this.refresh((ApplicationContext)context);
    }
public void registerShutdownHook() {
        if (this.shutdownHook == null) {
            this.shutdownHook = new Thread("SpringContextShutdownHook") {
                public void run() {
                    synchronized(AbstractApplicationContext.this.startupShutdownMonitor) {
                        AbstractApplicationContext.this.doClose();
                    }
                }
            };
            Runtime.getRuntime().addShutdownHook(this.shutdownHook);
        }
    }
    this.lazyInitialization = false;

设置bean定义是否默认为懒加载,若设置为true,则会添加LazyInitializationBeanFactoryPostProcessor到context的beanFactoryPostProcessor列表中,遍历所有BeanDefinition,若没有设置lazyInit属性,则默认修改为true。

    this.applicationContextFactory = ApplicationContextFactory.DEFAULT;

默认的applicationContext工厂,根据webApplicationType返回对应的context。

ApplicationContextFactory DEFAULT = (webApplicationType) -> {
        try {
            switch(webApplicationType) {
            case SERVLET:
                return new AnnotationConfigServletWebServerApplicationContext();
            case REACTIVE:
                return new AnnotationConfigReactiveWebServerApplicationContext();
            default:
                return new AnnotationConfigApplicationContext();
            }
        } catch (Exception var2) {
            throw new IllegalStateException("Unable create a default ApplicationContext instance, you may need a custom ApplicationContextFactory", var2);
        }
    };
    this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));

SpringApplication构造函数传入的class参数,存储在primarySource属性中
primarySources

    this.webApplicationType = WebApplicationType.deduceFromClasspath();

根据不同的webApplicationType生成对应的ApplicationContext、Enviroment属性。判断条件:
1、若存在DispatcherHandler,则webApplicationType=REACTIVE
2、若存在"javax.servlet.Servlet"、"org.springframework.web.context.ConfigurableWebApplicationContext"都有加载,则返回SERVLET,(SERVLET_INDICATOR_CLASSES = new String[]{“javax.servlet.Servlet”, “org.springframework.web.context.ConfigurableWebApplicationContext”};)
3、否则返回NONE
相关判断代码:

static WebApplicationType deduceFromClasspath() {
        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];
                if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
                    return NONE;
                }
            }

            return SERVLET;
        }
    }
    this.bootstrappers = new ArrayList(this.getSpringFactoriesInstances(Bootstrapper.class));

获取所有Bootstrapper.class类型bean,在BoostrapperContext初始化阶段会调用所有的Boostrapper的initialize方法初始化context:

private DefaultBootstrapContext createBootstrapContext() {
        DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
        this.bootstrappers.forEach((initializer) -> {
            initializer.intitialize(bootstrapContext);
        });
        return bootstrapContext;
    }
    this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));

获取所有ApplicationContextInitializer.class类型bean,在ApplicationContext初始化阶段会调用所有initialzer初始化context,发生在BeanDefinition加载之前。
默认的Initializer有7个:
initializers

    this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));

加载所有ApplicationListener,监听Application生命周期事件,默认的Listener有9个:
listeners

    this.mainApplicationClass = this.deduceMainApplicationClass();

根据调用栈找到main方法所在类。

private Class<?> deduceMainApplicationClass() {
        try {
            StackTraceElement[] stackTrace = (new RuntimeException()).getStackTrace();
            StackTraceElement[] var2 = stackTrace;
            int var3 = stackTrace.length;

            for(int var4 = 0; var4 < var3; ++var4) {
                StackTraceElement stackTraceElement = var2[var4];
                if ("main".equals(stackTraceElement.getMethodName())) {
                    return Class.forName(stackTraceElement.getClassName());
                }
            }
        } catch (ClassNotFoundException var6) {
        }

        return null;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值