zoukankan      html  css  js  c++  java
  • spring boot源码分析之SpringApplication

    spring boot提供了sample程序,学习spring boot之前先跑一个最简单的示例:

    /*
     * Copyright 2012-2016 the original author or authors.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package sample.simple;
    
    import sample.simple.ExitException;
    import sample.simple.service.HelloWorldService;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.CommandLineRunner;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    @SpringBootApplication
    public class SampleSimpleApplication implements CommandLineRunner {
    
        // Simple example shows how a command line spring application can execute an
        // injected bean service. Also demonstrates how you can use @Value to inject
        // command line args ('--name=whatever') or application properties
    
        @Autowired
        private HelloWorldService helloWorldService;
    
        public void run(String... args) {
            System.out.println(this.helloWorldService.getHelloMessage());
            if (args.length > 0 && args[0].equals("exitcode")) {
                throw new ExitException();
            }
        }
    
        public static void main(String[] args) throws Exception {
            SpringApplication application = new SpringApplication(
                    SampleSimpleApplication.class);
            application.setApplicationContextClass(AnnotationConfigApplicationContext.class);
            SpringApplication.run(SampleSimpleApplication.class, args);
        }
    
    }

    可以发现在主方法main里启动了一个SpringApplication,启动方法是run方法。

    SpringApplication用来从java main方法启动一个spring应用,默认的启动步骤如下:

    1)创建一个合适的ApplicationContext实例,这个实例取决于classpath。

    2)注册一个CommandLinePropertySource,以spring属性的形式来暴露命令行参数。

    3)刷新ApplicationContext,加载所有的单例bean。

    4)触发所有的命令行CommanLineRunner来执行bean。

    大部分场景下,可以从你的application的main方法中直接调用它的run()静态方法。示例如下:

     @Configuration
     @EnableAutoConfiguration
     public class MyApplication  {
    
     // ... Bean definitions
    
     public static void main(String[] args) throws Exception {
       SpringApplication.run(MyApplication.class, args);
     }

    定制则可以这样:

     public static void main(String[] args) throws Exception {
       SpringApplication app = new SpringApplication(MyApplication.class);
       // ... customize app settings here
       app.run(args)
     }

    springApplication可以读取不同种类的源文件:

    • - java类由AnnotatedBeanDefinitionReader加载。
    • Resource - xml资源文件由XmlBeanDefinitionReader读取, 或者groovy脚本由GroovyBeanDefinitionReader读取
    • Package - java包文件由ClassPathBeanDefinitionScanner扫描读取。
    • CharSequence - 字符序列可以是类名、资源文件、包名,根据不同方式加载。如果一个字符序列不可以解析程序到类,也不可以解析到资源文件,那么就认为它是一个包。

    1.初始化过程

        public SpringApplication(Object... sources) {
            initialize(sources);
        }
        private void initialize(Object[] sources) {
            if (sources != null && sources.length > 0) {
                this.sources.addAll(Arrays.asList(sources));
            }
            this.webEnvironment = deduceWebEnvironment();
            setInitializers((Collection) getSpringFactoriesInstances(
                    ApplicationContextInitializer.class));
            setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
            this.mainApplicationClass = deduceMainApplicationClass();
        }

    2.运行方法run

    /**
         * Run the Spring application, creating and refreshing a new
         * {@link ApplicationContext}.
         * @param args the application arguments (usually passed from a Java main method)
         * @return a running {@link ApplicationContext}
         */
        public ConfigurableApplicationContext run(String... args) {
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();
            ConfigurableApplicationContext context = null;
            configureHeadlessProperty();
            SpringApplicationRunListeners listeners = getRunListeners(args);
            listeners.started();
            try {
                ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                        args);
                context = createAndRefreshContext(listeners, applicationArguments);
                afterRefresh(context, applicationArguments);
                listeners.finished(context, null);
                stopWatch.stop();
                if (this.logStartupInfo) {
                    new StartupInfoLogger(this.mainApplicationClass)
                            .logStarted(getApplicationLog(), stopWatch);
                }
                return context;
            }
            catch (Throwable ex) {
                handleRunFailure(context, listeners, ex);
                throw new IllegalStateException(ex);
            }
        }

    2.1 配置属性

        private void configureHeadlessProperty() {
            System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, System.getProperty(
                    SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
        }

    2.2 获取监听器

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

    2.3 启动监听器

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

    listener最终会被初始化为ParentContextCloserApplicationListener,FileEncodingApplicationListener,AnsiOutputApplicationListener,ConfigFileApplicationListener,DelegatingApplicationListener,LiquibaseServiceLocatorApplicationListener,ClasspathLoggingApplicationListener,LoggingApplicationListener这几个类的对象组成的list。

    下图画出了加载的ApplicationListener,并说明了他们的作用。

    2.4 创建并刷新容器(重点)

    private ConfigurableApplicationContext createAndRefreshContext(
                SpringApplicationRunListeners listeners,
                ApplicationArguments applicationArguments) {
            ConfigurableApplicationContext context;
            // Create and configure the environment
            ConfigurableEnvironment environment = getOrCreateEnvironment();
            configureEnvironment(environment, applicationArguments.getSourceArgs());
            listeners.environmentPrepared(environment);
            if (isWebEnvironment(environment) && !this.webEnvironment) {
                environment = convertToStandardEnvironment(environment);
            }
    
            if (this.bannerMode != Banner.Mode.OFF) {
                printBanner(environment);
            }
    
            // Create, load, refresh and run the ApplicationContext
            context = createApplicationContext();
            context.setEnvironment(environment);
            postProcessApplicationContext(context);
            applyInitializers(context);
            listeners.contextPrepared(context);
            if (this.logStartupInfo) {
                logStartupInfo(context.getParent() == null);
                logStartupProfileInfo(context);
            }
    
            // Add boot specific singleton beans
            context.getBeanFactory().registerSingleton("springApplicationArguments",
                    applicationArguments);
    
            // Load the sources
            Set<Object> sources = getSources();
            Assert.notEmpty(sources, "Sources must not be empty");
            load(context, sources.toArray(new Object[sources.size()]));
            listeners.contextLoaded(context);
    
            // Refresh the context
            refresh(context);
            if (this.registerShutdownHook) {
                try {
                    context.registerShutdownHook();
                }
                catch (AccessControlException ex) {
                    // Not allowed in some environments.
                }
            }
            return context;
        }

    2.4.1 获取或者创建环境

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

    若是有指定环境,则返回指定的ConfigurableEnvironment

    public interface ConfigurableEnvironment extends Environment, ConfigurablePropertyResolver

    使用示例如下:

    以最高搜索级别增加一个属性

       ConfigurableEnvironment environment = new StandardEnvironment();
       MutablePropertySources propertySources = environment.getPropertySources();
       Map myMap = new HashMap();
       myMap.put("xyz", "myValue");
       propertySources.addFirst(new MapPropertySource("MY_MAP", myMap));

    移除默认系统属性

       MutablePropertySources propertySources = environment.getPropertySources();
       propertySources.remove(StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME)

    测试环境mock系统属性

       MutablePropertySources propertySources = environment.getPropertySources();
       MockPropertySource mockEnvVars = new MockPropertySource().withProperty("xyz", "myValue");
       propertySources.replace(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, mockEnvVars);

    若是web环境,则使用StandardServletEnvironment

    public class StandardServletEnvironment extends StandardEnvironment implements ConfigurableWebEnvironment

    它用于基于server相关的web应用,所有web相关(Servlet相关)Application类默认初始化一个实例。

    默认返回StandardEnvironment

    public class StandardEnvironment extends AbstractEnvironment

    StandardEnvironment例如非web环境等

    2.4.2 配置环境

        /**
         * Template method delegating to
         * {@link #configurePropertySources(ConfigurableEnvironment, String[])} and
         * {@link #configureProfiles(ConfigurableEnvironment, String[])} in that order.
         * Override this method for complete control over Environment customization, or one of
         * the above for fine-grained control over property sources or profiles, respectively.
         * @param environment this application's environment
         * @param args arguments passed to the {@code run} method
         * @see #configureProfiles(ConfigurableEnvironment, String[])
         * @see #configurePropertySources(ConfigurableEnvironment, String[])
         */
        protected void configureEnvironment(ConfigurableEnvironment environment,
                String[] args) {
            configurePropertySources(environment, args);
            configureProfiles(environment, args);
        }

    2.4.3 创建ApplicationContext

    /**
         * Strategy method used to create the {@link ApplicationContext}. By default this
         * method will respect any explicitly set application context or application context
         * class before falling back to a suitable default.
         * @return the application context (not yet refreshed)
         * @see #setApplicationContextClass(Class)
         */
        protected ConfigurableApplicationContext createApplicationContext() {
            Class<?> contextClass = this.applicationContextClass;
            if (contextClass == null) {
                try {
                    contextClass = Class.forName(this.webEnvironment
                            ? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
                }
                catch (ClassNotFoundException ex) {
                    throw new IllegalStateException(
                            "Unable create a default ApplicationContext, "
                                    + "please specify an ApplicationContextClass",
                            ex);
                }
            }
            return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
        }

    2.4.4 加载bean到ApplicationContext

        /**
         * Load beans into the application context.
         * @param context the context to load beans into
         * @param sources the sources to load
         */
        protected void load(ApplicationContext context, Object[] sources) {
            if (logger.isDebugEnabled()) {
                logger.debug(
                        "Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
            }
            BeanDefinitionLoader loader = createBeanDefinitionLoader(
                    getBeanDefinitionRegistry(context), sources);
            if (this.beanNameGenerator != null) {
                loader.setBeanNameGenerator(this.beanNameGenerator);
            }
            if (this.resourceLoader != null) {
                loader.setResourceLoader(this.resourceLoader);
            }
            if (this.environment != null) {
                loader.setEnvironment(this.environment);
            }
            loader.load();
        }

    2.4.5 刷新ApplicationContext

        /**
         * Refresh the underlying {@link ApplicationContext}.
         * @param applicationContext the application context to refresh
         */
        protected void refresh(ApplicationContext applicationContext) {
            Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
            ((AbstractApplicationContext) applicationContext).refresh();
        }

    小结:

    上面仅仅是入门,若有谬误,请指正。后面随着学习的深入会修改。

    参考文献:

    【1】http://www.cnblogs.com/java-zhao/p/5540309.html

    【2】http://zhaox.github.io/java/2016/03/22/spring-boot-start-flow

  • 相关阅读:
    请求转发和重定向
    jvm调优(新生代、老年代调优)
    servlet(对servlet的理解、生命周期)
    http的get和post请求方式
    jvm垃圾回收器(串行、吞吐量优先、响应时间优先、G1)
    java后端学习路线总结
    jvm:java中的引用(强引用、软引用、虚引用、弱引用)
    jvm直接内存(分配与回收)
    ssm(增删改查、拦截器、过滤器)
    springmvc:文件的上传与下载
  • 原文地址:https://www.cnblogs.com/davidwang456/p/5846513.html
Copyright © 2011-2022 走看看