(面试)SpringBoot启动原理-源码(深入)

目录

SpringBoot启动过程

运行run()方法


 

最近再疯狂复习刷八股文,今天总结一下SpringBoot

SpringBoot总的来说(个人认为),大概分为5个模块

1.Spring原理(注解)2,SpringMVC原理 3,自动配置原理 4,SpringBoot启动原理

5,第三方配置的框架

SpringBoot启动过程

首先我们SpringBoot启动主要分为两步骤:1.创建SpringApplication 2.运行SpringApplication

 看看SpringApplication里面的结构:

 解释:1.里面保存了很多信息——>比如:自定义的环境,handless(缺少键盘等外部情况),懒加载,加载器等等

2.通过断言机制Assert判断当前类是否为空

注意会将我们启动的主类保存再LinkedHashSet中,然后运行通过deduceFromClassPath()方法判断运行项目的类型

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

2.deduceFromClassPath()方法->判断运行项目的类型

 3.然后就是getSpringFactoriesInstances(ApplicationContextInitializer.class)) getSpringFactoriesInstances(ApplicationListener.class))方法

目的时去Spring.factories中寻找ApplicationContextInitializer初始化器和ApplicationListener监听器

1、ApplicationContextInitializer 这个类当springboot上下文Context初始化完成后会调用

2、ApplicationListener 当springboot启动时事件change后都会触发

我们来说说ApplicationContextInitializer的概念:

ApplicationContextInitializer也是Spring框架原有的概念,这个类的主要目的就是 在ConfigurableApplicationContext类型(或者子类型)的ApplicationContext做refresh之前,允许我们对ConfigurableApplicationContext的实例做进一步的设置或者处理。(剪短说就是在容器刷新之前调用该类的 initialize 方法。并将 ConfigurableApplicationContext 类的实例传递给该方法)

场景:

  • 通常用于需要对应用程序上下文进行编程初始化的web应用程序中。例如,根据上下文环境注册属性源或激活配置文件等。
public class DemoApplicationContextInitializer implements ApplicationContextInitializer {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        // do whatever you want with applicationContext,
        // e.g. applicationContext.registerShutdownHook();
    }
}

如果我们真的需要自定义一个ApplicationContextInitializer,那么只要像上面这样,通过SpringFactoriesLoader机制进行配置,或者通过SpringApplication.addInitializers(..)设置即可

下面我们来自定义ApplicationListener

public class StarterApplicationListener implements ApplicationListener {
 
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        System.out.println(event.toString());
        System.out.println("ApplicationListener .... " + System.currentTimeMillis());
    }
 
}

 然后在META-INF/spring.factories 文件配置那两个类

org.springframework.context.ApplicationContextInitializer=\
org.admin.starter.test.listener.StarterApplicationContextInitializer
 
org.springframework.context.ApplicationListener=\
 org.admin.starter.test.listener.StarterApplicationListener

然后我们继续debug,因为我们之前时到getSpringFactoriesInstances()方法加载ApplicationInitializer和ApplicationListener的实例(很明显通过类对象反射得到我们的类信息)

这里再提一下反射真的重要,结合JVM来思考,简而言之这里就是通过类加载器加载字节码文件产生类对象,我们类对象中含有instanceKlass的地址(Class对象也就是我们常说的mirror,暴露给我,的Java层,也就是开发者,可以通过反射得到),了解JVM就知道每一个Java类都会创建一个C++实例,也就是Klass实例(里面有instanceKlass专门描述Java的)->存储了Java类中描述的方法字段等等,我们的Class对象与方法区中的klass互指,所以可以得到方法区中的类信息

然后之前人们所说的new一个对象得到信息,那是因为对象头里面含有klassword,这个指针指向了方法区的Klass从而得到类信息

(30条消息) 【JVM】底层实现(一):浅谈 OOP-Klass 对象模型_A minor的博客-CSDN博客

再回到我们的getSpringFactoriesInstances()方法

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
    	//返回指定类型(ApplicationListener)的实例
		return getSpringFactoriesInstances(type, new Class<?>[] {});
	}
 
//真正执行的方法
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
    	//得到类加载器
		ClassLoader classLoader = getClassLoader();
		// 得到对应的bean的名字
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    	//利用反射生成实例化对象
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
    	//添加到启动的listeners中
		return instances;
	}

 上面就是SpringApplication初始化的代码,new SpringApplication()没做啥事情 ,利用SPI机制主要加载了META-INF/spring.factories 下面定义的事件监听器接口实现类

