Spring Boot 源码解析-SpringApplication

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】

文章目录

我们生成一个spring boot 项目时,会自带一个启动类. 代码如下:

    @SpringBootApplication
    public class AdminBootstrap {
        public static void main(String[] args) {
             new SpringApplicationBuilder(AdminBootstrap.class).web(true).run(args); 
        }

    }   

就是这么简单的代码,构成了spring boot的世界. 那么代码中只有一个@SpringBootApplication 注解 和 调用了SpringApplication#run方法.那么我们先来解析SpringApplication的run方法.

SpringApplication初始化

  1. 首先调用SpringApplication.initialize
        @SuppressWarnings({ "unchecked", "rawtypes" })
        private void initialize(Object[] sources) {
            //1.如果sources长度大于0的话,加入到SpringApplication的sources中,该sources是一个LinkedHashSet.
            if (sources != null && sources.length > 0) {
                this.sources.addAll(Arrays.asList(sources));
            }
            //2.调用deduceWebEnvironment方法判断是否是web环境
            this.webEnvironment = deduceWebEnvironment();
            //3.设置initializers.
            setInitializers((Collection) getSpringFactoriesInstances(
                    ApplicationContextInitializer.class));
            //4.设置Listeners.
            setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
            //5.设置mainApplicationClass.
            this.mainApplicationClass = deduceMainApplicationClass();
        }
  • deduceWebEnvironment发现会调用ClassUtils类的isPresent方法,检查classpath中是否存在javax.servlet.Servlet类和org.springframework.web.context.ConfigurableWebApplicationContext类,如果存在的话,返回true.否则返回false.

        private static final String[] WEB_ENVIRONMENT_CLASSES = { "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;
        }
  • 在设置Initializers时首先调用getSpringFactoriesInstances方法加载ApplicationContextInitializer.然后直接赋值给initializers.代码如下:
        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) {
            //1.首先获得ClassLoader
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            // Use names and ensure unique to protect against duplicates
            //2.调用SpringFactoriesLoader#loadFactoryNames进行加载,然后放入到LinkedHashSet进行去重.

            Set<String> names = new LinkedHashSet<String>(
                    SpringFactoriesLoader.loadFactoryNames(type, classLoader));
            //3.调用createSpringFactoriesInstances进行初始化
            List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
                    classLoader, args, names);
            //4.对实例进行排序
            AnnotationAwareOrderComparator.sort(instances);
            return instances;
        }
  • 使用给定的类加载器从“META-INF / spring.factories”加载给定类型的工厂实现的完全限定类名。
        public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
            //1.获得factoryClassName,对于当前来说factoryClassName =org.springframework.context.ApplicationContextInitializer.
            String factoryClassName = factoryClass.getName();
            try {
                //2.通过传入的classLoader加载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>();
                //3.通过调用PropertiesLoaderUtils#loadProperties将其转为Properties.
                while (urls.hasMoreElements()) {
                    URL url = urls.nextElement();
                    Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
                    String factoryClassNames = properties.getProperty(factoryClassName);

                //4.获得factoryClassName对应的值进行返回. 
                result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
                }
                return result;
            }
            catch (IOException ex) {
                throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
                        "] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
            }
        }
  • 对于当前来说,在spring-boot/META-INF/spring.factories中.org.springframework.context.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.context.embedded.ServerPortInfoApplicationContextInitializer
  • createSpringFactoriesInstances遍历传入的names,也就是之前通过SpringFactoriesLoader加载的类名.通过遍历,依次调用其构造器进行初始化.加入到instances.然后进行返回.
        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;
        }

对于当前场景来说:
ConfigurationWarningsApplicationContextInitializer,DelegatingApplicationContextInitializer,ServerPortInfoApplicationContextInitializer初始化没有做任何事.

ContextIdApplicationContextInitializer在初始化时.会获得spring boot的应用名.搜索路径如下:

    spring.application.name
    vcap.application.name
    spring.config.name
    如果都没有配置的话,返回application
        private static final String NAME_PATTERN = "${spring.application.name:${vcap.application.name:${spring.config.name:application}}}";

        public ContextIdApplicationContextInitializer() {
            this(NAME_PATTERN);
        }

        public ContextIdApplicationContextInitializer(String name) {
            this.name = name;
        }
  • 设置SpringApplication#setListeners时,还是同样的套路.调用getSpringFactoriesInstances加载META-INF/spring.factories中配置的org.springframework.context.ApplicationListener.
    对于当前来说.加载的类如下:
    # Application Listeners
    org.springframework.context.ApplicationListener=\
    org.springframework.boot.ClearCachesApplicationListener,\
    org.springframework.boot.builder.ParentContextCloserApplicationListener,\
    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.liquibase.LiquibaseServiceLocatorApplicationListener,\
    org.springframework.boot.logging.ClasspathLoggingApplicationListener,\
    org.springframework.boot.logging.LoggingApplicationListener

这些类在构造器中都没有做任何事.

  • 调用SpringApplication#deduceMainApplicationClass方法.获得应用的启动类.该方法通过获取当前方法调用栈,找到main函数的类.代码如下:
        private Class<?> deduceMainApplicationClass() {
            try {
                StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
                for (StackTraceElement stackTraceElement : stackTrace) {
                    if ("main".equals(stackTraceElement.getMethodName())) {
                        return Class.forName(stackTraceElement.getClassName());
                    }
                }
            }
            catch (ClassNotFoundException ex) {
                // Swallow and continue
            }
            return null;
        }

SpringApplication的run方法

        public ConfigurableApplicationContext run(String... args) {
            //1.初始化StopWatch,调用其start方法开始计时.
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();
            ConfigurableApplicationContext context = null;
            FailureAnalyzers analyzers = null;
            //2.调用configureHeadlessProperty设置系统属性java.awt.headless,这里设置为true,表示运行在服务器端,在没有显示器和鼠标键盘的模式下工作,模拟输入输出设备功能
            configureHeadlessProperty();
            //3.调用SpringApplicationRunListeners#starting
            SpringApplicationRunListeners listeners = getRunListeners(args);
            listeners.starting();
            try {
                //4.创建一个DefaultApplicationArguments对象,它持有着args参数,就是main函数传进来的参数.调用prepareEnvironment方法.
                ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                        args);
                ConfigurableEnvironment environment = prepareEnvironment(listeners,
                        applicationArguments);
                //5.打印banner
                Banner printedBanner = printBanner(environment);
                //6.创建SpringBoot上下文
                context = createApplicationContext();
                //7.初始化FailureAnalyzers
                analyzers = new FailureAnalyzers(context);
                //8.调用prepareContext
                prepareContext(context, environment, listeners, applicationArguments,
                        printedBanner);
                //9.调用AbstractApplicationContext#refresh方法,并注册钩子
                refreshContext(context);
                //10.在容器完成刷新后,依次调用注册的Runners
                afterRefresh(context, applicationArguments);
                //11.调用SpringApplicationRunListeners#finished
                listeners.finished(context, null);
                //12.停止计时
                stopWatch.stop();
                //13.初始化过程中出现异常时调用handleRunFailure进行处理,然后抛出IllegalStateException异常.
                if (this.logStartupInfo) {
                    new StartupInfoLogger(this.mainApplicationClass)
                            .logStarted(getApplicationLog(), stopWatch);
                }
                return context;
            }
            catch (Throwable ex) {
                handleRunFailure(context, listeners, analyzers, ex);
                throw new IllegalStateException(ex);
            }
        }

来源:[]()

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » Spring Boot 源码解析-SpringApplication

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