一、Spring Boot启动源码解析

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

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

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

简要说明:本次源码解析基于spring boot 1.5.9版本,该源码解析纯粹是为了记录自己所理解的东西,如果有误,请指正。

Spring boot 启动类:

    @SpringBootApplication
    public class Application {

        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    }

@SpringBootApplication该注解源码如下:

主要是引用了其他三个注解@SpringBootConfiguration、@EnableAutoConfiguration和@ComponentScan暂不作分析。

SpringApplication.run(Application.class, args) run方法的执行

    /**
     * Static helper that can be used to run a {@link SpringApplication} from the
     * specified sources using default settings and user supplied arguments.
     * @param sources the sources to load
     * @param args the application arguments (usually passed from a Java main method)
     * @return the running {@link ApplicationContext}
     */
    public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
        return new SpringApplication(sources).run(args);
    }

先构造一个SpringApplication对象

    /**
     * Create a new {@link SpringApplication} instance. The application context will load
     * beans from the specified sources (see {@link SpringApplication class-level}
     * documentation for details. The instance can be customized before calling
     * {@link #run(String...)}.
     * @param sources the bean sources
     * @see #run(Object, String[])
     * @see #SpringApplication(ResourceLoader, Object...)
     */
    public SpringApplication(Object... sources) {
        initialize(sources);
    }

然后执行initialize(sources)方法,此处的sources即执行main方法的类实例对象Application.class,initialize(source)源码如下:

    @SuppressWarnings({ "unchecked", "rawtypes" })
    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();
    }

由以上源码可知主要执行4个方法:

deduceWebEnvironment()

判断当前环境是否是web环境,太简单不做详述。

初始化ApplicationContextInitializer的子类

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

第二个方法setinitializers()之前先执行getSpringFactoriesInstances(ApplicationContextInitializer.class)

        private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
                Class<?>[] parameterTypes, Object... args) {
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            // Use names and ensure unique to protect against duplicates
            Set<String> names = new LinkedHashSet<String>(
                    SpringFactoriesLoader.loadFactoryNames(type, classLoader));
            List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
                    classLoader, args, names);
            AnnotationAwareOrderComparator.sort(instances);
            return instances;
        }

在方法SpringFactoriesLoader.loadFactoryNames(type,classLoader)中

    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
    public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
            String factoryClassName = factoryClass.getName();
            try {
                Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                        ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
                List<String> result = new ArrayList<String>();
                while (urls.hasMoreElements()) {
                    URL url = urls.nextElement();
                    Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
                    String factoryClassNames = properties.getProperty(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);
            }
        } 

即通过在jar中的 META-INF/spring.factories中找到需要初始化的ApplicationContextInitializer的实现类

spring-boot-1.5.9.RELEASE.jar包中的META-INF/spring.factories中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

将需要初始化的bean名字存放到Set names,接下来就是实例化这些类

    List<T> instances = createSpringFactoriesInstances(type, parameterTypes,classLoader, args, names);

初始化ApplicationListener的子类

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

方法如3.初始化ApplicationContextInitializer一样的执行过程

deduceMainApplicationClass找到main方法的类实例

    this.mainApplicationClass = deduceMainApplicationClass();
    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;
    }

通过new一个运行时异常,找到栈,在栈信息中遍历得到main方法的主类

至此SpringApplication初始化完成。

开始执行SpringApplication的run方法

20191017100345\_1.png

第321行代码SpringApplicationRunListeners listeners = getRunlisteners(args)

20191017100345\_2.png

和new SpringApplication时使用方法一致,在META-INF/spring.factories中得到SpringApplicationRunListener的实现类并且实例化这些对象

第292行执行SpringApplicationRunListener子类的接口中的starting方法

第296行执行prepareEnvironment(listeners,applicationArguments)方法

20191017100345\_3.png

上图第323行getOrCreateEnvironment()方法new StandardServletEnvironment()类

上图第324行configureEnvironment(environment, applicationArguments.getSourceArgs())

    protected void configureEnvironment(ConfigurableEnvironment environment,
          String[] args) {
       configurePropertySources(environment, args);
       configureProfiles(environment, args);
    }

configurePropertySources得到new StandardServletEnvironment()构造方法时配置的4个:

    servletContextInitParams
    servletConfigInitParams
    systemProperties
    systemEnvironment

configureProfiles(environment, args)得到spring.profiles.active的值

然后执行第325行的springApplicationRunListeners中所有listerns的environmentPrepared()方法

接下来执行run方法的第298行printBanner方法,该方法打印banner信息,即启动时默认的Spring异形文字信息,可以有Image和Textg两种形式默认使用console。通过在properties中配置”banner.location”指定banner.txt的位置来修改文本信息。默认使用 SpringBootBanner类中的信息。如果是图片使用”banner.image.location”指定图片位置,图片只支持gif、jpg、png三种扩展名格式。

接下来执行第329行createApplicationContext()方法,创建org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext的实例,即spring的上下文环境。

第301行执行prepareContext(context, environment, listeners, applicationArguments, printedBanner)方法。上下文环境准备工作。

    private void prepareContext(ConfigurableApplicationContext context,
          ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
          ApplicationArguments applicationArguments, Banner printedBanner) {
       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);
       if (printedBanner != null) {
          context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
       }

       // 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);
    }

applyInitializers(context)中开始执行ApplicationContextInitializer接口的initialize方法,还有SpringApplicationListener接口中的contextPrepared和contextLoaded方法。

run方法第303行的refreshContext()方法中执行上下文的refresh()方法,即Spring初始化过程中父类AbstractApplicationContext的refresh(),该方法不做详述,太复杂,但与spring一致。

run方法第304行afterRefres()方法启用ApplicationRunner和CommandLineRunner接口中的run方法


来源:[]()

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

评论 抢沙发

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

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

支付宝扫一扫打赏

微信扫一扫打赏