赞
踩
一直很好奇SpringBoot这么一个大怪物,启动的时候做了哪些事情,然后看了很多老师讲的教学视频,然后自己想好好整理一下,做下学习笔记下次也方便自己阅读
1、首先从main方法开始看
- public static void main(String[] args) {
- //代码很简单SpringApplication.run();
- SpringApplication.run(ConsumerApp.class, args);
- }
- public static ConfigurableApplicationContext run(Class<?> primarySource,
- String... args) {
- //这个里面调用了run() 方法,我们转到定义
- return run(new Class<?>[] { primarySource }, args);
- }
-
-
-
- //这个run方法代码也很简单,就做了两件事情
- //1、new了一个SpringApplication() 这么一个对象
- //2、执行new出来的SpringApplication()对象的run()方法
- public static ConfigurableApplicationContext run(Class<?>[] primarySources,
- String[] args) {
- return new SpringApplication(primarySources).run(args);
- }
2、上面代码主要分以下两步
3、接下来我们来看看new SpringApplication()代码
- public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
- this.resourceLoader = resourceLoader;
- Assert.notNull(primarySources, "PrimarySources must not be null");
- //1、先把主类保存起来
- this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
-
- //2、判断运行项目的类型
- this.webApplicationType = WebApplicationType.deduceFromClasspath();
-
- //3、扫描当前路径下META-INF/spring.factories文件的
- ApplicationContextInitializer并加载
- setInitializers((Collection) getSpringFactoriesInstances(
- ApplicationContextInitializer.class));
-
- //4、同样也是扫描当前路径下META-INF/spring.factories文件下的
- ApplicationListener并加载
- setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
- this.mainApplicationClass = deduceMainApplicationClass();
- }
这里面还是要说一下ApplicationContextInitializer和ApplicationListener
我们来看一个案例,就更好理解上面这两个类
- /**
- * Context初始化后调用类
- * @author ShiMinChen
- *
- */
- public class StarterApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
-
- @Override
- public void initialize(ConfigurableApplicationContext applicationContext) {
- System.out.println("applicationContext 初始化完成 ... ");
- }
-
- }
- public class StarterApplicationListener implements ApplicationListener {
-
- @Override
- public void onApplicationEvent(ApplicationEvent event) {
- System.out.println(event.toString());
- System.out.println("ApplicationListener .... " + System.currentTimeMillis());
- }
-
- }
我们需要把这两个类集成到springboot里面去,其实操作也挺简单的
然后在META-INF/spring.factories 文件配置那两个类
- org.springframework.context.ApplicationContextInitializer=\
- org.admin.starter.test.listener.StarterApplicationContextInitializer
-
- org.springframework.context.ApplicationListener=\
- org.admin.starter.test.listener.StarterApplicationListener
4、我们代码DEBUG一下,在loadSpringFactories() 方法打一个断点
- 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);
- }
- }
总结:上面就是SpringApplication初始化的代码,new SpringApplication()没做啥事情 ,主要加载了META-INF/spring.factories 下面定义的事件监听器接口实现类
5、接下来看看run()方法,这个里面感觉有一大堆东西
- public ConfigurableApplicationContext run(String... args) {
-
- <!--1、这个是一个计时器,没什么好说的-->
- StopWatch stopWatch = new StopWatch();
- stopWatch.start();
- ConfigurableApplicationContext context = null;
- Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
-
-
- <!--2、这个也不是重点,就是设置了一些环境变量-->
- configureHeadlessProperty();
-
-
- <!--3、获取事件监听器SpringApplicationRunListener类型,并且执行starting()方法-->
- SpringApplicationRunListeners listeners = getRunListeners(args);
- listeners.starting();
-
- try {
-
-
- <!--4、把参数args封装成DefaultApplicationArguments,这个了解一下就知道-->
- ApplicationArguments applicationArguments = new DefaultApplicationArguments(
- args);
-
- <!--5、这个很重要准备环境了,并且把环境跟spring上下文绑定好,并且执行environmentPrepared()方法-->
- ConfigurableEnvironment environment = prepareEnvironment(listeners,
- applicationArguments);
-
- <!--6、判断一些环境的值,并设置一些环境的值-->
- configureIgnoreBeanInfo(environment);
-
- <!--7、打印banner-->
- Banner printedBanner = printBanner(environment);
-
-
- <!--8、创建上下文,根据项目类型创建上下文-->
- context = createApplicationContext();
-
-
- <!--9、获取异常报告事件监听-->
- exceptionReporters = getSpringFactoriesInstances(
- SpringBootExceptionReporter.class,
- new Class[] { ConfigurableApplicationContext.class }, context);
-
-
- <!--10、准备上下文,执行完成后调用contextPrepared()方法,contextLoaded()方法-->
- prepareContext(context, environment, listeners, applicationArguments,
- printedBanner);
-
-
- <!--11、这个是spring启动的代码了,这里就回去里面就回去扫描并且初始化单实列bean了-->
- //这个refreshContext()加载了bean,还启动了内置web容器,需要细细的去看看
- refreshContext(context);
-
- <!--12、啥事情都没有做-->
- afterRefresh(context, applicationArguments);
- stopWatch.stop();
- if (this.logStartupInfo) {
- new StartupInfoLogger(this.mainApplicationClass)
- .logStarted(getApplicationLog(), stopWatch);
- }
-
-
- <!--13、执行ApplicationRunListeners中的started()方法-->
- listeners.started(context);
-
- <!--执行Runner(ApplicationRunner和CommandLineRunner)-->
- callRunners(context, applicationArguments);
- }
- catch (Throwable ex) {
- handleRunFailure(context, listeners, exceptionReporters, ex);
- throw new IllegalStateException(ex);
- }
- listeners.running(context);
- return context;
- }
我们还是重点来看refreshContext(context) 这个方法,这个方法启动spring的代码加载了bean,还启动了内置web容器
- private void refreshContext(ConfigurableApplicationContext context) {
- // 转到定义看看
- refresh(context);
- if (this.registerShutdownHook) {
- try {
- context.registerShutdownHook();
- }
- catch (AccessControlException ex) {
- // Not allowed in some environments.
- }
- }
- }
- protected void refresh(ApplicationContext applicationContext) {
- Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
- //看看refresh()方法去
- ((AbstractApplicationContext) applicationContext).refresh();
- }
转到AbstractApplicationContext - >refresh()方法里面发现这是spring容器启动代码
- @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();
- }
- }
- }
spring容器启动代码就不说了,这里主要看一下onRefresh() 这个方法,转到定义发现这个方法里面啥都没有,因为这个AbstractApplicationContext是一个抽象类,所以我们要找到继承AbstractApplicationContext的子类,去看子类里面的onRefresh()
- protected void onRefresh() throws BeansException {
- //这是一个空方法,AbstractApplicationContext 这个类是一个抽象类,
- //所以我们要找到集成AbstractApplicationContext的子类,去看子类里面的onRefresh()
- // For subclasses: do nothing by default.
- }
我们这里是一个Web项目,所以我们就去看 ServletWebServerApplicationContext 这个类 ,我还是把类的关系图贴一下
我们就去看 ServletWebServerApplicationContext 这个类下面的 onRefresh() 方法
- protected void onRefresh() {
- super.onRefresh();
- try {
- //看到内置容器的影子了,进去看看
- createWebServer();
- }
- catch (Throwable ex) {
- throw new ApplicationContextException("Unable to start web server", ex);
- }
- }
- private void createWebServer() {
- WebServer webServer = this.webServer;
- ServletContext servletContext = getServletContext();
- if (webServer == null && servletContext == null) {
- //1、这个获取webServerFactory还是要进去看看
- ServletWebServerFactory factory = getWebServerFactory();
- this.webServer = factory.getWebServer(getSelfInitializer());
- }
- else if (servletContext != null) {
- try {
- getSelfInitializer().onStartup(servletContext);
- }
- catch (ServletException ex) {
- throw new ApplicationContextException("Cannot initialize servlet context",
- ex);
- }
- }
- initPropertySources();
- }
我们继续看下getWebServletFactory() 这个方法,这个里面其实就是选择出哪种类型的web容器了
- protected ServletWebServerFactory getWebServerFactory() {
- // Use bean names so that we don't consider the hierarchy
- String[] beanNames = getBeanFactory()
- .getBeanNamesForType(ServletWebServerFactory.class);
- if (beanNames.length == 0) {
- throw new ApplicationContextException(
- "Unable to start ServletWebServerApplicationContext due to missing "
- + "ServletWebServerFactory bean.");
- }
- if (beanNames.length > 1) {
- throw new ApplicationContextException(
- "Unable to start ServletWebServerApplicationContext due to multiple "
- + "ServletWebServerFactory beans : "
- + StringUtils.arrayToCommaDelimitedString(beanNames));
- }
- return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
- }
我们再回头去看factory.getWebServer(getSelfInitializer()) ,转到定义就会看到很熟悉的名字tomcat
- public WebServer getWebServer(ServletContextInitializer... initializers) {
- //tomcat这位大哥出现了
- Tomcat tomcat = new Tomcat();
- File baseDir = (this.baseDirectory != null ? this.baseDirectory
- : createTempDir("tomcat"));
- tomcat.setBaseDir(baseDir.getAbsolutePath());
- Connector connector = new Connector(this.protocol);
- tomcat.getService().addConnector(connector);
- customizeConnector(connector);
- tomcat.setConnector(connector);
- tomcat.getHost().setAutoDeploy(false);
- configureEngine(tomcat.getEngine());
- for (Connector additionalConnector : this.additionalTomcatConnectors) {
- tomcat.getService().addConnector(additionalConnector);
- }
- prepareContext(tomcat.getHost(), initializers);
- return getTomcatWebServer(tomcat);
- }
Tomcat 就在这里启动的
总结:1、run() 方法主要调用了spring容器启动方法扫描配置,加载bean到spring容器中
2、启动的内置Web容器
本人也是一个刚入到的小白,也是看了很多大神写的这类文章,按照自己思路整理一下,就是为了加深下印象,萤火之光也不敢与日月争辉,但也希望对大家有帮助
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。