Springboot启动配置原理/20190402---03---68/69/70/71(没学)

 

主要讲启动原理 运行流程 启动配置原理 作为我springboot结课的最后一篇博客

github地址:https://github.com/FandyWw/spring-boot-07

运行这个:点进去run。

第一步创建SpringApplication对象

        调用initialize方法。

       

            

private void initialize(Object[] sources) {
        //第一步保存主配置类
		if (sources != null && sources.length > 0) {
			this.sources.addAll(Arrays.asList(sources));
		}
        //第二步决定是不是web应用
		this.webEnvironment = deduceWebEnvironment();
        //第三步实际上就是赋值 主要看getSpringFactoriesInstances方法
		setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
        //第四步还是啊找,此时是找listener
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}

             注意看这个流程里面做了什么事情。

             加载保存主配置类

            第2步:

            判断当前的应用是不是web应用

           第3步的核心方法是:getSpringFactoriesInstances,点进去。

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

   上面的loadFactoryNames,点进去。

 public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();

        try {
            Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
            ArrayList result = new ArrayList();

            while(urls.hasMoreElements()) {
                URL 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 var8) {
            throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + "META-INF/spring.factories" + "]", var8);
        }
    }

      实际上就是从,类路径下META-INF/spring.factories找到配置的所有的ApplicationContextInitializer,保存起来。

       就在这里面:

             

      好多包都有这个的,接下来还是从类路径下找

        最后一步:

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

这个是从多个配置类中找到主配置类,就是看那个类有main方法,可以传多个看哪个又main方法。

-------------------------------------------------------------------------------------------------------------------------------------------68

第二步执行run方法。

         //运行。

public ConfigurableApplicationContext run(String... args) {
        //开启关闭监听
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
        //声明ioc容器
		ConfigurableApplicationContext context = null;
		FailureAnalyzers analyzers = null;
		configureHeadlessProperty();
        //这个十分重要得,火气监听器就是那五个组件里面的
		SpringApplicationRunListeners listeners = getRunListeners(args);
        //开启监听器,回调所有的starting方法。
		listeners.starting();
		try {
            //不重要就是封装命令行参数
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
            //准备环境这个方法里同样回调所有的监听器的方法
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
            //打印banner图标
			Banner printedBanner = printBanner(environment);
            //创建ioc容器这里也要回调listener
			context = createApplicationContext();
            //不重要 异常分析报告
			analyzers = new FailureAnalyzers(context);
            //准备上下文环境  这里面又三个回调方法
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
            //重要
			refreshContext(context);
            //回调runner
			afterRefresh(context, applicationArguments);
            //回调所有的listener方法
			listeners.finished(context, null);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}
			return context;
		}
		catch (Throwable ex) {
			handleRunFailure(context, listeners, analyzers, ex);
			throw new IllegalStateException(ex);
		}
	}

   解析:

       

StopWatch stopWatch = new StopWatch();

       监听不用管。

ConfigurableApplicationContext context = null;

    声明一个ioc容器。

    进入这个方法:configureHeadlessProperty();


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

  jwt相关可以先不要看。

接下来获取比较重要的一个监听器:SpringApplicationRunListeners listeners = getRunListeners(args);

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

由getSpringFactoriesInstances可知,是在类路径下的META-INF/spring.factories,获取的。

打开监听器。

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

回调所有的starting方法。

这个starting方法是SpringApplicationRunListener类的,下面是这个类的代码

/*
 * 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 org.springframework.boot;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.commons.logging.Log;

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.util.ReflectionUtils;

/**
 * A collection of {@link SpringApplicationRunListener}.
 *
 * @author Phillip Webb
 */
class SpringApplicationRunListeners {

	private final Log log;

	private final List<SpringApplicationRunListener> listeners;

	SpringApplicationRunListeners(Log log,
			Collection<? extends SpringApplicationRunListener> listeners) {
		this.log = log;
		this.listeners = new ArrayList<SpringApplicationRunListener>(listeners);
	}

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

