/** * springboot应用的启动入口 */ @RestController @SpringBootApplication public class SampleApplication { @RequestMapping("/") public String sayhello(){ return "hello world"; } public static void main(String[] args) { // TODO Auto-generated method stub SpringApplication.run(SampleApplication.class, args); } }
Spring Boot程序的启动入口就一行代码,SpringApplication.run(SampleApplication.class,args)java
SpringApplication的静态run方法内部实际上是new了一个SpringApplication的实例,构造函数保存了SourceClass。在SpringApplication实例初始化的时候,它作了几件事情:react
推断应用入口类web
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); this.webApplicationType = deduceWebApplicationType(); setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); }
下面具体从源码来看看每一步是怎么作的spring
private WebApplicationType deduceWebApplicationType() { if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null) && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) { return WebApplicationType.REACTIVE; } for (String className : WEB_ENVIRONMENT_CLASSES) { if (!ClassUtils.isPresent(className, null)) { return WebApplicationType.NONE; } } return WebApplicationType.SERVLET; }private static final String REACTIVE_WEB_ENVIRONMENT_CLASS = "org.springframework."
//相关常量
private static final String MVC_WEB_ENVIRONMENT_CLASS = "org.springframework."
+ "web.servlet.DispatcherServlet";springboot
private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };app
可能会出现三种结果:框架
setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class));
这里出现了一个新的概念 - 初始化器。函数
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) { return getSpringFactoriesInstances(type, new Class<?>[] {}); } // 这里的入参type就是ApplicationContextInitializer.class private <T> Collection<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<>( SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }
这里面首先会根据入参type读取全部的names(是一个String集合),而后根据这个集合来完成对应的实例化操做。spring-boot
// 这里的入参type就是ApplicationContextInitializer.class
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); } private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap<String, String> result = cache.get(classLoader); if (result != null) return result; try { 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()) { List<String> factoryClassNames = Arrays.asList( StringUtils.commaDelimitedListToStringArray((String) entry.getValue())); result.addAll((String) entry.getKey(), factoryClassNames); } } cache.put(classLoader, result); return result; } catch (IOException ex) { throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } }
/**
* The location to look for factories.
* <p>Can be present in multiple JAR files.
*/
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";ui
这个方法会尝试从类路径的META-INF/spring.factories处读取相应配置文件,而后进行遍历,读取配置文件中Key为:org.springframework.context.ApplicationContextInitializer的value。以spring-boot-autoconfigure这个包为例,它的META-INF/spring.factories部分定义以下所示:
# Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\ org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer
所以这两个类名会被读取出来,而后放入到集合中,准备开始下面的实例化操做:
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names) { List<T> instances = new ArrayList<>(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; }
类加载,确认被加载的类确实是org.springframework.context.ApplicationContextInitializer的子类,而后就是获得构造器进行初始化,最后放入到实例列表中。
所以,所谓的初始化器就是org.springframework.context.ApplicationContextInitializer的实现类,这个接口是这样定义的:
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> { /** * Initialize the given application context. * @param applicationContext the application to configure */ void initialize(C applicationContext); }
根据类文档,这个接口的主要功能是:
在Spring上下文被刷新以前进行初始化的操做。典型地好比在Web应用中,注册Property Sources或者是激活Profiles。Property Sources比较好理解,就是配置文件。Profiles是Spring为了在不一样环境下(如DEV,TEST,PRODUCTION等),加载不一样的配置项而抽象出来的一个实体。
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 这里的入参type是:org.springframework.context.ApplicationListener.class
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {}); } private <T> Collection<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<>( SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }
能够发现,这个加载相应的类名,而后完成实例化的过程和上面在设置初始化器时一模一样,一样,仍是以spring-boot-autoconfigure这个包中的spring.factories为例,看看相应的Key-Value:
# Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.boot.autoconfigure.BackgroundPreinitializer
至于ApplicationListener接口,它是Spring框架中一个至关基础的接口了,代码以下:
@FunctionalInterface public interface ApplicationListener<E extends ApplicationEvent> extends EventListener { /** * Handle an application event. * @param event the event to respond to */ void onApplicationEvent(E event); }
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的栈帧来获得入口类的名字。
至此,对于SpringApplication实例的初始化过程就结束了。
SpringApplication实例的run方法内部都作了哪些呢:
1.建立SpringApplication本身的SpringApplicationRunListener。遍历执行全部经过SpringFactoriesLoader能够查找到并加载的SpringApplicationRunListener。调用.它们的started()方法,告诉这些SpringApplicationRunListener,“嘿,SpringBoot应用要开始执行咯!”。
2.建立并配置当前Spring Boot应用将要使用的Environment(包括配置要使用的PropertySource以及Profile)。
3. 遍历调用全部SpringApplicationRunListener的environmentPrepared()的方法,告诉他们:“当前SpringBoot应用使用的Environment准备好了咯!”。
4. 若是SpringApplication的showBanner属性被设置为true,则打印banner。
5.建立ApplicationContext,由于如今应用类型是Servlet,建立的是AnnotationConfigServletWebServerApplicationContext。
6.对ApplicationContext进行配置,将以前准备好的Environment设置给建立好的ApplicationContext使用。还有配置BeanNameGenerator、ResourceLoader等。
7.ApplicationContext建立好以后,而后遍历ApplicationContextInitializer的initialize(applicationContext)方法来对已经建立好的ApplicationContext进行进一步的处理。这里的ApplicationContextInitializer就是建立SpringApplication实例时设置的初始化器。
8. 遍历调用全部SpringApplicationRunListener的contextPrepared()方法。
9. 最核心的一步,将以前经过@EnableAutoConfiguration获取的全部配置以及其余形式的IoC容器配置加载到已经准备完毕的ApplicationContext。
10.遍历调用全部SpringApplicationRunListener的contextLoaded()方法。
11.启动ApplicationContext,AnnotationConfigServletWebServerApplicationContext继承了SpringFrameWork自己提供的GenericWebApplicationContext提供的功能并进行了扩展,以支持配置并启动Embed Tomcat。
(1)、对BeanFactory进行一些初始化配置。
(2)、执行BeanFactoryPostProcessor,其中包括对BeanDefinition的进一步处理。最重要的是ConfigurationClassPostProcessor,用来解析处理全部@Configuration标签类,并将Bean定义注册到BeanFactory中。由于@SpringBootApplication中包含了@EnableAutoConfiguration的meta-annotation,会进行自动配置处理,基本原理是判断工程依赖了哪些第三方组件并对其进行自动化配置,这样处理完@Configuration标签后,BeanFactory中就已经有大量的Bean定义了。
(3)、注册BeanPostProcessor,这些Processor会在首次getBean时执行。主要功能包括进行Autowire、Required等标签的处理,完成自动绑定等功能。也有特殊的关于WebServleterFactory的后续处理。
(4)、在ApplicationContext的onRefresh方法中会对Web容器(Tomcat)进行配置,包括注册Servlet、Filter、Listener等。
(5)、在ApplicationContext的finishRefresh方法中启动Web容器(Tomcat),完成应用的启动。