SpringBoot启动源码分析

个人博客导航页(点击右侧链接即可打开个人博客):大牛带你入门技术栈 

SpringBoot自启动源码分析

项目启动代码

public static void main(String[] args) {
        // springboot项目启动方式 
        SpringApplication.run(DemoApplication.class, args);
    }

springApplication.java

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
        // 调用run方法
        return run(new Class<?>[] { primarySource }, args);
}
    public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
        // 最终创建了一个SpringApplication对象
        return new SpringApplication(primarySources).run(args);
    }
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.resourceLoader = resourceLoader;
        // 必须传一个类
        Assert.notNull(primarySources, "PrimarySources must not be null");
        // 用一个Set集合存储我们的启动类
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        // webApplication的枚举类型
        // 主要判断我们启动的类型是什么,一般我们启动的是servlet
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        // 初始化一些应用上下文
        // 加载springboot包下的/META-INF/spring.facotries中的8个类
        setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
        // 初始化监听器
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        // 通过类加载器启动我们的类
        // Class.forName()
        this.mainApplicationClass = deduceMainApplicationClass();
    }

setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

我们来看看初始化应用上下文中做了什么

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
        // 获取类加载器,我们传入的ApplicationContextInitializer.class
        ClassLoader classLoader = getClassLoader();
        // Use names and ensure unique to protect against duplicates
        // 这个方法重点
        // 这个方法里会加载spring.factories文件里的东西
        Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        // 实例化对象
        List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }

SpringFactoriesLoader.loadFactoryNames(type, classLoader)

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        // 初始化监听器的时候就会直接取了
        MultiValueMap<String, String> result = cache.get(classLoader);
        if (result != null) {
            return result;
        }
​
        try {
            // 扫描我们类路径下META-INF/spring.factories文件
            Enumeration<URL> urls = (classLoader != null ?
                    classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                    ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
            result = new LinkedMultiValueMap<>();
            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();
                    for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                        // 添加到map中
                        // 这里map的映射关系是 接口-> 实现类
                        result.add(factoryTypeName, factoryImplementationName.trim());
                    }
                }
            }
            cache.put(classLoader, result);
            return result;
        }
        catch (IOException ex) {
            throw new IllegalArgumentException("Unable to load factories from location [" +
                    FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
    }
// 这个方法进行实例化
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
            ClassLoader classLoader, Object[] args, Set<String> names) {
        List<T> instances = new ArrayList<>(names.size());
        // 获取该类加载器下对应的实现类
        // 我们可以手动实现ApplicationInitialzer和Listener
        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;
    }

通过查看源码可知,如果要加载自己的类也可以通过自己建一个/META-INF/spring.factories文件

# PropertySource Loaders
com.example.demo.service.NewService=\
com.example.demo.service.NewServiceImpl
​
org.springframework.context.ApplicationContextInitializer=\
com.example.demo.service.TextApplicationInitializer

springApplication对象建好以后就是调用我们的run方法了

public ConfigurableApplicationContext run(String... args) {
        // 秒表
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        configureHeadlessProperty();
        SpringApplicationRunListeners listeners = getRunListeners(args);
        // 启动监听,SpringApplicationRunListeners
        listeners.starting();
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            // 配置环境
            ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
            configureIgnoreBeanInfo(environment);
            // 打印图标
            Banner printedBanner = printBanner(environment);
            // 默认加载AnnotationConfigServletWebServerApplicationContext.class
            context = createApplicationContext();
            exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
            // 这里面调用了applyInitializers()方法 执行了 ApplicationContextInitialzer的initialize()方法
            prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            refreshContext(context);
            afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
            }
            listeners.started(context);
            callRunners(context, applicationArguments);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, listeners);
            throw new IllegalStateException(ex);
        }
​
        try {
            listeners.running(context);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, null);
            throw new IllegalStateException(ex);
        }
        return context;
    }

springboot配置

附录

1.WebApplicationType枚举

public enum WebApplicationType {
​
    /**
     * 该应用程序不应作为Web应用程序运行,也不应启动嵌入式Web服务器。
     */
    NONE,
​
    /**
     * 该应用程序应作为基于Servlet的Web应用程序运行,并应启动嵌入式Servlet Web服务器。
     */
    SERVLET,
​
    /**
     * 该应用程序应作为反应式Web应用程序运行,并应启动嵌入式反应式Web服务器。
     */
    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";
}

2.SpringApplicationBanner

通过类字段可以看到yml文件中配置banner的路径,以及将我们要展示的banner的文件名字取为banner.txt

    static final String BANNER_LOCATION_PROPERTY = "spring.banner.location";
​
    static final String BANNER_IMAGE_LOCATION_PROPERTY = "spring.banner.image.location";
​
    static final String DEFAULT_BANNER_LOCATION = "banner.txt";
​
    static final String[] IMAGE_EXTENSION = { "gif", "jpg", "png" };
// 指定Banner.Mode的方式
enum Mode {
​
        /**
         * Disable printing of the banner.
         */
        OFF,
​
        /**
         * Print the banner to System.out.
         */
        CONSOLE,
​
        /**
         * Print the banner to the log file.
         */
        LOG
​
    }

附Java/C/C++/机器学习/算法与数据结构/前端/安卓/Python/程序员必读/书籍书单大全:

(点击右侧 即可打开个人博客内有干货):技术干货小栈
=====>>①【Java大牛带你入门到进阶之路】<<====
=====>>②【算法数据结构+acm大牛带你入门到进阶之路】<<===
=====>>③【数据库大牛带你入门到进阶之路】<<=====
=====>>④【Web前端大牛带你入门到进阶之路】<<====
=====>>⑤【机器学习和python大牛带你入门到进阶之路】<<====
=====>>⑥【架构师大牛带你入门到进阶之路】<<=====
=====>>⑦【C++大牛带你入门到进阶之路】<<====
=====>>⑧【ios大牛带你入门到进阶之路】<<====
=====>>⑨【Web安全大牛带你入门到进阶之路】<<=====
=====>>⑩【Linux和操作系统大牛带你入门到进阶之路】<<=====

天下没有不劳而获的果实,望各位年轻的朋友,想学技术的朋友,在决心扎入技术道路的路上披荆斩棘,把书弄懂了,再去敲代码,把原理弄懂了,再去实践,将会带给你的人生,你的工作,你的未来一个美梦。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值