	public void environmentPrepared(ConfigurableEnvironment environment) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.environmentPrepared(environment);
		}
	}

	public void contextPrepared(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.contextPrepared(context);
		}
	}

	public void contextLoaded(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.contextLoaded(context);
		}
	}

	public void finished(ConfigurableApplicationContext context, Throwable exception) {
		for (SpringApplicationRunListener listener : this.listeners) {
			callFinishedListener(listener, context, exception);
		}
	}

	private void callFinishedListener(SpringApplicationRunListener listener,
			ConfigurableApplicationContext context, Throwable exception) {
		try {
			listener.finished(context, exception);
		}
		catch (Throwable ex) {
			if (exception == null) {
				ReflectionUtils.rethrowRuntimeException(ex);
			}
			if (this.log.isDebugEnabled()) {
				this.log.error("Error handling failed", ex);
			}
			else {
				String message = ex.getMessage();
				message = (message == null ? "no error message" : message);
				this.log.warn("Error handling failed (" + message + ")");
			}
		}
	}

}

这几个函数就是用来回调的。

 

接下来看准备环境做了什么?

ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
private ConfigurableEnvironment prepareEnvironment(
			SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		configureEnvironment(environment, applicationArguments.getSourceArgs());
        //注意创建环境完成后回调这个方法表示环境准备完成
		listeners.environmentPrepared(environment);
		if (!this.webEnvironment) {
			environment = new EnvironmentConverter(getClassLoader())
					.convertToStandardEnvironmentIfNecessary(environment);
		}
		return environment;
	}

准备环境就不看了,注意这个:listeners.environmentPrepared(environment);

所以创建环境完成后回调SpringApplicationRunListeners.environmentPrepared(environment)方法。表示环境准备完成。

接下来:

     Banner printedBanner = printBanner(environment);打印banner图标。

     

 

 

 

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------

   接下来很重要:

context = createApplicationContext();

进入方法创建ioc容器。

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

创建web的ioc容器还是其他的普通的容器 。。。。。。。。。。。。     

return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);

利用反射最终创建ioc容器。

接下来:

analyzers = new FailureAnalyzers(context);

出现异常的时候做分析报告的。

接下来:

prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);

    准备上下文环境,点进去。将environment环境保存到ioc中,applyInitializers(context);

private void prepareContext(ConfigurableApplicationContext context,
			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments, Banner printedBanner) {
        //环境保存到ioc中
		context.setEnvironment(environment);
                 //后置处理器组装了一些小组件
		postProcessApplicationContext(context);
        //回调所有的ApplicationInatializer的initialize方法
		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()]));
                  //最后一步listener回调
		listeners.contextLoaded(context);
	}

 进去applyInitializers(context)------准备上下文环境;获取所有的 ApplicationContextInitializer调用initialize方法。

ApplicationContextInitializer是之前加入进去的

	protected void applyInitializers(ConfigurableApplicationContext context) {
		for (ApplicationContextInitializer initializer : getInitializers()) {
			Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
					initializer.getClass(), ApplicationContextInitializer.class);
			Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
			initializer.initialize(context);
		}
	}

10.16

             就是回调这个。

             进去这个方法:listeners.contextPrepared(context);  回调所有的listener.contextPrepared()方法。

public void contextPrepared(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.contextPrepared(context);
		}
	}

  这句:

             

listeners.contextLoaded(context);

  几个方法的回调都是之前保存好的。

  接下来重要的之前讲过 刷新容器:  

refreshContext(context);

刷新容器,就是ioc容器初始化的过程,扫描配置类,初始化bean。如果是web应用还会创建嵌入式的tomcat。

refresh方法:

@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
				initMessageSource();

				// Initialize event multicaster for this context.
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				onRefresh();

				// Check for listener beans and register them.
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

				// Reset 'active' flag.
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
	}

 

这个方法初始化剩下的单实例的bean,就是所有的组件。

再次进入这个方法:

afterRefresh(context, applicationArguments);

 

protected void afterRefresh(ConfigurableApplicationContext context,
			ApplicationArguments args) {
		callRunners(context, args);
	}