运行run()方法

 1.StopWatch:创建一个Stopwatch实例,方便记录时间以及任务名字

 2.然后start()记录启动时间

 3.让应用进入headless模式:缺少显示设备的情况

 4.获取所有 RunListener(运行监听器【为了方便所有Listener进行事件感知】,然后遍历它们执行 starting() 方法

然后我们进入getRunListener()方法看看

发现是不是调用getSpringFactoriesInstances()方法在Spring.factories寻找事件监听器

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

5.然后就是准备环境prepareEnvironment()

6.然后就是printBanner()打印我们的一个图标

7.this.createApplicationContext()根据项目类型创建我们的IOC容器,里面会注入几个核心组件类

 8.然后就是refreshContext()刷新我们的容器

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

其实也就是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();
			}
		}
	}

refresh()这里面调用了onRefresh()方法,进入一看发现调用了createWebServer()方法

protected void onRefresh() {
		super.onRefresh();
		try {
            //看到内置容器的影子了,进去看看
			createWebServer();
		}
		catch (Throwable ex) {
			throw new ApplicationContextException("Unable to start web server", ex);
		}
	}

 createWebServer()中的getWebServerFactory()方法选择出了哪种web容器也就是tomcat

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

 

所以说内置的Servlet容器就是在onRefresh()方法中启动的,至此一个servlet容器就启动ok

 9.然后就是stopWatch.stop()获取运行的时间

在这里插入图片描述

 10.listeners.started(context):监听器调用,监听容器

void started(ConfigurableApplicationContext context) {
        Iterator var2 = this.listeners.iterator();

        while(var2.hasNext()) {
            SpringApplicationRunListener listener = (SpringApplicationRunListener)var2.next();
            listener.started(context);
        }

    }
default void started(ConfigurableApplicationContext context) {
    }
 public void started(ConfigurableApplicationContext context) {
        context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
    }

然后我们跟进一下publishEvent()方法

public interface ApplicationEventPublisher {
    default void publishEvent(ApplicationEvent event) {
        this.publishEvent((Object)event);
    }

    void publishEvent(Object var1);
}
 protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
        Assert.notNull(event, "Event must not be null");
        Object applicationEvent;
        if (event instanceof ApplicationEvent) {
            applicationEvent = (ApplicationEvent)event;
        } else {
            applicationEvent = new PayloadApplicationEvent(this, event);
            if (eventType == null) {
                eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType();
            }
        }

这里介绍一下ApplicationEventPublisher

(31条消息) ApplicationEventPublisher的使用学习_谦虚使人发胖的博客-CSDN博客_applicationeventpublisher

1.ApplicationEventPublisherAware
  ApplicationEventPublisherAware 是由 Spring 提供的用于为 Service 注入 ApplicationEventPublisher 事件发布器的接口,使用这个接口,我们自己的 Service 就拥有了发布事件的能力。
  用户注册后,不再是显示地调用其他的业务 Service,而是发布一个用户注册事件。
2.ApplicationListener
  ApplicationListener接口是由 Spring 提供的事件订阅者必须实现的接口,我们一般把该 Service 关心的事件类型作为泛型传入。处理事件,通过 event.getSource() 即可拿到事件的具体内容

3.ApplicationEventPublisher

(31条消息) 观察者模式Spring之publishEvent事件处理_懒虫虫~的博客-CSDN博客_publishevent
  ApplicationEventPublisher是ApplicationContext的父接口之一。这接口的作用是:Interface that encapsulates event publication functionality.
  功能就是发布事件,也就是把某个事件告诉的所有与这个事件相关的监听
在这里插入图片描述

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
springboot uview-ui商城源码是一个使用了Spring Boot和uView-UI开发的电商平台的源代码。它提供了一个完整的电商平台的基础功能和界面设计,方便开发者进行二次开发和定制。 该源码采用了Spring Boot框架作为后端的开发框架,Spring Boot是一个快速开发Java应用程序的框架,它可以帮助开发者快速搭建基于Java的Web应用程序。而uView-UI是一个基于Vue.js的UI组件库,它提供了丰富的界面组件和样式,帮助开发者快速构建漂亮的前端界面。 该商城源码包含了用户管理、商品管理、订单管理等基本功能模块。用户管理模块可以实现用户注册、登录、个人信息修改等操作;商品管理模块可以实现商品的添加、编辑、删除等操作;订单管理模块可以实现订单的查询、支付、取消等操作。 此外,该商城源码还包含了购物车、搜索功能、商品详情页等重要功能。购物车功能可以实现商品的添加、删除、数量修改等操作;搜索功能可以实现关键词的检索,并显示与关键词相关的商品列表;商品详情页可以显示商品的详细信息,包括图片、价格、库存等。 开发者可以根据自己的需求进行二次开发和定制,例如可以添加更多的功能模块、修改样式和布局、集成支付接口等。同时,由于该源码使用了Spring Boot和uView-UI这两个流行的框架,开发者可以借助它们的丰富生态系统和社区支持,更高效地开发和维护电商平台。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Fairy要carry

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值