1.入口类
/** * springboot应用的启动入口*/@RestController@SpringBootApplicationpublic 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)
2.执行过程
2.1SpringApplication的实例化
SpringApplication的静态run方法内部其实是new了一个SpringApplication的实例,构造函数保存了SourceClass。在SpringApplication实例初始化的时候,它做了几件事情:
- 推断应用类型是standard还是Web。根据classpath里面是否存在某个特征类(org.springframework.web.context.ConfigurableWebApplicationContext)来决定是否应该创建一个为Web应用使用的ApplicationContext类型。
- 使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationContextInitializer。SpringFactoriesLoader从类路径下各个jar的配置文件META-INF/spring.factories加载配置
- 使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationListener。
-
推断应用入口类
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(); }
下面具体从源码来看看每一步是怎么做的
2.1 推断应用类型是Standard还是Web
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." + "web.reactive.DispatcherHandler";
private static final String MVC_WEB_ENVIRONMENT_CLASS = "org.springframework."
+ "web.servlet.DispatcherServlet";
private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };可能会出现三种结果:
- WebApplicationType.REACTIVE - 当类路径中存在REACTIVE_WEB_ENVIRONMENT_CLASS并且不存在MVC_WEB_ENVIRONMENT_CLASS时
- WebApplicationType.NONE - 也就是非Web型应用(Standard型),此时类路径中不包含WEB_ENVIRONMENT_CLASSES中定义的任何一个类时
- WebApplicationType.SERVLET - 类路径中包含了WEB_ENVIRONMENT_CLASSES中定义的所有类型时
2.2 设置初始化器ApplicationContextInitializer
setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class));
这里出现了一个新的概念 - 初始化器。
privateCollection getSpringFactoriesInstances(Class type) { return getSpringFactoriesInstances(type, new Class [] {}); }// 这里的入参type就是ApplicationContextInitializer.classprivate Collection getSpringFactoriesInstances(Class type, Class [] parameterTypes, Object... args) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // Use names and ensure unique to protect against duplicates Set names = new LinkedHashSet<>( SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }
这里面首先会根据入参type读取所有的names(是一个String集合),然后根据这个集合来完成对应的实例化操作。
// 这里的入参type就是ApplicationContextInitializer.class
public static ListloadFactoryNames(Class factoryClass, @Nullable ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); } private static Map > loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap result = cache.get(classLoader); if (result != null) return result; try { Enumeration 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 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";这个方法会尝试从类路径的META-INF/spring.factories处读取相应配置文件,然后进行遍历,读取配置文件中Key为:org.springframework.context.ApplicationContextInitializer的value。以spring-boot-autoconfigure这个包为例,它的META-INF/spring.factories部分定义如下所示:
# Initializersorg.springframework.context.ApplicationContextInitializer=\org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\ org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer
因此这两个类名会被读取出来,然后放入到集合中,准备开始下面的实例化操作:
privateList createSpringFactoriesInstances(Class type, Class [] parameterTypes, ClassLoader classLoader, Object[] args, Set names) { List 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{ /** * 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等),加载不同的配置项而抽象出来的一个实体。
2.3 设置监听器ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 这里的入参type是:org.springframework.context.ApplicationListener.class privateCollection getSpringFactoriesInstances(Class type) { return getSpringFactoriesInstances(type, new Class [] {}); } private Collection getSpringFactoriesInstances(Class type, Class [] parameterTypes, Object... args) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // Use names and ensure unique to protect against duplicates Set names = new LinkedHashSet<>( SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }
可以发现,这个加载相应的类名,然后完成实例化的过程和上面在设置初始化器时如出一辙,同样,还是以spring-boot-autoconfigure这个包中的spring.factories为例,看看相应的Key-Value:
# Application Listenersorg.springframework.context.ApplicationListener=\org.springframework.boot.autoconfigure.BackgroundPreinitializer
至于ApplicationListener接口,它是Spring框架中一个相当基础的接口了,代码如下:
@FunctionalInterfacepublic interface ApplicationListenerextends EventListener { /** * Handle an application event. * @param event the event to respond to */ void onApplicationEvent(E event);}
2.4 推断应用入口类
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实例的初始化过程就结束了。
2.2 执行SpringApplication实例的run方法
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),完成应用的启动。
3. 总结