private void callRunners(ApplicationContext context, ApplicationArguments args) {
		List<Object> runners = new ArrayList<Object>();
        //在ioc容器获取所有的这个两种runner
		runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
		runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
		AnnotationAwareOrderComparator.sort(runners);
		for (Object runner : new LinkedHashSet<Object>(runners)) {
			if (runner instanceof ApplicationRunner) {
				callRunner((ApplicationRunner) runner, args);
			}
			if (runner instanceof CommandLineRunner) {
				callRunner((CommandLineRunner) runner, args);
			}
		}
	}

     

     注意这两个时间监听的组件:ApplicationRunner    CommandLineRunner

    

有优先级的ApplicationRunner先回调,CommandLineRunner再来回调。

 再下来:

listeners.finished(context, null);

 所有的listener回调finished方法。

 最后返回ioc容器。 

总结: 

              

               下面是几个事件监听机制。有些是初始化器。

         

70-------------------------------------------------------------------------------------------------

          事件监听机制,插手监听类。

          首先是初始化器ApplicationContextInitializer的实现类,是来监听启动的:

package com.atguigu.springboot.listener;

import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;

public class HelloApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        System.out.println("ApplicationContextInitializer...initialize..."+applicationContext);
    }
}

        

        传入的泛型是用来监听什么的具体的,我们监听ioc容器的启动。

        ctrl+H快捷键

------------------------------------------------------------------------------------------------

   下一个组件:HelloSpringApplicationRunListener

                  

package com.atguigu.springboot.listener;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;

public class HelloSpringApplicationRunListener implements SpringApplicationRunListener {

    //必须有的构造器
    public HelloSpringApplicationRunListener(SpringApplication application, String[] args){

    }

    //调用时机源码有
    @Override
    public void starting() {
        System.out.println("SpringApplicationRunListener...starting...");
    }

    @Override
    public void environmentPrepared(ConfigurableEnvironment environment) {
        Object o = environment.getSystemProperties().get("os.name");
        System.out.println("SpringApplicationRunListener...environmentPrepared.."+o);
    }

    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {
        System.out.println("SpringApplicationRunListener...contextPrepared...");
    }

    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {
        System.out.println("SpringApplicationRunListener...contextLoaded...");
    }

    @Override
    public void finished(ConfigurableApplicationContext context, Throwable exception) {
        System.out.println("SpringApplicationRunListener...finished...");
    }
}

几个时机:

      

            starting()方法在ioc容器还没创建调用的

           

                   基本环境准备好了调用。

                   

                   prepareContext准备好了调用

                   

                  prepareContext运行完了调用

 --------------------------------------------------------------------        

下一个组件:HelloApplicationRunner

package com.atguigu.springboot.listener;

import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

@Component
public class HelloApplicationRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("ApplicationRunner...run....");
    }
}

  

   下一个组件:HelloCommandLineRunner

package com.atguigu.springboot.listener;

import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import java.util.Arrays;


@Component
public class HelloCommandLineRunner implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        System.out.println("CommandLineRunner...run..."+ Arrays.asList(args));
    }
}

------------------------------------------------------------------------------------------------------------------------------------------------------------------------

重要的:

放在容器中的只要加一个注解@Component

配置的话就是要按照原来的规则:类路径下建文件夹。

          

org.springframework.context.ApplicationContextInitializer=\
com.atguigu.springboot.listener.HelloApplicationContextInitializer

org.springframework.boot.SpringApplicationRunListener=\
com.atguigu.springboot.listener.HelloSpringApplicationRunListener

原理源码:

            

启动查看结果:

 

71--------------------------------------------------------------------------------

    自定义starts场景启动器

               已经自动配置好了只要写属性就可以了。

         starter:

                 1.场景使用的依赖

                 2.如何编写自动配置

 例子:找到这个类

                    

        使用@configure注解指定是配置类 

       @ConditionalOnXXX指定条件成立再启动

       判断@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)指定自动配置类的顺序

       @Bean添加组件。

                      

             自动配置类要是能加载的话:

                   

        模式:启动器只是用来做依赖导入,专门写一个自动配置模块,启动器依赖自动配置模块,别人只需要引入启动器。

         启动器依赖自动配置 别人使用依赖启动器。

        

    

     导入模块。

     最后自定义启动器,移步我的博客:https://blog.youkuaiyun.com/qq_28764557/article/details/89005008

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值